Exemplo n.º 1
0
    def __init__(self, media=None, partIndex=0):
        self.media = media
        self.part = None
        self.forceTranscode = False
        self.isDirectPlayable = False
        self.videoStream = None
        self.audioStream = None
        self.subtitleStream = None
        self.isSelected = False
        self.subtitleDecision = self.SUBTITLES_DEFAULT

        self.sorts = util.AttributeDict()

        if media:
            self.indirectHeaders = media.indirectHeaders
            self.part = media.parts[partIndex]
            if self.part:
                # We generally just rely on PMS to have told us selected streams, so
                # initialize our streams accordingly.

                self.videoStream = self.part.getSelectedStreamOfType(plexstream.PlexStream.TYPE_VIDEO)
                self.audioStream = self.part.getSelectedStreamOfType(plexstream.PlexStream.TYPE_AUDIO)
                self.subtitleStream = self.part.getSelectedStreamOfType(plexstream.PlexStream.TYPE_SUBTITLE)
            else:
                util.WARN_LOG("Media does not contain a valid part")

            util.LOG("Choice media: {0} part:{1}".format(media, partIndex))
            for streamType in ("videoStream", "audioStream", "subtitleStream"):
                attr = getattr(self, streamType)
                if attr:
                    util.LOG("Choice {0}: {1}".format(streamType, repr(attr)))
        else:
            util.WARN_LOG("Could not create media choice for invalid media")
Exemplo n.º 2
0
    def getDecisionPath(self, directPlay=False):
        if not self.item or not self.metadata:
            return None

        decisionPath = self.metadata.decisionPath
        if not decisionPath:
            server = self.metadata.transcodeServer or self.item.getServer()
            decisionPath = self.buildTranscode(server, util.AttributeDict(), self.metadata.partIndex, True, False).decisionPath

        # Modify the decision params based on the transcode url
        if decisionPath:
            if directPlay:
                decisionPath = decisionPath.replace("directPlay=0", "directPlay=1")

                # Clear all subtitle parameters and add the a valid subtitle type based
                # on the video player. This will let the server decide if it can supply
                # sidecar subs, burn or embed w/ an optional transcode.
                for key in ("subtitles", "advancedSubtitles"):
                    decisionPath = re.sub('([?&]{0}=)\w+'.format(key), '', decisionPath)
                subType = 'sidecar'  # AppSettings().getBoolPreference("custom_video_player"), "embedded", "sidecar")
                decisionPath = http.addUrlParam(decisionPath, "subtitles=" + subType)

            # Global variables for all decisions
            decisionPath = http.addUrlParam(decisionPath, "mediaBufferSize=50000")
            decisionPath = http.addUrlParam(decisionPath, "hasMDE=1")
            decisionPath = http.addUrlParam(decisionPath, 'X-Plex-Platform=Chrome')

        return decisionPath
Exemplo n.º 3
0
    def __init__(self, timelineType, *args, **kwargs):
        util.AttributeDict.__init__(self, *args, **kwargs)
        self.type = timelineType
        self.state = "stopped"
        self.item = None
        self.choice = None
        self.playQueue = None

        self.controllable = util.AttributeDict()
        self.controllableStr = None

        self.attrs = util.AttributeDict()

        # Set default controllable for all content. Other controllable aspects
        # will be set based on the players content.
        #
        self.setControllable("playPause", True)
        self.setControllable("stop", True)
Exemplo n.º 4
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)
Exemplo n.º 5
0
    def deSerialize(cls, jstring):
        import plexserver
        obj = json.loads(jstring)
        server = plexserver.PlexServer.deSerialize(obj['server'])
        server.identifier = None
        ad = util.AttributeDict()
        ad.attrib = obj['obj']
        ad.find = lambda x: None
        po = buildItem(server, ad, ad.initpath, container=server)

        return po
Exemplo n.º 6
0
    def getManualConnections(self):
        manualConnections = []

        jstring = plexapp.INTERFACE.getPreference('manual_connections')
        if jstring:
            connections = json.loads(jstring)
            if isinstance(connections, list):
                for conn in connections:
                    conn = util.AttributeDict(conn)
                    if conn.connection:
                        manualConnections.append(conn)

        return manualConnections
Exemplo n.º 7
0
    def __init__(self):
        self.deviceInfo = plexapp.INTERFACE.getGlobal("deviceInfo")

        self.textSize = util.AttributeDict({
            'extrasmall': 15,
            'small': 20,
            'medium': 30,
            'large': 45,
            'extralarge': 65,
        })

        self.burnedSize = util.AttributeDict({
            'extrasmall': "60",
            'small': "80",
            'medium': "100",
            'large': "135",
            'extralarge': "200"
        })

        self.colors = util.AttributeDict({
            'white': 0xffffffff,
            'black': 0x000000ff,
            'red': 0xff0000ff,
            'green': 0x008000ff,
            'blue': 0x0000ffff,
            'yellow': 0xffff00ff,
            'magenta': 0xff00ffff,
            'cyan': 0x00ffffff,
        })

        self.defaults = util.AttributeDict({
            'textSize': self.textSize.medium,
            'textColor': self.colors.white,
            'textOpacity': 80,
            'backgroundColor': self.colors.black,
            'backgroundOpacity': 70,
            'burnedSize': None
        })
