Search in sources :

Example 1 with DLNAResource

use of net.pms.dlna.DLNAResource in project UniversalMediaServer by UniversalMediaServer.

the class RemotePlayHandler method mkPage.

private String mkPage(String id, HttpExchange t) throws IOException {
    HashMap<String, Object> vars = new HashMap<>();
    vars.put("serverName", configuration.getServerDisplayName());
    LOGGER.debug("Make play page " + id);
    RootFolder root = parent.getRoot(RemoteUtil.userName(t), t);
    if (root == null) {
        LOGGER.debug("root not found");
        throw new IOException("Unknown root");
    }
    WebRender renderer = (WebRender) root.getDefaultRenderer();
    renderer.setBrowserInfo(RemoteUtil.getCookie("UMSINFO", t), t.getRequestHeaders().getFirst("User-agent"));
    // List<DLNAResource> res = root.getDLNAResources(id, false, 0, 0, renderer);
    DLNAResource r = root.getDLNAResource(id, renderer);
    if (r == null) {
        LOGGER.debug("Bad web play id: " + id);
        throw new IOException("Bad Id");
    }
    if (!r.isCodeValid(r)) {
        LOGGER.debug("coded object with invalid code");
        throw new IOException("Bad code");
    }
    if (r instanceof VirtualVideoAction) {
        // waste of resource to play dummy video
        if (((VirtualVideoAction) r).enable()) {
            renderer.notify(renderer.INFO, r.getName() + " enabled");
        } else {
            renderer.notify(renderer.INFO, r.getName() + " disabled");
        }
        return returnPage();
    }
    Format format = r.getFormat();
    boolean isImage = format.isImage();
    boolean isVideo = format.isVideo();
    boolean isAudio = format.isAudio();
    String query = t.getRequestURI().getQuery();
    boolean forceFlash = StringUtils.isNotEmpty(RemoteUtil.getQueryVars(query, "flash"));
    boolean forcehtml5 = StringUtils.isNotEmpty(RemoteUtil.getQueryVars(query, "html5"));
    boolean flowplayer = isVideo && (forceFlash || (!forcehtml5 && configuration.getWebFlash()));
    // hack here to ensure we got a root folder to use for recently played etc.
    root.getDefaultRenderer().setRootFolder(root);
    String id1 = URLEncoder.encode(id, "UTF-8");
    String name = StringEscapeUtils.escapeHtml(r.resumeName());
    String mime = root.getDefaultRenderer().getMimeType(r.mimeType(), r.getMedia());
    String mediaType = isVideo ? "video" : isAudio ? "audio" : isImage ? "image" : "";
    String auto = "autoplay";
    @SuppressWarnings("unused") String coverImage = "";
    if (isVideo) {
        if (mime.equals(FormatConfiguration.MIMETYPE_AUTO)) {
            if (r.getMedia() != null && r.getMedia().getMimeType() != null) {
                mime = r.getMedia().getMimeType();
            }
        }
        if (!flowplayer) {
            if (!RemoteUtil.directmime(mime) || RemoteUtil.transMp4(mime, r.getMedia()) || r.isResume()) {
                WebRender render = (WebRender) r.getDefaultRenderer();
                mime = render != null ? render.getVideoMimeType() : RemoteUtil.transMime();
            }
        }
    }
    vars.put("isVideo", isVideo);
    vars.put("name", name);
    vars.put("id1", id1);
    vars.put("autoContinue", configuration.getWebAutoCont(format));
    if (configuration.isDynamicPls()) {
        if (r.getParent() instanceof Playlist) {
            vars.put("plsOp", "del");
            vars.put("plsSign", "-");
            vars.put("plsAttr", RemoteUtil.getMsgString("Web.4", t));
        } else {
            vars.put("plsOp", "add");
            vars.put("plsSign", "+");
            vars.put("plsAttr", RemoteUtil.getMsgString("Web.5", t));
        }
    }
    addNextByType(r, vars);
    if (isImage) {
        // do this like this to simplify the code
        // skip all player crap since img tag works well
        int delay = configuration.getWebImgSlideDelay() * 1000;
        if (delay > 0 && configuration.getWebAutoCont(format)) {
            vars.put("delay", delay);
        }
    } else {
        vars.put("mediaType", mediaType);
        vars.put("auto", auto);
        vars.put("mime", mime);
        if (flowplayer) {
            if (RemoteUtil.directmime(mime) && !RemoteUtil.transMp4(mime, r.getMedia()) && !r.isResume() && !forceFlash) {
                vars.put("src", true);
            }
        } else {
            vars.put("width", renderer.getVideoWidth());
            vars.put("height", renderer.getVideoHeight());
        }
    }
    if (configuration.useWebControl()) {
        vars.put("push", true);
    }
    if (isVideo && configuration.getWebSubs()) {
        // only if subs are requested as <track> tags
        // otherwise we'll transcode them in
        boolean isFFmpegFontConfig = configuration.isFFmpegFontConfig();
        if (isFFmpegFontConfig) {
            // do not apply fontconfig to flowplayer subs
            configuration.setFFmpegFontConfig(false);
        }
        OutputParams p = new OutputParams(configuration);
        p.sid = r.getMediaSubtitle();
        Player.setAudioAndSubs(r.getName(), r.getMedia(), p);
        if (p.sid != null && p.sid.getType().isText()) {
            try {
                File subFile = SubtitleUtils.getSubtitles(r, r.getMedia(), p, configuration, SubtitleType.WEBVTT);
                LOGGER.debug("subFile " + subFile);
                if (subFile != null) {
                    vars.put("sub", parent.getResources().add(subFile));
                }
            } catch (Exception e) {
                LOGGER.debug("error when doing sub file " + e);
            }
        }
        // return back original fontconfig value
        configuration.setFFmpegFontConfig(isFFmpegFontConfig);
    }
    return parent.getResources().getTemplate(isImage ? "image.html" : flowplayer ? "flow.html" : "play.html").execute(vars);
}
Also used : WebRender(net.pms.configuration.WebRender) HashMap(java.util.HashMap) IOException(java.io.IOException) IOException(java.io.IOException) DLNAResource(net.pms.dlna.DLNAResource) Playlist(net.pms.dlna.Playlist) Format(net.pms.formats.Format) VirtualVideoAction(net.pms.dlna.virtual.VirtualVideoAction) RootFolder(net.pms.dlna.RootFolder) OutputParams(net.pms.io.OutputParams) File(java.io.File)

Example 2 with DLNAResource

use of net.pms.dlna.DLNAResource in project UniversalMediaServer by UniversalMediaServer.

the class Request method answer.

/**
 * Construct a proper HTTP response to a received request. After the response has been
 * created, it is sent and the resulting {@link ChannelFuture} object is returned.
 * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC-2616</a>
 * for HTTP header field definitions.
 * @param output The {@link HttpResponse} object that will be used to construct the response.
 * @param startStopListenerDelegate The {@link StartStopListenerDelegate} object that is used
 * 			to notify plugins that the {@link DLNAResource} is about to start playing.
 * @throws IOException
 */
