OSDN Git Service

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