Exemplo n.º 8
0
    def __init__(self):

        # Constants
        self.NAVIGATION = "navigation"
        self.FULLSCREEN_VIDEO = "fullScreenVideo"
        self.FULLSCREEN_MUSIC = "fullScreenMusic"
        self.FULLSCREEN_PHOTO = "fullScreenPhoto"
        self.TIMELINE_TYPES = ["video", "music", "photo"]

        # Members
        self.serverTimelines = util.AttributeDict()
        self.subscribers = util.AttributeDict()
        self.pollReplies = util.AttributeDict()
        self.timelines = util.AttributeDict()
        self.location = self.NAVIGATION

        self.textFieldName = None
        self.textFieldContent = None
        self.textFieldSecure = None

        # Initialization
        for timelineType in self.TIMELINE_TYPES:
            self.timelines[timelineType] = TimelineData(timelineType)
Exemplo n.º 9
0
    def build(self):
        if self.media.parts and self.media.parts[0]:
            obj = util.AttributeDict()

            part = self.media.parts[0]
            path = part.key or part.thumb
            server = self.item.getServer()

            obj.url = server.buildUrl(path, True)
            obj.enableBlur = server.supportsPhotoTranscoding

            util.DEBUG_LOG("Constructed photo item for playback: {0}".format(dict(obj)))

            self.metadata = obj

        return self.metadata
Exemplo n.º 10
0
    def read_model(self, filepath):
        self.model.read(filepath + "tf")

        if os.path.isfile(filepath + "vars"):
            try:
                with open(filepath + "vars", "rb") as f:
                    data = pickle.load(f)
            except:
                with open(filepath + "vars", "rt") as f:
                    data = json.load(f)

            self.step = data.get("step") + 1

            history = data.get("history", None)
            if history is not None:
                self.history = util.AttributeDict(history)
Exemplo n.º 11
0
    def build(self, directPlay=None):
        directPlay = directPlay or self.choice.isDirectPlayable

        obj = util.AttributeDict()

        # TODO(schuyler): Do we want/need to add anything generic here? Title? Duration?

        if directPlay:
            obj = self.buildDirectPlay(obj)
        else:
            obj = self.buildTranscode(obj)

        self.metadata = obj

        util.LOG("Constructed audio item for playback: {0}".format(dict(obj)))

        return self.metadata
Exemplo n.º 12
0
    def __init__(self, server, contentType, options=None):
        signalsmixin.SignalsMixin.__init__(self)
        self.id = None
        self.selectedId = None
        self.version = -1
        self.isShuffled = False
        self.isRepeat = False
        self.isRepeatOne = False
        self.isLocalPlayQueue = False
        self.isMixed = None
        self.totalSize = 0
        self.windowSize = 0
        self.forcedWindow = False
        self.container = None

        # Forced limitations
        self.allowShuffle = False
        self.allowSeek = True
        self.allowRepeat = False
        self.allowSkipPrev = False
        self.allowSkipNext = False
        self.allowAddToQueue = False

        self.refreshOnTimeline = False

        self.server = server
        self.type = contentType
        self._items = []
        self.options = options or util.AttributeDict()

        self.usage = None

        self.refreshTimer = None

        self.canceled = False
        self.responded = False
        self.initialized = False

        self.composite = plexobjects.PlexValue('', parent=self)

        # Add a few default options for specific PQ types
        if self.type == "audio":
            self.options.includeRelated = True
        elif self.type == "photo":
            self.setRepeat(True)
Exemplo n.º 13
0
    def __init__(self, data, initpath=None, server=None, media=None):
        plexobjects.PlexObject.__init__(self, data, initpath, server)
        self.container_ = self.container
        self.container = media
        self.streams = []

        # If we weren't given any data, this is a synthetic part
        if data is not None:
            self.streams = [
                plexstream.PlexStream(e,
                                      initpath=self.initpath,
                                      server=self.server) for e in data
                if e.tag == 'Stream'
            ]
            if self.indexes:
                indexKeys = self.indexes('').split(",")
                self.indexes = util.AttributeDict()
                for indexKey in indexKeys:
                    self.indexes[indexKey] = True
Exemplo n.º 14
0
    def __init__(self, settings):
        self.S = settings

        self.feature_extractor = FeatureExtractor(
            self.S.image_width, self.S.image_height, self.S.batch_size)

        self.model = Model(settings)
        self.model.add_layers()
        self.model.add_optimizer()

        self.history = util.AttributeDict()
        self.history.val_accuracy = []
        self.history.val_iou = []
        self.history.train_loss = []
        self.history.train_accuracy = []
        self.history.train_iou = []
        self.history.val_loss_estimate = []
        self.history.val_accuracy_estimate = []
        self.history.val_iou_estimate = []

        self.step = 0
        self.model.start()
