OSDN Git Service

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