/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portlet.documentlibrary.util;
import com.liferay.document.library.kernel.exception.NoSuchFileEntryException;
import com.liferay.document.library.kernel.model.DLProcessorConstants;
import com.liferay.document.library.kernel.util.AudioProcessor;
import com.liferay.document.library.kernel.util.DLPreviewableProcessor;
import com.liferay.document.library.kernel.util.DLUtil;
import com.liferay.exportimport.kernel.lar.PortletDataContext;
import com.liferay.portal.fabric.InputResource;
import com.liferay.portal.fabric.OutputResource;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.messaging.DestinationNames;
import com.liferay.portal.kernel.process.ClassPathUtil;
import com.liferay.portal.kernel.process.ProcessCallable;
import com.liferay.portal.kernel.process.ProcessChannel;
import com.liferay.portal.kernel.process.ProcessException;
import com.liferay.portal.kernel.process.ProcessExecutorUtil;
import com.liferay.portal.kernel.repository.model.FileEntry;
import com.liferay.portal.kernel.repository.model.FileVersion;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.ServerDetector;
import com.liferay.portal.kernel.util.SetUtil;
import com.liferay.portal.kernel.util.StreamUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.SystemEnv;
import com.liferay.portal.kernel.xml.Element;
import com.liferay.portal.kernel.xuggler.XugglerUtil;
import com.liferay.portal.log.Log4jLogFactoryImpl;
import com.liferay.portal.repository.liferayrepository.model.LiferayFileVersion;
import com.liferay.portal.util.PropsUtil;
import com.liferay.portal.util.PropsValues;
import com.liferay.util.log4j.Log4JUtil;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
import org.apache.commons.lang.time.StopWatch;
/**
* @author Juan González
* @author Sergio González
* @author Mika Koivisto
* @author Ivica Cardic
*/
public class AudioProcessorImpl
extends DLPreviewableProcessor implements AudioProcessor {
@Override
public void afterPropertiesSet() {
boolean valid = true;
if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
valid = false;
}
else {
for (String previewType : _PREVIEW_TYPES) {
if (!previewType.equals("mp3") && !previewType.equals("ogg")) {
valid = false;
break;
}
}
}
if (!valid && _log.isWarnEnabled()) {
StringBundler sb = new StringBundler(5);
sb.append("Liferay is incorrectly configured to generate video ");
sb.append("previews using video containers other than MP3 or ");
sb.append("OGG. Please change the property ");
sb.append(PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO_CONTAINERS);
sb.append(" in portal-ext.properties.");
_log.warn(sb.toString());
}
FileUtil.mkdirs(PREVIEW_TMP_PATH);
}
@Override
public void generateAudio(
FileVersion sourceFileVersion, FileVersion destinationFileVersion)
throws Exception {
_generateAudio(sourceFileVersion, destinationFileVersion);
}
@Override
public Set<String> getAudioMimeTypes() {
return _audioMimeTypes;
}
@Override
public InputStream getPreviewAsStream(FileVersion fileVersion, String type)
throws Exception {
return doGetPreviewAsStream(fileVersion, type);
}
@Override
public long getPreviewFileSize(FileVersion fileVersion, String type)
throws Exception {
return doGetPreviewFileSize(fileVersion, type);
}
@Override
public String getType() {
return DLProcessorConstants.AUDIO_PROCESSOR;
}
@Override
public boolean hasAudio(FileVersion fileVersion) {
boolean hasAudio = false;
try {
hasAudio = _hasAudio(fileVersion);
if (!hasAudio && isSupported(fileVersion)) {
_queueGeneration(null, fileVersion);
}
}
catch (Exception e) {
_log.error(e, e);
}
return hasAudio;
}
@Override
public boolean isAudioSupported(FileVersion fileVersion) {
return isSupported(fileVersion);
}
@Override
public boolean isAudioSupported(String mimeType) {
return isSupported(mimeType);
}
@Override
public boolean isSupported(String mimeType) {
if (_audioMimeTypes.contains(mimeType) && XugglerUtil.isEnabled()) {
return true;
}
return false;
}
@Override
public void trigger(
FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
super.trigger(sourceFileVersion, destinationFileVersion);
_queueGeneration(sourceFileVersion, destinationFileVersion);
}
@Override
protected void doExportGeneratedFiles(
PortletDataContext portletDataContext, FileEntry fileEntry,
Element fileEntryElement)
throws Exception {
exportPreviews(portletDataContext, fileEntry, fileEntryElement);
}
@Override
protected void doImportGeneratedFiles(
PortletDataContext portletDataContext, FileEntry fileEntry,
FileEntry importedFileEntry, Element fileEntryElement)
throws Exception {
importPreviews(
portletDataContext, fileEntry, importedFileEntry, fileEntryElement);
}
protected void exportPreviews(
PortletDataContext portletDataContext, FileEntry fileEntry,
Element fileEntryElement)
throws Exception {
FileVersion fileVersion = fileEntry.getFileVersion();
if (!isSupported(fileVersion) || !hasPreviews(fileVersion)) {
return;
}
if (!portletDataContext.isPerformDirectBinaryImport()) {
if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
return;
}
for (String previewType : _PREVIEW_TYPES) {
if (previewType.equals("mp3") || previewType.equals("ogg")) {
exportPreview(
portletDataContext, fileEntry, fileEntryElement,
"audio", previewType);
}
}
}
}
@Override
protected List<Long> getFileVersionIds() {
return _fileVersionIds;
}
@Override
protected String getPreviewType(FileVersion fileVersion) {
return _PREVIEW_TYPES[0];
}
@Override
protected String[] getPreviewTypes() {
return _PREVIEW_TYPES;
}
@Override
protected String getThumbnailType(FileVersion fileVersion) {
return null;
}
protected void importPreviews(
PortletDataContext portletDataContext, FileEntry fileEntry,
FileEntry importedFileEntry, Element fileEntryElement)
throws Exception {
if ((_PREVIEW_TYPES.length == 0) || (_PREVIEW_TYPES.length > 2)) {
return;
}
for (String previewType : _PREVIEW_TYPES) {
if (previewType.equals("mp3") || previewType.equals("ogg")) {
importPreview(
portletDataContext, fileEntry, importedFileEntry,
fileEntryElement, "audio", previewType);
}
}
}
private void _generateAudio(
FileVersion sourceFileVersion, FileVersion destinationFileVersion)
throws Exception {
String tempFileId = DLUtil.getTempFileId(
destinationFileVersion.getFileEntryId(),
destinationFileVersion.getVersion());
File[] previewTempFiles = new File[_PREVIEW_TYPES.length];
for (int i = 0; i < _PREVIEW_TYPES.length; i++) {
previewTempFiles[i] = getPreviewTempFile(
tempFileId, _PREVIEW_TYPES[i]);
}
File audioTempFile = null;
InputStream inputStream = null;
try {
if (sourceFileVersion != null) {
copy(sourceFileVersion, destinationFileVersion);
return;
}
if (!XugglerUtil.isEnabled() || _hasAudio(destinationFileVersion)) {
return;
}
audioTempFile = FileUtil.createTempFile(
destinationFileVersion.getExtension());
if (!hasPreviews(destinationFileVersion)) {
File file = null;
if (destinationFileVersion instanceof LiferayFileVersion) {
try {
LiferayFileVersion liferayFileVersion =
(LiferayFileVersion)destinationFileVersion;
file = liferayFileVersion.getFile(false);
}
catch (UnsupportedOperationException uoe) {
}
}
if (file == null) {
inputStream = destinationFileVersion.getContentStream(
false);
FileUtil.write(audioTempFile, inputStream);
file = audioTempFile;
}
try {
_generateAudioXuggler(
destinationFileVersion, file, previewTempFiles);
}
catch (Exception e) {
_log.error(e, e);
}
}
}
catch (NoSuchFileEntryException nsfee) {
if (_log.isDebugEnabled()) {
_log.debug(nsfee, nsfee);
}
}
finally {
StreamUtil.cleanUp(inputStream);
_fileVersionIds.remove(destinationFileVersion.getFileVersionId());
for (int i = 0; i < previewTempFiles.length; i++) {
FileUtil.delete(previewTempFiles[i]);
}
FileUtil.delete(audioTempFile);
}
}
private void _generateAudioXuggler(
FileVersion fileVersion, File srcFile, File destFile,
String containerType)
throws Exception {
if (hasPreview(fileVersion, containerType)) {
return;
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
if (PropsValues.DL_FILE_ENTRY_PREVIEW_FORK_PROCESS_ENABLED) {
ProcessCallable<String> processCallable =
new LiferayAudioProcessCallable(
ServerDetector.getServerId(),
PropsUtil.get(PropsKeys.LIFERAY_HOME),
Log4JUtil.getCustomLogSettings(), srcFile, destFile,
containerType,
PropsUtil.getProperties(
PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
ProcessChannel<String> processChannel =
ProcessExecutorUtil.execute(
ClassPathUtil.getPortalProcessConfig(),
processCallable);
Future<String> future =
processChannel.getProcessNoticeableFuture();
String processIdentity = String.valueOf(
fileVersion.getFileVersionId());
futures.put(processIdentity, future);
future.get();
}
else {
LiferayConverter liferayConverter = new LiferayAudioConverter(
srcFile.getCanonicalPath(), destFile.getCanonicalPath(),
containerType,
PropsUtil.getProperties(
PropsKeys.DL_FILE_ENTRY_PREVIEW_AUDIO, false));
liferayConverter.convert();
}
}
catch (CancellationException ce) {
if (_log.isInfoEnabled()) {
_log.info(
"Cancellation received for " +
fileVersion.getFileVersionId() + " " +
fileVersion.getTitle());
}
}
catch (Exception e) {
_log.error(e, e);
}
addFileToStore(
fileVersion.getCompanyId(), PREVIEW_PATH,
getPreviewFilePath(fileVersion, containerType), destFile);
if (_log.isInfoEnabled()) {
_log.info(
"Xuggler generated a " + containerType + " preview audio for " +
fileVersion.getFileVersionId() + " in " +
stopWatch.getTime() + "ms");
}
}
private void _generateAudioXuggler(
FileVersion fileVersion, File srcFile, File[] destFiles) {
try {
for (int i = 0; i < destFiles.length; i++) {
_generateAudioXuggler(
fileVersion, srcFile, destFiles[i], _PREVIEW_TYPES[i]);
}
}
catch (Exception e) {
_log.error(e, e);
}
}
private boolean _hasAudio(FileVersion fileVersion) throws Exception {
if (!isSupported(fileVersion)) {
return false;
}
return hasPreviews(fileVersion);
}
private void _queueGeneration(
FileVersion sourceFileVersion, FileVersion destinationFileVersion) {
if (_fileVersionIds.contains(
destinationFileVersion.getFileVersionId()) ||
!isSupported(destinationFileVersion)) {
return;
}
_fileVersionIds.add(destinationFileVersion.getFileVersionId());
sendGenerationMessage(
DestinationNames.DOCUMENT_LIBRARY_AUDIO_PROCESSOR,
sourceFileVersion, destinationFileVersion);
}
private static final String[] _PREVIEW_TYPES =
PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_CONTAINERS;
private static final Log _log = LogFactoryUtil.getLog(
AudioProcessorImpl.class);
private final Set<String> _audioMimeTypes = SetUtil.fromArray(
PropsValues.DL_FILE_ENTRY_PREVIEW_AUDIO_MIME_TYPES);
private final List<Long> _fileVersionIds = new Vector<>();
private static class LiferayAudioProcessCallable
implements ProcessCallable<String> {
public LiferayAudioProcessCallable(
String serverId, String liferayHome,
Map<String, String> customLogSettings, File inputFile,
File outputFile, String audioContainer,
Properties audioProperties) {
_serverId = serverId;
_liferayHome = liferayHome;
_customLogSettings = customLogSettings;
_inputFile = inputFile;
_outputFile = outputFile;
_audioContainer = audioContainer;
_audioProperties = audioProperties;
}
@Override
public String call() throws ProcessException {
XugglerAutoInstallHelper.installNativeLibraries();
Properties systemProperties = System.getProperties();
SystemEnv.setProperties(systemProperties);
Class<?> clazz = getClass();
ClassLoader classLoader = clazz.getClassLoader();
Log4JUtil.initLog4J(
_serverId, _liferayHome, classLoader, new Log4jLogFactoryImpl(),
_customLogSettings);
try {
LiferayConverter liferayConverter = new LiferayAudioConverter(
_inputFile.getCanonicalPath(),
_outputFile.getCanonicalPath(), _audioContainer,
_audioProperties);
liferayConverter.convert();
}
catch (Exception e) {
throw new ProcessException(e);
}
return StringPool.BLANK;
}
private static final long serialVersionUID = 1L;
private final String _audioContainer;
private final Properties _audioProperties;
private final Map<String, String> _customLogSettings;
@InputResource
private final File _inputFile;
private final String _liferayHome;
@OutputResource
private final File _outputFile;
private final String _serverId;
}
}