Пример #1
0
    def getServerDecision(self):
        directPlay = not (self.metadata and self.metadata.isTranscoded)
        decisionPath = self.getDecisionPath(directPlay)
        newDecision = None

        if decisionPath:
            server = self.metadata.transcodeServer or self.item.getServer()
            request = plexrequest.PlexRequest(server, decisionPath)
            response = request.getWithTimeout(10)

            if response.isSuccess() and response.container:
                decision = serverdecision.ServerDecision(self, response, self)

                if decision.isSuccess():
                    util.LOG("MDE: Server was happy with client's original decision. {0}".format(decision))
                elif decision.isDecision(True):
                    util.WARN_LOG("MDE: Server was unhappy with client's original decision. {0}".format(decision))
                    return decision.getDecision()
                else:
                    util.LOG("MDE: Server was unbiased about the decision. {0}".format(decision))

                # Check if the server has provided a new media item to use it. If
                # there is no item, then we'll continue along as if there was no
                # decision made.
                newDecision = decision.getDecision(False)
            else:
                util.WARN_LOG("MDE: Server failed to provide a decision")
        else:
            util.WARN_LOG("MDE: Server or item does not support decisions")

        return newDecision or self
Пример #2
0
    def delete(self):
        if not self.ratingKey:
            return

        req = plexrequest.PlexRequest(self.server, '/library/metadata/{0}'.format(self.ratingKey), method='DELETE')
        req.getToStringWithTimeout(10)
        self.deleted = req.wasOK()
        return self.deleted
Пример #3
0
 def removeItem(self, item):
     request = plexrequest.PlexRequest(
         self.server, "/playQueues/" + str(self.id) + "/items/" +
         item.get("playQueueItemID", "-1"), "DELETE")
     self.addRequestOptions(request)
     context = request.createRequestContext(
         "delete", callback.Callable(self.onResponse))
     plexapp.APP.startRequest(request, context)
Пример #4
0
 def addItem(self, item, addNext=False, excludeSeedItem=False):
     request = plexrequest.PlexRequest(self.server,
                                       "/playQueues/" + str(self.id), "PUT")
     request.addParam("uri", item.getItemUri())
     request.addParam("next", addNext and "1" or "0")
     request.addParam("excludeSeedItem", excludeSeedItem and "1" or "0")
     self.addRequestOptions(request)
     context = request.createRequestContext(
         "add", callback.Callable(self.onResponse))
     plexapp.APP.startRequest(request, context)
Пример #5
0
    def sendTimelineToServer(self, timelineType, timeline, time):
        if not hasattr(timeline.item,
                       'getServer') or not timeline.item.getServer():
            return

        serverTimeline = self.getServerTimeline(timelineType)

        # Only send timeline if it's the first, item changes, playstate changes or timer pops
        itemsEqual = timeline.item and serverTimeline.item and timeline.item.ratingKey == serverTimeline.item.ratingKey
        if itemsEqual and timeline.state == serverTimeline.state and not serverTimeline.isExpired(
        ):
            return

        serverTimeline.reset()
        serverTimeline.item = timeline.item
        serverTimeline.state = timeline.state

        # Ignore sending timelines for multi part media with no duration
        obj = timeline.choice
        if obj and obj.part and obj.part.duration.asInt(
        ) == 0 and obj.media.parts and len(obj.media.parts) > 1:
            util.WARN_LOG(
                "Timeline not supported: the current part doesn't have a valid duration"
            )
            return

        # It's possible with timers and in player seeking for the time to be greater than the
        # duration, which causes a 400, so in that case we'll set the time to the duration.
        duration = timeline.item.duration.asInt() or timeline.duration
        if time > duration:
            time = duration

        params = util.AttributeDict()
        params["time"] = time
        params["duration"] = duration
        params["state"] = timeline.state
        params["guid"] = timeline.item.guid
        params["ratingKey"] = timeline.item.ratingKey
        params["url"] = timeline.item.url
        params["key"] = timeline.item.key
        params["containerKey"] = timeline.item.container.address
        if timeline.playQueue:
            params["playQueueItemID"] = timeline.playQueue.selectedId

        path = "/:/timeline"
        for paramKey in params:
            if params[paramKey]:
                path = http.addUrlParam(
                    path, paramKey + "=" + urllib.quote(str(params[paramKey])))

        request = plexrequest.PlexRequest(timeline.item.getServer(), path)
        context = request.createRequestContext(
            "timelineUpdate", callback.Callable(self.onTimelineResponse))
        context.playQueue = timeline.playQueue
        plexapp.APP.startRequest(request, context)
Пример #6
0
def createPlayQueueForId(id, server=None, contentType=None):
    obj = PlayQueue(server, contentType)
    obj.id = id

    request = plexrequest.PlexRequest(server, "/playQueues/" + str(id))
    request.addParam("own", "1")
    obj.addRequestOptions(request)
    context = request.createRequestContext("own",
                                           callback.Callable(obj.onResponse))
    plexapp.APP.startRequest(request, context)

    return obj