public void answer(OutputStream output, StartStopListenerDelegate startStopListenerDelegate) throws IOException {
    this.output = output;
    List<String> responseHeader = new ArrayList<>();
    // 0 and above are valid Content-Length values, -1 means omit
    long CLoverride = -2;
    if (lowRange != 0 || highRange != 0) {
        appendToHeader(responseHeader, http10 ? HTTP_206_OK_10 : HTTP_206_OK);
    } else {
        if (soapaction != null && soapaction.contains("X_GetFeatureList")) {
            // If we don't return a 500 error, Samsung 2012 TVs time out.
            appendToHeader(responseHeader, http10 ? HTTP_500_10 : HTTP_500);
        } else {
            appendToHeader(responseHeader, http10 ? HTTP_200_OK_10 : HTTP_200_OK);
        }
    }
    StringBuilder response = new StringBuilder();
    DLNAResource dlna = null;
    boolean xbox360 = (mediaRenderer == null ? false : mediaRenderer.isXbox360());
    // Samsung 2012 TVs have a problematic preceding slash that needs to be removed.
    if (argument.startsWith("/")) {
        LOGGER.trace("Stripping preceding slash from: " + argument);
        argument = argument.substring(1);
    }
    if ((method.equals("GET") || method.equals("HEAD")) && argument.startsWith("console/")) {
        // Request to output a page to the HTML console.
        appendToHeader(responseHeader, "Content-Type: text/html");
        response.append(HTMLConsole.servePage(argument.substring(8)));
    } else if ((method.equals("GET") || method.equals("HEAD")) && argument.startsWith("get/")) {
        // Request to retrieve a file
        /**
         * Skip the leading "get/"
         * e.g. "get/0$1$5$3$4/Foo.mp4" -> "0$1$5$3$4/Foo.mp4"
         *
         * ExSport: I spotted on Android it is asking for "/get/0$2$4$2$1$3" which generates exception with response:
         * "Http: Response, HTTP/1.1, Status: Internal server error, URL: /get/0$2$4$2$1$3"
         * This should fix it
         */
        // Note: we intentionally include the trailing filename here because it may
        // be used to reconstruct lost Temp items.
        String id = argument.substring(argument.indexOf("get/") + 4);
        // Some clients escape the separators in their request: unescape them.
        id = id.replace("%24", "$");
        // Retrieve the DLNAresource itself.
        dlna = PMS.get().getRootFolder(mediaRenderer).getDLNAResource(id, mediaRenderer);
        String fileName = id.substring(id.indexOf('/') + 1);
        if (transferMode != null) {
            appendToHeader(responseHeader, "TransferMode.DLNA.ORG: " + transferMode);
        }
        if (dlna != null && dlna.isFolder() && !fileName.startsWith("thumbnail0000")) {
            // if we found a folder we MUST be asked for thumbnails
            // otherwise this is not allowed
            dlna = null;
        }
        if (dlna != null) {
            // DLNAresource was found.
            if (fileName.startsWith("thumbnail0000")) {
                // This is a request for a thumbnail file.
                DLNAImageProfile imageProfile = ImagesUtil.parseThumbRequest(fileName);
                appendToHeader(responseHeader, "Content-Type: " + imageProfile.getMimeType());
                appendToHeader(responseHeader, "Accept-Ranges: bytes");
                appendToHeader(responseHeader, "Expires: " + getFUTUREDATE() + " GMT");
                appendToHeader(responseHeader, "Connection: keep-alive");
                DLNAThumbnailInputStream thumbInputStream;
                if (!configuration.isShowCodeThumbs() && !dlna.isCodeValid(dlna)) {
                    thumbInputStream = dlna.getGenericThumbnailInputStream(null);
                } else {
                    dlna.checkThumbnail();
                    thumbInputStream = dlna.fetchThumbnailInputStream();
                }
                if (dlna instanceof RealFile && FullyPlayed.isFullyPlayedThumbnail(((RealFile) dlna).getFile())) {
                    thumbInputStream = FullyPlayed.addFullyPlayedOverlay(thumbInputStream);
                }
                inputStream = thumbInputStream.transcode(imageProfile, mediaRenderer != null ? mediaRenderer.isThumbnailPadding() : false);
                if (contentFeatures != null) {
                    appendToHeader(responseHeader, "ContentFeatures.DLNA.ORG: " + dlna.getDlnaContentFeatures(imageProfile, true));
                }
                if (inputStream != null && (lowRange > 0 || highRange > 0)) {
                    if (lowRange > 0) {
                        inputStream.skip(lowRange);
                    }
                    inputStream = DLNAResource.wrap(inputStream, highRange, lowRange);
                }
                appendToHeader(responseHeader, "Accept-Ranges: bytes");
                appendToHeader(responseHeader, "Connection: keep-alive");
            } else if (dlna.getMedia() != null && dlna.getMedia().getMediaType() == MediaType.IMAGE && dlna.isCodeValid(dlna)) {
                // This is a request for an image
                DLNAImageProfile imageProfile = ImagesUtil.parseImageRequest(fileName, null);
                if (imageProfile == null) {
                    // Parsing failed for some reason, we'll have to pick a profile
                    if (dlna.getMedia().getImageInfo() != null && dlna.getMedia().getImageInfo().getFormat() != null) {
                        switch(dlna.getMedia().getImageInfo().getFormat()) {
                            case GIF:
                                imageProfile = DLNAImageProfile.GIF_LRG;
                                break;
                            case PNG:
                                imageProfile = DLNAImageProfile.PNG_LRG;
                                break;
                            default:
                                imageProfile = DLNAImageProfile.JPEG_LRG;
                        }
                    } else {
                        imageProfile = DLNAImageProfile.JPEG_LRG;
                    }
                }
                appendToHeader(responseHeader, "Content-Type: " + imageProfile.getMimeType());
                appendToHeader(responseHeader, "Accept-Ranges: bytes");
                appendToHeader(responseHeader, "Expires: " + getFUTUREDATE() + " GMT");
                appendToHeader(responseHeader, "Connection: keep-alive");
                try {
                    InputStream imageInputStream;
                    if (dlna.getPlayer() instanceof ImagePlayer) {
                        ProcessWrapper transcodeProcess = dlna.getPlayer().launchTranscode(dlna, dlna.getMedia(), new OutputParams(configuration));
                        imageInputStream = transcodeProcess != null ? transcodeProcess.getInputStream(0) : null;
                    } else {
                        imageInputStream = dlna.getInputStream();
                    }
                    if (imageInputStream == null) {
                        LOGGER.warn("Input stream returned for \"{}\" was null, no image will be sent to renderer", fileName);
                    } else {
                        inputStream = DLNAImageInputStream.toImageInputStream(imageInputStream, imageProfile, false);
                        if (contentFeatures != null) {
                            appendToHeader(responseHeader, "ContentFeatures.DLNA.ORG: " + dlna.getDlnaContentFeatures(imageProfile, false));
                        }
                        if (inputStream != null && (lowRange > 0 || highRange > 0)) {
                            if (lowRange > 0) {
                                inputStream.skip(lowRange);
                            }
                            inputStream = DLNAResource.wrap(inputStream, highRange, lowRange);
                        }
                        appendToHeader(responseHeader, "Accept-Ranges: bytes");
                        appendToHeader(responseHeader, "Connection: keep-alive");
                    }
                } catch (IOException e) {
                    appendToHeader(responseHeader, "Content-Length: 0");
                    appendToHeader(responseHeader, "");
                    responseHeader.set(0, http10 ? HTTP_415_UNSUPPORTED_MEDIA_TYPE_10 : HTTP_415_UNSUPPORTED_MEDIA_TYPE);
                    sendHeader(responseHeader);
                    LOGGER.debug("Could not send image \"{}\": {}", dlna.getName(), e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName());
                    LOGGER.trace("", e);
                    return;
                }
            } else if (dlna.getMedia() != null && fileName.contains("subtitle0000") && dlna.isCodeValid(dlna)) {
                // This is a request for a subtitles file
                appendToHeader(responseHeader, "Content-Type: text/plain");
                appendToHeader(responseHeader, "Expires: " + getFUTUREDATE() + " GMT");
                DLNAMediaSubtitle sub = dlna.getMediaSubtitle();
                if (sub != null) {
                    // http://www.ps3mediaserver.org/forum/viewtopic.php?f=3&t=15805&p=75534#p75534
                    if (sub.isExternal()) {
                        try {
                            if (sub.getType() == SubtitleType.SUBRIP && mediaRenderer.isRemoveTagsFromSRTsubs()) {
                                // remove tags from .srt subs when renderer doesn't support them
                                inputStream = SubtitleUtils.removeSubRipTags(sub.getExternalFile());
                            } else {
                                inputStream = new FileInputStream(sub.getExternalFile());
                            }
                            LOGGER.trace("Loading external subtitles file: {}", sub);
                        } catch (IOException ioe) {
                            LOGGER.debug("Couldn't load external subtitles file: {}\nCause: {}", sub, ioe.getMessage());
                            LOGGER.trace("", ioe);
                        }
                    } else {
                        LOGGER.trace("Not loading external subtitles file because it is embedded: {}", sub);
                    }
                } else {
                    LOGGER.trace("Not loading external subtitles because dlna.getMediaSubtitle() returned null");
                }
            } else if (dlna.isCodeValid(dlna)) {
                // This is a request for a regular file.
                DLNAResource.Rendering origRendering = null;
                if (!mediaRenderer.equals(dlna.getDefaultRenderer())) {
                    // Adjust rendering details for this renderer
                    origRendering = dlna.updateRendering(mediaRenderer);
                }
                String name = dlna.getDisplayName(mediaRenderer);
                if (dlna.isNoName()) {
                    name = dlna.getName() + " " + dlna.getDisplayName(mediaRenderer);
                }
                inputStream = dlna.getInputStream(Range.create(lowRange, highRange, timeseek, timeRangeEnd), mediaRenderer);
                if (dlna.isResume()) {
                    // Update timeseek to possibly adjusted resume time
                    timeseek = dlna.getResume().getTimeOffset() / (double) 1000;
                }
                if (inputStream == null) {
                    // No inputStream indicates that transcoding / remuxing probably crashed.
                    LOGGER.error("There is no inputstream to return for " + name);
                } else {
                    startStopListenerDelegate.start(dlna);
                    appendToHeader(responseHeader, "Content-Type: " + getRendererMimeType(dlna.mimeType(), mediaRenderer, dlna.getMedia()));
                    if (dlna.getMedia() != null && !configuration.isDisableSubtitles() && dlna.getMediaSubtitle() != null && dlna.getMediaSubtitle().isStreamable()) {
                        // Some renderers (like Samsung devices) allow a custom header for a subtitle URL
                        String subtitleHttpHeader = mediaRenderer.getSubtitleHttpHeader();
                        if (isNotBlank(subtitleHttpHeader)) {
                            // Device allows a custom subtitle HTTP header; construct it
                            DLNAMediaSubtitle sub = dlna.getMediaSubtitle();
                            String subtitleUrl;
                            String subExtension = sub.getType().getExtension();
                            if (isNotBlank(subExtension)) {
                                subExtension = "." + subExtension;
                            }
                            subtitleUrl = "http://" + PMS.get().getServer().getHost() + ':' + PMS.get().getServer().getPort() + "/get/" + id.substring(0, id.indexOf('/')) + "/subtitle0000" + subExtension;
                            appendToHeader(responseHeader, subtitleHttpHeader + ": " + subtitleUrl);
                        } else {
                            LOGGER.trace("Did not send subtitle headers because mediaRenderer.getSubtitleHttpHeader() returned {}", subtitleHttpHeader == null ? "null" : "\"" + subtitleHttpHeader + "\"");
                        }
                    } else if (LOGGER.isTraceEnabled()) {
                        ArrayList<String> reasons = new ArrayList<>();
                        if (dlna.getMedia() == null) {
                            reasons.add("dlna.getMedia() is null");
                        }
                        if (configuration.isDisableSubtitles()) {
                            reasons.add("configuration.isDisabledSubtitles() is true");
                        }
                        if (dlna.getMediaSubtitle() == null) {
                            reasons.add("dlna.getMediaSubtitle() is null");
                        } else if (!dlna.getMediaSubtitle().isStreamable()) {
                            reasons.add("dlna.getMediaSubtitle().isStreamable() is false");
                        }
                        LOGGER.trace("Did not send subtitle headers because {}", StringUtil.createReadableCombinedString(reasons));
                    }
                    // Response generation:
                    // We use -1 for arithmetic convenience but don't send it as a value.
                    // If Content-Length < 0 we omit it, for Content-Range we use '*' to signify unspecified.
                    boolean chunked = mediaRenderer.isChunkedTransfer();
                    // Determine the total size. Note: when transcoding the length is
                    // not known in advance, so DLNAMediaInfo.TRANS_SIZE will be returned instead.
                    long totalsize = dlna.length(mediaRenderer);
                    if (chunked && totalsize == DLNAMediaInfo.TRANS_SIZE) {
                        // In chunked mode we try to avoid arbitrary values.
                        totalsize = -1;
                    }
                    long remaining = totalsize - lowRange;
                    long requested = highRange - lowRange;
                    if (requested != 0) {
                        // Determine the range (i.e. smaller of known or requested bytes)
                        long bytes = remaining > -1 ? remaining : inputStream.available();
                        if (requested > 0 && bytes > requested) {
                            bytes = requested + 1;
                        }
                        // Calculate the corresponding highRange (this is usually redundant).
                        highRange = lowRange + bytes - (bytes > 0 ? 1 : 0);
                        LOGGER.trace((chunked ? "Using chunked response. " : "") + "Sending " + bytes + " bytes.");
                        appendToHeader(responseHeader, "Content-Range: bytes " + lowRange + "-" + (highRange > -1 ? highRange : "*") + "/" + (totalsize > -1 ? totalsize : "*"));
                        // mode if the request is open-ended and totalsize is unknown we omit it.
                        if (chunked && requested < 0 && totalsize < 0) {
                            CLoverride = -1;
                        } else {
                            CLoverride = bytes;
                        }
                    } else {
                        // Content-Length refers to the total remaining size of the stream here.
                        CLoverride = remaining;
                    }
                    if (contentFeatures != null) {
                        appendToHeader(responseHeader, "ContentFeatures.DLNA.ORG: " + dlna.getDlnaContentFeatures(mediaRenderer));
                    }
                    if (dlna.getPlayer() == null || xbox360) {
                        appendToHeader(responseHeader, "Accept-Ranges: bytes");
                    }
                    appendToHeader(responseHeader, "Connection: keep-alive");
                }
                if (origRendering != null) {
                    // Restore original rendering details
                    dlna.updateRendering(origRendering);
                }
            }
        }
    } else if ((method.equals("GET") || method.equals("HEAD")) && (argument.toLowerCase().endsWith(".png") || argument.toLowerCase().endsWith(".jpg") || argument.toLowerCase().endsWith(".jpeg"))) {
        if (argument.toLowerCase().endsWith(".png")) {
            appendToHeader(responseHeader, "Content-Type: image/png");
        } else {
            appendToHeader(responseHeader, "Content-Type: image/jpeg");
        }
        appendToHeader(responseHeader, "Accept-Ranges: bytes");
        appendToHeader(responseHeader, "Connection: keep-alive");
        appendToHeader(responseHeader, "Expires: " + getFUTUREDATE() + " GMT");
        inputStream = getResourceInputStream(argument);
    } else if ((method.equals("GET") || method.equals("HEAD")) && (argument.equals("description/fetch") || argument.endsWith("1.0.xml"))) {
        appendToHeader(responseHeader, CONTENT_TYPE);
        appendToHeader(responseHeader, "Cache-Control: no-cache");
        appendToHeader(responseHeader, "Expires: 0");
        appendToHeader(responseHeader, "Accept-Ranges: bytes");
        appendToHeader(responseHeader, "Connection: keep-alive");
        inputStream = getResourceInputStream((argument.equals("description/fetch") ? "PMS.xml" : argument));
        if (argument.equals("description/fetch")) {
            byte[] b = new byte[inputStream.available()];
            inputStream.read(b);
            String s = new String(b, StandardCharsets.UTF_8);
            // .substring(0, PMS.get().usn().length()-2));
            s = s.replace("[uuid]", PMS.get().usn());
            if (PMS.get().getServer().getHost() != null) {
                s = s.replace("[host]", PMS.get().getServer().getHost());
                s = s.replace("[port]", "" + PMS.get().getServer().getPort());
            }
            if (xbox360) {
                LOGGER.debug("DLNA changes for Xbox 360");
                s = s.replace("Universal Media Server", configuration.getServerDisplayName() + " : Windows Media Connect");
                s = s.replace("<modelName>UMS</modelName>", "<modelName>Windows Media Connect</modelName>");
                s = s.replace("<serviceList>", "<serviceList>" + CRLF + "<service>" + CRLF + "<serviceType>urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1</serviceType>" + CRLF + "<serviceId>urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar</serviceId>" + CRLF + "<SCPDURL>/upnp/mrr/scpd</SCPDURL>" + CRLF + "<controlURL>/upnp/mrr/control</controlURL>" + CRLF + "</service>" + CRLF);
            } else {
                s = s.replace("Universal Media Server", configuration.getServerDisplayName());
            }
            inputStream = new ByteArrayInputStream(s.getBytes(StandardCharsets.UTF_8));
        }
    } else if (method.equals("POST") && (argument.contains("MS_MediaReceiverRegistrar_control") || argument.contains("mrr/control"))) {
        appendToHeader(responseHeader, CONTENT_TYPE_UTF8);
        response.append(HTTPXMLHelper.XML_HEADER);
        response.append(CRLF);
        response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER);
        response.append(CRLF);
        if (soapaction != null && soapaction.contains("IsAuthorized")) {
            response.append(HTTPXMLHelper.XBOX_360_2);
            response.append(CRLF);
        } else if (soapaction != null && soapaction.contains("IsValidated")) {
            response.append(HTTPXMLHelper.XBOX_360_1);
            response.append(CRLF);
        }
        response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER);
        response.append(CRLF);
    } else if (method.equals("POST") && argument.endsWith("upnp/control/connection_manager")) {
        appendToHeader(responseHeader, CONTENT_TYPE_UTF8);
        if (soapaction != null && soapaction.contains("ConnectionManager:1#GetProtocolInfo")) {
            response.append(HTTPXMLHelper.XML_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.PROTOCOLINFO_RESPONSE);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER);
            response.append(CRLF);
        }
    } else if (method.equals("POST") && argument.endsWith("upnp/control/content_directory")) {
        appendToHeader(responseHeader, CONTENT_TYPE_UTF8);
        if (soapaction != null && soapaction.contains("ContentDirectory:1#GetSystemUpdateID")) {
            response.append(HTTPXMLHelper.XML_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.GETSYSTEMUPDATEID_HEADER);
            response.append(CRLF);
            response.append("<Id>").append(DLNAResource.getSystemUpdateId()).append("</Id>");
            response.append(CRLF);
            response.append(HTTPXMLHelper.GETSYSTEMUPDATEID_FOOTER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER);
            response.append(CRLF);
        } else if (soapaction != null && soapaction.contains("ContentDirectory:1#GetSortCapabilities")) {
            response.append(HTTPXMLHelper.XML_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SORTCAPS_RESPONSE);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER);
            response.append(CRLF);
        } else if (soapaction != null && soapaction.contains("ContentDirectory:1#X_GetFeatureList")) {
            // Added for Samsung 2012 TVs
            response.append(HTTPXMLHelper.XML_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SAMSUNG_ERROR_RESPONSE);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER);
            response.append(CRLF);
        } else if (soapaction != null && soapaction.contains("ContentDirectory:1#GetSearchCapabilities")) {
            response.append(HTTPXMLHelper.XML_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SEARCHCAPS_RESPONSE);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER);
            response.append(CRLF);
        } else if (soapaction != null && (soapaction.contains("ContentDirectory:1#Browse") || soapaction.contains("ContentDirectory:1#Search"))) {
            objectID = getEnclosingValue(content, "<ObjectID", "</ObjectID>");
            String containerID = null;
            if ((objectID == null || objectID.length() == 0)) {
                containerID = getEnclosingValue(content, "<ContainerID", "</ContainerID>");
                if (containerID == null || (xbox360 && !containerID.contains("$"))) {
                    objectID = "0";
                } else {
                    objectID = containerID;
                    containerID = null;
                }
            }
            Object sI = getEnclosingValue(content, "<StartingIndex", "</StartingIndex>");
            Object rC = getEnclosingValue(content, "<RequestedCount", "</RequestedCount>");
            browseFlag = getEnclosingValue(content, "<BrowseFlag", "</BrowseFlag>");
            if (sI != null) {
                startingIndex = Integer.parseInt(sI.toString());
            }
            if (rC != null) {
                requestCount = Integer.parseInt(rC.toString());
            }
            response.append(HTTPXMLHelper.XML_HEADER);
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_HEADER);
            response.append(CRLF);
            if (soapaction.contains("ContentDirectory:1#Search")) {
                response.append(HTTPXMLHelper.SEARCHRESPONSE_HEADER);
            } else {
                response.append(HTTPXMLHelper.BROWSERESPONSE_HEADER);
            }
            response.append(CRLF);
            response.append(HTTPXMLHelper.RESULT_HEADER);
            response.append(HTTPXMLHelper.DIDL_HEADER);
            boolean browseDirectChildren = browseFlag != null && browseFlag.equals("BrowseDirectChildren");
            if (soapaction.contains("ContentDirectory:1#Search")) {
                browseDirectChildren = true;
            }
            // Xbox 360 virtual containers ... d'oh!
            String searchCriteria = null;
            if (xbox360 && configuration.getUseCache() && PMS.get().getLibrary() != null && containerID != null) {
                if (containerID.equals("7") && PMS.get().getLibrary().getAlbumFolder() != null) {
                    objectID = PMS.get().getLibrary().getAlbumFolder().getResourceId();
                } else if (containerID.equals("6") && PMS.get().getLibrary().getArtistFolder() != null) {
                    objectID = PMS.get().getLibrary().getArtistFolder().getResourceId();
                } else if (containerID.equals("5") && PMS.get().getLibrary().getGenreFolder() != null) {
                    objectID = PMS.get().getLibrary().getGenreFolder().getResourceId();
                } else if (containerID.equals("F") && PMS.get().getLibrary().getPlaylistFolder() != null) {
                    objectID = PMS.get().getLibrary().getPlaylistFolder().getResourceId();
                } else if (containerID.equals("4") && PMS.get().getLibrary().getAllFolder() != null) {
                    objectID = PMS.get().getLibrary().getAllFolder().getResourceId();
                } else if (containerID.equals("1")) {
                    String artist = getEnclosingValue(content, "upnp:artist = &quot;", "&quot;)");
                    if (artist != null) {
                        objectID = PMS.get().getLibrary().getArtistFolder().getResourceId();
                        searchCriteria = artist;
                    }
                }
            } else if (soapaction.contains("ContentDirectory:1#Search")) {
                searchCriteria = getEnclosingValue(content, "<SearchCriteria", "</SearchCriteria>");
            }
            List<DLNAResource> files = PMS.get().getRootFolder(mediaRenderer).getDLNAResources(objectID, browseDirectChildren, startingIndex, requestCount, mediaRenderer, searchCriteria);
            if (searchCriteria != null && files != null) {
                UMSUtils.postSearch(files, searchCriteria);
                if (xbox360) {
                    if (files.size() > 0) {
                        files = files.get(0).getChildren();
                    }
                }
            }
            int minus = 0;
            if (files != null) {
                for (DLNAResource uf : files) {
                    if (xbox360 && containerID != null) {
                        uf.setFakeParentId(containerID);
                    }
                    if (uf.isCompatible(mediaRenderer) && (uf.getPlayer() == null || uf.getPlayer().isPlayerCompatible(mediaRenderer)) || // all possible combination not only those supported by renderer because the renderer setting could be wrong.
                    files.get(0).getParent() instanceof FileTranscodeVirtualFolder) {
                        response.append(uf.getDidlString(mediaRenderer));
                    } else {
                        minus++;
                    }
                }
            }
            response.append(HTTPXMLHelper.DIDL_FOOTER);
            response.append(HTTPXMLHelper.RESULT_FOOTER);
            response.append(CRLF);
            int filessize = 0;
            if (files != null) {
                filessize = files.size();
            }
            response.append("<NumberReturned>").append(filessize - minus).append("</NumberReturned>");
            response.append(CRLF);
            DLNAResource parentFolder = null;
            if (files != null && filessize > 0) {
                parentFolder = files.get(0).getParent();
            } else {
                parentFolder = PMS.get().getRootFolder(mediaRenderer).getDLNAResource(objectID, mediaRenderer);
            }
            if (browseDirectChildren && mediaRenderer.isUseMediaInfo() && mediaRenderer.isDLNATreeHack()) {
                // with the new parser, files are parsed and analyzed *before*
                // creating the DLNA tree, every 10 items (the ps3 asks 10 by 10),
                // so we do not know exactly the total number of items in the DLNA folder to send
                // (regular files, plus the #transcode folder, maybe the #imdb one, also files can be
                // invalidated and hidden if format is broken or encrypted, etc.).
                // let's send a fake total size to force the renderer to ask following items
                // returns 11 when 10 asked
                int totalCount = startingIndex + requestCount + 1;
                // If no more elements, send the startingIndex
                if (filessize - minus <= 0) {
                    totalCount = startingIndex;
                }
                response.append("<TotalMatches>").append(totalCount).append("</TotalMatches>");
            } else if (browseDirectChildren) {
                response.append("<TotalMatches>").append(((parentFolder != null) ? parentFolder.childrenNumber() : filessize) - minus).append("</TotalMatches>");
            } else {
                // From upnp spec: If BrowseMetadata is specified in the BrowseFlags then TotalMatches = 1
                response.append("<TotalMatches>1</TotalMatches>");
            }
            response.append(CRLF);
            response.append("<UpdateID>");
            if (parentFolder != null) {
                response.append(parentFolder.getUpdateId());
            } else {
                response.append('1');
            }
            response.append("</UpdateID>");
            response.append(CRLF);
            if (soapaction.contains("ContentDirectory:1#Search")) {
                response.append(HTTPXMLHelper.SEARCHRESPONSE_FOOTER);
            } else {
                response.append(HTTPXMLHelper.BROWSERESPONSE_FOOTER);
            }
            response.append(CRLF);
            response.append(HTTPXMLHelper.SOAP_ENCODING_FOOTER);
            response.append(CRLF);
        }
    } else if (method.equals("SUBSCRIBE")) {
        if (soapaction == null) {
            // Ignore this
            return;
        }
        appendToHeader(responseHeader, CONTENT_TYPE_UTF8);
        appendToHeader(responseHeader, "Content-Length: 0");
        appendToHeader(responseHeader, "Connection: close");
        appendToHeader(responseHeader, "SID: " + PMS.get().usn());
        appendToHeader(responseHeader, "Server: " + PMS.get().getServerName());
        appendToHeader(responseHeader, "Timeout: Second-1800");
        appendToHeader(responseHeader, "");
        sendHeader(responseHeader);
        responseHeader.clear();
        if (soapaction != null) {
            String cb = soapaction.replace("<", "").replace(">", "");
            try {
                URL soapActionUrl = new URL(cb);
                String addr = soapActionUrl.getHost();
                int port = soapActionUrl.getPort();
                try (Socket sock = new Socket(addr, port)) {
                    OutputStream out = sock.getOutputStream();
                    sendLine(out, "NOTIFY /" + argument + " HTTP/1.1");
                    sendLine(out, "SID: " + PMS.get().usn());
                    sendLine(out, "SEQ: " + 0);
                    sendLine(out, "NT: upnp:event");
                    sendLine(out, "NTS: upnp:propchange");
                    sendLine(out, "HOST: " + addr + ":" + port);
                    sendLine(out, CONTENT_TYPE_UTF8);
                }
            } catch (MalformedURLException ex) {
                LOGGER.debug("Cannot parse address and port from soap action \"" + soapaction + "\"", ex);
            }
        } else {
            LOGGER.debug("Expected soap action in request");
        }
        if (argument.contains("connection_manager")) {
            response.append(HTTPXMLHelper.eventHeader("urn:schemas-upnp-org:service:ConnectionManager:1"));
            response.append(HTTPXMLHelper.eventProp("SinkProtocolInfo"));
            response.append(HTTPXMLHelper.eventProp("SourceProtocolInfo"));
            response.append(HTTPXMLHelper.eventProp("CurrentConnectionIDs"));
            response.append(HTTPXMLHelper.EVENT_FOOTER);
        } else if (argument.contains("content_directory")) {
            response.append(HTTPXMLHelper.eventHeader("urn:schemas-upnp-org:service:ContentDirectory:1"));
            response.append(HTTPXMLHelper.eventProp("TransferIDs"));
            response.append(HTTPXMLHelper.eventProp("ContainerUpdateIDs"));
            response.append(HTTPXMLHelper.eventProp("SystemUpdateID", "" + DLNAResource.getSystemUpdateId()));
            response.append(HTTPXMLHelper.EVENT_FOOTER);
        }
    }
    appendToHeader(responseHeader, "Server: " + PMS.get().getServerName());
    if (response.length() > 0) {
        // A response message was constructed; convert it to data ready to be sent.
        byte[] responseData = response.toString().getBytes("UTF-8");
        appendToHeader(responseHeader, "Content-Length: " + responseData.length);
        appendToHeader(responseHeader, "");
        sendHeader(responseHeader);
        // HEAD requests only require headers to be set, no need to set contents.
        if (!method.equals("HEAD")) {
            output.write(responseData);
        // LOGGER.trace(response.toString());
        }
    } else if (inputStream != null) {
        if (CLoverride > -2) {
            // Content-Length override has been set, send or omit as appropriate
            if (CLoverride > -1 && CLoverride != DLNAMediaInfo.TRANS_SIZE) {
                // Since PS3 firmware 2.50, it is wiser not to send an arbitrary Content-Length,
                // as the PS3 will display a network error and request the last seconds of the
                // transcoded video. Better to send no Content-Length at all.
                appendToHeader(responseHeader, "Content-Length: " + CLoverride);
            }
        } else {
            int cl = inputStream.available();
            LOGGER.trace("Available Content-Length: " + cl);
            appendToHeader(responseHeader, "Content-Length: " + cl);
        }
        if (timeseek > 0 && dlna != null) {
            // Add timeseek information headers.
            String timeseekValue = StringUtil.formatDLNADuration(timeseek);
            String timetotalValue = dlna.getMedia().getDurationString();
            appendToHeader(responseHeader, "TimeSeekRange.dlna.org: npt=" + timeseekValue + "-" + timetotalValue + "/" + timetotalValue);
            appendToHeader(responseHeader, "X-Seek-Range: npt=" + timeseekValue + "-" + timetotalValue + "/" + timetotalValue);
        }
        // Send the response headers to the client.
        appendToHeader(responseHeader, "");
        sendHeader(responseHeader);
        long sendB = 0;
        if (lowRange != DLNAMediaInfo.ENDFILE_POS && !method.equals("HEAD")) {
            // , ((lowRange > 0 && highRange > 0)?(highRange-lowRange):-1)
            sendB = sendBytes(inputStream);
        }
        if (sendB > -1) {
            LOGGER.trace("Sending stream: {} bytes of {}", sendB, argument);
        } else {
            // Premature end
            startStopListenerDelegate.stop();
        }
    } else {
        // inputStream is null
        appendToHeader(responseHeader, "Content-Length: 0");
        appendToHeader(responseHeader, "");
        responseHeader.set(0, http10 ? HTTP_204_NO_CONTENT_10 : HTTP_204_NO_CONTENT);
        sendHeader(responseHeader);
    }
    if (LOGGER.isTraceEnabled()) {
        // Log trace information
        StringBuilder header = new StringBuilder();
        for (int i = 0; i < responseHeader.size(); i++) {
            if (isNotBlank(responseHeader.get(i))) {
                header.append("  ").append(responseHeader.get(i)).append("\n");
            }
        }
        String rendererName;
        if (mediaRenderer != null) {
            if (isNotBlank(mediaRenderer.getRendererName())) {
                if (isBlank(mediaRenderer.getConfName()) || mediaRenderer.getRendererName().equals(mediaRenderer.getConfName())) {
                    rendererName = mediaRenderer.getRendererName();
                } else {
                    rendererName = mediaRenderer.getRendererName() + " [" + mediaRenderer.getConfName() + "]";
                }
            } else if (isNotBlank(mediaRenderer.getConfName())) {
                rendererName = mediaRenderer.getConfName();
            } else {
                rendererName = "Unnamed";
            }
        } else {
            rendererName = "Unknown";
        }
        if (method.equals("HEAD")) {
            LOGGER.trace("HEAD only response sent to {}:\n\nHEADER:\n{}", rendererName, header);
        } else {
            String formattedResponse = null;
            if (isNotBlank(response)) {
                try {
                    formattedResponse = StringUtil.prettifyXML(response.toString(), 4);
                } catch (SAXException | ParserConfigurationException | XPathExpressionException | TransformerException e) {
                    formattedResponse = "  Content isn't valid XML, using text formatting: " + e.getMessage() + "\n";
                    formattedResponse += "    " + response.toString().replaceAll("\n", "\n    ");
                }
            }
            if (isNotBlank(formattedResponse)) {
                LOGGER.trace("Response sent to {}:\n\nHEADER:\n{}\nCONTENT:\n{}", rendererName, header, formattedResponse);
                Matcher matcher = DIDL_PATTERN.matcher(response);
                if (matcher.find()) {
                    try {
                        LOGGER.trace("The unescaped <Result> sent to {} is:\n{}", mediaRenderer.getConfName(), StringUtil.prettifyXML(StringEscapeUtils.unescapeXml(matcher.group(1)), 2));
                    } catch (SAXException | ParserConfigurationException | XPathExpressionException | TransformerException e) {
                        LOGGER.warn("Failed to prettify DIDL-Lite document: {}", e.getMessage());
                        LOGGER.trace("", e);
                    }
                }
            } else if (inputStream != null && !responseHeader.contains("Content-Length: 0")) {
                LOGGER.trace("Transfer response sent to {}:\n\nHEADER:\n{}", rendererName, header);
            } else {
                LOGGER.trace("Empty response sent to {}:\n\nHEADER:\n{}", rendererName, header);
            }
        }
    }
}
Also used : ImagePlayer(net.pms.encoders.ImagePlayer) MalformedURLException(java.net.MalformedURLException) Matcher(java.util.regex.Matcher) XPathExpressionException(javax.xml.xpath.XPathExpressionException) ArrayList(java.util.ArrayList) URL(java.net.URL) SAXException(org.xml.sax.SAXException) DLNAImageProfile(net.pms.dlna.DLNAImageProfile) ProcessWrapper(net.pms.io.ProcessWrapper) RealFile(net.pms.dlna.RealFile) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException) OutputParams(net.pms.io.OutputParams) TransformerException(javax.xml.transform.TransformerException) DLNAMediaSubtitle(net.pms.dlna.DLNAMediaSubtitle) DLNAImageInputStream(net.pms.dlna.DLNAImageInputStream) DLNAThumbnailInputStream(net.pms.dlna.DLNAThumbnailInputStream) FileTranscodeVirtualFolder(net.pms.dlna.FileTranscodeVirtualFolder) DLNAResource(net.pms.dlna.DLNAResource) DLNAThumbnailInputStream(net.pms.dlna.DLNAThumbnailInputStream) Socket(java.net.Socket)

