Who is interested below you can find patch for support of "forced subs" and "additional tags" for audio and subtitle streams(descriptions)
- Code: Select all
Index: net/pms/configuration/PmsConfiguration.java
===================================================================
--- net/pms/configuration/PmsConfiguration.java (revision 410)
+++ net/pms/configuration/PmsConfiguration.java (working copy)
@@ -123,6 +123,7 @@
private static final int DEFAULT_SERVER_PORT = 5001;
private static final int DEFAULT_PROXY_SERVER_PORT = -1;
private static final String UNLIMITED_BITRATE = "0";
+ private static final String KEY_FORCED_SUBS_TAGS = "forced_subs_tags";
private static final String DEFAULT_AVI_SYNTH_SCRIPT =
"#AviSynth script is now fully customisable !\n"
@@ -1118,4 +1119,7 @@
public void setAudioResample(boolean value) {
configuration.setProperty(KEY_AUDIO_RESAMPLE, value);
}
+ public String getForcedSubsTags() {
+ return getString(KEY_FORCED_SUBS_TAGS, "forced");
+ }
}
Index: net/pms/configuration/RendererConfiguration.java
===================================================================
--- net/pms/configuration/RendererConfiguration.java (revision 410)
+++ net/pms/configuration/RendererConfiguration.java (working copy)
@@ -303,6 +303,17 @@
return getBoolean(SHOW_DVD_TITLE_DURATION, false);
}
+ private static final String SHOW_AUDIO_METADATA="ShowAudioMetadata";
+ private static final String SHOW_SUB_METADATA="ShowSubMetadata";
+
+ public boolean isShowAudioMetadata() {
+ return getBoolean(SHOW_AUDIO_METADATA, true);
+ }
+
+ public boolean isShowSubMetadata() {
+ return getBoolean(SHOW_SUB_METADATA, true);
+ }
+
private RendererConfiguration() throws ConfigurationException {
this(null);
}
Index: net/pms/dlna/DLNAMediaAudio.java
===================================================================
--- net/pms/dlna/DLNAMediaAudio.java (revision 410)
+++ net/pms/dlna/DLNAMediaAudio.java (working copy)
@@ -13,6 +13,7 @@
public int year;
public int track;
public int delay;
+ public String flavor;
public DLNAMediaAudio() {
bitsperSample = 16;
@@ -76,7 +77,7 @@
}
public String toString() {
- return "Audio: " + getAudioCodec() + " / lang: " + lang + " / ID: " + id;
+ return "Audio: " + getAudioCodec() + " / lang: " + lang + " / flavor: " + flavor + " / ID: " + id;
}
@Override
Index: net/pms/dlna/DLNAMediaDatabase.java
===================================================================
--- net/pms/dlna/DLNAMediaDatabase.java (revision 410)
+++ net/pms/dlna/DLNAMediaDatabase.java (working copy)
@@ -174,6 +174,7 @@
sb.append(" FILEID INT NOT NULL"); //$NON-NLS-1$
sb.append(", ID INT NOT NULL"); //$NON-NLS-1$
sb.append(", LANG VARCHAR2(3)"); //$NON-NLS-1$
+ sb.append(", FLAVOR VARCHAR2(64)"); //$NON-NLS-1$
sb.append(", NRAUDIOCHANNELS NUMERIC"); //$NON-NLS-1$
sb.append(", SAMPLEFREQ VARCHAR2(16)"); //$NON-NLS-1$
sb.append(", CODECA VARCHAR2(32)"); //$NON-NLS-1$
@@ -192,6 +193,7 @@
sb.append(" FILEID INT NOT NULL"); //$NON-NLS-1$
sb.append(", ID INT NOT NULL"); //$NON-NLS-1$
sb.append(", LANG VARCHAR2(3)"); //$NON-NLS-1$
+ sb.append(", FLAVOR VARCHAR2(64)"); //$NON-NLS-1$
sb.append(", TYPE INT"); //$NON-NLS-1$
sb.append(", constraint PKSUB primary key (FILEID, ID))"); //$NON-NLS-1$
@@ -302,6 +304,7 @@
DLNAMediaAudio audio = new DLNAMediaAudio();
audio.id = subrs.getInt("ID"); //$NON-NLS-1$
audio.lang = subrs.getString("LANG"); //$NON-NLS-1$
+ audio.flavor = subrs.getString("FLAVOR"); //$NON-NLS-1$
audio.nrAudioChannels = subrs.getInt("NRAUDIOCHANNELS"); //$NON-NLS-1$
audio.sampleFrequency = subrs.getString("SAMPLEFREQ"); //$NON-NLS-1$
audio.codecA = subrs.getString("CODECA"); //$NON-NLS-1$
@@ -325,6 +328,7 @@
DLNAMediaSubtitle sub = new DLNAMediaSubtitle();
sub.id = subrs.getInt("ID"); //$NON-NLS-1$
sub.lang = subrs.getString("LANG"); //$NON-NLS-1$
+ sub.flavor = subrs.getString("FLAVOR"); //$NON-NLS-1$
sub.type = subrs.getInt("TYPE"); //$NON-NLS-1$
media.subtitlesCodes.add(sub);
}
@@ -405,35 +409,37 @@
if (media != null && id > -1) {
PreparedStatement insert = null;
if (media.audioCodes.size() > 0)
- insert = conn.prepareStatement("INSERT INTO AUDIOTRACKS VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); //$NON-NLS-1$
+ insert = conn.prepareStatement("INSERT INTO AUDIOTRACKS VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); //$NON-NLS-1$
for(DLNAMediaAudio audio:media.audioCodes) {
insert.clearParameters();
insert.setInt(1, id);
insert.setInt(2, audio.id);
insert.setString(3, audio.lang);
- insert.setInt(4, audio.nrAudioChannels);
- insert.setString(5, audio.sampleFrequency);
- insert.setString(6, audio.codecA);
- insert.setInt(7, audio.bitsperSample);
- insert.setString(8, StringUtils.trimToEmpty(audio.album));
- insert.setString(9, StringUtils.trimToEmpty(audio.artist));
- insert.setString(10, StringUtils.trimToEmpty(audio.songname));
- insert.setString(11, StringUtils.trimToEmpty(audio.genre));
- insert.setInt(12, audio.year);
- insert.setInt(13, audio.track);
- insert.setInt(14, audio.delay);
+ insert.setString(4, audio.flavor);
+ insert.setInt(5, audio.nrAudioChannels);
+ insert.setString(6, audio.sampleFrequency);
+ insert.setString(7, audio.codecA);
+ insert.setInt(8, audio.bitsperSample);
+ insert.setString(9, StringUtils.trimToEmpty(audio.album));
+ insert.setString(10, StringUtils.trimToEmpty(audio.artist));
+ insert.setString(11, StringUtils.trimToEmpty(audio.songname));
+ insert.setString(12, StringUtils.trimToEmpty(audio.genre));
+ insert.setInt(13, audio.year);
+ insert.setInt(14, audio.track);
+ insert.setInt(15, audio.delay);
insert.executeUpdate();
}
if (media.subtitlesCodes.size() > 0)
- insert = conn.prepareStatement("INSERT INTO SUBTRACKS VALUES (?, ?, ?, ?)"); //$NON-NLS-1$
+ insert = conn.prepareStatement("INSERT INTO SUBTRACKS VALUES (?, ?, ?, ?, ?)"); //$NON-NLS-1$
for(DLNAMediaSubtitle sub:media.subtitlesCodes) {
if (sub.file == null) { // no save of external subtitles
insert.clearParameters();
insert.setInt(1, id);
insert.setInt(2, sub.id);
insert.setString(3, sub.lang);
- insert.setInt(4, sub.type);
+ insert.setString(4, sub.flavor);
+ insert.setInt(5, sub.type);
insert.executeUpdate();
}
}
Index: net/pms/dlna/DLNAMediaInfo.java
===================================================================
--- net/pms/dlna/DLNAMediaInfo.java (revision 410)
+++ net/pms/dlna/DLNAMediaInfo.java (working copy)
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.ListIterator;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -415,7 +416,9 @@
ArrayList<String> lines = (ArrayList<String>) pw.getResults();
int langId = 0;
int subId = 0;
+ ListIterator<String> FFmpegMetaData = lines.listIterator();
for(String line:lines) {
+ FFmpegMetaData.next();
line = line.trim();
if (line.startsWith("Output"))
matchs = false;
@@ -478,7 +481,7 @@
}
}
//
- audioCodes.add(audio);
+// audioCodes.add(audio);
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
if (token.startsWith("Stream")) {
@@ -502,8 +505,21 @@
audio.bitsperSample = 32;
} else if (token.equals("s24")) {
audio.bitsperSample = 24;
+ } else if (token.equals("s16")) {
+ audio.bitsperSample = 16;
}
}
+ int FFmpegMetaDataNr = FFmpegMetaData.nextIndex();
+ if (FFmpegMetaDataNr > -1) line = lines.get(FFmpegMetaDataNr);
+ if (line.indexOf("Metadata:") > -1) {
+ line = lines.get(FFmpegMetaData.nextIndex()+1);
+ int aa = line.indexOf(": ");
+ int bb = line.length();
+ if (aa > -1 && bb > aa) {
+ audio.flavor = line.substring(aa+2, bb);
+ }
+ }
+ audioCodes.add(audio);
} else if (line.indexOf("Video:") > -1) {
StringTokenizer st = new StringTokenizer(line, ",");
while (st.hasMoreTokens()) {
@@ -547,7 +563,18 @@
} else
lang.lang = DLNAMediaLang.UND;
lang.id = subId++;
- subtitlesCodes.add(lang);
+// subtitlesCodes.add(lang);
+ int FFmpegMetaDataNr = FFmpegMetaData.nextIndex();
+ if (FFmpegMetaDataNr > -1) line = lines.get(FFmpegMetaDataNr);
+ if (line.indexOf("Metadata:") > -1) {
+ line = lines.get(FFmpegMetaData.nextIndex()+1);
+ int aa = line.indexOf(": ");
+ int bb = line.length();
+ if (aa > -1 && bb > aa) {
+ lang.flavor = line.substring(aa+2, bb);
+ }
+ }
+ subtitlesCodes.add(lang);
}
}
}
@@ -794,12 +821,12 @@
public String toString() {
String s = "container: " + container + " / bitrate: " + bitrate + " / size: " + size + " / codecV: " + codecV + " / duration: " + duration + " / width: " + width + " / height: " + height + " / frameRate: " + frameRate + " / thumb size : " + (thumb!=null?thumb.length:0);
for(DLNAMediaAudio audio:audioCodes) {
- s += "\n\taudio: id=" + audio.id + " / lang: " + audio.lang + " / codec: " + audio.codecA + " / sf:" + audio.sampleFrequency + " / na: " + audio.nrAudioChannels + " / bs: " + audio.bitsperSample;
+ s += "\n\taudio: id=" + audio.id + " / lang: " + audio.lang + " / flavor: " + audio.flavor + " / codec: " + audio.codecA + " / sf:" + audio.sampleFrequency + " / na: " + audio.nrAudioChannels + " / bs: " + audio.bitsperSample;
if (audio.artist != null)
s += " / " + audio.artist + "|" + audio.album + "|" + audio.songname + "|" + audio.year + "|" + audio.track;
}
for(DLNAMediaSubtitle sub:subtitlesCodes) {
- s += "\n\tsub: id=" + sub.id + " / lang: " + sub.lang + " / type: " + sub.type;
+ s += "\n\tsub: id=" + sub.id + " / lang: " + sub.lang + " / flavor: " + sub.flavor + " / type: " + sub.type;
}
return s;
}
Index: net/pms/dlna/DLNAMediaSubtitle.java
===================================================================
--- net/pms/dlna/DLNAMediaSubtitle.java (revision 410)
+++ net/pms/dlna/DLNAMediaSubtitle.java (working copy)
@@ -49,7 +49,7 @@
}
public String toString() {
- return "Sub: " + getSubType() + " / lang: " + lang + " / ID: " + id + " / FILE: " + (file!=null?file.getAbsolutePath():"-");
+ return "Sub: " + getSubType() + " / lang: " + lang + " / flavor: " + flavor + " / ID: " + id + " / FILE: " + (file!=null?file.getAbsolutePath():"-");
}
public void checkUnicode() {
Index: net/pms/dlna/DLNAResource.java
===================================================================
--- net/pms/dlna/DLNAResource.java (revision 410)
+++ net/pms/dlna/DLNAResource.java (working copy)
@@ -528,10 +528,10 @@
name += " {External Subtitles}";
if (media_audio != null)
- name = (player!=null?("[" + player.name() + "]"):"") + " {Audio: " + media_audio.getAudioCodec() + "/" + media_audio.getLang() + "}";
+ name = (player!=null?("[" + player.name() + "]"):"") + " {Audio: " + media_audio.getAudioCodec() + "/" + media_audio.getLang() + ((media_audio.flavor!=null&&mediaRenderer!=null&&mediaRenderer.isShowAudioMetadata())?(" ("+media_audio.flavor+")"):"") + "}";
if (media_subtitle != null && media_subtitle.id != -1)
- name += " {Sub: " + media_subtitle.getSubType() + "/" + media_subtitle.getLang() + (media_subtitle.flavor!=null?("/"+media_subtitle.flavor):"") + "}";
+ name += " {Sub: " + media_subtitle.getSubType() + "/" + media_subtitle.getLang() + ((media_subtitle.flavor!=null&&mediaRenderer!=null&&mediaRenderer.isShowSubMetadata())?(" ("+media_subtitle.flavor+")"):"") + "}";
if (avisynth)
name = (player!=null?("[" + player.name()):"") + " + AviSynth]";
Index: net/pms/dlna/MediaInfoParser.java
===================================================================
--- net/pms/dlna/MediaInfoParser.java (revision 410)
+++ net/pms/dlna/MediaInfoParser.java (working copy)
@@ -95,6 +95,11 @@
currentAudioTrack.lang = getLang(value);
else if (step == MediaInfo.StreamKind.Text)
currentSubTrack.lang = getLang(value);
+ } else if (key.equals("Title")) {
+ if (step == MediaInfo.StreamKind.Audio)
+ currentAudioTrack.flavor = getFlavor(value);
+ else if (step == MediaInfo.StreamKind.Text)
+ currentSubTrack.flavor = getFlavor(value);
} else if (key.equals("Width")) {
media.width = getPixelValue(value);
} else if (key.equals("Encryption") && !media.encrypted) {
@@ -376,6 +381,11 @@
return value;
}
+ public static String getFlavor(String value) {
+ value = value.trim();
+ return value;
+ }
+
public static String getDuration(String value) {
int h = 0, m = 0, s = 0;
StringTokenizer st = new StringTokenizer(value, " ");
Index: net/pms/encoders/Player.java
===================================================================
--- net/pms/encoders/Player.java (revision 410)
+++ net/pms/encoders/Player.java (working copy)
@@ -155,7 +155,7 @@
if (matchedSub != null && params.sid == null) {
if (matchedSub.lang != null && matchedSub.lang.equals("off")) {
PMS.debug(" Disabled the subtitles: " + matchedSub);
- return;
+ //return;
} else
params.sid = matchedSub;
}
@@ -168,16 +168,39 @@
FileUtil.doesSubtitlesExists(video, media, false);
if (configuration.getUseSubtitles()) {
+ boolean forcedSubsFound = false;
// priority to external subtitles
for(DLNAMediaSubtitle sub:media.subtitlesCodes) {
- PMS.debug("Found subtitles track : " + sub);
- if (sub.file != null) {
- PMS.debug("Found external file : " + sub.file.getAbsolutePath());
- params.sid = sub;
- break;
+ if (matchedSub !=null && matchedSub.lang !=null && matchedSub.lang.equals("off")) {
+ StringTokenizer st = new StringTokenizer(configuration.getForcedSubsTags(), ","); //$NON-NLS-1$
+ while (st != null && sub.flavor != null && st.hasMoreTokens()) {
+ String forcedTags = st.nextToken();
+ forcedTags = forcedTags.trim();
+ if (sub.flavor.toLowerCase().indexOf(forcedTags) > -1) {
+ if (Iso639.isCodesMatching(sub.lang,configuration.getMencoderSubLanguages())) {
+ PMS.debug("Forcing prefered subtitles : " + sub.getLang() + "/" + sub.flavor);
+ PMS.debug("Forced subtitles track : " + sub);
+ if (sub.file != null) {
+ PMS.debug("Found external forced file : " + sub.file.getAbsolutePath());
+ }
+ params.sid = sub;
+ forcedSubsFound = true;
+ break;
+ }
+ }
+ }
+ if (forcedSubsFound == true) break;
+ } else {
+ PMS.debug("Found subtitles track : " + sub);
+ if (sub.file != null) {
+ PMS.debug("Found external file : " + sub.file.getAbsolutePath());
+ params.sid = sub;
+ break;
+ }
}
}
}
+ if (matchedSub !=null && matchedSub.lang !=null && matchedSub.lang.equals("off")) return;
//
if (params.sid == null) {
What does it mean and how to use it?
It shows descriptions for AUDIO/SUBTITLE tracks if defined so you can imagine if audio is documentary or main stream, in case of subtitles if it is full, forced or documentary subtitles etc.
In case of subtitles we can use this tag for loading FORCED subtitles automatically (if present).
I added 3 variables for better handling with this addon:
Here you can define tags which should be considered as forced subtitles.forced_subs_tags can be added to pms.conf file. If not, "forced" string is used as default
example: forced_subs_tags=forced,singing,burned
It is configurable on renderer level because some renderers can't scroll long lines so you can disable it on problematic renderers where you loose language part due to very long line.ShowAudioMetadata and ShowSubMetadata can be added to renderer conf file. If not, TRUE is used as default = description visible if exists
example: ShowAudioMetadata=false
ShowSubMetadata=true
How to use it:
1. Install latest r410 or newer
2. put update.jar next to pms.exe/pms.sh and pms.jar file(in installed directory) or compile whole build yourself
3. restart PMS and reset Media Library if used (old database doesn't include new additional info about audio/subtitles tags)
Prerequisites for "forced" subs support & displaying additional audio/subtitles info:
You need:
- latest FFmpeg version which supports Metadata (already included from Windows r381 builds and newer, don't know about other OS)
- or enable MediaInfo engine in renderer config (recent build with MediaInfo.dll needed)
How to configure forced subtitles:
To define tag for external subtitle file use this example: moviename.cs-full.srt or moviename.cs-forced.srt etc. Delimiter for tag is "-"
For tagging subtitles in MKV container(if tag is missing), you can use e.g. MKVTOOLNIX(mkvmerge GUI) and add this info as "Track name".

When configured, how to load it automatically?
- you need to enable "Autoload *.srt/*.sub subtitles with the same name" in PMS
- and choose [MENCODER] or [MENCODER]{External Subtitles} inside #--TRANSCODE--# folder
- or choose Movie_Name.avi[MENCODER]{External Subtitles} outside #--TRANSCODE--# folder
That's all
