OSDN Git Service

30ebd897e4cba162bcb83efc38cf016e5102846a
[coroid/inqubus.git] / frontend / src / saccubus / worker / Download.java
1 package saccubus.worker;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.URISyntaxException;
6 import java.util.concurrent.Callable;
7 import java.util.logging.Logger;
8 import javax.swing.SwingWorker;
9 import nicobrowser.GetFlvResult;
10 import nicobrowser.NamePattern;
11 import nicobrowser.NicoHttpClient;
12 import nicobrowser.ProgressListener;
13 import nicobrowser.WayBackInfo;
14 import nicobrowser.entity.NicoContent.Status;
15 import org.apache.http.HttpException;
16 import saccubus.worker.profile.CommentProfile;
17 import saccubus.worker.profile.GeneralProfile;
18 import saccubus.worker.profile.OutputProfile;
19 import saccubus.worker.profile.Profile;
20 import saccubus.worker.profile.ProxyProfile;
21
22 /**
23  * <p>タイトル: さきゅばす</p>
24  *
25  * <p>説明: ニコニコ動画の動画をコメントつきで保存</p>
26  *
27  * <p>著作権: Copyright (c) 2007 PSI</p>
28  *
29  * <p>会社名: </p>
30  *
31  * @author 未入力
32  * @version 1.0
33  */
34 public class Download extends SwingWorker<DownloadResult, DownloadProgress> implements Callable<Boolean> {
35
36     private static final Logger logger = Logger.getLogger(Download.class.getName());
37     private final Profile profile;
38     private final String videoId;
39
40     /**
41      * コンバータを構築します.
42      * @param videoId 対象となる動画のID.
43      * @param time
44      * @param profile
45      * @param listener
46      * @param flag
47      */
48     public Download(Profile profile, String videoId) {
49         this.videoId = videoId;
50         this.profile = profile;
51     }
52
53     @Override
54     public Boolean call() throws Exception {
55         try {
56             final DownloadResult result = doInBackground();
57             return Boolean.valueOf(result.getResultValue());
58         } finally {
59             // TODO 何か処理が必要?
60 //            getStopFlag().finished();
61         }
62     }
63
64 //    // TODO Runnableを実装しなくなったので削除する
65 //    public void run() {
66 //        try {
67 //            call();
68 //        } catch (Exception ex) {
69 //            String text = (ex.getMessage() != null) ? ex.getMessage() : "予期しないエラー発生のため中断しました。";
70 //            sendText(text);
71 //            logger.log(Level.SEVERE, null, ex);
72 //        }
73 //    }
74     @Override
75     protected DownloadResult doInBackground() throws Exception {
76         if (!shouldRun(profile)) {
77             publish(new DownloadProgress("何もすることがありません"));
78             return new DownloadResult(true);
79         }
80
81         publish(new DownloadProgress("ログイン中"));
82
83         NicoHttpClient client = null;
84         nicobrowser.VideoInfo vi = null;
85         NamePattern videoNamePattern = null;
86         WayBackInfo wbi = null;
87         if (needsLogin()) {
88             client = createClientAndLogin();
89             vi = client.getVideoInfo(videoId);
90
91             final String name = profile.getVideoSetting().getFileName();
92             final String replaceFrom = profile.getGeneralSetting().getReplaceFrom();
93             final String replaceTo = profile.getGeneralSetting().getReplaceTo();
94             videoNamePattern = new NamePattern(name, replaceFrom, replaceTo, vi.getTitleInWatchPage());
95
96             if (needsBackLog()) {
97                 final String key = client.getWayBackKey(vi);
98                 wbi = new WayBackInfo(key, profile.getCommentSetting().getBackLogPoint());
99             }
100         }
101
102         checkStop();
103
104         File commentFile;
105         if (profile.getCommentSetting().isDownload()) {
106             final CommentProfile prof = profile.getCommentSetting();
107             final GeneralProfile gene = profile.getGeneralSetting();
108
109             final NamePattern pattern = new NamePattern(prof.getFileName(), gene.getReplaceFrom(), gene.getReplaceTo(),
110                     vi.getTitleInWatchPage());
111             // TODO コメントファイルに{low}は使えないことをどこかに書くべきか
112             final String name = pattern.createFileName(videoId, true);
113             final File file = new File(profile.getCommentSetting().getDir(), name);
114
115             commentFile = client.getCommentFile(vi, file.getPath(), wbi, profile.getCommentSetting().
116                     getLengthRelatedCommentSize(),
117                     profile.getCommentSetting().isDisablePerMinComment());
118         } else {
119             commentFile = profile.getCommentSetting().getLocalFile();
120         }
121
122         checkStop();
123
124         File videoFile;
125         GetFlvResult vf = null;
126         if (profile.getVideoSetting().isDownload()) {
127             vf = client.getFlvFile(vi, profile.getVideoSetting().getDir(), videoNamePattern,
128                     Status.GET_INFO, true, new ProgressListener() {
129
130                 @Override
131                 public void progress(long fileSize, long downloadSize) {
132                     final double vol = (double) downloadSize / (double) fileSize * 100.0;
133                     publish(new DownloadProgress(String.format("ダウンロード%.2f%%", vol)));
134                 }
135             });
136
137             videoFile = vf.getFile();
138         } else {
139             videoFile = profile.getVideoSetting().getLocalFile();
140         }
141
142         if (!profile.getOutputFileSetting().isConvert()) {
143             publish(new DownloadProgress("動画・コメントを保存し、変換は行いませんでした。"));
144             return new DownloadResult(true);
145         }
146
147         if (!videoFile.isFile()) {
148             throw new IOException("入力動画ファイルが存在しません:" + videoFile.getPath());
149         }
150
151         if (profile.getOutputFileSetting().isAddComment()) {
152             if (!commentFile.isFile()) {
153                 throw new IOException("入力コメントファイルが存在しません:" + commentFile.getPath());
154             }
155         } else {
156             commentFile = null;
157         }
158
159         /*ビデオ名の確定*/
160         final boolean isNotLow = (vf == null) ? true : (vf.getStatus() != Status.GET_LOW);
161         File convertedVideoFile = getOutputFileName(vi.getTitleInWatchPage(), isNotLow);
162
163         return new DownloadResult(true);
164
165         // TODO FFMPEG 実行開始は別タスクとして実装する.
166 //        boolean res = new FfmpegCommand(getListener(), getStopFlag(), commentFile, videoFile,
167 //                convertedVideoFile, profile.getFfmpeg(), profile.getGeneralSetting()).execute();
168 //        return res;
169     }
170
171     /** @return 何か実行すべき処理があればtrue. */
172     private static boolean shouldRun(Profile profile) {
173         return profile.getOutputFileSetting().isConvert() || needsDownload(profile);
174     }
175
176     /** @return 何かダウンロードするものがあればtrue. */
177     private static boolean needsDownload(Profile profile) {
178         return (profile.getVideoSetting().isDownload() || profile.getCommentSetting().isDownload());
179     }
180
181     // TODO どこかに処理を移す必要がある.
182     /**
183      * (ネットワーク設定以外の)設定を検証する.
184      * @throws IllegalArgumentException 設定に不備がある場合.
185      */
186 //    private void validSetting() {
187 //        if (profile.getOutputFileSetting().isConvert()) {
188 //            File a = profile.getFfmpeg().getFfmpeg();
189 //            if (!a.canRead()) {
190 //                throw new IllegalArgumentException("FFmpegが見つかりません。");
191 //            }
192 //            if (profile.getFfmpeg().getVhook().getPath().indexOf(' ') >= 0) {
193 //                throw new IllegalArgumentException("すいません。現在vhookライブラリには半角空白は使えません。");
194 //            }
195 //            a = profile.getFfmpeg().getVhook();
196 //            if (!a.canRead()) {
197 //                throw new IllegalArgumentException("Vhookライブラリが見つかりません。");
198 //            }
199 //            a = profile.getFfmpeg().getFont();
200 //            if (!a.canRead()) {
201 //                throw new IllegalArgumentException("フォントが見つかりません。");
202 //            }
203 //        }
204 //    }
205
206     /**
207      * HttpClientを生成し, ニコニコ動画サーバへログインします.
208      * @return 生成したHttpClientインスタンス.
209      * @throws IOException ログイン失敗.
210      * @throws InterruptedException ログイン失敗.
211      */
212     // TODO HttpException を投げるのをやめたい. コンパイル時にHttpComponentが必要になるので.
213     private NicoHttpClient createClientAndLogin() throws IOException, InterruptedException, HttpException {
214         final NicoHttpClient client = createClient(profile.getProxySetting());
215         final boolean hasLogin;
216         try {
217             hasLogin = client.login(profile.getLoginInfo().getMail(), profile.getLoginInfo().getPassword());
218             if (!hasLogin) {
219                 throw new IOException("login fail");
220             }
221         } catch (URISyntaxException ex) {
222             throw new IOException("login fail", ex);
223         }
224         return client;
225     }
226
227     private NicoHttpClient createClient(ProxyProfile proxy) {
228         if (proxy.use()) {
229             return new NicoHttpClient(proxy.getHost(), proxy.getPort());
230         } else {
231             return new NicoHttpClient();
232         }
233     }
234
235     /** @return ログインする必要があればtrue. */
236     private boolean needsLogin() {
237         return profile.getVideoSetting().isDownload() || profile.getCommentSetting().isDownload();
238     }
239
240     /** @return 過去ログ取得の必要があればtrue. */
241     private boolean needsBackLog() {
242         return profile.getCommentSetting().getBackLogPoint() >= 0L;
243     }
244
245     /**
246      * 出力ファイルの保存先を確定します.
247      * @param title 動画タイトル.
248      * @param isNotLow 入力動画ファイルがlowでないかどうか.
249      * @return 出力ファイルの保存先.
250      */
251     private File getOutputFileName(String title, boolean isNotLow) {
252         final OutputProfile prof = profile.getOutputFileSetting();
253         final GeneralProfile gene = profile.getGeneralSetting();
254         final File dir = prof.getDir();
255         final String replaceFrom = gene.getReplaceFrom();
256         final String replaceTo = gene.getReplaceTo();
257         final NamePattern pattern = new NamePattern(prof.getFileName(), replaceFrom, replaceTo, title);
258         final String name = pattern.createFileName(videoId, isNotLow);
259         return new File(dir, name);
260     }
261
262     private void checkStop() throws InterruptedException {
263         if (Thread.interrupted()) {
264             throw new InterruptedException("中止要求を受け付けました");
265         }
266     }
267 }