OSDN Git Service

Cookie関係情報をコンフィグから取得しプロファイルを作成する処理の実装
[coroid/inqubus.git] / frontend / src / saccubus / worker / impl / download / Download.java
1 package saccubus.worker.impl.download;
2
3 import static saccubus.worker.impl.download.DownloadStatus.*;
4
5 import java.io.File;
6 import java.io.IOException;
7 import java.util.Date;
8 import java.util.EnumSet;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import nicobrowser.DownloadCommentType;
12 import nicobrowser.GetFlvResult;
13 import nicobrowser.NamePattern;
14 import nicobrowser.NicoHttpClient;
15 import nicobrowser.ProgressListener;
16 import nicobrowser.WayBackInfo;
17 import nicobrowser.entity.NicoContent.Status;
18 import saccubus.worker.Worker;
19 import saccubus.worker.WorkerListener;
20 import saccubus.worker.profile.CommentProfile;
21 import saccubus.worker.profile.GeneralProfile;
22 import saccubus.worker.profile.DownloadProfile;
23 import saccubus.worker.profile.ProxyProfile;
24
25 /**
26  * <p>タイトル: さきゅばす</p>
27  *
28  * <p>説明: ニコニコ動画の動画をコメントつきで保存</p>
29  *
30  * <p>著作権: Copyright (c) 2007 PSI</p>
31  *
32  * <p>会社名: </p>
33  *
34  * @author 未入力
35  * @version 1.0
36  */
37 public class Download extends Worker<DownloadResult, DownloadProgress> {
38
39     private static final Logger logger = LoggerFactory.getLogger(Download.class);
40     private static final Object timeLockObj = new Object();
41     private static volatile long lastStartTime = 0L;
42     private final DownloadProfile profile;
43     private final String videoId;
44     private final int waitDownload;
45
46     public Download(DownloadProfile profile, String videoId) {
47         this(profile, videoId, null, 30);
48     }
49
50     /**
51      * コンバータを構築します.
52      * @param videoId 対象となる動画のID.
53      * @param time
54      * @param profile
55      * @param listener
56      * @param flag
57      */
58     public Download(DownloadProfile profile, String videoId, WorkerListener<DownloadResult, DownloadProgress> listener,
59             int wait) {
60         super(listener);
61         this.videoId = videoId;
62         this.profile = profile;
63         this.waitDownload = wait;
64     }
65
66     @Override
67     public DownloadResult work() throws Exception {
68         waitAndGo();
69
70         publish(new DownloadProgress(PROCESS, 0.0, "ログイン中"));
71
72         NicoHttpClient client = null;
73         nicobrowser.VideoInfo vi = null;
74         NamePattern videoNamePattern = null;
75         WayBackInfo wbi = null;
76         if (needsLogin()) {
77             client = createClientAndLogin();
78             vi = client.getVideoInfo(videoId);
79
80             final String name = profile.getVideoProfile().getFileName();
81             final String replaceFrom = profile.getGeneralProfile().getReplaceFrom();
82             final String replaceTo = profile.getGeneralProfile().getReplaceTo();
83             videoNamePattern = new NamePattern(name, replaceFrom, replaceTo, vi.getTitleInWatchPage());
84
85             if (needsBackLog()) {
86                 final String key = client.getWayBackKey(vi);
87                 wbi = new WayBackInfo(key, profile.getCommentProfile().getBackLogPoint());
88             }
89         }
90
91         checkStop();
92
93         File commentFile;
94         if (profile.getCommentProfile().isDownload()) {
95             final CommentProfile prof = profile.getCommentProfile();
96             final GeneralProfile gene = profile.getGeneralProfile();
97
98             final NamePattern pattern = new NamePattern(prof.getFileName(), gene.getReplaceFrom(), gene.getReplaceTo(),
99                     vi.getTitleInWatchPage());
100             // TODO コメントファイルに{low}は使えないことをどこかに書くべきか
101             final String name = pattern.createFileName(videoId, true);
102             final File file = new File(profile.getCommentProfile().getDir(), name);
103
104             final EnumSet<DownloadCommentType> commentSet = EnumSet.of(DownloadCommentType.OWNER);
105             if (profile.getCommentProfile().isDisablePerMinComment()) {
106                 commentSet.add(DownloadCommentType.COMMENT_OLD);
107             } else {
108                 commentSet.add(DownloadCommentType.COMMENT);
109             }
110             commentFile = client.getCommentFile(vi, file.getPath(), commentSet, wbi, profile.getCommentProfile().
111                     getLengthRelatedCommentSize());
112         } else {
113             commentFile = profile.getCommentProfile().getLocalFile();
114         }
115
116         checkStop();
117
118         File videoFile;
119         GetFlvResult vf;
120         if (profile.getVideoProfile().isDownload()) {
121             vf = client.getFlvFile(vi, profile.getVideoProfile().getDir(), videoNamePattern,
122                     Status.GET_INFO, true, new ProgressListener() {
123
124                 @Override
125                 public void progress(long fileSize, long downloadSize) {
126                     final double vol = (double) downloadSize / (double) fileSize * 100.0;
127                     publish(new DownloadProgress(PROCESS, vol, String.format("ダウンロード%.2f%%", vol)));
128                 }
129             });
130
131             videoFile = vf.getFile();
132         } else {
133             videoFile = profile.getVideoProfile().getLocalFile();
134         }
135
136         return new DownloadResult(true, videoFile, commentFile);
137     }
138
139     /** @return 何かダウンロードするものがあればtrue. */
140     private static boolean needsDownload(DownloadProfile profile) {
141         return (profile.getVideoProfile().isDownload() || profile.getCommentProfile().isDownload());
142     }
143
144     /**
145      * HttpClientを生成し, ニコニコ動画サーバへログインします.
146      * @return 生成したHttpClientインスタンス.
147      * @throws IOException ログイン失敗.
148      * @throws InterruptedException ログイン失敗.
149      */
150     private NicoHttpClient createClientAndLogin() throws IOException, InterruptedException {
151         final NicoHttpClient client = createClient(profile.getProxyProfile());
152         if (profile.getLoginProfile().needsLogin()) {
153
154             final boolean hasLogin = client.login(profile.getLoginProfile().getMail(), profile.getLoginProfile().
155                     getPassword());
156             if (!hasLogin) {
157                 throw new IOException("login fail");
158             }
159         } else {
160             client.addCookie(profile.getLoginProfile().getCookies());
161         }
162
163         return client;
164     }
165
166     private NicoHttpClient createClient(ProxyProfile proxy) {
167         if (proxy.use()) {
168             return new NicoHttpClient(proxy.getHost(), proxy.getPort());
169         } else {
170             return new NicoHttpClient();
171         }
172     }
173
174     /** @return ログインする必要があればtrue. */
175     private boolean needsLogin() {
176         return profile.getVideoProfile().isDownload() || profile.getCommentProfile().isDownload();
177     }
178
179     /** @return 過去ログ取得の必要があればtrue. */
180     private boolean needsBackLog() {
181         return profile.getCommentProfile().getBackLogPoint() >= 0L;
182     }
183
184     private void checkStop() throws InterruptedException {
185         if (Thread.interrupted()) {
186             throw new InterruptedException("中止要求を受け付けました");
187         }
188     }
189
190     /**
191      * ニコニコ動画サービスに連続アクセスするとはじかれるため, 前回のアクセス開始時刻から一定期間は
192      * 次のアクセスに行かないよう待機する必要があります.
193      * @throws InterruptedException 中断されました.
194      */
195     private void waitAndGo() throws InterruptedException {
196         synchronized (timeLockObj) {
197             final long now = new Date().getTime();
198             final long needSleep = (waitDownload * 1000L) - (now - lastStartTime);
199             if (needSleep > 0L) {
200                 publish(new DownloadProgress(DownloadStatus.PROCESS, -1.0, "過剰アクセス抑制待機 " + needSleep / 1000));
201                 Thread.sleep(needSleep);
202             }
203             lastStartTime = new Date().getTime();
204         }
205     }
206 }