/*
* PS3 Media Server, for streaming any medias to your PS3.
* Copyright (C) 2008 A.Brochard
* Copyright (C) 2012 I. Sokolov
*
* 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.ibm.icu.text.CharsetMatch;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Locale;
import net.pms.formats.v2.SubtitleType;
import static net.pms.formats.v2.SubtitleType.UNKNOWN;
import static net.pms.util.Constants.CHARSET_UTF_8;
import net.pms.util.FileUtil;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class keeps track of the subtitle information for media.
*/
public class DLNAMediaSubtitle extends DLNAMediaLang implements Cloneable {
private static final Logger LOGGER = LoggerFactory.getLogger(DLNAMediaSubtitle.class);
private SubtitleType type = UNKNOWN;
private String subtitlesTrackTitleFromMetadata;
private File externalFile;
private String subsCharacterSet;
private String liveSubURL;
private String liveSubFile;
private boolean isStreamable = false;
private File convertedFile;
/**
* Returns whether or not the subtitles are embedded.
*
* @return True if the subtitles are embedded, false otherwise.
* @since 1.51.0
*/
public boolean isEmbedded() {
return (externalFile == null);
}
/**
* Returns whether or not the subtitles are external.
*
* @return True if the subtitles are external file, false otherwise.
* @since 1.70.0
*/
public boolean isExternal() {
return !isEmbedded();
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("id: ");
result.append(getId());
result.append(", type: ");
result.append(type);
if (isNotBlank(subtitlesTrackTitleFromMetadata)) {
result.append(", subtitles track title from metadata: ");
result.append(subtitlesTrackTitleFromMetadata);
}
result.append(", lang: ");
result.append(getLang());
if (externalFile != null) {
result.append(", externalFile: ");
result.append(externalFile.toString());
result.append(", external file character set: ");
result.append(subsCharacterSet);
}
if (convertedFile != null) {
result.append(", convertedFile: ");
result.append(convertedFile.toString());
}
return result.toString();
}
/**
* @deprecated charset is autodetected for text subtitles after setExternalFile()
*/
@Deprecated
public void checkUnicode() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* @return the type
*/
public SubtitleType getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(SubtitleType type) {
if (type == null) {
throw new IllegalArgumentException("Can't set null SubtitleType.");
}
this.type = type;
}
/**
* @deprecated use getSubtitlesTrackTitleFromMetadata()
*/
@Deprecated
public String getFlavor() {
return getSubtitlesTrackTitleFromMetadata();
}
/**
* @deprecated use setSubtitlesTrackTitleFromMetadata()
*/
@Deprecated
public void setFlavor(String value) {
setSubtitlesTrackTitleFromMetadata(value);
}
public String getSubtitlesTrackTitleFromMetadata() {
return subtitlesTrackTitleFromMetadata;
}
public void setSubtitlesTrackTitleFromMetadata(String value) {
this.subtitlesTrackTitleFromMetadata = value;
}
/**
* @deprecated use {@link #FileUtil.convertFileFromUtf16ToUtf8()} for UTF-16 -> UTF-8 conversion.
*/
public File getPlayableExternalFile() {
return getExternalFile();
}
/**
* @return the externalFile
*/
public File getExternalFile() {
return externalFile;
}
/**
* Set external subs file, detect its Character Set and Language. When the {@code forcedLang} is not {@code null},
* based on the language tag in the file name e.g {@code subsname.en.srt}, than it has priority over the detected language.
*
* @param externalFile the externalFile to set
* @param forcedLang language forced by file name language tag
*/
public void setExternalFile(File externalFile, String forcedLang) throws FileNotFoundException {
if (externalFile == null) {
throw new FileNotFoundException("Can't read file: no file supplied");
} else if (!FileUtil.getFilePermissions(externalFile).isReadable()) {
throw new FileNotFoundException("Insufficient permission to read " + externalFile.getAbsolutePath());
}
this.externalFile = externalFile;
setFileSubsCharacterSet(forcedLang);
}
/**
* Detects and set Character Set and language of the subs file. When the {@code forcedLang} is not {@code null}
* than it as priority over the detected language.
*
* @param forcedLang forced language
*/
private void setFileSubsCharacterSet(String forcedLang) {
if (type.isPicture()) {
subsCharacterSet = null;
} else {
try {
CharsetMatch match = FileUtil.getFileCharsetMatch(externalFile);
if (match != null) {
subsCharacterSet = match.getName().toUpperCase(Locale.ROOT);
if (forcedLang == null) { // set the detected language when the language is not specified in the filename
lang = match.getLanguage();
}
LOGGER.debug("Set detected charset \"{}\" and language \"{}\" for {}", match.getName(), lang, externalFile.getAbsolutePath());
} else {
subsCharacterSet = null;
LOGGER.debug("No charset detected for {}", externalFile.getAbsolutePath());
}
} catch (IOException ex) {
subsCharacterSet = null;
LOGGER.warn("Exception during external file charset detection: ", ex.getMessage());
}
}
}
/**
* @deprecated use {@link #setSubCharacterSet(String)}
*/
public void setExternalFileCharacterSet(String charSet) {
setSubCharacterSet(charSet);
}
public void setSubCharacterSet(String charSet) {
subsCharacterSet = charSet;
}
/**
* @deprecated use {@link #getSubCharacterSet()}
*/
public String getExternalFileCharacterSet() {
return getSubCharacterSet();
}
public String getSubCharacterSet() {
return subsCharacterSet;
}
/**
* @return true if subtitles is UTF-8 encoded, false otherwise.
*/
public boolean isSubsUtf8() {
return equalsIgnoreCase(subsCharacterSet, CHARSET_UTF_8);
}
/**
* @return true if external subtitles file is UTF-8 encoded, false otherwise.
*/
public boolean isExternalFileUtf8() {
return FileUtil.isCharsetUTF8(subsCharacterSet);
}
/**
* @return true if external subtitles file is UTF-16 encoded, false otherwise.
*/
public boolean isExternalFileUtf16() {
return FileUtil.isCharsetUTF16(subsCharacterSet);
}
/**
* @return true if external subtitles file is UTF-32 encoded, false otherwise.
*/
public boolean isExternalFileUtf32() {
return FileUtil.isCharsetUTF32(subsCharacterSet);
}
/**
* @return true if external subtitles file is UTF-8, UTF-16 or UTF-32 encoded, false otherwise.
*/
public boolean isExternalFileUtf() {
return (isExternalFileUtf8() || isExternalFileUtf16() || isExternalFileUtf32());
}
public void setLiveSub(String url) {
setLiveSub(url, null);
}
public void setLiveSub(String url, String file) {
liveSubURL = url;
liveSubFile = file;
}
public String getLiveSubURL() {
return liveSubURL;
}
public String getLiveSubFile() {
return liveSubFile;
}
public boolean isStreamable() {
return isExternal() && isStreamable;
}
public void setSubsStreamable(boolean isStreamable) {
this.isStreamable = isStreamable;
}
public void setConvertedFile (File convertedFile) {
this.convertedFile = convertedFile;
}
public File getConvertedFile() {
return convertedFile;
}
}