Example #1
0
    def toFileItem(plexServer, plexItem, mediaType=None, plexLibType=None):
        # determine the matching Plex library type if possible
        checkMediaType = mediaType is not None
        if checkMediaType and not plexLibType:
            mappedMediaType = Api.getPlexMediaType(mediaType)
            if not mappedMediaType:
                log(
                    'cannot import unsupported media type "{}"'.format(
                        mediaType), xbmc.LOGERROR)
                return None

            plexLibType = mappedMediaType['libtype']

        # make sure the item matches the media type
        if plexLibType is not None and not Api.validatePlexLibraryItemType(
                plexItem, plexLibType):
            log(
                'cannot import {} item from invalid Plex library item: {}'.
                format(mediaType, plexItem), xbmc.LOGERROR)
            return None

        # determine the Kodi media type based on the Plex library type
        if not checkMediaType:
            plexLibType = plexItem.type
            mappedMediaTypes = Api.getKodiMediaTypesFromPlexLibraryTpe(
                plexLibType)
            if not mappedMediaTypes:
                log(
                    'cannot import unsupported Plex library type "{}"'.format(
                        plexLibType), xbmc.LOGERROR)
                return None

            if len(mappedMediaTypes) > 1:
                log(
                    '{} supported media type for Plex library type "{}"'.
                    format(len(mappedMediaTypes), plexLibType), xbmc.LOGDEBUG)

            mediaType = mappedMediaTypes[0]['kodi']

        itemId = plexItem.ratingKey
        if not itemId:
            log('cannot import {} item without identifier'.format(mediaType),
                xbmc.LOGERROR)
            return None

        item = ListItem(label=plexItem.title)

        # fill video details
        Api.fillVideoInfos(plexServer, itemId, plexItem, mediaType, item)

        if not item.getPath():
            log(
                'failed to retrieve a path for {} item "{}"'.format(
                    mediaType, item.getLabel()), xbmc.LOGWARNING)
            return None

        return item
Example #2
0
def downloads():
	if kodiutils.get_setting_as_bool("folder") and kodiutils.get_setting("downloadPath") and xbmcvfs.exists(kodiutils.get_setting("downloadPath")):
		dirs, files = xbmcvfs.listdir(kodiutils.get_setting("downloadPath"))
		if files:
			items = []
			for file_ in files:
				cm = []
				liz = ListItem(file_.split(".")[0])
				liz.setPath(os.path.join(kodiutils.get_setting("downloadPath"),file_))
				liz.setProperty('IsPlayable', 'true')
				cm.append((kodiutils.get_string(32055), 'XBMC.RunPlugin(plugin://%s/delete_file/%s)' % (ADDON.getAddonInfo("id"),urllib.quote(os.path.join(kodiutils.get_setting("downloadPath"),file_), safe='')) ))
				liz.addContextMenuItems(cm, replaceItems=False)
				items.append((liz.getPath(), liz, False))
			if items:
				addDirectoryItems(plugin.handle, items, totalItems=len(items))
	endOfDirectory(plugin.handle)