Exemplo n.º 15
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()
Exemplo n.º 16
0
    def _build(self,
               directPlay=None,
               directStream=True,
               currentPartIndex=None):
        isForced = directPlay is not None
        if isForced:
            util.LOG(
                directPlay and "Forced Direct Play" or
                "Forced Transcode; allowDirectStream={0}".format(directStream))

        directPlay = directPlay or self.choice.isDirectPlayable
        server = self.item.getServer()

        # A lot of our content metadata is independent of the direct play decision.
        # Add that first.

        obj = util.AttributeDict()
        obj.duration = self.media.duration.asInt()

        videoRes = self.media.getVideoResolution()
        obj.fullHD = videoRes >= 1080
        obj.streamQualities = (
            videoRes >= 480
            and self.item.settings.getGlobal("IsHD")) and ["HD"] or ["SD"]

        frameRate = self.media.videoFrameRate or "24p"
        if frameRate == "24p":
            obj.frameRate = 24
        elif frameRate == "NTSC":
            obj.frameRate = 30

        # Add soft subtitle info
        if self.choice.subtitleDecision == self.choice.SUBTITLES_SOFT_ANY:
            obj.subtitleUrl = server.buildUrl(
                self.choice.subtitleStream.getSubtitlePath(), True)
        elif self.choice.subtitleDecision == self.choice.SUBTITLES_SOFT_DP:
            obj.subtitleConfig = {
                'TrackName':
                "mkv/" + str(self.choice.subtitleStream.index.asInt() + 1)
            }

        # Create one content metadata object for each part and store them as a
        # linked list. We probably want a doubly linked list, except that it
        # becomes a circular reference nuisance, so we make the current item the
        # base object and singly link in each direction from there.

        baseObj = obj
        prevObj = None
        startOffset = 0

        startPartIndex = currentPartIndex or 0
        for partIndex in range(startPartIndex, len(self.media.parts)):
            isCurrentPart = (currentPartIndex is not None
                             and partIndex == currentPartIndex)
            partObj = util.AttributeDict()
            partObj.update(baseObj)

            partObj.live = False
            partObj.partIndex = partIndex
            partObj.startOffset = startOffset

            part = self.media.parts[partIndex]

            partObj.partDuration = part.duration.asInt()

            if part.isIndexed():
                partObj.sdBifPath = part.getIndexPath("sd")
                partObj.hdBifPath = part.getIndexPath("hd")

            # We have to evaluate every part before playback. Normally we'd expect
            # all parts to be identical, but in reality they can be different.

            if partIndex > 0 and (not isForced and directPlay
                                  or not isCurrentPart):
                choice = mediadecisionengine.MediaDecisionEngine(
                ).evaluateMediaVideo(self.item, self.media, partIndex)
                canDirectPlay = (choice.isDirectPlayable is True)
            else:
                canDirectPlay = directPlay

            if canDirectPlay:
                partObj = self.buildDirectPlay(partObj, partIndex)
            else:
                transcodeServer = self.item.getTranscodeServer(True, "video")
                if transcodeServer is None:
                    return None
                partObj = self.buildTranscode(transcodeServer, partObj,
                                              partIndex, directStream,
                                              isCurrentPart)

            # Set up our linked list references. If we couldn't build an actual
            # object: fail fast. Otherwise, see if we're at our start offset
            # yet in order to decide if we need to link forwards or backwards.
            # We also need to account for parts missing a duration, by verifying
            # the prevObj is None or if the startOffset has incremented.

            if partObj is None:
                obj = None
                break
            elif prevObj is None or (startOffset > 0 and int(
                    self.seekValue / 1000) >= startOffset):
                obj = partObj
                partObj.prevObj = prevObj
            elif prevObj is not None:
                prevObj.nextPart = partObj

            startOffset = startOffset + int(part.duration.asInt() / 1000)

            prevObj = partObj

        # Only set PlayStart for the initial part, and adjust for the part's offset
        if obj is not None:
            if obj.live:
                # Start the stream at the end. Per Roku, this can be achieved using
                # a number higher than the duration. Using the current time should
                # ensure it's definitely high enough.

                obj.playStart = util.now() + 1800
            else:
                obj.playStart = int(self.seekValue / 1000) - obj.startOffset

        self.metadata = obj

        util.LOG("Constructed video item for playback: {0}".format(dict(obj)))

        return self.metadata
