OSDN Git Service

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