Example #3
0
    def fillVideoInfos(
            plexServer: plexapi.server.PlexServer,
            itemId: int,
            plexItem: video.Video,
            mediaType: str,
            item: ListItem
    ):
        """
        Populate the provided ListItem object with existing data from plexItem
        and additional detail pulled from the provided plexServer

        :param plexServer: Plex server to gather additional details from
        :type plexServer: plexapi.server.PlexServer
        :param itemId: Unique ID of the plex Video object item
        :type itemId: int
        :param plexItem: Plex object populated with information about the item
        :type plexItem: video.Video
        :param mediaType: Kodi Media type object
        :type mediaType: str
        :param item: Instantiated Kodi ListItem to populate with additional details
        :type item: :class:`ListItem`
        """
        info = {
            'mediatype': mediaType,
            'path': '',
            'filenameandpath': '',
            'title': item.getLabel() or '',
            'sorttitle': '',
            'originaltitle': '',
            'plot': plexItem.summary or '',
            'dateadded': Api.convertDateTimeToDbDateTime(plexItem.addedAt),
            'year': 0,
            'set': '',
            'rating': 0.0,
            'userrating': 0.0,
            'mpaa': '',
            'duration': 0,
            'playcount': 0,
            'lastplayed': '',
            'director': [],
            'writer': [],
            'genre': [],
            'country': [],
            'tag': []
        }

        date = None
        isFolder = False

        resumePoint = {
            'totaltime': 0,
            'resumetime': 0
        }

        artwork = {}
        collections = []
        media = []
        locations = []
        roles = []

        if isinstance(plexItem, video.Video):
            info.update({
                'sorttitle': plexItem.titleSort,
                'playcount': plexItem.viewCount,
                'lastplayed': Api.convertDateTimeToDbDateTime(plexItem.lastViewedAt),
            })
            info['tag'].append(plexItem.librarySectionTitle)

        if isinstance(plexItem, video.Movie):
            info.update({
                'mpaa': plexItem.contentRating or '',
                'duration': Api.MillisecondsToSeconds(plexItem.duration),
                'originaltitle': plexItem.originalTitle or '',
                'premiered': Api.convertDateTimeToDbDate(plexItem.originallyAvailableAt),
                'rating': plexItem.rating or 0.0,
                'studio': Api.ListFromString(plexItem.studio),
                'tagline': plexItem.tagline or '',
                'userrating': plexItem.userRating or 0.0,
                'year': plexItem.year or 0,
                'country': Api.ListFromMediaTags(plexItem.countries),
                'director': Api.ListFromMediaTags(plexItem.directors),
                'genre': Api.ListFromMediaTags(plexItem.genres),
                'writer': Api.ListFromMediaTags(plexItem.writers),
            })

            date = info['premiered']
            resumePoint['resumetime'] = Api.MillisecondsToSeconds(plexItem.viewOffset)
            collections = plexItem.collections
            media = plexItem.media
            roles = plexItem.roles
        elif isinstance(plexItem, library.Collections):
            isFolder = True
        elif isinstance(plexItem, video.Show):
            info.update({
                'mpaa': plexItem.contentRating or '',
                'duration': Api.MillisecondsToSeconds(plexItem.duration),
                'premiered': Api.convertDateTimeToDbDate(plexItem.originallyAvailableAt),
                'rating': plexItem.rating or 0.0,
                'studio': Api.ListFromString(plexItem.studio),
                'year': plexItem.year or 0,
                'genre': Api.ListFromMediaTags(plexItem.genres),
            })

            date = info['premiered']
            isFolder = True
            locations = plexItem.locations
            collections = plexItem.collections
            roles = plexItem.roles

            banner = plexItem.banner
            if banner:
                artwork['banner'] = plexServer.url(banner, includeToken=True)
        elif isinstance(plexItem, video.Season):
            info.update({
                'tvshowtitle': plexItem.parentTitle or '',
                'season': plexItem.index,
            })
            isFolder = True
        elif isinstance(plexItem, video.Episode):
            info.update({
                'tvshowtitle': plexItem.grandparentTitle or '',
                'season': plexItem.parentIndex,
                'episode': plexItem.index,
                'mpaa': plexItem.contentRating or '',
                'duration': Api.MillisecondsToSeconds(plexItem.duration),
                'aired': Api.convertDateTimeToDbDate(plexItem.originallyAvailableAt),
                'rating': plexItem.rating or 0.0,
                'year': plexItem.year or 0,
                'director': Api.ListFromMediaTags(plexItem.directors),
                'writer': Api.ListFromMediaTags(plexItem.writers),
            })

            date = info['aired']
            resumePoint['resumetime'] = Api.MillisecondsToSeconds(plexItem.viewOffset)
            media = plexItem.media

        # handle collections / sets
        collections = Api.ListFromMediaTags(collections)
        if collections:
            # Kodi can only store one set per media item
            info['set'] = collections[0]

        # set the item's datetime if available
        if date:
            item.setDateTime(date)

        # specify whether the item is a folder or not
        item.setIsFolder(isFolder)

        # add the item's ID as a unique ID belonging to Plex
        item.getVideoInfoTag().setUniqueIDs({
            PLEX_PROTOCOL: itemId
        }, PLEX_PROTOCOL)

        # handle actors / cast
        cast = []
        for index, role in enumerate(roles):
            cast.append({
                'name': role.tag.strip(),
                'role': role.role.strip(),
                'order': index
            })
        if cast:
            item.setCast(cast)

        # handle resume point
        if resumePoint['resumetime'] > 0 and info['duration'] > 0:
            resumePoint['totaltime'] = info['duration']
            item.setProperties(resumePoint)

        # handle stream details
        mediaPart = None
        for mediaStream in media:
            for part in mediaStream.parts:
                # pick the first MediaPart with a valid file and stream URL
                if mediaPart is None and part.file is not None and part.key is not None:
                    mediaPart = part

                for videoStream in part.videoStreams():
                    item.addStreamInfo('video', {
                        'codec': videoStream.codec,
                        'language': videoStream.language,
                        'width': videoStream.width,
                        'height': videoStream.height,
                        'duration': info['duration']
                    })

                for audioStream in part.audioStreams():
                    item.addStreamInfo(
                        'audio', {
                            'codec': audioStream.codec,
                            'language': audioStream.language,
                            'channels': audioStream.channels
                        }
                    )

                for subtitleStream in part.subtitleStreams():
                    item.addStreamInfo(
                        'subtitle', {
                            'language': subtitleStream.language
                        }
                    )

        if mediaPart:
            # extract the absolute / actual path and the stream URL from the selected MediaPart
            info['path'] = mediaPart.file
            item.setPath(plexServer.url(mediaPart.key, includeToken=True))
        elif isFolder:
            # for folders use locations for the path
            if locations:
                info['path'] = locations[0]
            item.setPath(plexServer.url(plexItem.key, includeToken=True))
        info['filenameandpath'] = item.getPath()

        # set all the video infos
        item.setInfo('video', info)

        # handle artwork
        poster = None
        fanart = None
        if isinstance(plexItem, video.Video):
            poster = plexItem.thumbUrl
            fanart = plexItem.artUrl
        elif isinstance(plexItem, library.Collections) and plexItem.thumb:
            poster = plexServer.url(plexItem.thumb, includeToken=True)

        if poster:
            artwork['poster'] = poster
        if fanart:
            artwork['fanart'] = fanart
        if artwork:
            item.setArt(artwork)
