package org.jtheque.movies.services.impl; /* * Copyright JTheque (Baptiste Wicht) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.jtheque.errors.ErrorService; import org.jtheque.errors.Errors; import org.jtheque.movies.IMovieConfiguration; import org.jtheque.movies.IMoviesModule; import org.jtheque.movies.services.able.IFFMpegService; import org.jtheque.movies.utils.PreciseDuration; import org.jtheque.movies.utils.Resolution; import org.jtheque.utils.ScannerUtils; import org.jtheque.utils.StringUtils; import org.jtheque.utils.io.SimpleApplicationConsumer; import org.jtheque.utils.ui.ImageUtils; import org.slf4j.LoggerFactory; import javax.annotation.Resource; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Random; import java.util.Scanner; import java.util.regex.Pattern; /** * A files service implementation. * * @author Baptiste Wicht */ public final class FFMpegService implements IFFMpegService { private static final Pattern PATTERN = Pattern.compile(", "); private static final Random RANDOM = new Random(); private static final int THUMBNAIL_WIDTH = 200; private static final int MAX_RANDOM_TIME = 50; private static final Pattern MILLISECONDS_PATTERN = Pattern.compile("\\."); @Resource private IMoviesModule moviesModule; @Resource private ErrorService errorService; @Override public Resolution getResolution(File f) { if (testFFMpegIsInstalled()) { String line = ScannerUtils.getLineStartingWith(getInformations(f), "Stream #0.0: Video:"); if (StringUtils.isNotEmpty(line)) { String resolution = PATTERN.split(line)[2].trim(); if (resolution.contains(" ")) { resolution = resolution.substring(0, resolution.indexOf(' ')); } return new Resolution(resolution); } } return null; } @Override public PreciseDuration getDuration(File f) { if (testFFMpegIsInstalled()) { String line = ScannerUtils.getLineStartingWith(getInformations(f), "Duration:"); if (StringUtils.isNotEmpty(line)) { return new PreciseDuration(formatDuration(line)); } } return null; } /** * Format the duration of the movie. * * @param line The line of the duration. * * @return The duration. */ private static CharSequence formatDuration(String line) { StringBuilder duration = new StringBuilder(line.substring(10, line.indexOf(','))); String milliSeconds = MILLISECONDS_PATTERN.split(duration)[1]; while (milliSeconds.length() != 3) { if (milliSeconds.length() > 3) { duration.deleteCharAt(duration.length() - 1); } if (milliSeconds.length() < 3) { duration.append(0); } milliSeconds = MILLISECONDS_PATTERN.split(duration)[1]; } return duration; } @Override public BufferedImage generateRandomPreviewImage(File f) { return generatePreviewImage(f, getRandomTime()); } @Override public BufferedImage generatePreviewImage(File f, String time) { if (testFFMpegIsInstalled()) { String fileName = moviesModule.getThumbnailFolderPath() + "temp.jpg"; SimpleApplicationConsumer p = new SimpleApplicationConsumer(moviesModule.getConfig().getFFmpegLocation(), "-i", f.getAbsolutePath(), "-f", "mjpeg", "-t", "0.001", "-ss", time, "-y", fileName); try { p.consume(); } catch (IOException e) { LoggerFactory.getLogger(getClass()).error(e.getMessage(), e); } return ImageUtils.createThumbnail(openImage(new File(fileName)), THUMBNAIL_WIDTH); } return null; } @Override public BufferedImage generateImageFromUserInput(File file) { return ImageUtils.createThumbnail(openImage(file), THUMBNAIL_WIDTH); } /** * Open the image specified by a file. * * @param file The file of the image. * * @return The thumbnail of the image. */ private static BufferedImage openImage(File file) { return ImageUtils.openCompatibleImage(file); } /** * Indicate if FFMpeg is installed or not. If it's not installed, this method will display an error message. * * @return true if it's installed else false. */ private boolean testFFMpegIsInstalled() { boolean notInstalled = !ffmpegIsInstalled(); if (notInstalled) { errorService.addError(Errors.newI18nError("movie.errors.ffmpeg")); } return !notInstalled; } @Override public boolean ffmpegIsInstalled() { IMovieConfiguration config = moviesModule.getConfig(); return StringUtils.isNotEmpty(config.getFFmpegLocation()) && new File(config.getFFmpegLocation()).exists(); } /** * Return the FFmpeg informations of the specified files. * * @param f The file to the informations from. * * @return A Scanner on the informations result. */ private Scanner getInformations(File f) { IMovieConfiguration config = moviesModule.getConfig(); SimpleApplicationConsumer p = new SimpleApplicationConsumer(config.getFFmpegLocation(), "-i", f.getAbsolutePath()); try { p.consume(); return new Scanner(p.getResult()); } catch (IOException e) { LoggerFactory.getLogger(getClass()).error(e.getMessage(), e); } return null; } /** * Return a random time to generate random preview image. * * @return A random time to generate with ffmpeg. */ private static String getRandomTime() { return String.valueOf(RANDOM.nextInt(MAX_RANDOM_TIME) + 1); } }