001    /**
002     * Copyright (c) 2000-2011 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portlet.documentlibrary.util;
016    
017    import com.liferay.portal.kernel.exception.SystemException;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.messaging.DestinationNames;
021    import com.liferay.portal.kernel.messaging.MessageBusUtil;
022    import com.liferay.portal.kernel.repository.model.FileVersion;
023    import com.liferay.portal.kernel.util.FileUtil;
024    import com.liferay.portal.kernel.util.PropsKeys;
025    import com.liferay.portal.kernel.util.SetUtil;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.repository.liferayrepository.model.LiferayFileVersion;
029    import com.liferay.portal.util.PrefsPropsUtil;
030    import com.liferay.portal.util.PropsValues;
031    import com.liferay.portlet.documentlibrary.NoSuchFileEntryException;
032    import com.liferay.portlet.documentlibrary.store.DLStoreUtil;
033    
034    import java.io.File;
035    import java.io.InputStream;
036    
037    import java.util.List;
038    import java.util.Set;
039    import java.util.Vector;
040    
041    /**
042     * @author Juan González
043     * @author Sergio González
044     * @author Mika Koivisto
045     */
046    public class VideoProcessor extends DefaultPreviewableProcessor {
047    
048            public static final String PREVIEW_TYPE = "flv";
049    
050            public static final String THUMBNAIL_TYPE = "jpg";
051    
052            public static void generateVideo(FileVersion fileVersion)
053                    throws Exception {
054    
055                    _instance._generateVideo(fileVersion);
056            }
057    
058            public static DLProcessor getInstance() {
059                    return _instance;
060            }
061    
062            public static InputStream getPreviewAsStream(FileVersion fileVersion)
063                    throws Exception {
064    
065                    return _instance.doGetPreviewAsStream(fileVersion);
066            }
067    
068            public static long getPreviewFileSize(FileVersion fileVersion)
069                    throws Exception {
070    
071                    return _instance.doGetPreviewFileSize(fileVersion);
072            }
073    
074            public static InputStream getThumbnailAsStream(FileVersion fileVersion)
075                    throws Exception {
076    
077                    return _instance.doGetThumbnailAsStream(fileVersion);
078            }
079    
080            public static long getThumbnailFileSize(FileVersion fileVersion)
081                    throws Exception {
082    
083                    return _instance.doGetThumbnailFileSize(fileVersion);
084            }
085    
086            public static Set<String> getVideoMimeTypes() {
087                    return _videoMimeTypes;
088            }
089    
090            public static boolean hasVideo(FileVersion fileVersion) {
091                    boolean hasVideo = false;
092    
093                    try {
094                            hasVideo = _instance._hasVideo(fileVersion);
095    
096                            if (!hasVideo && _instance.isSupported(fileVersion)) {
097                                    _instance._queueGeneration(fileVersion);
098                            }
099                    }
100                    catch (Exception e) {
101                            _log.error(e, e);
102                    }
103    
104                    return hasVideo;
105            }
106    
107            public VideoProcessor() {
108                    FileUtil.mkdirs(PREVIEW_TMP_PATH);
109                    FileUtil.mkdirs(THUMBNAIL_TMP_PATH);
110            }
111    
112            public boolean isSupported(String mimeType) {
113                    return _videoMimeTypes.contains(mimeType);
114            }
115    
116            public void trigger(FileVersion fileVersion) {
117                    _instance._queueGeneration(fileVersion);
118            }
119    
120            @Override
121            protected String getPreviewType() {
122                    return PREVIEW_TYPE;
123            }
124    
125            @Override
126            protected String getThumbnailType() {
127                    return THUMBNAIL_TYPE;
128            }
129    
130            private void _generateThumbnailXuggler(
131                            FileVersion fileVersion, File file, int height, int width)
132                    throws Exception {
133    
134                    String tempFileId = DLUtil.getTempFileId(
135                            fileVersion.getFileEntryId(), fileVersion.getVersion());
136    
137                    File thumbnailTempFile = getThumbnailTempFile(tempFileId);
138    
139                    try {
140                            try {
141                                    LiferayVideoThumbnailConverter liferayVideoThumbnailConverter =
142                                            new LiferayVideoThumbnailConverter(
143                                                    file.getCanonicalPath(), thumbnailTempFile,
144                                                    THUMBNAIL_TYPE, height, width, _THUMBNAIL_PERCENTAGE);
145    
146                                    liferayVideoThumbnailConverter.convert();
147                            }
148                            catch (Exception e) {
149                                    _log.error(e, e);
150                            }
151    
152                            addFileToStore(
153                                    fileVersion.getCompanyId(), THUMBNAIL_PATH,
154                                    getThumbnailFilePath(fileVersion), thumbnailTempFile);
155                    }
156                    catch (Exception e) {
157                            throw new SystemException(e);
158                    }
159                    finally {
160                            FileUtil.delete(thumbnailTempFile);
161                    }
162    
163                    if (_log.isInfoEnabled()) {
164                            _log.info(
165                                    "Xuggler generated a thumbnail for " +
166                                            fileVersion.getFileVersionId());
167                    }
168            }
169    
170            private void _generateVideo(FileVersion fileVersion) throws Exception {
171                    String tempFileId = DLUtil.getTempFileId(
172                            fileVersion.getFileEntryId(), fileVersion.getVersion());
173    
174                    File videoTempFile = _getVideoTempFile(
175                            tempFileId, fileVersion.getExtension());
176                    File previewTempFile = getPreviewTempFile(tempFileId);
177    
178                    try {
179                            if (!PrefsPropsUtil.getBoolean(
180                                            PropsKeys.XUGGLER_ENABLED, PropsValues.XUGGLER_ENABLED) ||
181                                    _hasVideo(fileVersion)) {
182    
183                                    return;
184                            }
185    
186                            File file = null;
187    
188                            if (_isGeneratePreview(fileVersion) ||
189                                    _isGenerateThumbnail(fileVersion)) {
190    
191                                    if (fileVersion instanceof LiferayFileVersion) {
192                                            try {
193                                                    LiferayFileVersion liferayFileVersion =
194                                                            (LiferayFileVersion)fileVersion;
195    
196                                                    file = liferayFileVersion.getFile(false);
197                                            }
198                                            catch (UnsupportedOperationException uoe) {
199                                            }
200                                    }
201    
202                                    if (file == null) {
203                                            InputStream inputStream = fileVersion.getContentStream(
204                                                    false);
205    
206                                            FileUtil.write(videoTempFile, inputStream);
207    
208                                            file = videoTempFile;
209                                    }
210                            }
211    
212                            if (_isGeneratePreview(fileVersion)) {
213                                    try {
214                                            _generateVideoXuggler(
215                                                    fileVersion, file, previewTempFile,
216                                                    PropsValues.DL_FILE_ENTRY_PREVIEW_VIDEO_HEIGHT,
217                                                    PropsValues.DL_FILE_ENTRY_PREVIEW_VIDEO_WIDTH);
218                                    }
219                                    catch (Exception e) {
220                                            _log.error(e, e);
221                                    }
222                            }
223    
224                            if (_isGenerateThumbnail(fileVersion)) {
225                                    try {
226                                            _generateThumbnailXuggler(
227                                                    fileVersion, file,
228                                                    PropsValues.DL_FILE_ENTRY_PREVIEW_VIDEO_HEIGHT,
229                                                    PropsValues.DL_FILE_ENTRY_PREVIEW_VIDEO_WIDTH);
230                                    }
231                                    catch (Exception e) {
232                                            _log.error(e, e);
233                                    }
234                            }
235                    }
236                    catch (NoSuchFileEntryException nsfee) {
237                    }
238                    finally {
239                            _fileVersionIds.remove(fileVersion.getFileVersionId());
240    
241                            FileUtil.delete(previewTempFile);
242                            FileUtil.delete(videoTempFile);
243                    }
244            }
245    
246            private void _generateVideoXuggler(
247                            FileVersion fileVersion, File srcFile, File destFile, int height,
248                            int width)
249                    throws Exception {
250    
251                    try {
252                            LiferayVideoConverter liferayVideoConverter =
253                                    new LiferayVideoConverter(
254                                            srcFile.getCanonicalPath(), destFile.getCanonicalPath(),
255                                            height, width, _SAMPLE_RATE);
256    
257                            liferayVideoConverter.convert();
258                    }
259                    catch (Exception e) {
260                            _log.error(e, e);
261                    }
262    
263                    addFileToStore(
264                            fileVersion.getCompanyId(), PREVIEW_PATH,
265                            getPreviewFilePath(fileVersion), destFile);
266    
267                    if (_log.isInfoEnabled()) {
268                            _log.info(
269                                    "Xuggler generated a preview video for " +
270                                            fileVersion.getFileVersionId());
271                    }
272            }
273    
274            private File _getVideoTempFile(String tempFileId, String targetExtension) {
275                    String videoTempFilePath = _getVideoTempFilePath(
276                            tempFileId, targetExtension);
277    
278                    return new File(videoTempFilePath);
279            }
280    
281            private String _getVideoTempFilePath(
282                    String tempFileId, String targetExtension) {
283    
284                    StringBundler sb = new StringBundler(5);
285    
286                    sb.append(PREVIEW_TMP_PATH);
287                    sb.append(tempFileId);
288    
289                    if (PREVIEW_TYPE.equals(targetExtension)) {
290                            sb.append("_tmp");
291                    }
292    
293                    sb.append(StringPool.PERIOD);
294                    sb.append(targetExtension);
295    
296                    return sb.toString();
297            }
298    
299            private boolean _hasVideo(FileVersion fileVersion) throws Exception {
300                    if (!isSupported(fileVersion)) {
301                            return false;
302                    }
303    
304                    boolean previewExists = DLStoreUtil.hasFile(
305                            fileVersion.getCompanyId(), REPOSITORY_ID,
306                            getPreviewFilePath(fileVersion));
307                    boolean thumbnailExists = DLStoreUtil.hasFile(
308                            fileVersion.getCompanyId(), REPOSITORY_ID,
309                            getThumbnailFilePath(fileVersion));
310    
311                    if (PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED &&
312                            PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED) {
313    
314                            if (previewExists && thumbnailExists) {
315                                    return true;
316                            }
317                    }
318                    else if (PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED && previewExists) {
319                            return true;
320                    }
321                    else if (PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED &&
322                                     thumbnailExists) {
323    
324                            return true;
325                    }
326    
327                    return false;
328            }
329    
330            private boolean _isGeneratePreview(FileVersion fileVersion)
331                    throws Exception {
332    
333                    String previewFilePath = getPreviewFilePath(fileVersion);
334    
335                    if (PropsValues.DL_FILE_ENTRY_PREVIEW_ENABLED &&
336                            !DLStoreUtil.hasFile(
337                                    fileVersion.getCompanyId(), REPOSITORY_ID, previewFilePath)) {
338    
339                            return true;
340                    }
341                    else {
342                            return false;
343                    }
344            }
345    
346            private boolean _isGenerateThumbnail(FileVersion fileVersion)
347                    throws Exception {
348    
349                    String thumbnailFilePath = getThumbnailFilePath(fileVersion);
350    
351                    if (PropsValues.DL_FILE_ENTRY_THUMBNAIL_ENABLED &&
352                            !DLStoreUtil.hasFile(
353                                    fileVersion.getCompanyId(), REPOSITORY_ID, thumbnailFilePath)) {
354    
355                            return true;
356                    }
357                    else {
358                            return false;
359                    }
360            }
361    
362            private void _queueGeneration(FileVersion fileVersion) {
363                    if (_fileVersionIds.contains(fileVersion.getFileVersionId()) ||
364                            !isSupported(fileVersion)) {
365    
366                            return;
367                    }
368    
369                    _fileVersionIds.add(fileVersion.getFileVersionId());
370    
371                    MessageBusUtil.sendMessage(
372                            DestinationNames.DOCUMENT_LIBRARY_VIDEO_PROCESSOR, fileVersion);
373            }
374    
375            private static int _SAMPLE_RATE = 44100;
376    
377            private static int _THUMBNAIL_PERCENTAGE =
378                    PropsValues.DL_FILE_ENTRY_THUMBNAIL_VIDEO_FRAME_PERCENTAGE;
379    
380            private static Log _log = LogFactoryUtil.getLog(VideoProcessor.class);
381    
382            private static VideoProcessor _instance = new VideoProcessor();
383    
384            private static List<Long> _fileVersionIds = new Vector<Long>();
385            private static Set<String> _videoMimeTypes = SetUtil.fromArray(
386                    PropsValues.DL_FILE_ENTRY_PREVIEW_VIDEO_MIME_TYPES);
387    
388    }