Example #4
0
    def toFileItem(
            plexServer: plexapi.server.PlexServer,
            plexItem: video.Video,
            mediaType: str = "",
            plexLibType: str = ""
    ) -> ListItem:
        """Validate, populate, and convert the provided plexItem into a Kodi GUI ListItem object

        :param plexServer: Plex server to gather additional details from
        :type plexServer: plexapi.server.PlexServer
        :param plexItem: Plex object populated with information about the item
        :type plexItem: video.Video
        :param mediaType: Kodi Media type object, defaults to ''
        :type mediaType: str, optional
        :param plexLibType: Type of plex library (movie, show, season, episode, collection), defaults to ''
        :type plexLibType: str, optional
        :return: ListItem object populated with the retreived plex item details
        :rtype: ListItem
        """
        # determine the matching Plex library type if possible
        checkMediaType = mediaType is not None
        if checkMediaType and not plexLibType:
            mappedMediaType = Api.getPlexMediaType(mediaType)
            if not mappedMediaType:
                log(f"cannot import unsupported media type '{mediaType}'", xbmc.LOGERROR)
                return None

            plexLibType = mappedMediaType['libtype']

        # make sure the item matches the media type
        if plexLibType is not None and not Api.validatePlexLibraryItemType(plexItem, plexLibType):
            log(f"cannot import {mediaType} item from invalid Plex library item: {plexItem}", xbmc.LOGERROR)
            return None

        # determine the Kodi media type based on the Plex library type
        if not checkMediaType:
            plexLibType = plexItem.type
            mappedMediaTypes = Api.getKodiMediaTypesFromPlexLibraryType(plexLibType)
            if not mappedMediaTypes:
                log(f"cannot import unsupported Plex library type '{plexLibType}'", xbmc.LOGERROR)
                return None

            if len(mappedMediaTypes) > 1:
                log(
                    f"{len(mappedMediaTypes)} supported media type for Plex library type '{plexLibType}'",
                    xbmc.LOGDEBUG
                )

            mediaType = mappedMediaTypes[0]['kodi']

        itemId = plexItem.ratingKey
        if not itemId:
            log(f"cannot import {mediaType} item without identifier", xbmc.LOGERROR)
            return None

        item = ListItem(label=plexItem.title)

        # fill video details
        Api.fillVideoInfos(plexServer, itemId, plexItem, mediaType, item)

        if not item.getPath():
            log(f"failed to retrieve a path for {mediaType} item '{item.getLabel()}'", xbmc.LOGWARNING)
            return None

        return item