Пример #7
0
    def moveItem(self, item, after):
        if after:
            query = "?after=" + after.get("playQueueItemID", "-1")
        else:
            query = ""

        request = plexrequest.PlexRequest(
            self.server, "/playQueues/" + str(self.id) + "/items/" +
            item.get("playQueueItemID", "-1") + "/move" + query, "PUT")
        self.addRequestOptions(request)
        context = request.createRequestContext(
            "move", callback.Callable(self.onResponse))
        plexapp.APP.startRequest(request, context)
Пример #8
0
    def setShuffle(self, shuffle=None):
        if shuffle is None:
            shuffle = not self.isShuffled

        if self.isShuffled == shuffle:
            return

        if shuffle:
            command = "/shuffle"
        else:
            command = "/unshuffle"

        # Don't change self.isShuffled, it'll be set in OnResponse if all goes well

        request = plexrequest.PlexRequest(
            self.server, "/playQueues/" + str(self.id) + command, "PUT")
        self.addRequestOptions(request)
        context = request.createRequestContext(
            "shuffle", callback.Callable(self.onResponse))
        plexapp.APP.startRequest(request, context)
Пример #9
0
    def setSelectedStream(self, streamType, streamId, async):
        if streamType == plexstream.PlexStream.TYPE_AUDIO:
            typeString = "audio"
        elif streamType == plexstream.PlexStream.TYPE_SUBTITLE:
            typeString = "subtitle"
        elif streamType == plexstream.PlexStream.TYPE_VIDEO:
            typeString = "video"
        else:
            return None

        path = "/library/parts/{0}?{1}StreamID={2}".format(
            self.id(''), typeString, streamId)

        if self.getServer().supportsFeature("allPartsStreamSelection"):
            path = path + "&allParts=1"

        request = plexrequest.PlexRequest(self.getServer(), path, "PUT")

        if async:
            context = request.createRequestContext("ignored")
            import plexapp
            plexapp.APP.startRequest(request, context, "")
        else:
            request.postToStringWithTimeout()

        matching = plexstream.NoneStream()

        # Update any affected streams
        for stream in self.streams:
            if stream.streamType.asInt() == streamType:
                if stream.id == streamId:
                    stream.setSelected(True)
                    matching = stream
                elif stream.isSelected():
                    stream.setSelected(False)

        return matching
Пример #10
0
    def refresh(self, force=True, delay=False, wait=False):
        # Ignore refreshing local PQs
        if self.isLocal():
            return

        if wait:
            self.responded = False
            self.initialized = False
        # We refresh our play queue if the caller insists or if we only have a
        # portion of our play queue loaded. In particular, this means that we don't
        # refresh the play queue if we're asked to refresh because a new track is
        # being played but we have the entire album loaded already.

        if force or self.isWindowed():
            if delay:
                # We occasionally want to refresh the PQ in response to moving to a
                # new item and starting playback, but if we refresh immediately:
                # we probably end up refreshing before PMS realizes we've moved on.
                # There's no great solution, but delaying our refresh by just a few
                # seconds makes us much more likely to get an accurate window (and
                # accurate selected IDs) from PMS.

                if not self.refreshTimer:
                    self.refreshTimer = plexapp.createTimer(
                        5000, self.onRefreshTimer)
                    plexapp.APP.addTimer(self.refreshTimer)
            else:
                request = plexrequest.PlexRequest(
                    self.server, "/playQueues/" + str(self.id))
                self.addRequestOptions(request)
                context = request.createRequestContext(
                    "refresh", callback.Callable(self.onResponse))
                plexapp.APP.startRequest(request, context)

        if wait:
            return self.waitForInitialization()
Пример #11
0
    def resolveIndirect(self):
        if not self.isIndirect() or locks.LOCKS.isLocked("resolve_indirect"):
            return self

        part = self.parts[0]
        if part is None:
            util.DEBUG("Failed to resolve indirect media: missing valid part")
            return None

        postBody = None
        postUrl = part.postURL
        request = plexrequest.PlexRequest(
            self.getServer(), part.key, postUrl is not None and "POST"
            or "GET")

        if postUrl is not None:
            util.DEBUG(
                "Fetching content for indirect media POST URL: {0}".format(
                    postUrl))
            # Force setting the certificate to handle following https redirects
            postRequest = http.HttpRequest(postUrl, None, True)
            postResponse = postRequest.getToStringWithTimeout(30)
            if len(postResponse) > 0 and type(
                    postRequest.event) == "roUrlEvent":
                util.DEBUG(
                    "Retrieved data from postURL, posting to resolve container"
                )
                crlf = chr(13) + chr(10)
                postBody = ""
                for header in postRequest.event.getResponseHeadersArray():
                    for name in header:
                        postBody = postBody + name + ": " + header[name] + crlf
                postBody = postBody + crlf + postResponse
            else:
                util.DEBUG("Failed to resolve indirect media postUrl")
                self.Set("indirect", "-1")
                return self

            request.addParam("postURL", postUrl)

        response = request.doRequestWithTimeout(30, postBody)

        item = response.items[0]
        if item is None or item.mediaItems[0] is None:
            util.DEBUG("Failed to resolve indirect media: no media items")
            self.indirect = -1
            return self

        media = item.mediaItems[0]

        # Add indirect headers to the media item
        media.indirectHeaders = util.AttributeDict()
        for header in (item.container.httpHeaders or '').split("&"):
            arr = header.split("=")
            if len(arr) == 2:
                media.indirectHeaders[arr[0]] = arr[1]

        # Reset the fallback media id if applicable
        if self.id.asInt() < 0:
            media.id = self.id

        return media.resolveIndirect()
