1 package saccubus.converter;
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;
26 * <p>説明: ニコニコ動画の動画をコメントつきで保存</p>
28 * <p>著作権: Copyright (c) 2007 PSI</p>
35 public class Converter extends AbstractCommand implements Callable<Boolean> {
37 private final Profile profile;
38 private final String movieId;
42 * @param id 対象となる動画のID.
48 public Converter(String id, Profile profile,
49 TextProgressListener listener, ConvertStopFlag flag) {
50 super(listener, flag);
52 this.profile = profile;
56 public Boolean call() throws Exception {
57 boolean result = false;
59 result = runConvert();
61 getStopFlag().finished();
63 return Boolean.valueOf(result);
66 // TODO Runnableを実装しなくなったので削除する
70 } catch (Exception ex) {
71 String text = (ex.getMessage() != null) ? ex.getMessage() : "予期しないエラー発生のため中断しました。";
77 private boolean runConvert() throws IOException, InterruptedException, Exception {
78 if (!shouldRun(profile)) {
79 sendText("何もすることがありません");
84 final FfmpegProfile ov = profile.getFfmpeg().getFfmpegOption();
86 // TODO ログインしないで良い場合もある.
91 NicoHttpClient client = null;
92 nicobrowser.VideoInfo vi = null;
93 NamePattern videoNamePattern = null;
94 WayBackInfo wbi = null;
96 client = createClientAndLogin();
97 vi = client.getVideoInfo(movieId);
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());
104 if (needsBackLog()) {
105 final String key = client.getWayBackKey(vi);
106 wbi = new WayBackInfo(key, profile.getCommentSetting().getBackLogPoint());
114 if (profile.getCommentSetting().isDownload()) {
115 final CommentProfile prof = profile.getCommentSetting();
116 final GeneralProfile gene = profile.getGeneralSetting();
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);
124 commentFile = client.getCommentFile(vi, file.getPath(), wbi, profile.getCommentSetting().getLengthRelatedCommentSize(),
125 profile.getCommentSetting().isDisablePerMinComment());
127 commentFile = profile.getCommentSetting().getLocalFile();
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() {
139 public void progress(long fileSize, long downloadSize) {
140 throw new UnsupportedOperationException("Not supported yet.");
144 public boolean getCancel() {
145 throw new UnsupportedOperationException("Not supported yet.");
149 videoFile = vf.getFile();
151 videoFile = profile.getVideoSetting().getLocalFile();
154 if (!profile.getOutputFileSetting().isConvert()) {
155 sendText("動画・コメントを保存し、変換は行いませんでした。");
159 if (!videoFile.isFile()) {
160 throw new IOException("入力動画ファイルが存在しません:" + videoFile.getPath());
163 if (profile.getOutputFileSetting().isAddComment()) {
164 if (!commentFile.isFile()) {
165 throw new IOException("入力コメントファイルが存在しません:" + commentFile.getPath());
172 final boolean isNotLow = (vf == null) ? true : (vf.getStatus() != Status.GET_LOW);
173 File convertedVideoFile = getOutputFileName(vi.getTitleInWatchPage(), isNotLow);
175 boolean res = new FfmpegCommand(getListener(), getStopFlag(), commentFile, videoFile,
176 convertedVideoFile, profile.getFfmpeg(), profile.getGeneralSetting()).execute();
180 public boolean isConverted() {
181 return getStopFlag().isFinished();
185 public ConvertStopFlag getStopFlag() {
186 return super.getStopFlag();
189 /** @return 何か実行すべき処理があればtrue. */
190 private static boolean shouldRun(Profile profile) {
191 return profile.getOutputFileSetting().isConvert() || needsDownload(profile);
194 /** @return 何かダウンロードするものがあればtrue. */
195 private static boolean needsDownload(Profile profile) {
196 return (profile.getVideoSetting().isDownload() || profile.getCommentSetting().isDownload());
200 * (ネットワーク設定以外の)設定を検証する.
201 * @throws IllegalArgumentException 設定に不備がある場合.
203 private void validSetting() {
204 if (profile.getOutputFileSetting().isConvert()) {
205 File a = profile.getFfmpeg().getFfmpeg();
207 throw new IllegalArgumentException("FFmpegが見つかりません。");
209 if (profile.getFfmpeg().getVhook().getPath().indexOf(' ') >= 0) {
210 throw new IllegalArgumentException("すいません。現在vhookライブラリには半角空白は使えません。");
212 a = profile.getFfmpeg().getVhook();
214 throw new IllegalArgumentException("Vhookライブラリが見つかりません。");
216 a = profile.getFfmpeg().getFont();
218 throw new IllegalArgumentException("フォントが見つかりません。");
224 * HttpClientを生成し, ニコニコ動画サーバへログインします.
225 * @return 生成したHttpClientインスタンス.
226 * @throws IOException ログイン失敗.
227 * @throws InterruptedException ログイン失敗.
229 // TODO HttpException を投げるのをやめたい. コンパイル時にHttpComponentが必要になるので.
230 private NicoHttpClient createClientAndLogin() throws IOException, InterruptedException, HttpException {
231 final NicoHttpClient client = createClient(profile.getProxySetting());
232 final boolean hasLogin;
234 hasLogin = client.login(profile.getLoginInfo().getMail(), profile.getLoginInfo().getPassword());
236 throw new IOException("login fail");
238 } catch (URISyntaxException ex) {
239 throw new IOException("login fail", ex);
244 private NicoHttpClient createClient(ProxyProfile proxy) {
246 return new NicoHttpClient(proxy.getHost(), proxy.getPort());
248 return new NicoHttpClient();
252 /** @return ログインする必要があればtrue. */
253 private boolean needsLogin() {
254 return profile.getVideoSetting().isDownload() || profile.getCommentSetting().isDownload();
257 /** @return 過去ログ取得の必要があればtrue. */
258 private boolean needsBackLog() {
259 return profile.getCommentSetting().getBackLogPoint() >= 0L;
264 * @param title 動画タイトル.
265 * @param isNotLow 入力動画ファイルがlowでないかどうか.
266 * @return 出力ファイルの保存先.
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);