OSDN Git Service

2d950e156c2807be1d9c6f0c1ad18fddaf2e58ad
[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 saccubus.ConvertStopFlag;
14 import saccubus.net.TextProgressListener;
15 import yukihane.saccubus.converter.profile.CommentProfile;
16 import yukihane.saccubus.converter.profile.FfmpegOption;
17 import yukihane.saccubus.converter.profile.GeneralProfile;
18 import yukihane.saccubus.converter.profile.OutputProfile;
19 import yukihane.saccubus.converter.profile.Profile;
20 import yukihane.saccubus.converter.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 Converter extends AbstractCommand implements Callable<Boolean> {
35
36     private final Profile profile;
37     private final String movieId;
38
39     /**
40      * コンバータを構築します.
41      * @param id 対象となる動画のID.
42      * @param time
43      * @param profile
44      * @param listener
45      * @param flag
46      */
47     public Converter(String id, Profile profile,
48             TextProgressListener listener, ConvertStopFlag flag) {
49         super(listener, flag);
50         this.movieId = id;
51         this.profile = profile;
52     }
53
54     @Override
55     public Boolean call() throws Exception {
56         boolean result = false;
57         try {
58             result = runConvert();
59         } finally {
60             getStopFlag().finished();
61         }
62         return Boolean.valueOf(result);
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             ex.printStackTrace();
73         }
74     }
75
76     private boolean runConvert() throws IOException, InterruptedException, Exception {
77         if (!shouldRun(profile)) {
78             sendText("何もすることがありません");
79             return true;
80         }
81
82         validSetting();
83         final FfmpegOption ov = profile.getFfmpeg().getFfmpegOption();
84
85         // TODO ログインしないで良い場合もある.
86         sendText("ログイン中");
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         stopFlagReturn();
110
111
112         File commentFile;
113         if (profile.getCommentSetting().isDownload()) {
114             final CommentProfile prof = profile.getCommentSetting();
115             final GeneralProfile gene = profile.getGeneralSetting();
116
117             final NamePattern pattern = new NamePattern(prof.getFileName(), gene.getReplaceFrom(), gene.getReplaceTo(),
118                     vi.getTitleInWatchPage());
119             // TODO コメントファイルに{low}は使えないことをどこかに書くべきか
120             final String name = pattern.createFileName(movieId, true);
121             final File file = new File(profile.getCommentSetting().getDir(), name);
122
123             commentFile = client.getCommentFile(vi, file.getPath(), wbi, profile.getCommentSetting().getBackComment(),
124                     profile.getCommentSetting().isReduceComment());
125         } else {
126             commentFile = profile.getCommentSetting().getLocalFile();
127         }
128
129         stopFlagReturn();
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             sendText("動画・コメントを保存し、変換は行いませんでした。");
155             return 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         boolean res = new FfmpegCommand(getListener(), getStopFlag(), commentFile, videoFile,
175                 convertedVideoFile, profile.getFfmpeg(), profile.getGeneralSetting()).execute();
176         return res;
177     }
178
179     public boolean isConverted() {
180         return getStopFlag().isFinished();
181     }
182
183     @Override
184     public ConvertStopFlag getStopFlag() {
185         return super.getStopFlag();
186     }
187
188     /** @return 何か実行すべき処理があればtrue. */
189     private static boolean shouldRun(Profile profile) {
190         return profile.getOutputFileSetting().isConvert() || needsDownload(profile);
191     }
192
193     /** @return 何かダウンロードするものがあればtrue. */
194     private static boolean needsDownload(Profile profile) {
195         return (profile.getVideoSetting().isDownload() || profile.getCommentSetting().isDownload());
196     }
197
198     /**
199      * (ネットワーク設定以外の)設定を検証する.
200      * @throws IllegalArgumentException 設定に不備がある場合.
201      */
202     private void validSetting() {
203         if (profile.getOutputFileSetting().isConvert()) {
204             File a = profile.getFfmpeg().getFfmpeg();
205             if (!a.canRead()) {
206                 throw new IllegalArgumentException("FFmpegが見つかりません。");
207             }
208             if (profile.getFfmpeg().getVhook().getPath().indexOf(' ') >= 0) {
209                 throw new IllegalArgumentException("すいません。現在vhookライブラリには半角空白は使えません。");
210             }
211             a = profile.getFfmpeg().getVhook();
212             if (!a.canRead()) {
213                 throw new IllegalArgumentException("Vhookライブラリが見つかりません。");
214             }
215             a = profile.getFfmpeg().getFont();
216             if (!a.canRead()) {
217                 throw new IllegalArgumentException("フォントが見つかりません。");
218             }
219         }
220     }
221
222     /**
223      * HttpClientを生成し, ニコニコ動画サーバへログインします.
224      * @return 生成したHttpClientインスタンス.
225      * @throws IOException ログイン失敗.
226      * @throws InterruptedException ログイン失敗.
227      */
228     private NicoHttpClient createClientAndLogin() throws IOException, InterruptedException {
229         final NicoHttpClient client = createClient(profile.getProxySetting());
230         final boolean hasLogin;
231         try {
232             hasLogin = client.login(profile.getLoginInfo().getMail(), profile.getLoginInfo().getPass());
233             if (!hasLogin) {
234                 throw new IOException("login fail");
235             }
236         } catch (URISyntaxException ex) {
237             throw new IOException("login fail", ex);
238         }
239         return client;
240     }
241
242     private NicoHttpClient createClient(ProxyProfile proxy) {
243         if (proxy.use()) {
244             return new NicoHttpClient(proxy.getHost(), proxy.getPort());
245         } else {
246             return new NicoHttpClient();
247         }
248     }
249
250     /** @return ログインする必要があればtrue. */
251     private boolean needsLogin() {
252         return profile.getVideoSetting().isDownload() || profile.getCommentSetting().isDownload();
253     }
254
255     /** @return 過去ログ取得の必要があればtrue. */
256     private boolean needsBackLog() {
257         return profile.getCommentSetting().getBackLogPoint() >= 0L;
258     }
259
260     /**
261      * 出力ファイルの保存先を確定します.
262      * @param title 動画タイトル.
263      * @param isNotLow 入力動画ファイルがlowでないかどうか.
264      * @return 出力ファイルの保存先.
265      */
266     private File getOutputFileName(String title, boolean isNotLow) {
267         final OutputProfile prof = profile.getOutputFileSetting();
268         final GeneralProfile gene = profile.getGeneralSetting();
269         final File dir = prof.getDir();
270         final String replaceFrom = gene.getReplaceFrom();
271         final String replaceTo = gene.getReplaceTo();
272         final NamePattern pattern = new NamePattern(prof.getFileName(), replaceFrom, replaceTo, title);
273         final String name = pattern.createFileName(movieId, isNotLow);
274         return new File(dir, name);
275     }
276 }