private static void assertFolderIsValid(File folder) throws IOException { if (!folder.isDirectory()) { throw new IOException("Temp directory must be a directory: " + folder); } if (!FileUtil.isDirectoryWritable(folder)) { throw new IOException("Temp directory is not writable: " + folder); } }
@Override public boolean isValid() { checktype(); setSrtFile(FileUtil.doesSubtitlesExists(pere, null)); return getFormat() != null; }
@Override public String getSystemName() { return FileUtil.getFileNameWithoutExtension(pere.getAbsolutePath()) + "." + FileUtil.getExtension(name); }
/** * This method populates the supplied {@link OutputParams} object with the correct subtitles (sid) * based on the given filename, its MediaInfo metadata and PMS configuration settings. * * <p>TODO: Rewrite this crazy method to be more concise and logical. * * @param fileName The file name used to determine the availability of subtitles. * @param media The MediaInfo metadata for the file. * @param params The parameters to populate. */ public static void setSubtitleOutputParameters( String fileName, DLNAMediaInfo media, OutputParams params) { PmsConfiguration configuration = PMS.getConfiguration(params); String currentLang = null; DLNAMediaSubtitle matchedSub = null; if (params.aid != null) { currentLang = params.aid.getLang(); } if (params.sid != null && params.sid.getId() == -1) { LOGGER.trace("Don't want subtitles!"); params.sid = null; return; } /** Check for live subtitles */ if (params.sid != null && !StringUtils.isEmpty(params.sid.getLiveSubURL())) { LOGGER.debug("Live subtitles " + params.sid.getLiveSubURL()); try { matchedSub = params.sid; String file = OpenSubtitle.fetchSubs(matchedSub.getLiveSubURL(), matchedSub.getLiveSubFile()); if (!StringUtils.isEmpty(file)) { matchedSub.setExternalFile(new File(file)); params.sid = matchedSub; return; } } catch (IOException e) { } } StringTokenizer st = new StringTokenizer(configuration.getAudioSubLanguages(), ";"); /** Check for external and internal subtitles matching the user's language preferences */ boolean matchedInternalSubtitles = false; boolean matchedExternalSubtitles = false; while (st.hasMoreTokens()) { String pair = st.nextToken(); if (pair.contains(",")) { String audio = pair.substring(0, pair.indexOf(',')); String sub = pair.substring(pair.indexOf(',') + 1); audio = audio.trim(); sub = sub.trim(); LOGGER.trace( "Searching for a match for: " + currentLang + " with " + audio + " and " + sub); if (Iso639.isCodesMatching(audio, currentLang) || (currentLang != null && audio.equals("*"))) { if (sub.equals("off")) { /** * Ignore the "off" language for external subtitles if the user setting is enabled TODO: * Prioritize multiple external subtitles properly instead of just taking the first one * we load */ if (configuration.isForceExternalSubtitles()) { for (DLNAMediaSubtitle present_sub : media.getSubtitleTracksList()) { if (present_sub.getExternalFile() != null) { matchedSub = present_sub; matchedExternalSubtitles = true; LOGGER.trace( "Ignoring the \"off\" language because there are external subtitles"); break; } } } if (!matchedExternalSubtitles) { matchedSub = new DLNAMediaSubtitle(); matchedSub.setLang("off"); } } else { for (DLNAMediaSubtitle present_sub : media.getSubtitleTracksList()) { if (present_sub.matchCode(sub) || sub.equals("*")) { if (present_sub.getExternalFile() != null) { if (configuration.isAutoloadExternalSubtitles()) { // Subtitle is external and we want external subtitles, look no further matchedSub = present_sub; LOGGER.trace("Matched external subtitles track: " + matchedSub); break; } else { // Subtitle is external but we do not want external subtitles, keep searching LOGGER.trace( "External subtitles ignored because of user setting: " + present_sub); } } else if (!matchedInternalSubtitles) { matchedSub = present_sub; LOGGER.trace("Matched internal subtitles track: " + matchedSub); if (configuration.isAutoloadExternalSubtitles()) { // Subtitle is internal and we will wait to see if an external one is available // instead matchedInternalSubtitles = true; } else { // Subtitle is internal and we will use it break; } } } } } if (matchedSub != null && !matchedInternalSubtitles) { break; } } } } /** * Check for external subtitles that were skipped in the above code block because they didn't * match language preferences, if there wasn't already a match and the user settings specify it. */ if (matchedSub == null && configuration.isForceExternalSubtitles()) { for (DLNAMediaSubtitle present_sub : media.getSubtitleTracksList()) { if (present_sub.getExternalFile() != null) { matchedSub = present_sub; LOGGER.trace( "Matched external subtitles track that did not match language preferences: " + matchedSub); break; } } } /** * Disable chosen subtitles if the user has disabled all subtitles or if the language * preferences have specified the "off" language. * * <p>TODO: Can't we save a bunch of looping by checking for isDisableSubtitles just after the * Live Subtitles check above? */ if (matchedSub != null && params.sid == null) { if (configuration.isDisableSubtitles() || (matchedSub.getLang() != null && matchedSub.getLang().equals("off"))) { LOGGER.trace("Disabled the subtitles: " + matchedSub); } else { params.sid = matchedSub; } } /** Check for forced subtitles. */ if (!configuration.isDisableSubtitles() && params.sid == null && media != null) { // Check for subtitles again File video = new File(fileName); FileUtil.isSubtitlesExists(video, media, false); if (configuration.isAutoloadExternalSubtitles()) { boolean forcedSubsFound = false; // Priority to external subtitles for (DLNAMediaSubtitle sub : media.getSubtitleTracksList()) { if (matchedSub != null && matchedSub.getLang() != null && matchedSub.getLang().equals("off")) { st = new StringTokenizer(configuration.getForcedSubtitleTags(), ","); while (sub.getSubtitlesTrackTitleFromMetadata() != null && st.hasMoreTokens()) { String forcedTags = st.nextToken(); forcedTags = forcedTags.trim(); if (sub.getSubtitlesTrackTitleFromMetadata().toLowerCase().contains(forcedTags) && Iso639.isCodesMatching( sub.getLang(), configuration.getForcedSubtitleLanguage())) { LOGGER.trace( "Forcing preferred subtitles: " + sub.getLang() + "/" + sub.getSubtitlesTrackTitleFromMetadata()); LOGGER.trace("Forced subtitles track: " + sub); if (sub.getExternalFile() != null) { LOGGER.trace( "Found external forced file: " + sub.getExternalFile().getAbsolutePath()); } params.sid = sub; forcedSubsFound = true; break; } } if (forcedSubsFound == true) { break; } } else { LOGGER.trace("Found subtitles track: " + sub); if (sub.getExternalFile() != null) { LOGGER.trace("Found external file: " + sub.getExternalFile().getAbsolutePath()); params.sid = sub; break; } } } } if (matchedSub != null && matchedSub.getLang() != null && matchedSub.getLang().equals("off")) { return; } if (params.sid == null) { st = new StringTokenizer(UMSUtils.getLangList(params.mediaRenderer), ","); while (st.hasMoreTokens()) { String lang = st.nextToken(); lang = lang.trim(); LOGGER.trace("Looking for a subtitle track with lang: " + lang); for (DLNAMediaSubtitle sub : media.getSubtitleTracksList()) { if (sub.matchCode(lang) && !(!configuration.isAutoloadExternalSubtitles() && sub.getExternalFile() != null)) { params.sid = sub; LOGGER.trace("Matched subtitles track: " + params.sid); return; } } } } } }
@Override public void handle(HttpExchange t) throws IOException { if (RemoteUtil.deny(t)) { throw new IOException("Access denied"); } RootFolder root = parent.getRoot(RemoteUtil.userName(t), t); if (root == null) { throw new IOException("Unknown root"); } Headers h = t.getRequestHeaders(); for (String h1 : h.keySet()) { LOGGER.debug("key " + h1 + "=" + h.get(h1)); } String id = RemoteUtil.getId(path, t); id = RemoteUtil.strip(id); RendererConfiguration r = render; if (render == null) { r = root.getDefaultRenderer(); } DLNAResource dlna = root.getDLNAResource(id, r); if (dlna == null) { // another error LOGGER.debug("media unkonwn"); throw new IOException("Bad id"); } if (!dlna.isCodeValid(dlna)) { LOGGER.debug("coded object with invalid code"); throw new IOException("Bad code"); } DLNAMediaSubtitle sid = null; long len = dlna.length(); Range range = RemoteUtil.parseRange(t.getRequestHeaders(), len); String mime = root.getDefaultRenderer().getMimeType(dlna.mimeType()); // DLNAResource dlna = res.get(0); WebRender render = (WebRender) r; DLNAMediaInfo m = dlna.getMedia(); if (m == null) { m = new DLNAMediaInfo(); dlna.setMedia(m); } if (mime.equals(FormatConfiguration.MIMETYPE_AUTO) && m.getMimeType() != null) { mime = m.getMimeType(); } int code = 200; dlna.setDefaultRenderer(r); if (dlna.getFormat().isVideo()) { if (flash) { mime = "video/flash"; } else if (!RemoteUtil.directmime(mime) || RemoteUtil.transMp4(mime, m)) { mime = render != null ? render.getVideoMimeType() : RemoteUtil.transMime(); if (FileUtil.isUrl(dlna.getSystemName())) { dlna.setPlayer(new FFmpegWebVideo()); } else { dlna.setPlayer(new FFMpegVideo()); } // code = 206; } if (PMS.getConfiguration().getWebSubs() && dlna.getMediaSubtitle() != null && dlna.getMediaSubtitle().isExternal()) { // fetched on the side sid = dlna.getMediaSubtitle(); dlna.setMediaSubtitle(null); } } if (!RemoteUtil.directmime(mime) && dlna.getFormat().isAudio()) { dlna.setPlayer(new FFmpegAudio()); code = 206; } m.setMimeType(mime); LOGGER.debug("dumping media " + mime + " " + dlna); InputStream in = dlna.getInputStream(range, root.getDefaultRenderer()); Headers hdr = t.getResponseHeaders(); hdr.add("Content-Type", mime); hdr.add("Accept-Ranges", "bytes"); if (range != null) { long end = range.asByteRange().getEnd(); long start = range.asByteRange().getStart(); String rStr = start + "-" + end + "/*"; hdr.add("Content-Range", "bytes " + rStr); if (start != 0) { code = 206; } } hdr.add("Server", PMS.get().getServerName()); hdr.add("Connection", "keep-alive"); t.sendResponseHeaders(code, 0); OutputStream os = t.getResponseBody(); render.start(dlna); if (sid != null) { dlna.setMediaSubtitle(sid); } RemoteUtil.dump(in, os, render); }