Example 3 with DLNAResource

use of net.pms.dlna.DLNAResource in project UniversalMediaServer by UniversalMediaServer.

the class RemoteBrowseHandler method mkBrowsePage.

private String mkBrowsePage(String id, HttpExchange t) throws IOException {
    LOGGER.debug("Make browse page " + id);
    String user = RemoteUtil.userName(t);
    RootFolder root = parent.getRoot(user, true, t);
    String search = RemoteUtil.getQueryVars(t.getRequestURI().getQuery(), "str");
    List<DLNAResource> resources = root.getDLNAResources(id, true, 0, 0, root.getDefaultRenderer(), search);
    boolean upnpAllowed = RemoteUtil.bumpAllowed(t);
    boolean upnpControl = RendererConfiguration.hasConnectedControlPlayers();
    if (!resources.isEmpty() && resources.get(0).getParent() != null && (resources.get(0).getParent() instanceof CodeEnter)) {
        // this is a code folder the search string is  entered code
        CodeEnter ce = (CodeEnter) resources.get(0).getParent();
        ce.setEnteredCode(search);
        if (!ce.validCode(ce)) {
            // invalid code throw error
            throw new IOException("Auth error");
        }
        DLNAResource real = ce.getResource();
        if (!real.isFolder()) {
            // no folder   -> redirect
            Headers hdr = t.getResponseHeaders();
            hdr.add("Location", "/play/" + real.getId());
            RemoteUtil.respond(t, "", 302, "text/html");
            // return null here to avoid multiple responses
            return null;
        }
        // redirect to ourself
        Headers hdr = t.getResponseHeaders();
        hdr.add("Location", "/browse/" + real.getResourceId());
        RemoteUtil.respond(t, "", 302, "text/html");
        return null;
    }
    if (StringUtils.isNotEmpty(search) && !(resources instanceof CodeEnter)) {
        UMSUtils.postSearch(resources, search);
    }
    boolean hasFile = false;
    ArrayList<String> folders = new ArrayList<>();
    ArrayList<HashMap<String, String>> media = new ArrayList<>();
    StringBuilder sb = new StringBuilder();
    sb.setLength(0);
    sb.append("<a href=\"javascript:history.back()\" title=\"").append(RemoteUtil.getMsgString("Web.10", t)).append("\">");
    sb.append("<span>").append(RemoteUtil.getMsgString("Web.10", t)).append("</span>");
    sb.append("</a>");
    folders.add(sb.toString());
    // Generate innerHtml snippets for folders and media items
    for (DLNAResource resource : resources) {
        String newId = resource.getResourceId();
        String idForWeb = URLEncoder.encode(newId, "UTF-8");
        String thumb = "/thumb/" + idForWeb;
        String name = StringEscapeUtils.escapeHtml(resource.resumeName());
        if (resource instanceof VirtualVideoAction) {
            // Let's take the VVA real early
            sb.setLength(0);
            HashMap<String, String> item = new HashMap<>();
            sb.append("<a href=\"#\" onclick=\"umsAjax('/play/").append(idForWeb).append("', true);return false;\" title=\"").append(name).append("\">").append("<img class=\"thumb\" src=\"").append(thumb).append("\" alt=\"").append(name).append("\">").append("</a>");
            item.put("thumb", sb.toString());
            sb.setLength(0);
            sb.append("<a href=\"#\" onclick=\"umsAjax('/play/").append(idForWeb).append("', true);return false;\" title=\"").append(name).append("\">").append("<span class=\"caption\">").append(name).append("</span>").append("</a>");
            item.put("caption", sb.toString());
            item.put("bump", "<span class=\"floatRight\"></span>");
            media.add(item);
            hasFile = true;
            continue;
        }
        if (resource.isFolder()) {
            sb.setLength(0);
            // The resource is a folder
            String p = "/browse/" + idForWeb;
            boolean code = (resource instanceof CodeEnter);
            String txt = RemoteUtil.getMsgString("Web.8", t);
            if (code) {
                txt = RemoteUtil.getMsgString("Web.9", t);
            }
            if (resource.getClass().getName().contains("SearchFolder") || code) {
                // search folder add a prompt
                // NOTE!!!
                // Yes doing getClass.getname is REALLY BAD, but this
                // is to make legacy plugins utilize this function as well
                sb.append("<a href=\"javascript:void(0);\" onclick=\"searchFun('").append(p).append("','").append(txt).append("');\" title=\"").append(name).append("\">");
            } else {
                sb.append("<a href=\"").append(p).append("\" oncontextmenu=\"searchFun('").append(p).append("','").append(txt).append("');\" title=\"").append(name).append("\">");
            }
            sb.append("<span>").append(name).append("</span>");
            sb.append("</a>");
            folders.add(sb.toString());
        } else {
            // The resource is a media file
            sb.setLength(0);
            HashMap<String, String> item = new HashMap<>();
            if (upnpAllowed) {
                if (upnpControl) {
                    sb.append("<a class=\"bumpIcon\" href=\"javascript:bump.start('//").append(parent.getAddress()).append("','/play/").append(idForWeb).append("','").append(name.replace("'", "\\'")).append("')\" title=\"").append(RemoteUtil.getMsgString("Web.1", t)).append("\"></a>");
                } else {
                    sb.append("<a class=\"bumpIcon icondisabled\" href=\"javascript:notify('warn','").append(RemoteUtil.getMsgString("Web.2", t)).append("')\" title=\"").append(RemoteUtil.getMsgString("Web.3", t)).append("\"></a>");
                }
                if (resource.getParent() instanceof Playlist) {
                    sb.append("\n<a class=\"playlist_del\" href=\"#\" onclick=\"umsAjax('/playlist/del/").append(idForWeb).append("', true);return false;\" title=\"").append(RemoteUtil.getMsgString("Web.4", t)).append("\"></a>");
                } else {
                    sb.append("\n<a class=\"playlist_add\" href=\"#\" onclick=\"umsAjax('/playlist/add/").append(idForWeb).append("', false);return false;\" title=\"").append(RemoteUtil.getMsgString("Web.5", t)).append("\"></a>");
                }
            } else {
                // ensure that we got a string
                sb.append("");
            }
            item.put("bump", sb.toString());
            sb.setLength(0);
            if (WebRender.supports(resource) || resource.isResume() || resource.getType() == Format.IMAGE) {
                sb.append("<a href=\"/play/").append(idForWeb).append("\" title=\"").append(name).append("\">").append("<img class=\"thumb\" src=\"").append(thumb).append("\" alt=\"").append(name).append("\">").append("</a>");
                item.put("thumb", sb.toString());
                sb.setLength(0);
                sb.append("<a href=\"/play/").append(idForWeb).append("\" title=\"").append(name).append("\">").append("<span class=\"caption\">").append(name).append("</span>").append("</a>");
                item.put("caption", sb.toString());
            } else if (upnpControl && upnpAllowed) {
                // Include it as a web-disabled item so it can be thrown via upnp
                sb.append("<a class=\"webdisabled\" href=\"javascript:notify('warn','").append(RemoteUtil.getMsgString("Web.6", t)).append("')\"").append(" title=\"").append(name).append(' ').append(RemoteUtil.getMsgString("Web.7", t)).append("\">").append("<img class=\"thumb\" src=\"").append(thumb).append("\" alt=\"").append(name).append("\">").append("</a>");
                item.put("thumb", sb.toString());
                sb.setLength(0);
                sb.append("<span class=\"webdisabled caption\">").append(name).append("</span>");
                item.put("caption", sb.toString());
            }
            media.add(item);
            hasFile = true;
        }
    }
    HashMap<String, Object> vars = new HashMap<>();
    vars.put("name", id.equals("0") ? configuration.getServerDisplayName() : StringEscapeUtils.escapeHtml(root.getDLNAResource(id, null).getDisplayName()));
    vars.put("hasFile", hasFile);
    vars.put("folders", folders);
    vars.put("media", media);
    if (configuration.useWebControl()) {
        vars.put("push", true);
    }
    return parent.getResources().getTemplate("browse.html").execute(vars);
}
Also used : HashMap(java.util.HashMap) Headers(com.sun.net.httpserver.Headers) ArrayList(java.util.ArrayList) IOException(java.io.IOException) DLNAResource(net.pms.dlna.DLNAResource) Playlist(net.pms.dlna.Playlist) VirtualVideoAction(net.pms.dlna.virtual.VirtualVideoAction) RootFolder(net.pms.dlna.RootFolder) CodeEnter(net.pms.dlna.CodeEnter)