Example #5
0
    def createVideoInfoItemWithVideoSetters(embyServer,
                                            itemId,
                                            itemPath,
                                            itemObj,
                                            mediaType,
                                            libraryView='',
                                            allowDirectPlay=True):

        item = ListItem(path=itemPath,
                        label=itemObj.get(constants.PROPERTY_ITEM_NAME, ''),
                        offscreen=True)
        item.setIsFolder(itemObj.get(constants.PROPERTY_ITEM_IS_FOLDER, False))

        # handle date
        premiereDate = itemObj.get(constants.PROPERTY_ITEM_PREMIERE_DATE)
        if premiereDate:
            item.setDateTime(premiereDate)

        videoInfoTag = item.getVideoInfoTag()

        userdata = {}
        if constants.PROPERTY_ITEM_USER_DATA in itemObj:
            userdata = itemObj[constants.PROPERTY_ITEM_USER_DATA]

        duration = int(
            Api.ticksToSeconds(
                itemObj.get(constants.PROPERTY_ITEM_RUN_TIME_TICKS, 0)))

        videoInfoTag.setMediaType(mediaType)
        videoInfoTag.setPath(itemObj.get(constants.PROPERTY_ITEM_PATH, ''))
        videoInfoTag.setFilenameAndPath(item.getPath())
        videoInfoTag.setTitle(item.getLabel() or '')
        videoInfoTag.setSortTitle(
            itemObj.get(constants.PROPERTY_ITEM_SORT_NAME, ''))
        videoInfoTag.setOriginalTitle(
            itemObj.get(constants.PROPERTY_ITEM_ORIGINAL_TITLE, ''))
        videoInfoTag.setPlot(
            Api._mapOverview(itemObj.get(constants.PROPERTY_ITEM_OVERVIEW,
                                         '')))
        videoInfoTag.setPlotOutline(
            itemObj.get(constants.PROPERTY_ITEM_SHORT_OVERVIEW, ''))
        videoInfoTag.setDateAdded(
            Api.convertDateTimeToDbDateTime(
                itemObj.get(constants.PROPERTY_ITEM_DATE_CREATED, '')))
        videoInfoTag.setYear(
            itemObj.get(constants.PROPERTY_ITEM_PRODUCTION_YEAR, 0))
        videoInfoTag.setMpaa(
            Api._mapMpaa(
                itemObj.get(constants.PROPERTY_ITEM_OFFICIAL_RATING, '')))
        videoInfoTag.setDuration(duration)
        videoInfoTag.setPlaycount(
            userdata.get(constants.PROPERTY_ITEM_USER_DATA_PLAY_COUNT, 0
                         ) if userdata.
            get(constants.PROPERTY_ITEM_USER_DATA_PLAYED, False) else 0)
        videoInfoTag.setLastPlayed(
            Api.convertDateTimeToDbDateTime(
                userdata.get(
                    constants.PROPERTY_ITEM_USER_DATA_LAST_PLAYED_DATE, '')))
        videoInfoTag.setArtists(
            itemObj.get(constants.PROPERTY_ITEM_ARTISTS, []))
        videoInfoTag.setAlbum(itemObj.get(constants.PROPERTY_ITEM_ALBUM, ''))
        videoInfoTag.setGenres(itemObj.get(constants.PROPERTY_ITEM_GENRES, []))
        videoInfoTag.setCountries(
            itemObj.get(constants.PROPERTY_ITEM_PRODUCTION_LOCATIONS, []))

        # process ratings
        if constants.PROPERTY_ITEM_COMMUNITY_RATING in itemObj:
            defaultRating = itemObj.get(
                constants.PROPERTY_ITEM_COMMUNITY_RATING)
            videoInfoTag.setRating(defaultRating, isDefault=True)
        # handle critic rating as rotten tomato rating
        if constants.PROPERTY_ITEM_CRITIC_RATING in itemObj:
            criticRating = float(
                itemObj.get(constants.PROPERTY_ITEM_CRITIC_RATING)) / 10.0
            videoInfoTag.setRating(criticRating, type='tomatometerallcritics')

        # handle unique / provider IDs
        uniqueIds = \
            {key.lower(): value for key, value in iteritems(itemObj.get(constants.PROPERTY_ITEM_PROVIDER_IDS, {}))}
        defaultUniqueId = Api._mapDefaultUniqueId(uniqueIds, mediaType)
        # add the item's ID as a unique ID belonging to Emby
        uniqueIds[constants.EMBY_PROTOCOL] = itemId
        videoInfoTag.setUniqueIDs(uniqueIds, defaultUniqueId)

        # process tags
        tags = []
        if constants.PROPERTY_ITEM_TAG_ITEMS in itemObj:
            tags = [
                tag.get(constants.PROPERTY_ITEM_TAG_ITEMS_NAME)
                for tag in itemObj.get(constants.PROPERTY_ITEM_TAG_ITEMS)
                if constants.PROPERTY_ITEM_TAG_ITEMS_NAME in tag
            ]
        # add the library view as a tag
        if libraryView:
            tags.append(libraryView)
        videoInfoTag.setTags(tags)

        # handle aired / premiered
        if premiereDate:
            pos = premiereDate.find('T')
            if pos >= 0:
                premiereDate = premiereDate[:pos]

            if mediaType == xbmcmediaimport.MediaTypeEpisode:
                videoInfoTag.setFirstAired(premiereDate)
            else:
                videoInfoTag.setPremiered(premiereDate)

        # handle trailers
        trailerUrl = Api.getTrailer(embyServer,
                                    itemId,
                                    itemObj,
                                    allowDirectPlay=allowDirectPlay)
        if trailerUrl:
            videoInfoTag.setTrailer(trailerUrl)

        # handle taglines
        embyTaglines = itemObj.get(constants.PROPERTY_ITEM_TAGLINES, [])
        if embyTaglines:
            videoInfoTag.setTagLine(embyTaglines[0])

        # handle studios
        studios = []
        for studio in itemObj.get(constants.PROPERTY_ITEM_STUDIOS, []):
            studios.append(Api._mapStudio(studio['Name']))
        videoInfoTag.setStudios(studios)

        # handle tvshow, season and episode specific properties
        if mediaType == xbmcmediaimport.MediaTypeTvShow:
            videoInfoTag.setTvShowTitle(videoInfoTag.getTitle())
            videoInfoTag.setTvShowStatus(
                itemObj.get(constants.PROPERTY_ITEM_STATUS, ''))
        elif mediaType in (xbmcmediaimport.MediaTypeSeason,
                           xbmcmediaimport.MediaTypeEpisode):
            videoInfoTag.setTvShowTitle(
                itemObj.get(constants.PROPERTY_ITEM_SERIES_NAME, ''))
            index = itemObj.get(constants.PROPERTY_ITEM_INDEX_NUMBER, 0)
            if mediaType == xbmcmediaimport.MediaTypeSeason:
                videoInfoTag.setSeason(index)

                # ATTENTION
                # something is wrong with the SortName property for seasons which interfers with Kodi
                # abusing sorttitle for custom season titles
                videoInfoTag.setSortTitle('')
            else:
                videoInfoTag.setSeason(
                    itemObj.get(constants.PROPERTY_ITEM_PARENT_INDEX_NUMBER,
                                0))
                videoInfoTag.setEpisode(index)

        # handle resume point
        videoInfoTag.setResumePoint(
            Api.ticksToSeconds(
                userdata.get(
                    constants.PROPERTY_ITEM_USER_DATA_PLAYBACK_POSITION_TICKS,
                    0)), duration)

        # handle actors / cast
        cast = []
        writers = []
        directors = []
        for index, person in enumerate(
                itemObj.get(constants.PROPERTY_ITEM_PEOPLE, [])):
            name = person.get(constants.PROPERTY_ITEM_PEOPLE_NAME, '')
            castType = person.get(constants.PROPERTY_ITEM_PEOPLE_TYPE, '')
            if castType == constants.PROPERTY_ITEM_PEOPLE_TYPE_ACTOR:
                role = person.get(constants.PROPERTY_ITEM_PEOPLE_ROLE, '')
                # determine the thumbnail (if available)
                thumbnail = ''
                personId = person.get(constants.PROPERTY_ITEM_PEOPLE_ID, None)
                primaryImageTag = person.get(
                    constants.PROPERTY_ITEM_PEOPLE_PRIMARY_IMAGE_TAG, '')
                if personId and primaryImageTag:
                    thumbnail = \
                        embyServer.BuildImageUrl(personId, constants.PROPERTY_ITEM_IMAGE_TAGS_PRIMARY, primaryImageTag)

                cast.append(xbmc.Actor(name, role, index, thumbnail))
            elif castType == constants.PROPERTY_ITEM_PEOPLE_TYPE_WRITER:
                writers.append(name)
            elif castType == constants.PROPERTY_ITEM_PEOPLE_TYPE_DIRECTOR:
                directors.append(name)

        videoInfoTag.setCast(cast)
        videoInfoTag.setWriters(writers)
        videoInfoTag.setDirectors(directors)

        # stream details
        for stream in itemObj.get(constants.PROPERTY_ITEM_MEDIA_STREAMS, []):
            streamType = stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_TYPE,
                                    '')
            if streamType == 'video':
                details = Api._mapVideoStream({
                    'codec':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_CODEC, ''),
                    'profile':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_PROFILE,
                               ''),
                    'language':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_LANGUAGE,
                               ''),
                    'width':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_WIDTH, 0),
                    'height':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_HEIGHT, 0),
                    'aspect':
                    stream.get(
                        constants.PROPERTY_ITEM_MEDIA_STREAM_ASPECT_RATIO,
                        '0'),
                    'stereomode':
                    stream.get(
                        constants.PROPERTY_ITEM_MEDIA_STREAM_VIDEO_3D_FORMAT,
                        'mono'),
                    'duration':
                    duration
                })
                videoInfoTag.addVideoStream(
                    xbmc.VideoStreamDetail(
                        width=details['width'],
                        height=details['height'],
                        aspect=details['aspect'],
                        duration=details['duration'],
                        codec=details['codec'],
                        stereoMode=details['stereomode'],
                        language=details['language'],
                    ))
            elif streamType == 'audio':
                details = Api._mapAudioStream({
                    'codec':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_CODEC, ''),
                    'profile':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_PROFILE,
                               ''),
                    'language':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_LANGUAGE,
                               ''),
                    'channels':
                    stream.get(constants.PROPERTY_ITEM_MEDIA_STREAM_CHANNELS,
                               2)
                })
                videoInfoTag.addAudioStream(
                    xbmc.AudioStreamDetail(
                        channels=details['channels'],
                        codec=details['codec'],
                        language=details['language'],
                    ))
            elif streamType == 'subtitle':
                videoInfoTag.addSubtitleStream(
                    xbmc.SubtitleStreamDetail(language=stream.get(
                        constants.PROPERTY_ITEM_MEDIA_STREAM_LANGUAGE, ''), ))

        return item
