/*
* PS3 Media Server, for streaming any medias to your PS3.
* Copyright (C) 2008 A.Brochard
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License only.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package net.pms.dlna;
import com.sun.jna.Platform;
import java.io.*;
import java.sql.SQLException;
import java.util.ArrayList;
import net.pms.PMS;
import net.pms.formats.Format;
import net.pms.formats.FormatFactory;
import net.pms.util.FileUtil;
import net.pms.util.ProcessUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RealFile extends MapFile {
private static final Logger LOGGER = LoggerFactory.getLogger(RealFile.class);
private boolean useSuperThumb;
public RealFile(File file) {
getConf().getFiles().add(file);
setLastModified(file.lastModified());
useSuperThumb = false;
}
public RealFile(File file, String name) {
getConf().getFiles().add(file);
getConf().setName(name);
setLastModified(file.lastModified());
useSuperThumb = false;
}
@Override
// FIXME: this is called repeatedly for invalid files e.g. files MediaInfo can't parse
public boolean isValid() {
File file = this.getFile();
if (!file.isDirectory()) {
resolveFormat();
}
if (getType() == Format.SUBTITLE) {
// Don't add subtitles as separate resources
return false;
}
if (getType() == Format.VIDEO && file.exists() && configuration.isAutoloadExternalSubtitles() && file.getName().length() > 4) {
setHasExternalSubtitles(FileUtil.isSubtitlesExists(file, null));
}
boolean valid = file.exists() && (getFormat() != null || file.isDirectory());
if (valid && getParent().getDefaultRenderer() != null && getParent().getDefaultRenderer().isUseMediaInfo()) {
// we need to resolve the DLNA resource now
run();
// Given that here getFormat() has already matched some (possibly plugin-defined) format:
// Format.UNKNOWN + bad parse = inconclusive
// known types + bad parse = bad/encrypted file
if (getType() != Format.UNKNOWN && getMedia() != null && (getMedia().isEncrypted() || getMedia().getContainer() == null || getMedia().getContainer().equals(DLNAMediaLang.UND))) {
valid = false;
if (getMedia().isEncrypted()) {
LOGGER.info("The file {} is encrypted. It will be hidden", file.getAbsolutePath());
} else {
LOGGER.info("The file {} could not be parsed. It will be hidden", file.getAbsolutePath());
}
}
// XXX isMediaInfoThumbnailGeneration is only true for the "default renderer"
if (getParent().getDefaultRenderer().isMediaInfoThumbnailGeneration()) {
checkThumbnail();
}
}
return valid;
}
@Override
public InputStream getInputStream() {
try {
return new FileInputStream(getFile());
} catch (FileNotFoundException e) {
LOGGER.debug("File not found: {}", getFile().getAbsolutePath());
}
return null;
}
@Override
public long length() {
if (getPlayer() != null && getPlayer().type() != Format.IMAGE) {
return DLNAMediaInfo.TRANS_SIZE;
} else if (getMedia() != null && getMedia().isMediaparsed()) {
return getMedia().getSize();
}
return getFile().length();
}
@Override
public boolean isFolder() {
return getFile().isDirectory();
}
public File getFile() {
return getConf().getFiles().get(0);
}
@Override
public String getName() {
if (this.getConf().getName() == null) {
String name = null;
File file = getFile();
if (file.getName().trim().isEmpty()) {
if (Platform.isWindows()) {
name = PMS.get().getRegistry().getDiskLabel(file);
}
if (name != null && name.length() > 0) {
name = file.getAbsolutePath().substring(0, 1) + ":\\ [" + name + "]";
} else {
name = file.getAbsolutePath().substring(0, 1);
}
} else {
name = file.getName();
}
this.getConf().setName(name);
}
return this.getConf().getName().replaceAll("_imdb([^_]+)_", "");
}
@Override
protected void resolveFormat() {
if (getFormat() == null) {
setFormat(FormatFactory.getAssociatedFormat(getFile().getAbsolutePath()));
}
super.resolveFormat();
}
@Override
public String getSystemName() {
return ProcessUtil.getShortFileNameIfWideChars(getFile().getAbsolutePath());
}
@Override
public synchronized void resolve() {
File file = getFile();
if (file.isFile() && (getMedia() == null || !getMedia().isMediaparsed())) {
boolean found = false;
InputFile input = new InputFile();
input.setFile(file);
String fileName = file.getAbsolutePath();
if (getSplitTrack() > 0) {
fileName += "#SplitTrack" + getSplitTrack();
}
if (configuration.getUseCache()) {
DLNAMediaDatabase database = PMS.get().getDatabase();
if (database != null) {
ArrayList<DLNAMediaInfo> medias;
try {
medias = database.getData(fileName, file.lastModified());
if (medias.size() == 1) {
setMedia(medias.get(0));
getMedia().postParse(getType(), input);
found = true;
} else if (medias.size() > 1) {
LOGGER.warn(
"Found {} cached records for {} - this should be impossible, please file a bug report",
medias.size(),
getName()
);
}
} catch (InvalidClassException e) {
LOGGER.debug("Cached information about {} seems to be from a previous version, reparsing information", getName());
LOGGER.trace("", e);
} catch (IOException | SQLException e) {
LOGGER.debug("Error while getting cached information about {}, reparsing information: {}", getName(), e.getMessage());
LOGGER.trace("", e);
}
}
}
if (!found) {
if (getMedia() == null) {
setMedia(new DLNAMediaInfo());
}
if (getFormat() != null) {
getFormat().parse(getMedia(), input, getType(), getParent().getDefaultRenderer());
} else {
// Don't think that will ever happen
getMedia().parse(input, getFormat(), getType(), false, isResume(), getParent().getDefaultRenderer());
}
if (configuration.getUseCache() && getMedia().isMediaparsed() && !getMedia().isParsing()) {
DLNAMediaDatabase database = PMS.get().getDatabase();
if (database != null) {
try {
database.insertOrUpdateData(fileName, file.lastModified(), getType(), getMedia());
} catch (SQLException e) {
LOGGER.error(
"Database error while trying to add parsed information for \"{}\" to the cache: {}",
fileName,
e.getMessage());
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("SQL error code: {}", e.getErrorCode());
if (
e.getCause() instanceof SQLException &&
((SQLException) e.getCause()).getErrorCode() != e.getErrorCode()
) {
LOGGER.trace("Cause SQL error code: {}", ((SQLException) e.getCause()).getErrorCode());
}
LOGGER.trace("", e);
}
}
}
}
}
}
}
@Override
public DLNAThumbnailInputStream getThumbnailInputStream() throws IOException {
if (useSuperThumb || getParent() instanceof FileTranscodeVirtualFolder && (getMediaSubtitle() != null || getMediaAudio() != null)) {
return super.getThumbnailInputStream();
}
File file = getFile();
File cachedThumbnail = null;
MediaType mediaType = getMedia() != null ? getMedia().getMediaType() : MediaType.UNKNOWN;
File thumbFolder = file.getParentFile();
boolean alternativeCheck = false;
if (mediaType != MediaType.IMAGE) {
while (cachedThumbnail == null) {
cachedThumbnail = FileUtil.getFileNameWithNewExtension(thumbFolder, file, "jpg");
if (cachedThumbnail != null) {
break;
}
cachedThumbnail = FileUtil.getFileNameWithNewExtension(thumbFolder, file, "png");
if (cachedThumbnail != null) {
break;
}
cachedThumbnail = FileUtil.getFileNameWithAddedExtension(thumbFolder, file, ".cover.jpg");
if (cachedThumbnail != null) {
break;
}
cachedThumbnail = FileUtil.getFileNameWithAddedExtension(thumbFolder, file, ".cover.png");
if (cachedThumbnail != null) {
break;
}
if (mediaType == MediaType.AUDIO && getParent() != null && getParent() instanceof RealFile) {
cachedThumbnail = ((RealFile) getParent()).getPotentialCover();
}
if (cachedThumbnail != null || alternativeCheck) {
break;
}
if (StringUtils.isNotBlank(configuration.getAlternateThumbFolder())) {
thumbFolder = new File(configuration.getAlternateThumbFolder());
if (!thumbFolder.isDirectory()) {
thumbFolder = null;
break;
}
}
alternativeCheck = true;
}
}
if (file.isDirectory()) {
cachedThumbnail = FileUtil.getFileNameWithNewExtension(file.getParentFile(), file, "/folder.jpg");
if (cachedThumbnail == null) {
cachedThumbnail = FileUtil.getFileNameWithNewExtension(file.getParentFile(), file, "/folder.png");
}
}
boolean hasAlreadyEmbeddedCoverArt = getType() == Format.AUDIO && getMedia() != null && getMedia().getThumb() != null;
DLNAThumbnailInputStream result = null;
try {
if (cachedThumbnail != null && (!hasAlreadyEmbeddedCoverArt || file.isDirectory())) {
result = DLNAThumbnailInputStream.toThumbnailInputStream(new FileInputStream(cachedThumbnail));
} else if (getMedia() != null && getMedia().getThumb() != null) {
result = getMedia().getThumbnailInputStream();
}
} catch (IOException e) {
result = null;
LOGGER.debug("An error occurred while getting thumbnail for \"{}\", using generic thumbnail instead: {}", getName(), e.getMessage());
LOGGER.trace("", e);
}
return result != null ? result : super.getThumbnailInputStream();
}
@Override
public void checkThumbnail() {
InputFile input = new InputFile();
input.setFile(getFile());
checkThumbnail(input, getParent().getDefaultRenderer());
}
@Override
protected String getThumbnailURL(DLNAImageProfile profile) {
if (getType() == Format.IMAGE && !configuration.getImageThumbnailsEnabled()) {
return null;
}
return super.getThumbnailURL(profile);
}
@Override
public boolean isSubSelectable() {
return true;
}
@Override
public String write() {
return getName() + ">" + getFile().getAbsolutePath();
}
public void ignoreThumbHandling() {
useSuperThumb = true;
}
}