Example 4 with DLNAResource

use of net.pms.dlna.DLNAResource in project UniversalMediaServer by UniversalMediaServer.

the class RemotePlayHandler method handle.

@Override
public void handle(HttpExchange t) throws IOException {
    if (RemoteUtil.deny(t)) {
        throw new IOException("Access denied");
    }
    String p = t.getRequestURI().getPath();
    if (p.contains("/play/")) {
        LOGGER.debug("got a play request " + t.getRequestURI());
        String id = RemoteUtil.getId("play/", t);
        String response = mkPage(id, t);
        // LOGGER.trace("play page " + response);
        RemoteUtil.respond(t, response, 200, "text/html");
    } else if (p.contains("/playerstatus/")) {
        String json = IOUtils.toString(t.getRequestBody(), "UTF-8");
        LOGGER.trace("got player status: " + json);
        RemoteUtil.respond(t, "", 200, "text/html");
        RootFolder root = parent.getRoot(RemoteUtil.userName(t), t);
        if (root == null) {
            LOGGER.debug("root not found");
            throw new IOException("Unknown root");
        }
        WebRender renderer = (WebRender) root.getDefaultRenderer();
        ((WebRender.WebPlayer) renderer.getPlayer()).setData(json);
    } else if (p.contains("/playlist/")) {
        String[] tmp = p.split("/");
        // sanity
        if (tmp.length < 3) {
            throw new IOException("Bad request");
        }
        String op = tmp[tmp.length - 2];
        String id = tmp[tmp.length - 1];
        DLNAResource r = PMS.getGlobalRepo().get(id);
        if (r != null) {
            RootFolder root = parent.getRoot(RemoteUtil.userName(t), t);
            if (root == null) {
                LOGGER.debug("root not found");
                throw new IOException("Unknown root");
            }
            WebRender renderer = (WebRender) root.getDefaultRenderer();
            if (op.equals("add")) {
                PMS.get().getDynamicPls().add(r);
                renderer.notify(renderer.OK, "Added '" + r.getDisplayName() + "' to dynamic playlist");
            } else if (op.equals("del") && (r.getParent() instanceof Playlist)) {
                ((Playlist) r.getParent()).remove(r);
                renderer.notify(renderer.INFO, "Removed '" + r.getDisplayName() + "' from playlist");
            }
        }
        RemoteUtil.respond(t, returnPage(), 200, "text/html");
    }
}
Also used : WebRender(net.pms.configuration.WebRender) DLNAResource(net.pms.dlna.DLNAResource) Playlist(net.pms.dlna.Playlist) RootFolder(net.pms.dlna.RootFolder) IOException(java.io.IOException)

