/**
* Copyright (C) 2010-2017 Structr GmbH
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Structr. If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.media;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.common.PropertyView;
import org.structr.common.SecurityContext;
import org.structr.common.error.ErrorBuffer;
import org.structr.common.error.FrameworkException;
import org.structr.core.Export;
import static org.structr.core.GraphObject.type;
import org.structr.core.GraphObjectMap;
import org.structr.core.JsonInput;
import org.structr.core.app.StructrApp;
import org.structr.core.graph.ModificationQueue;
import static org.structr.core.graph.NodeInterface.name;
import static org.structr.core.graph.NodeInterface.owner;
import org.structr.core.graph.Tx;
import org.structr.core.property.ConstantBooleanProperty;
import org.structr.core.property.DoubleProperty;
import org.structr.core.property.EndNode;
import org.structr.core.property.EndNodes;
import org.structr.core.property.IntProperty;
import org.structr.core.property.Property;
import org.structr.core.property.StartNode;
import org.structr.core.property.StringProperty;
import org.structr.dynamic.File;
import org.structr.rest.RestMethodResult;
import org.structr.schema.SchemaService;
import org.structr.web.common.FileHelper;
import static org.structr.web.entity.FileBase.relativeFilePath;
import static org.structr.web.entity.FileBase.size;
import org.structr.web.entity.Image;
//~--- classes ----------------------------------------------------------------
/**
* A video whose binary data will be stored on disk.
*
*
*
*/
public class VideoFile extends File {
private static final Logger logger = LoggerFactory.getLogger(VideoFile.class.getName());
// register this type as an overridden builtin type
static {
SchemaService.registerBuiltinTypeOverride("VideoFile", VideoFile.class.getName());
}
public static final Property<List<VideoFile>> convertedVideos = new EndNodes<>("convertedVideos", VideoFileHasConvertedVideoFile.class);
public static final Property<VideoFile> originalVideo = new StartNode<>("originalVideo", VideoFileHasConvertedVideoFile.class);
public static final Property<Image> posterImage = new EndNode<>("posterImage", VideoFileHasPosterImage.class);
public static final Property<Boolean> isVideo = new ConstantBooleanProperty("isVideo", true);
public static final Property<String> videoCodecName = new StringProperty("videoCodecName").cmis();
public static final Property<String> videoCodec = new StringProperty("videoCodec").cmis();
public static final Property<String> pixelFormat = new StringProperty("pixelFormat").cmis();
public static final Property<String> audioCodecName = new StringProperty("audioCodecName").cmis();
public static final Property<String> audioCodec = new StringProperty("audioCodec").cmis();
public static final Property<Integer> audioChannels = new IntProperty("audioChannels").cmis();
public static final Property<Double> sampleRate = new DoubleProperty("sampleRate").cmis().indexed();
public static final Property<Double> duration = new DoubleProperty("duration").cmis().indexed();
public static final Property<Integer> width = new IntProperty("width").cmis().indexed();
public static final Property<Integer> height = new IntProperty("height").cmis().indexed();
public static final org.structr.common.View uiView = new org.structr.common.View(VideoFile.class, PropertyView.Ui,
type, name, contentType, size, relativeFilePath, owner, parent, path, isVideo, videoCodecName, videoCodec, pixelFormat,
audioCodecName, audioCodec, audioChannels, sampleRate, duration, width, height, originalVideo, convertedVideos,
posterImage
);
public static final org.structr.common.View publicView = new org.structr.common.View(VideoFile.class, PropertyView.Public,
type, name, owner, parent, path, isVideo, videoCodecName, videoCodec, pixelFormat,
audioCodecName, audioCodec, audioChannels, sampleRate, duration, width, height,
convertedVideos, posterImage
);
@Override
public boolean onCreation(SecurityContext securityContext, ErrorBuffer errorBuffer) throws FrameworkException {
updateVideoInfo();
return super.onCreation(securityContext, errorBuffer);
}
@Override
public boolean onModification(SecurityContext securityContext, ErrorBuffer errorBuffer, final ModificationQueue modificationQueue) throws FrameworkException {
updateVideoInfo();
return super.onModification(securityContext, errorBuffer, modificationQueue);
}
public String getDiskFilePath(final SecurityContext securityContext) {
try (final Tx tx = StructrApp.getInstance(securityContext).tx()) {
final String path = getRelativeFilePath();
tx.success();
if (path != null) {
return new java.io.File(FileHelper.getFilePath(path)).getAbsolutePath();
}
} catch (FrameworkException fex) {
logger.warn("", fex);
}
return null;
}
@Export
public void convert(final String scriptName, final String newFileName) throws FrameworkException {
AVConv.newInstance(securityContext, this, newFileName).doConversion(scriptName);
}
@Export
public void grab(final String scriptName, final String imageName, final long timeIndex) throws FrameworkException {
AVConv.newInstance(securityContext, this, imageName).grabFrame(scriptName, imageName, timeIndex);
}
@Export
public RestMethodResult getMetadata() throws FrameworkException {
final Map<String, String> metadata = AVConv.newInstance(securityContext, this).getMetadata();
final RestMethodResult result = new RestMethodResult(200);
final GraphObjectMap map = new GraphObjectMap();
for (final Entry<String, String> entry : metadata.entrySet()) {
map.setProperty(new StringProperty(entry.getKey()), entry.getValue());
}
result.addContent(map);
return result;
}
@Export
public void setMetadata(final String key, final String value) throws FrameworkException {
AVConv.newInstance(securityContext, this).setMetadata(key, value);
}
@Export
public void setMetadata(final JsonInput metadata) throws FrameworkException {
final Map<String, String> map = new LinkedHashMap<>();
for (final Entry<String, Object> entry : metadata.entrySet()) {
map.put(entry.getKey(), entry.getValue().toString());
}
AVConv.newInstance(securityContext, this).setMetadata(map);
}
@Export
public void updateVideoInfo() {
try (final Tx tx = StructrApp.getInstance(securityContext).tx()) {
final Map<String, Object> info = AVConv.newInstance(securityContext, this).getVideoInfo();
if (info != null && info.containsKey("streams")) {
final List<Map<String, Object>> streams = (List<Map<String, Object>>)info.get("streams");
for (final Map<String, Object> stream : streams) {
final String codecType = (String)stream.get("codec_type");
if (codecType != null) {
if ("video".equals(codecType)) {
setIfNotNull(videoCodecName, stream.get("codec_long_name"));
setIfNotNull(videoCodec, stream.get("codec_name"));
setIfNotNull(pixelFormat, stream.get("pix_fmt"));
setIfNotNull(width, toInt(stream.get("width")));
setIfNotNull(height, toInt(stream.get("height")));
setIfNotNull(duration, toDouble(stream.get("duration")));
} else if ("audio".equals(codecType)) {
setIfNotNull(audioCodecName, stream.get("codec_long_name"));
setIfNotNull(audioCodec, stream.get("codec_name"));
setIfNotNull(sampleRate, toInt(stream.get("sampleRate")));
}
}
}
}
tx.success();
} catch (FrameworkException fex) {
logger.warn("", fex);
}
}
private void setIfNotNull(final Property key, final Object value) throws FrameworkException {
if (value != null) {
setProperty(key, value);
}
}
private Integer toInt(final Object value) {
if (value instanceof Number) {
return ((Number)value).intValue();
}
if (value instanceof String) {
try {
return Integer.valueOf((String)value);
} catch (Throwable t) {
return null;
}
}
return null;
}
private Double toDouble(final Object value) {
if (value instanceof Number) {
return ((Number)value).doubleValue();
}
if (value instanceof String) {
try {
return Double.valueOf((String)value);
} catch (Throwable t) {
return null;
}
}
return null;
}
}