class MediaDecisionEngine(object):
    proxyTypes = util.AttributeDict({'NORMAL': 0, 'LOCAL': 42, 'CLOUD': 43})

    def __init__(self):
        self.softSubLanguages = None

    # TODO(schuyler): Do we need to allow this to be async? We may have to request
    # the media again to fetch details, and we may need to make multiple requests to
    # resolve an indirect. We can do it all async, we can block, or we can allow
    # both.

    def chooseMedia(self, item, forceUpdate=False):
        # If we've already evaluated this item, use our previous choice.
        if not forceUpdate and item.mediaChoice is not None and item.mediaChoice.media is not None and not item.mediaChoice.media.isIndirect(
        ):
            return item.mediaChoice

        # See if we're missing media/stream details for this item.
        if item.isLibraryItem() and item.isVideoItem() and len(
                item.media) > 0 and not item.media[0].hasStreams():
            # TODO(schuyler): Fetch the details
            util.WARN_LOG("Can't make media choice, missing details")

        # Take a first pass through the media items to create an array of candidates
        # that we'll evaluate more completely. If we find a forced item, we use it.
        # If we find an indirect, we only keep a single candidate.
        indirect = False
        candidates = []
        maxResolution = item.settings.getMaxResolution(item.getQualityType())
        for mediaIndex in range(len(item.media)):
            media = item.media[mediaIndex]
            media.mediaIndex = mediaIndex
            if media.isSelected():
                candidates = []
                candidates.append(media)
                break
            if media.isIndirect():
                # Only add indirect media if the resolution fits. We cannot
                # exit early as the user may have selected media.

                indirect = True
                if media.getVideoResolution() <= maxResolution:
                    candidates.append(media)

            elif media.isAccessible():
                # Only consider testing available media
                candidates.append(media)

        # Only use the first indirect media item
        if indirect and candidates:
            candidates = candidates[0]

        # Make sure we have at least one valid item, regardless of availability
        if len(candidates) == 0:
            candidates.append(item.media[0])

        # Now that we have an array of candidates, evaluate them completely.
        choices = []
        for media in candidates:
            choice = None
            if media is not None:
                if item.isVideoItem():
                    choice = self.evaluateMediaVideo(item, media)
                elif item.isMusicItem():
                    choice = self.evaluateMediaMusic(item, media)
                else:
                    choice = mediachoice.MediaChoice(media)
                choices.append(choice)
        item.mediaChoice = self.sortChoices(choices)[-1]
        util.LOG("MDE: MediaChoice: {0}".format(item.mediaChoice))
        return item.mediaChoice

    def sortChoices(self, choices):
        if choices is None:
            return []

        if len(choices) > 1:
            self.sort(choices, "bitrate")
            self.sort(choices, "audioChannels")
            self.sort(choices, "audioDS")
            self.sort(choices, "resolution")
            self.sort(choices, "videoDS")
            self.sort(choices, "directPlay")
            self.sort(choices, self.higherResIfCapable)
            self.sort(choices, self.cloudIfRemote)

        return choices

    def evaluateMediaVideo(self, item, media, partIndex=0):
        # Resolve indirects before doing anything else.
        if media.isIndirect():
            util.LOG("Resolve indirect media for {0}".format(item))
            media = media.resolveIndirect()

        choice = mediachoice.MediaChoice(media, partIndex)
        server = item.getServer()

        if not media:
            return choice

        choice.isSelected = media.isSelected()
        choice.protocol = media.protocol("http")

        maxResolution = item.settings.getMaxResolution(
            item.getQualityType(), self.isSupported4k(media,
                                                      choice.videoStream))
        maxBitrate = item.settings.getMaxBitrate(item.getQualityType())

        choice.resolution = media.getVideoResolution()
        if choice.resolution > maxResolution or media.bitrate.asInt(
        ) > maxBitrate:
            choice.forceTranscode = True

        if choice.subtitleStream:
            choice.subtitleDecision = self.evaluateSubtitles(
                choice.subtitleStream)
            choice.hasBurnedInSubtitles = (
                choice.subtitleDecision != choice.SUBTITLES_SOFT_DP
                and choice.subtitleDecision != choice.SUBTITLES_SOFT_ANY)
        else:
            choice.hasBurnedInSubtitles = False

        # For evaluation purposes, we only care about the first part
        part = media.parts[partIndex]
        if not part:
            return choice

        # Although PMS has already told us which streams are selected, we can't
        # necessarily tell the video player which streams we want. So we need to
        # iterate over the streams and see if there are any red flags that would
        # prevent direct play. If there are multiple video streams, we're hosed.
        # For audio streams, we have a fighting chance if the selected stream can
        # be selected by language, but we need to be careful about guessing which
        # audio stream the Roku will pick for a given language.

        numVideoStreams = 0
        problematicAudioStream = False

        if part.get('hasChapterVideoStream').asBool():
            numVideoStreams = 1

        for stream in part.streams:
            streamType = stream.streamType.asInt()
            if streamType == stream.TYPE_VIDEO:
                numVideoStreams = numVideoStreams + 1

                if stream.codec == "h264" or (
                        stream.codec == "hevc"
                        and item.settings.getPreference("allow_hevc", False)
                ) or (stream.codec == "vp9"
                      and item.settings.getGlobal("vp9Support")):
                    choice.sorts.videoDS = 1

        # Special cases to force direct play
        forceDirectPlay = False
        if choice.protocol == "hls":
            util.LOG("MDE: Assuming HLS is direct playable")
            forceDirectPlay = True
        elif not server.supportsVideoTranscoding:
            # See if we can use another server to transcode, otherwise force direct play
            transcodeServer = item.getTranscodeServer(True, "video")
            if not transcodeServer or not transcodeServer.supportsVideoTranscoding:
                util.LOG(
                    "MDE: force direct play because the server does not support video transcoding"
                )
                forceDirectPlay = True

        # See if we found any red flags based on the streams. Otherwise, go ahead
        # with our codec checks.

        if forceDirectPlay:
            # Consider the choice DP, but continue to allow the
            # choice to have the sorts set properly.
            choice.isDirectPlayable = True
        elif choice.hasBurnedInSubtitles:
            util.LOG("MDE: Need to burn in subtitles")
        elif choice.protocol != "http":
            util.LOG("MDE: " + choice.protocol + " not supported")
        elif numVideoStreams > 1:
            util.LOG("MDE: Multiple video streams, won't try to direct play")
        elif problematicAudioStream:
            util.LOG(
                "MDE: Problematic AAC stream with more than 2 channels prevents direct play"
            )
        elif self.canDirectPlay(item, choice):
            choice.isDirectPlayable = True
        elif item.isMediaSynthesized:
            util.LOG("MDE: assuming synthesized media can direct play")
            choice.isDirectPlayable = True

        # Check for a server decision. This is authority as it's the only playback type
        # the server will allow. This will also support forcing direct play, overriding
        # only our local MDE checks based on the user pref, and only if the server
        # agrees.
        decision = part.get("decision")
        if decision:
            if decision != serverdecision.ServerDecision.DECISION_DIRECT_PLAY:
                util.LOG("MDE: Server has decided this cannot direct play")
                choice.isDirectPlayable = False
            else:
                util.LOG("MDE: Server has allowed direct play")
                choice.isDirectPlayable = True

        # Setup sorts
        if choice.videoStream is not None:
            choice.sorts.bitrate = choice.videoStream.bitrate.asInt()
        elif choice.media is not None:
            choice.sorts.bitrate = choice.media.bitrate.asInt()
        else:
            choice.sorts.bitrate = 0

        if choice.audioStream is not None:
            choice.sorts.audioChannels = choice.audioStream.channels.asInt()
        elif choice.media is not None:
            choice.sorts.audioChannels = choice.media.audioChannels.asInt()
        else:
            choice.sorts.audioChannels = 0

        choice.sorts.videoDS = not (
            choice.sorts.videoDS is None
            or choice.forceTranscode is True) and choice.sorts.videoDS or 0
        choice.sorts.resolution = choice.resolution

        # Server properties probably don't need to be associated with each choice
        choice.sorts.canTranscode = server.supportsVideoTranscoding and 1 or 0
        choice.sorts.canRemuxOnly = server.supportsVideoRemuxOnly and 1 or 0
        choice.sorts.directPlay = (choice.isDirectPlayable is True
                                   and choice.forceTranscode
                                   is not True) and 1 or 0
        choice.sorts.proxyType = choice.media.proxyType and choice.media.proxyType or self.proxyTypes.NORMAL

        return choice

    def canDirectPlay(self, item, choice):
        maxResolution = item.settings.getMaxResolution(
            item.getQualityType(),
            self.isSupported4k(choice.media, choice.videoStream))
        height = choice.media.getVideoResolution()
        if height > maxResolution:
            util.LOG(
                "MDE: (DP) Video height is greater than max allowed: {0} > {1}"
                .format(height, maxResolution))
            if height > 1088 and item.settings.getPreference("allow_4k", True):
                util.LOG("MDE: (DP) Unsupported 4k media")
            return False

        maxBitrate = item.settings.getMaxBitrate(item.getQualityType())
        bitrate = choice.media.bitrate.asInt()
        if bitrate > maxBitrate:
            util.LOG(
                "MDE: (DP) Video bitrate is greater than the allowed max: {0} > {1}"
                .format(bitrate, maxBitrate))
            return False

        if choice.videoStream is None:
            util.ERROR_LOG("MDE: (DP) No video stream")
            return True

        if not item.settings.getGlobal("supports1080p60"):
            videoFrameRate = choice.videoStream.asInt()
            if videoFrameRate > 30 and height >= 1080:
                util.LOG(
                    "MDE: (DP) Frame rate is not supported for resolution: {0}@{1}"
                    .format(height, videoFrameRate))
                return False

        if choice.videoStream.codec == "hevc" and not item.settings.getPreference(
                "allow_hevc", False):
            util.LOG("MDE: (DP) Codec is HEVC, which is disabled")
            return False

        return True

        # container = choice.media.get('container')
        # videoCodec = choice.videoStream.codec
        # if choice.audioStream is None:
        #     audioCodec = None
        #     numChannels = 0
        # else:
        #     audioCodec = choice.audioStream.codec
        #     numChannels = choice.audioStream.channels.asInt()

        # Formats: https://support.roku.com/hc/en-us/articles/208754908-Roku-Media-Player-Playing-your-personal-videos-music-photos
        #  All Models: H.264/AVC  (MKV, MP4, MOV),
        # Roku 4 only: H.265/HEVC (MKV, MP4, MOV); VP9 (.MKV)

        # if True:  # container in ("mp4", "mov", "m4v", "mkv"):
        #     util.LOG("MDE: {0} container looks OK, checking streams".format(container))

        #     isHEVC = videoCodec == "hevc" and item.settings.getPreference("allow_hevc", False)
        #     isVP9 = videoCodec == "vp9" and container == "mkv" and item.settings.getGlobal("vp9Support")

        #     if videoCodec != "h264" and videoCodec != "mpeg4" and not isHEVC and not isVP9:
        #         util.LOG("MDE: Unsupported video codec: {0}".format(videoCodec))
        #         return False

        #     # TODO(schuyler): Fix ref frames check. It's more nuanced than this.
        #     if choice.videoStream.refFrames.asInt() > 8:
        #         util.LOG("MDE: Too many ref frames: {0}".format(choice.videoStream.refFrames))
        #         return False

        #     # HEVC supports a bitDepth of 10, otherwise 8 is the limit
        #     if choice.videoStream.bitDepth.asInt() > (isHEVC and 10 or 8):
        #         util.LOG("MDE: Bit depth too high: {0}".format(choice.videoStream.bitDepth))
        #         return False

        #     # We shouldn't have to whitelist particular audio codecs, we can just
        #     # check to see if the Roku can decode this codec with the number of channels.
        #     if not item.settings.supportsAudioStream(audioCodec, numChannels):
        #         util.LOG("MDE: Unsupported audio track: {0} ({1} channels)".format(audioCodec, numChannels))
        #         return False

        #     # # TODO(schuyler): We've reported this to Roku, they may fix it. If/when
        #     # # they do, we should move this behind a firmware version check.
        #     # if container == "mkv" and choice.videoStream.headerStripping.asBool() and audioCodec == "ac3":
        #     #     util.ERROR_LOG("MDE: Header stripping with AC3 audio")
        #     #     return False

        #     # Those were our problems, everything else should be OK.
        #     return True
        # else:
        #     util.LOG("MDE: Unsupported container: {0}".format(container))

        # return False

    def evaluateSubtitles(self, stream):
        if plexapp.INTERFACE.getPreference("burn_subtitles") == "always":
            # If the user prefers them burned, always burn
            return mediachoice.MediaChoice.SUBTITLES_BURN
        # elif stream.codec != "srt":
        #     # We only support soft subtitles for SRT. Anything else has to use the
        #     # transcoder, and we defer to it on whether the subs will have to be
        #     # burned or can be converted to SRT and muxed.

        #     return mediachoice.MediaChoice.SUBTITLES_DEFAULT
        elif stream.key is None:
            # Embedded subs don't have keys and can only be direct played
            result = mediachoice.MediaChoice.SUBTITLES_SOFT_DP
        else:
            # Sidecar subs can be direct played or used alongside a transcode
            result = mediachoice.MediaChoice.SUBTITLES_SOFT_ANY

        # # TODO(schuyler) If Roku adds support for non-Latin characters, remove
        # # this hackery. To the extent that we continue using this hackery, it
        # # seems that the Roku requires UTF-8 subtitles but only supports characters
        # # from Windows-1252. This should be the full set of languages that are
        # # completely representable in Windows-1252. PMS should specifically be
        # # returning ISO 639-2/B language codes.
        # # Update: Roku has added support for additional characters, but still only
        # # Latin characters. We can now basically support anything from the various
        # # ISO-8859 character sets, but nothing non-Latin.

        # if not self.softSubLanguages:
        #     self.softSubLanguages = frozenset((
        #         'afr',
        #         'alb',
        #         'baq',
        #         'bre',
        #         'cat',
        #         'cze',
        #         'dan',
        #         'dut',
        #         'eng',
        #         'epo',
        #         'est',
        #         'fao',
        #         'fin',
        #         'fre',
        #         'ger',
        #         'gla',
        #         'gle',
        #         'glg',
        #         'hrv',
        #         'hun',
        #         'ice',
        #         'ita',
        #         'lat',
        #         'lav',
        #         'lit',
        #         'ltz',
        #         'may',
        #         'mlt',
        #         'nno',
        #         'nob',
        #         'nor',
        #         'oci',
        #         'pol',
        #         'por',
        #         'roh',
        #         'rum',
        #         'slo',
        #         'slv',
        #         'spa',
        #         'srd',
        #         'swa',
        #         'swe',
        #         'tur',
        #         'vie',
        #         'wel',
        #         'wln'
        #     ))

        # if not (stream.languageCode or 'eng') in self.softSubLanguages:
        #     # If the language is unsupported,: we need to force burning
        #     result = mediachoice.MediaChoice.SUBTITLES_BURN

        return result

    def evaluateMediaMusic(self, item, media):
        # Resolve indirects before doing anything else.
        if media.isIndirect():
            util.LOG("Resolve indirect media for {0}".format(item))
            media = media.resolveIndirect()

        choice = mediachoice.MediaChoice(media)
        if media is None:
            return choice

        # Verify the server supports audio transcoding, otherwise force direct play
        if not item.getServer().supportsAudioTranscoding:
            util.LOG(
                "MDE: force direct play because the server does not support audio transcoding"
            )
            choice.isDirectPlayable = True
            return choice

        # See if this part has a server decision to transcode and obey it
        if choice.part and choice.part.get(
                "decision", serverdecision.ServerDecision.DECISION_DIRECT_PLAY
        ) != serverdecision.ServerDecision.DECISION_DIRECT_PLAY:
            util.WARN_LOG("MDE: Server has decided this cannot direct play")
            return choice

        # Verify the codec and container are compatible
        codec = media.audioCodec
        container = media.get('container')
        canPlayCodec = item.settings.supportsAudioStream(
            codec, media.audioChannels.asInt())
        canPlayContainer = (
            codec == container) or True  # (container in ("mp4", "mka", "mkv"))

        choice.isDirectPlayable = (canPlayCodec and canPlayContainer)
        if choice.isDirectPlayable:
            # Inspect the audio stream attributes if the codec/container can direct
            # play. For now we only need to verify the sample rate.

            if choice.audioStream is not None and choice.audioStream.samplingRate.asInt(
            ) >= 192000:
                util.LOG("MDE: sampling rate is not compatible")
                choice.isDirectPlayable = False
        else:
            util.LOG("MDE: container or codec is incompatible")

        return choice

    # Simple Quick sort function modeled after roku sdk function
    def sort(self, choices, key=None):
        if not isinstance(choices, list):
            return

        if key is None:
            choices.sort()
        elif isinstance(key, basestring):
            choices.sort(key=lambda x: getattr(x.media, key))
        elif hasattr(key, '__call__'):
            choices.sort(key=key)

    def higherResIfCapable(self, choice):
        if choice.media is not None:
            server = choice.media.getServer()
            if server.supportsVideoTranscoding and not server.supportsVideoRemuxOnly and (
                    choice.sorts.directPlay == 1 or choice.sorts.videoDS == 1):
                return util.validInt(choice.sorts.resolution)

        return 0

    def cloudIfRemote(self, choice):
        if choice.media is not None and choice.media.getServer(
        ).isLocalConnection(
        ) and choice.media.proxyType != self.proxyTypes.CLOUD:
            return 1

        return 0

    def isSupported4k(self, media, videoStream):
        if videoStream is None or not plexapp.INTERFACE.getPreference(
                "allow_4k", True):
            return False

        # # Roku 4 only: H.265/HEVC (MKV, MP4, MOV); VP9 (.MKV)
        # if media.get('container') in ("mp4", "mov", "m4v", "mkv"):
        #     isHEVC = (videoStream.codec == "hevc" and plexapp.INTERFACE.getPreference("allow_hevc"))
        #     isVP9 = (videoStream.codec == "vp9" and media.get('container') == "mkv" and plexapp.INTERFACE.getGlobal("vp9Support"))
        #     return (isHEVC or isVP9)

        # return False

        return True