Example 5 with DLNAResource

use of net.pms.dlna.DLNAResource in project UniversalMediaServer by UniversalMediaServer.

the class RemotePlayHandler method addNextByType.

private void addNextByType(DLNAResource d, HashMap<String, Object> vars) {
    List<DLNAResource> children = d.getParent().getChildren();
    boolean looping = configuration.getWebAutoLoop(d.getFormat());
    int type = d.getType();
    int size = children.size();
    int mod = looping ? size : 9999;
    int self = children.indexOf(d);
    for (int step = -1; step < 2; step += 2) {
        int i = self;
        int offset = (step < 0 && looping) ? size : 0;
        DLNAResource next = null;
        while (true) {
            i = (offset + i + step) % mod;
            if (i >= size || i < 0 || i == self) {
                // Not found
                break;
            }
            next = children.get(i);
            if (next.getType() == type && !next.isFolder()) {
                // Found
                break;
            }
            next = null;
        }
        String pos = step > 0 ? "next" : "prev";
        vars.put(pos + "Id", next != null ? next.getResourceId() : null);
        vars.put(pos + "Attr", next != null ? (" title=\"" + StringEscapeUtils.escapeHtml(next.resumeName()) + "\"") : " disabled");
    }
}
Also used : DLNAResource(net.pms.dlna.DLNAResource)

Aggregations

DLNAResource (net.pms.dlna.DLNAResource)8 IOException (java.io.IOException)5 RootFolder (net.pms.dlna.RootFolder)4 WebRender (net.pms.configuration.WebRender)3 Playlist (net.pms.dlna.Playlist)3 OutputParams (net.pms.io.OutputParams)3 Headers (com.sun.net.httpserver.Headers)2 ArrayList (java.util.ArrayList)2 HashMap (java.util.HashMap)2 VirtualVideoAction (net.pms.dlna.virtual.VirtualVideoAction)2 ImagePlayer (net.pms.encoders.ImagePlayer)2 ProcessWrapper (net.pms.io.ProcessWrapper)2 BufferedOutputStream (java.io.BufferedOutputStream)1 ByteArrayInputStream (java.io.ByteArrayInputStream)1 File (java.io.File)1 InputStream (java.io.InputStream)1 OutputStream (java.io.OutputStream)1 MalformedURLException (java.net.MalformedURLException)1 Socket (java.net.Socket)1 URL (java.net.URL)1