1 package saccubus.worker.impl.download;
3 import static saccubus.worker.impl.download.DownloadStatus.*;
6 import java.io.IOException;
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;
28 * <p>説明: ニコニコ動画の動画をコメントつきで保存</p>
30 * <p>著作権: Copyright (c) 2007 PSI</p>
37 public class Download extends Worker<DownloadResult, DownloadProgress> {
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;
46 public Download(DownloadProfile profile, String videoId) {
47 this(profile, videoId, null, 30);
52 * @param videoId 対象となる動画のID.
58 public Download(DownloadProfile profile, String videoId, WorkerListener<DownloadResult, DownloadProgress> listener,
61 this.videoId = videoId;
62 this.profile = profile;
63 this.waitDownload = wait;
67 public DownloadResult work() throws Exception {
70 publish(new DownloadProgress(PROCESS, 0.0, "ログイン中"));
72 NicoHttpClient client = null;
73 nicobrowser.VideoInfo vi = null;
74 NamePattern videoNamePattern = null;
75 WayBackInfo wbi = null;
77 client = createClientAndLogin();
78 vi = client.getVideoInfo(videoId);
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());
86 final String key = client.getWayBackKey(vi);
87 wbi = new WayBackInfo(key, profile.getCommentProfile().getBackLogPoint());
94 if (profile.getCommentProfile().isDownload()) {
95 final CommentProfile prof = profile.getCommentProfile();
96 final GeneralProfile gene = profile.getGeneralProfile();
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);
104 final EnumSet<DownloadCommentType> commentSet = EnumSet.of(DownloadCommentType.OWNER);
105 if(profile.getCommentProfile().isDisablePerMinComment()) {
106 commentSet.add(DownloadCommentType.COMMENT_OLD);
108 commentSet.add(DownloadCommentType.COMMENT);
110 commentFile = client.getCommentFile(vi, file.getPath(), commentSet, wbi, profile.getCommentProfile().
111 getLengthRelatedCommentSize());
113 commentFile = profile.getCommentProfile().getLocalFile();
120 if (profile.getVideoProfile().isDownload()) {
121 vf = client.getFlvFile(vi, profile.getVideoProfile().getDir(), videoNamePattern,
122 Status.GET_INFO, true, new ProgressListener() {
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)));
131 videoFile = vf.getFile();
133 videoFile = profile.getVideoProfile().getLocalFile();
136 return new DownloadResult(true, videoFile, commentFile);
139 /** @return 何かダウンロードするものがあればtrue. */
140 private static boolean needsDownload(DownloadProfile profile) {
141 return (profile.getVideoProfile().isDownload() || profile.getCommentProfile().isDownload());
145 * HttpClientを生成し, ニコニコ動画サーバへログインします.
146 * @return 生成したHttpClientインスタンス.
147 * @throws IOException ログイン失敗.
148 * @throws InterruptedException ログイン失敗.
150 private NicoHttpClient createClientAndLogin() throws IOException, InterruptedException {
151 final NicoHttpClient client = createClient(profile.getProxyProfile());
153 final boolean hasLogin = client.login(profile.getLoginProfile().getMail(), profile.getLoginProfile().getPassword());
155 throw new IOException("login fail");
161 private NicoHttpClient createClient(ProxyProfile proxy) {
163 return new NicoHttpClient(proxy.getHost(), proxy.getPort());
165 return new NicoHttpClient();
169 /** @return ログインする必要があればtrue. */
170 private boolean needsLogin() {
171 return profile.getVideoProfile().isDownload() || profile.getCommentProfile().isDownload();
174 /** @return 過去ログ取得の必要があればtrue. */
175 private boolean needsBackLog() {
176 return profile.getCommentProfile().getBackLogPoint() >= 0L;
179 private void checkStop() throws InterruptedException {
180 if (Thread.interrupted()) {
181 throw new InterruptedException("中止要求を受け付けました");
186 * ニコニコ動画サービスに連続アクセスするとはじかれるため, 前回のアクセス開始時刻から一定期間は
187 * 次のアクセスに行かないよう待機する必要があります.
188 * @throws InterruptedException 中断されました.
190 private void waitAndGo() throws InterruptedException {
191 synchronized (timeLockObj) {
192 final long now = new Date().getTime();
193 final long needSleep = (waitDownload * 1000L) - (now - lastStartTime);
194 if (needSleep > 0L) {
195 publish(new DownloadProgress(DownloadStatus.PROCESS, -1.0, "過剰アクセス抑制待機 " + needSleep / 1000));
196 Thread.sleep(needSleep);
198 lastStartTime = new Date().getTime();