Пример #12
0
def createRemotePlayQueue(item, contentType, options, args):
    util.DEBUG_LOG('Creating remote playQueue request...')
    obj = PlayQueue(item.getServer(), contentType, options)

    # The item's URI is made up of the library section UUID, a descriptor of
    # the item type (item or directory), and the item's path, URL-encoded.

    uri = "library://" + item.getLibrarySectionUuid() + "/"
    itemType = item.isDirectory() and "directory" or "item"
    path = None

    if not options.key:
        # if item.onDeck and len(item.onDeck) > 0:
        #     options.key = item.onDeck[0].getAbsolutePath("key")
        # el
        if not item.isDirectory():
            options.key = item.get("key")

    # If we're asked to play unwatched, ignore the option unless we are unwatched.
    options.unwatched = options.unwatched and item.isUnwatched()

    # TODO(schuyler): Until we build postplay, we're not allowed to queue containers for episodes.
    if item.type == "episode":
        options.context = options.CONTEXT_SELF
    elif item.type == "movie":
        if not options.extrasPrefixCount and not options.resume:
            options.extrasPrefixCount = plexapp.INTERFACE.getPreference(
                "cinema_trailers", 0)

    # How exactly to construct the item URI depends on the metadata type, though
    # whenever possible we simply use /library/metadata/:id.

    if item.isLibraryItem() and not item.isLibraryPQ:
        path = "/library/metadata/" + item.ratingKey
    else:
        path = item.getAbsolutePath("key")

    if options.context == options.CONTEXT_SELF:
        # If the context is specifically for just this item,: just use the
        # item's key and get out.
        pass
    elif item.type == "playlist":
        path = None
        uri = item.get("ratingKey")
        options.isPlaylist = True
    elif item.type == "track":
        # TODO(rob): Is there ever a time the container address is wrong? If we
        # expect to play a single track,: use options.CONTEXT_SELF.
        path = item.container.address or "/library/metadata/" + item.get(
            "parentRatingKey", "")
        itemType = "directory"
    elif item.isPhotoOrDirectoryItem():
        if item.type == "photoalbum" or item.parentKey:
            path = item.getParentPath(item.type == "photoalbum" and "key"
                                      or "parentKey")
            itemType = "item"
        elif item.isDirectory():
            path = item.getAbsolutePath("key")
        else:
            path = item.container.address
            itemType = "directory"
            options.key = item.getAbsolutePath("key")

    elif item.type == "episode":
        path = "/library/metadata/" + item.get("grandparentRatingKey", "")
        itemType = "directory"
        options.key = item.getAbsolutePath("key")
    # elif item.type == "show":
    #     path = "/library/metadata/" + item.get("ratingKey", "")

    if path:
        if args:
            path += util.joinArgs(args)

        util.DEBUG_LOG("playQueue path: " + str(path))

        if "/search" not in path:
            # Convert a few params to the PQ spec
            convert = {'type': "sourceType", 'unwatchedLeaves': "unwatched"}

            for key in convert:
                regex = re.compile("(?i)([?&])" + key + "=")
                path = regex.sub("\1" + convert[key] + "=", path)

        util.DEBUG_LOG("playQueue path: " + str(path))
        uri = uri + itemType + "/" + urllib.quote_plus(path)

    util.DEBUG_LOG("playQueue uri: " + str(uri))

    # Create the PQ request
    request = plexrequest.PlexRequest(obj.server, "/playQueues")

    request.addParam(not options.isPlaylist and "uri" or "playlistID", uri)
    request.addParam("type", contentType)
    # request.addParam('X-Plex-Client-Identifier', plexapp.INTERFACE.getGlobal('clientIdentifier'))

    # Add options we pass once during PQ creation
    if options.shuffle:
        request.addParam("shuffle", "1")
        options.key = None
    else:
        request.addParam("shuffle", "0")

    if options.key:
        request.addParam("key", options.key)

    # Add options we pass every time querying PQs
    obj.addRequestOptions(request)

    util.DEBUG_LOG('Initial playQueue request started...')
    context = request.createRequestContext("create",
                                           callback.Callable(obj.onResponse))
    plexapp.APP.startRequest(request, context, body='')

    return obj