Exemplo n.º 18
0
def main():

    parser = argparse.ArgumentParser(description='Process some integers.')
    parser.add_argument('--units',
                        metavar='units',
                        type=str,
                        help='an unit to visualize e.g. [0, 999]')
    parser.add_argument('--n_iters',
                        metavar='iter',
                        type=int,
                        default=10,
                        help='Number of sampling steps per each unit')
    parser.add_argument(
        '--threshold',
        metavar='w',
        type=float,
        default=-1.0,
        nargs='?',
        help='The probability threshold to decide whether to keep an image')
    parser.add_argument(
        '--save_every',
        metavar='save_iter',
        type=int,
        default=1,
        help='Save a sample every N iterations. 0 to disable saving')
    parser.add_argument('--reset_every',
                        metavar='reset_iter',
                        type=int,
                        default=0,
                        help='Reset the code every N iterations')
    parser.add_argument('--lr',
                        metavar='lr',
                        type=float,
                        default=2.0,
                        nargs='?',
                        help='Learning rate')
    parser.add_argument('--lr_end',
                        metavar='lr',
                        type=float,
                        default=-1.0,
                        nargs='?',
                        help='Ending Learning rate')
    parser.add_argument('--epsilon2',
                        metavar='eps',
                        type=float,
                        default=1.0,
                        nargs='?',
                        help='Scalar for condition ')
    parser.add_argument('--epsilon1',
                        metavar='eps',
                        type=float,
                        default=1.0,
                        nargs='?',
                        help='Scalar for prior')
    parser.add_argument('--epsilon3',
                        metavar='eps',
                        type=float,
                        default=1.0,
                        nargs='?',
                        help='Scalar for noise')
    parser.add_argument('--mask_epsilon',
                        metavar='eps',
                        type=float,
                        default=1e-6,
                        nargs='?',
                        help='Scalar for mask loss')
    parser.add_argument('--edge_epsilon',
                        metavar='eps',
                        type=float,
                        default=1.0,
                        nargs='?',
                        help='Scalar for edge loss')
    parser.add_argument('--content_epsilon',
                        metavar='eps',
                        type=float,
                        default=1.0,
                        nargs='?',
                        help='Scalar for content loss')
    parser.add_argument('--style_epsilon',
                        metavar='eps',
                        type=float,
                        default=1.0,
                        nargs='?',
                        help='Scalar for style loss')
    parser.add_argument('--content_layer',
                        metavar='layer',
                        type=str,
                        default='conv4',
                        nargs='?',
                        help='Layer to use for content loss')
    parser.add_argument('--mask_type',
                        metavar='mask',
                        type=str,
                        default='',
                        nargs='?',
                        help='Mask type. Only square and random available')
    parser.add_argument('--ratio_sample',
                        metavar='eps',
                        type=float,
                        default=1.0,
                        nargs='?',
                        help='Amount to sample for random mask')
    parser.add_argument('--seed',
                        metavar='n',
                        type=int,
                        default=0,
                        nargs='?',
                        help='Random seed')
    parser.add_argument('--xy',
                        metavar='n',
                        type=int,
                        default=0,
                        nargs='?',
                        help='Spatial position for conv units')
    parser.add_argument('--opt_layer',
                        metavar='s',
                        type=str,
                        help='Layer at which we optimize a code')
    parser.add_argument('--act_layer',
                        metavar='s',
                        type=str,
                        default="fc8",
                        help='Layer at which we activate a neuron')
    parser.add_argument('--init_dir',
                        metavar='s',
                        type=str,
                        default="None",
                        help='Init image')
    parser.add_argument('--write_labels',
                        action='store_true',
                        default=False,
                        help='Write class labels to images')
    parser.add_argument('--use_square',
                        action='store_true',
                        default=False,
                        help='Whether or not to use the square')
    parser.add_argument('--output_dir',
                        metavar='b',
                        type=str,
                        default=".",
                        help='Output directory for saving results')
    parser.add_argument('--net_weights',
                        metavar='b',
                        type=str,
                        default=settings.encoder_weights,
                        help='Weights of the net being visualized')
    parser.add_argument('--net_definition',
                        metavar='b',
                        type=str,
                        default=settings.encoder_definition,
                        help='Definition of the net being visualized')

    args = parser.parse_args()

    # Default to constant learning rate
    if args.lr_end < 0:
        args.lr_end = args.lr

    # summary
    print("-------------")
    print(" units: %s    xy: %s" % (args.units, args.xy))
    print(" n_iters: %s" % args.n_iters)
    print(" reset_every: %s" % args.reset_every)
    print(" save_every: %s" % args.save_every)
    print(" threshold: %s" % args.threshold)

    print(" epsilon1: %s" % args.epsilon1)
    print(" epsilon2: %s" % args.epsilon2)
    print(" epsilon3: %s" % args.epsilon3)
    print(" mask_epsilon: %s" % args.mask_epsilon)
    print(" edge_epsilon: %s" % args.edge_epsilon)
    print(" content_epsilon: %s" % args.content_epsilon)
    print(" style_epsilon: %s" % args.style_epsilon)
    print(" mask_type: %s" % args.mask_type)
    print(" content_layer: %s" % args.content_layer)

    print(" start learning rate: %s" % args.lr)
    print(" end learning rate: %s" % args.lr_end)
    print(" seed: %s" % args.seed)
    print(" opt_layer: %s" % args.opt_layer)
    print(" act_layer: %s" % args.act_layer)
    print(" init_file: %s" % args.init_dir)
    print("-------------")
    print(" output dir: %s" % args.output_dir)
    print(" net weights: %s" % args.net_weights)
    print(" net definition: %s" % args.net_definition)
    print("-------------")

    # encoder and generator for images
    encoder = caffe.Net(settings.encoder_definition, settings.encoder_weights,
                        caffe.TEST)
    generator = caffe.Net(settings.generator_definition,
                          settings.generator_weights, caffe.TEST)

    # condition network, here an image classification net
    net = caffe.Classifier(
        args.net_definition,
        args.net_weights,
        mean=np.float32([104.0, 117.0, 123.0]),  # ImageNet mean
        channel_swap=(
            2, 1,
            0))  # the reference model has channels in BGR order instead of RGB
    edge_detector = caffe.Net(settings.edge_definition, caffe.TEST)
    # make Sobel operator for edge detection
    laplace = np.array((0, -1, 0, -1, 4, -1, 0, -1, 0),
                       dtype=np.float32).reshape((3, 3))
    edge_detector.params['laplace'][0].data[0, 0, :, :] = laplace  # horizontal
    # Fix the seed
    np.random.seed(args.seed)
    args = util.AttributeDict(vars(args))
    # Separate the dash-separated list of units into numbers
    conditions = [{
        "unit": int(u),
        "xy": args.xy
    } for u in args.units.split("_")]
    files_to_read = [
        os.path.join(args.init_dir, f) for f in os.listdir(args.init_dir)
        if 'lena' in f
    ]
    attributes = ['content_epsilon', 'style_epsilon', 'edge_epsilon']
    # attributes = ['edge_epsilon']#,'style_epsilon', 'edge_epsilon']
    masks = ['random', 'laplace', 'square_random', 'square_laplace']

    eps = [(0, 0, 0), (1e-4, 0, 0), (0, 1e-6, 0), (0, 0, 1e-2),
           (0, 1e-6, 1e-2)]
    output_dir = args.output_dir
    images_to_save = []
    for image_file in files_to_read:
        image_name = re.split('\.|/', image_file)[-2]
        print('image_name', image_name)
        image_path = os.path.join(args.output_dir, image_name)
        if not os.path.exists(image_path):
            os.makedirs(image_path)

        images_col = None
        for i, (edge, content, style) in enumerate(eps):

            sampler = ClassConditionalSampler()
            start_image = sampler.load_image(
                shape=encoder.blobs["data"].data.shape,
                path=image_file,
                output_dir=output_dir,
                save=False)

            if images_col is None:
                images_col = [start_image.copy()]

            print('running', image_file, i)
            mask = get_mask(start_image, args.mask_type, inverse=False)
            start_code = sampler.get_code(encoder=encoder,
                                          data=start_image,
                                          layer=args.opt_layer,
                                          mask=mask)
            output_image, list_samples = sampler.sampling(
                condition_net=net,
                image_encoder=encoder,
                image_net=net,
                image_generator=generator,
                edge_detector=edge_detector,
                gen_in_layer=settings.generator_in_layer,
                gen_out_layer=settings.generator_out_layer,
                start_code=start_code,
                n_iters=args.n_iters,
                lr=args.lr,
                lr_end=args.lr_end,
                threshold=args.threshold,
                layer=args.act_layer,
                conditions=conditions,
                epsilon1=args.epsilon1,
                epsilon2=args.epsilon2,
                epsilon3=args.epsilon3,
                mask_epsilon=args.mask_epsilon,
                content_epsilon=content,
                style_epsilon=style,
                edge_epsilon=edge,
                content_layer=args.content_layer,
                output_dir=output_dir,
                mask=mask,
                input_image=start_image,
                reset_every=args.reset_every,
                save_every=args.save_every)

            print('Saving {} for {}'.format(i, image_name))
            images_col.append(output_image)
            file_path = os.path.join(image_path, str(i) + '.jpg')
            util.save_image(output_image, file_path)
        images_to_save.append(images_col)

    filename = "%s/%s_%04d_%04d_%s_h_%s_%s_%s__%s.jpg" % (
        args.output_dir, 'loss_survey', conditions[0]["unit"], args.n_iters,
        args.lr, str(args.epsilon1), str(args.epsilon2), str(
            args.epsilon3), args.seed)
    util.save_checkerboard(images_to_save,
                           filename,
                           labels=[
                               'ground truth', 'no loss', 'edge loss',
                               'content loss', 'style loss', 'content + style'
                           ])