Example #6
0
    def fillVideoInfos(plexServer: server.PlexServer,
                       itemId: int,
                       plexItem: video.Video,
                       mediaType: str,
                       item: ListItem,
                       allowDirectPlay: bool = False):
        """
        Populate the provided ListItem object with existing data from plexItem
        and additional detail pulled from the provided plexServer

        :param plexServer: Plex server to gather additional details from
        :type plexServer: server.PlexServer
        :param itemId: Unique ID of the plex Video object item
        :type itemId: int
        :param plexItem: Plex object populated with information about the item
        :type plexItem: video.Video
        :param mediaType: Kodi Media type object
        :type mediaType: str
        :param item: Instantiated Kodi ListItem to populate with additional details
        :type item: :class:`ListItem`
        :param allowDirectPlay: Settings definition on provider if directPlay is allowed
        :type allowDirectPlay: bool, optional
        """
        videoInfoTag = item.getVideoInfoTag()

        videoInfoTag.setMediaType(mediaType)
        videoInfoTag.setTitle(item.getLabel() or '')

        date = None
        isFolder = False

        resumeTime = 0.0
        duration = 0.0

        artwork = {}
        collections = []
        media = []
        locations = []
        roles = []

        if isinstance(plexItem, video.Video):
            videoInfoTag.setSortTitle(plexItem.titleSort or '')
            videoInfoTag.setPlot(plexItem.summary or '')
            videoInfoTag.setDateAdded(
                Api.convertDateTimeToDbDateTime(plexItem.addedAt))
            videoInfoTag.setPlaycount(plexItem.viewCount or 0)
            videoInfoTag.setLastPlayed(
                Api.convertDateTimeToDbDateTime(plexItem.lastViewedAt))
            videoInfoTag.setTags([plexItem.librarySectionTitle])

        if isinstance(plexItem, video.Movie):
            date = Api.convertDateTimeToDbDate(plexItem.originallyAvailableAt)
            duration = Api.MillisecondsToSeconds(plexItem.duration)
            resumeTime = Api.MillisecondsToSeconds(plexItem.viewOffset)
            collections = plexItem.collections or []
            media = plexItem.media or []
            roles = plexItem.roles or []

            videoInfoTag.setMpaa(plexItem.contentRating or '')
            videoInfoTag.setDuration(int(duration))
            videoInfoTag.setOriginalTitle(plexItem.originalTitle or '')
            videoInfoTag.setPremiered(date)
            videoInfoTag.setRating(plexItem.rating or 0.0)
            videoInfoTag.setTagLine(plexItem.tagline or '')
            videoInfoTag.setUserRating(int(plexItem.userRating or 0))
            videoInfoTag.setYear(plexItem.year or 0)
            videoInfoTag.setStudios(Api.ListFromString(plexItem.studio))
            videoInfoTag.setCountries(Api.ListFromMediaTags(
                plexItem.countries))
            videoInfoTag.setGenres(Api.ListFromMediaTags(plexItem.genres))
            videoInfoTag.setDirectors(Api.ListFromMediaTags(
                plexItem.directors))
            videoInfoTag.setWriters(Api.ListFromMediaTags(plexItem.writers))
        elif isinstance(plexItem, collection.Collection):
            # ignore empty collections
            if plexItem.childCount <= 0:
                return

            isFolder = True

            videoInfoTag.setPlot(plexItem.summary or '')
            videoInfoTag.setDateAdded(
                Api.convertDateTimeToDbDateTime(plexItem.addedAt))
        elif isinstance(plexItem, video.Show):
            isFolder = True
            date = Api.convertDateTimeToDbDate(plexItem.originallyAvailableAt)
            duration = Api.MillisecondsToSeconds(plexItem.duration)
            locations = plexItem.locations or []
            collections = plexItem.collections or []
            roles = plexItem.roles or []

            banner = plexItem.banner
            if banner:
                artwork['banner'] = plexServer.url(banner, includeToken=True)

            videoInfoTag.setMpaa(plexItem.contentRating or '')
            videoInfoTag.setDuration(int(duration))
            videoInfoTag.setOriginalTitle(plexItem.originalTitle or '')
            videoInfoTag.setPremiered(date)
            videoInfoTag.setRating(plexItem.rating or 0.0)
            videoInfoTag.setTagLine(plexItem.tagline or '')
            videoInfoTag.setYear(plexItem.year or 0)
            videoInfoTag.setStudios(Api.ListFromString(plexItem.studio))
            videoInfoTag.setGenres(Api.ListFromMediaTags(plexItem.genres))
        elif isinstance(plexItem, video.Season):
            isFolder = True

            videoInfoTag.setTvShowTitle(plexItem.parentTitle or '')
            videoInfoTag.setSeason(plexItem.index)
        elif isinstance(plexItem, video.Episode):
            date = Api.convertDateTimeToDbDate(plexItem.originallyAvailableAt)
            resumeTime = Api.MillisecondsToSeconds(plexItem.viewOffset)
            duration = Api.MillisecondsToSeconds(plexItem.duration)
            media = plexItem.media or []

            videoInfoTag.setTvShowTitle(plexItem.grandparentTitle or '')
            videoInfoTag.setSeason(int(plexItem.parentIndex))
            videoInfoTag.setEpisode(plexItem.index)
            videoInfoTag.setMpaa(plexItem.contentRating or '')
            videoInfoTag.setDuration(int(duration))
            videoInfoTag.setFirstAired(date)
            videoInfoTag.setRating(plexItem.rating or 0.0)
            videoInfoTag.setYear(plexItem.year or 0)
            videoInfoTag.setDirectors(Api.ListFromMediaTags(
                plexItem.directors))
            videoInfoTag.setWriters(Api.ListFromMediaTags(plexItem.writers))

        # handle collections / sets
        collections = Api.ListFromMediaTags(collections)
        if collections:
            # Kodi can only store one set per media item
            videoInfoTag.setSet(collections[0])

        # set the item's datetime if available
        if date:
            item.setDateTime(date)

        # specify whether the item is a folder or not
        item.setIsFolder(isFolder)

        # add the item's ID as a unique ID belonging to Plex
        uniqueIDs = {PLEX_PROTOCOL: str(itemId)}
        # retrieve and map GUIDS from Plex
        if isinstance(plexItem,
                      (video.Movie, video.Show, video.Season, video.Episode)):
            guids = Api._mapGuids(plexItem.guids)
            if guids:
                uniqueIDs = {**guids, **uniqueIDs}

        videoInfoTag.setUniqueIDs(uniqueIDs, PLEX_PROTOCOL)

        # handle actors / cast
        cast = []
        for index, role in enumerate(roles):
            actor = xbmc.Actor(role.tag.strip(), (role.role or '').strip(),
                               index, role.thumb)
            cast.append(actor)
        if cast:
            videoInfoTag.setCast(cast)

        # handle resume point
        if resumeTime > 0 and duration > 0.0:
            videoInfoTag.setResumePoint(resumeTime, duration)

        # handle stream details
        path = None
        for mediaStream in media:
            for part in mediaStream.parts:
                # pick the first MediaPart with a valid file and stream URL
                if not path and part.file and part.key:
                    path = part.file

                for videoStream in part.videoStreams():
                    videoInfoTag.addVideoStream(
                        xbmc.VideoStreamDetail(width=videoStream.width or 0,
                                               height=videoStream.height or 0,
                                               codec=videoStream.codec or '',
                                               duration=int(duration),
                                               language=videoStream.language
                                               or ''))

                for audioStream in part.audioStreams():
                    videoInfoTag.addAudioStream(
                        xbmc.AudioStreamDetail(channels=audioStream.channels
                                               or 2,
                                               codec=audioStream.codec or '',
                                               language=audioStream.language
                                               or ''))

                for index, subtitleStream in enumerate(part.subtitleStreams()):
                    videoInfoTag.addSubtitleStream(
                        xbmc.SubtitleStreamDetail(
                            language=subtitleStream.language or f"[{index}]"))

        if isFolder:
            # for folders use locations for the path
            if locations:
                path = locations[0]
            item.setPath(plexServer.url(plexItem.key, includeToken=True))
        else:
            # determine if directPlay is enabled and possible
            if allowDirectPlay:
                directPlayUrl = Api.getDirectPlayUrlFromPlexItem(plexItem)
                if directPlayUrl:
                    item.setPath(directPlayUrl)

            # otherwise determine the stream URL
            if not item.getPath():
                item.setPath(Api.getStreamUrlFromPlexItem(
                    plexItem, plexServer))

        if path:
            videoInfoTag.setPath(path)
        videoInfoTag.setFilenameAndPath(item.getPath())

        # handle artwork
        poster = None
        fanart = None
        if isinstance(plexItem, video.Video):
            poster = plexItem.thumbUrl
            fanart = plexItem.artUrl
        elif isinstance(plexItem, collection.Collection) and plexItem.thumb:
            poster = plexServer.url(plexItem.thumb, includeToken=True)

        if poster:
            artwork['poster'] = poster
        if fanart:
            artwork['fanart'] = fanart
        if artwork:
            item.setArt(artwork)