def settingOptionsFillerViews(handle, _):
    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    try:
        embyServer = Server(mediaProvider)
    except:
        return

    libraryViews = getLibraryViews(embyServer, mediaImport.getMediaTypes())
    views = []
    for libraryView in libraryViews:
        views.append((libraryView.name, libraryView.id))

    # get the import's settings
    settings = mediaImport.getSettings()

    # pass the list of views back to Kodi
    settings.setStringOptions(emby.constants.SETTING_IMPORT_VIEWS_SPECIFIC, views)
def loadImportSettings(handle: int, _options: dict):
    """Load and save settings from media import at handle ID, register some callbacks in the settings

    :param handle: Handle id from input
    :type handle: int
    :param _options: Options/parameters passed in with the call, Unused
    :type _options: dict
    """
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    settings = mediaImport.getSettings()
    if not settings:
        log('cannot retrieve media import settings', xbmc.LOGERROR)
        return

    # register force sync callback
    settings.registerActionCallback(plex.constants.SETTINGS_IMPORT_FORCE_SYNC,
                                    'forcesync')

    # register a setting options filler for the list of views
    settings.registerOptionsFillerCallback(
        plex.constants.SETTINGS_IMPORT_LIBRARY_SECTIONS,
        'settingoptionsfillerlibrarysections')

    settings.setLoaded()
Exemple #3
0
def setting_options_filler_views(handle, _):
    # retrieve the media provider
    media_provider = xbmcmediaimport.getProvider(handle)
    if not media_provider:
        log("cannot retrieve media provider", xbmc.LOGERROR)
        return

    # retrieve the media import
    media_import = xbmcmediaimport.getImport(handle)
    if not media_import:
        log("cannot retrieve media import", xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not media_provider.prepareSettings():
        log("cannot prepare media provider settings", xbmc.LOGERROR)
        return

    # TODO(stub): collect information and provide it as a list of options to the user
    #             each option is a tuple(label, key)
    options = [("Foo", "foo"), ("Bar", "bar")]

    # get the import"s settings
    settings = media_import.getSettings()

    # pass the list of views back to Kodi
    settings.setStringOptions("stub.importviews", options)
def isImportReady(handle, options):
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return
    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    try:
        embyServer = Server(mediaProvider)
    except:
        return

    # check if the chosen library views exist
    selectedViews = ImportSettings.GetLibraryViews(importSettings)
    matchingViews = getMatchingLibraryViews(embyServer,
                                            mediaImport.getMediaTypes(),
                                            selectedViews)

    xbmcmediaimport.setImportReady(handle, len(matchingViews) > 0)
def forceSync(handle: int, _options: dict):
    """Confirm if user wants to force a full sync and update the settings hash

    :param handle: Handle id from input
    :type handle: int
    :param _options: Options/parameters passed in with the call, Unused
    :type _options: dict
    """
    # ask the user whether he is sure
    force = xbmcgui.Dialog().yesno(localize(32022), localize(32065))
    if not force:
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    # reset the synchronization hash setting to force a full synchronization
    SynchronizationSettings.ResetHash(importSettings, save=False)
Exemple #6
0
def settingOptionsFillerViews(handle, options):
    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    embyServer = Server(mediaProvider)
    if not embyServer.Authenticate():
        log('failed to authenticate on media provider {}'.format(mediaProvider2str(mediaProvider)), xbmc.LOGERROR)
        return

    libraryViews = Library.GetViews(embyServer, mediaImport.getMediaTypes())
    views = []
    for libraryView in libraryViews:
        views.append((libraryView.name, libraryView.id))

    # get the import's settings
    settings = mediaImport.getSettings()

    # pass the list of views back to Kodi
    settings.setStringOptions(emby.constants.SETTING_IMPORT_VIEWS_SPECIFIC, views)
Exemple #7
0
def settingOptionsFillerLibrarySections(handle, options):
    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    server = Server(mediaProvider)
    if not server.Authenticate():
        log('failed to connect to Plex Media Server for {}'.format(mediaProvider2str(mediaProvider)), xbmc.LOGWARNING)
        return

    plexServer = server.PlexServer()

    # get all library sections
    mediaTypes = mediaImport.getMediaTypes()
    librarySections = getLibrarySections(plexServer, mediaTypes)
    sections = [ (section['title'], section['key']) for section in librarySections ]

    # get the import's settings
    settings = mediaImport.getSettings()

    # pass the list of views back to Kodi
    settings.setStringOptions(plex.constants.SETTINGS_IMPORT_LIBRARY_SECTIONS, sections)
Exemple #8
0
def is_import_ready(handle, _):
    # retrieve the media import
    media_import = xbmcmediaimport.getImport(handle)
    if not media_import:
        log("cannot retrieve media import", xbmc.LOGERROR)
        xbmcmediaimport.setImportReady(handle, False)
        return

    # prepare and get the media import settings
    import_settings = media_import.prepareSettings()
    if not import_settings:
        log("cannot prepare media import settings", xbmc.LOGERROR)
        xbmcmediaimport.setImportReady(handle, False)
        return

    # retrieve the media provider
    media_provider = xbmcmediaimport.getProvider(handle)
    if not media_provider:
        log("cannot retrieve media provider", xbmc.LOGERROR)
        xbmcmediaimport.setImportReady(handle, False)
        return

    # prepare the media provider settings
    if not media_provider.prepareSettings():
        log("cannot prepare media provider settings", xbmc.LOGERROR)
        xbmcmediaimport.setImportReady(handle, False)
        return

    # TODO(stub): check if the configuration of the media import is valid / complete

    xbmcmediaimport.setImportReady(handle, True)
def isImportReady(handle: int, _options: dict):
    """Validate that MediaImport at handle ID and associated provider are ready

    :param handle: Handle id from input
    :type handle: int
    :param _options: Options/parameters passed in with the call, Unused
    :type _options: dict
    """
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log("cannot retrieve media import", xbmc.LOGERROR)
        xbmcmediaimport.setImportReady(handle, False)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log("cannot prepare media import settings", xbmc.LOGERROR)
        xbmcmediaimport.setImportReady(handle, False)
        return

    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log("cannot retrieve media provider", xbmc.LOGERROR)
        xbmcmediaimport.setImportReady(handle, False)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log("cannot prepare media provider settings", xbmc.LOGERROR)
        xbmcmediaimport.setImportReady(handle, False)
        return

    try:
        server = Server(mediaProvider)
    except:
        pass

    importReady = False
    # check if authentication works with the current provider settings
    if server.Authenticate():
        # check if the chosen library sections exist
        selectedLibrarySections = getLibrarySectionsFromSettings(importSettings)
        matchingLibrarySections = getMatchingLibrarySections(
            server.PlexServer(),
            mediaImport.getMediaTypes(),
            selectedLibrarySections
        )
        importReady = len(matchingLibrarySections) > 0

    xbmcmediaimport.setImportReady(handle, importReady)
Exemple #10
0
def loadImportSettings(handle, options):
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    settings = mediaImport.getSettings()
    if not settings:
        log('cannot retrieve media import settings', xbmc.LOGERROR)
        return

    # register a setting options filler for the list of views
    settings.registerOptionsFillerCallback(emby.constants.SETTING_IMPORT_VIEWS_SPECIFIC, 'settingoptionsfillerviews')

    settings.setLoaded()
Exemple #11
0
def loadImportSettings(handle, options):
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    settings = mediaImport.getSettings()
    if not settings:
        log('cannot retrieve media import settings', xbmc.LOGERROR)
        return

     # register a setting options filler for the list of views
    settings.registerOptionsFillerCallback(plex.constants.SETTINGS_IMPORT_LIBRARY_SECTIONS, 'settingoptionsfillerlibrarysections')

    settings.setLoaded()
def settingOptionsFillerLibrarySections(handle: int, _options: dict):
    """Find and set the library sections setting from Plex matching a mediaImport's media type

    :param handle: Handle id from input
    :type handle: int
    :param _options: Options/parameters passed in with the call, Unused
    :type _options: dict
    """
    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    server = Server(mediaProvider)
    if not server.Authenticate():
        log(
            f"failed to connect to Plex Media Server for {mediaProvider2str(mediaProvider)}",
            xbmc.LOGWARNING)
        return

    plexServer = server.PlexServer()

    # get all library sections
    mediaTypes = mediaImport.getMediaTypes()
    librarySections = getLibrarySections(plexServer, mediaTypes)
    sections = [(section['title'], section['key'])
                for section in librarySections]

    # get the import's settings
    settings = mediaImport.getSettings()

    # pass the list of views back to Kodi
    settings.setStringOptions(plex.constants.SETTINGS_IMPORT_LIBRARY_SECTIONS,
                              sections)
def forceSync(handle, options):
    # ask the user whether he is sure
    force = xbmcgui.Dialog().yesno(localise(32042), localise(32053))
    if not force:
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    # reset the synchronization hash setting to force a full synchronization
    SynchronizationSettings.ResetHash(importSettings, save=False)
Exemple #14
0
def load_import_settings(handle, _):
    # retrieve the media import
    media_import = xbmcmediaimport.getImport(handle)
    if not media_import:
        log("cannot retrieve media import", xbmc.LOGERROR)
        return

    settings = media_import.getSettings()
    if not settings:
        log("cannot retrieve media import settings", xbmc.LOGERROR)
        return

    # TODO(stub): register action callbacks
    settings.registerActionCallback("stub.forcesync", "forcesync")

    # TODO(stub): register a setting options fillers
    settings.registerOptionsFillerCallback("stub.importviews",
                                           "settingoptionsfillerviews")

    settings.setLoaded()
Exemple #15
0
def update_on_provider(handle, _):
    # retrieve the media import
    media_import = xbmcmediaimport.getImport(handle)
    if not media_import:
        log("cannot retrieve media import", xbmc.LOGERROR)
        return

    # retrieve the media provider
    media_provider = media_import.getProvider()
    if not media_provider:
        log("cannot retrieve media provider", xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    import_settings = media_import.prepareSettings()
    if not import_settings:
        log("cannot prepare media import settings", xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not media_provider.prepareSettings():
        log("cannot prepare media provider settings", xbmc.LOGERROR)
        return

    # get the updated item
    item = xbmcmediaimport.getUpdatedItem(handle)
    if not item:
        log("cannot retrieve updated item", xbmc.LOGERROR)
        return

    log(f'updating "{item.getLabel()}" ({item.getPath()}) on {provider2str(media_provider)}...'
        )

    video_info_tag = item.getVideoInfoTag()
    if not video_info_tag:
        log("updated item is not a video item", xbmc.LOGERROR)
        return

    # TODO(stub): update playback related metadata (playcount, last played, resume point)

    xbmcmediaimport.finishUpdate_on_provider(handle)
Exemple #16
0
def isImportReady(handle, options):
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return
    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = xbmcmediaimport.getProvider(handle)
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    embyServer = None
    try:
        embyServer = Server(mediaProvider)
    except:
        return

    importReady = False
    # check if authentication works with the current provider settings
    if embyServer.Authenticate():
        # check if the chosen library views exist
        selectedViews = getLibraryViewsFromSettings(importSettings)
        matchingViews = getMatchingLibraryViews(embyServer, mediaImport.getMediaTypes(), selectedViews)
        importReady = len(matchingViews) > 0

    xbmcmediaimport.setImportReady(handle, importReady)
Exemple #17
0
def updateOnProvider(handle, options):
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = mediaImport.getProvider()
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    item = xbmcmediaimport.getUpdatedItem(handle)
    if not item:
        log('cannot retrieve updated item', xbmc.LOGERROR)
        return

    log('updating "{}" ({}) on {}...'.format(item.getLabel(), item.getPath(), mediaProvider2str(mediaProvider)))

    itemVideoInfoTag = item.getVideoInfoTag()
    if not itemVideoInfoTag:
        log('updated item is not a video item', xbmc.LOGERROR)
        return

    # determine the item's identifier
    itemId = kodi.Api.getEmbyItemIdFromVideoInfoTag(itemVideoInfoTag)
    if not itemId:
        log('cannot determine the identifier of the updated item: "{}"'.format(itemVideoInfoTag.getPath()), xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    # create an Emby server instance
    embyServer = Server(mediaProvider)
    if not embyServer.Authenticate():
        log('failed to authenticate on media provider {}'.format(mediaProvider2str(mediaProvider)), xbmc.LOGERROR)
        return

    # retrieve all details of the item
    itemObj = Library.GetItem(embyServer, itemId)
    if not itemObj:
        log('cannot retrieve details of updated item with id {}'.format(itemId), xbmc.LOGERROR)
        return

    if not emby.constants.PROPERTY_ITEM_USER_DATA in itemObj:
        log('cannot update item with id {} because it has no userdata'.format(itemId), xbmc.LOGERROR)
        return

    updateItemPlayed = False
    updatePlaybackPosition = False
    # retrieve playback states from the updated item
    playcount = itemVideoInfoTag.getPlayCount()
    watched = playcount > 0
    lastPlayed = itemVideoInfoTag.getLastPlayed()
    # retrieve playback position from the updated item
    playbackPositionInSeconds = max(0.0, float(item.getProperty('resumetime')))
    playbackPositionInTicks = kodi.Api.secondsToTicks(playbackPositionInSeconds)

    userDataObj = itemObj[emby.constants.PROPERTY_ITEM_USER_DATA]

    # check and update playcout if necessary
    if emby.constants.PROPERTY_ITEM_USER_DATA_PLAY_COUNT in userDataObj:
        # retrieve playcount from the original item
        itemPlayed = userDataObj[emby.constants.PROPERTY_ITEM_USER_DATA_PLAY_COUNT] > 0

        if watched != itemPlayed:
            updateItemPlayed = True

    # check and update playback position if necessary
    if emby.constants.PROPERTY_ITEM_USER_DATA_PLAYBACK_POSITION_TICKS in userDataObj:
        # retrieve playback position from the original item
        itemPlaybackPositionInTicks = userDataObj[emby.constants.PROPERTY_ITEM_USER_DATA_PLAYBACK_POSITION_TICKS]

        if playbackPositionInTicks != itemPlaybackPositionInTicks:
            updatePlaybackPosition = True

    # nothing to do if no playback related properties have been changed
    if not updateItemPlayed and not updatePlaybackPosition:
        log('no playback related properties of "{}" ({}) have changed => nothing to update on {}'.format(item.getLabel(), item.getPath(), mediaProvider2str(mediaProvider)))
        return

    log('updating playback related properties of "{}" ({}) on {}...'.format(item.getLabel(), item.getPath(), mediaProvider2str(mediaProvider)))
    if not UserData.Update(embyServer, itemId, updateItemPlayed, updatePlaybackPosition, watched, playcount, lastPlayed, playbackPositionInTicks):
        log('updating playback related properties of "{}" ({}) on {} failed'.format(item.getLabel(), item.getPath(), mediaProvider2str(mediaProvider)), xbmc.LOGERROR)

    xbmcmediaimport.finishUpdateOnProvider(handle)
Exemple #18
0
def execImport(handle, options):
    if not 'path' in options:
        log('cannot execute "import" without path', xbmc.LOGERROR)
        return

    # parse all necessary options
    mediaTypes = mediaTypesFromOptions(options)
    if not mediaTypes:
        log('cannot execute "import" without media types', xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = mediaImport.getProvider()
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    log('importing {} items from {}...'.format(mediaTypes, mediaProvider2str(mediaProvider)))

    # prepare the media provider settings
    mediaProviderSettings = mediaProvider.prepareSettings()
    if not mediaProviderSettings:
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    # create an Emby server instance
    embyServer = Server(mediaProvider)
    if not embyServer.Authenticate():
        log('failed to authenticate on media provider {}'.format(mediaProvider2str(mediaProvider)), xbmc.LOGERROR)
        return

    # build the base URL to retrieve items
    baseUrl = embyServer.BuildUserUrl(emby.constants.URL_ITEMS)
    baseUrlOptions = {
        'Recursive': 'true',
        'Fields': ','.join(EMBY_ITEM_FIELDS),
        'ExcludeLocationTypes': 'Virtual,Offline',
        'Limit': ITEM_REQUEST_LIMIT
    }
    baseUrl = Url.addOptions(baseUrl, baseUrlOptions)

    # get all (matching) library views
    selectedViews = getLibraryViewsFromSettings(importSettings)
    views = getMatchingLibraryViews(embyServer, mediaTypes, selectedViews)
    if not views:
        log('cannot retrieve items without any library views', xbmc.LOGERROR)
        return

    # determine whether Direct Play is allowed
    allowDirectPlay = mediaProviderSettings.getBool(emby.constants.SETTING_PROVIDER_PLAYBACK_ALLOW_DIRECT_PLAY)

    # determine whether to import collections
    importCollections = importSettings.getBool(emby.constants.SETTING_IMPORT_IMPORT_COLLECTIONS)

    # loop over all media types to be imported
    progress = 0
    progressTotal = len(mediaTypes)
    for mediaType in mediaTypes:
        if xbmcmediaimport.shouldCancel(handle, progress, progressTotal):
            return
        progress += 1

        if mediaType == xbmcmediaimport.MediaTypeVideoCollection and not importCollections:
            log('importing {} items from {} is disabled'.format(mediaType, mediaProvider2str(mediaProvider)), xbmc.LOGDEBUG)
            continue

        log('importing {} items from {}...'.format(mediaType, mediaProvider2str(mediaProvider)))

        mappedMediaType = kodi.Api.getEmbyMediaType(mediaType)
        if not mappedMediaType:
            log('cannot import unsupported media type "{}"'.format(mediaType), xbmc.LOGERROR)
            continue
        (_, embyMediaType, localizedMediaType) = mappedMediaType

        xbmcmediaimport.setProgressStatus(handle, __addon__.getLocalizedString(32001).format(__addon__.getLocalizedString(localizedMediaType)))

        urlOptions = {
            'IncludeItemTypes': embyMediaType
        }
        url = Url.addOptions(baseUrl, urlOptions)

        boxsetUrlOptions = {
            'IncludeItemTypes': kodi.EMBY_MEDIATYPE_BOXSET
        }
        boxsetUrl = Url.addOptions(baseUrl, boxsetUrlOptions)

        items = []
        boxsets = {}

        # handle library views
        for view in views:
            log('importing {} items from "{}" view from {}...'.format(mediaType, view.name, mediaProvider2str(mediaProvider)))
            items.extend(importItems(handle, embyServer, url, mediaType, view.id, embyMediaType=embyMediaType, viewName=view.name, allowDirectPlay=allowDirectPlay))

            if importCollections and items and mediaType == xbmcmediaimport.MediaTypeMovie:
                # retrieve all BoxSets / collections matching the current media type
                boxsetObjs = importItems(handle, embyServer, boxsetUrl, mediaType, view.id, raw=True, allowDirectPlay=allowDirectPlay)
                for boxsetObj in boxsetObjs:
                    if not emby.constants.PROPERTY_ITEM_ID in boxsetObj or not emby.constants.PROPERTY_ITEM_NAME in boxsetObj:
                        continue

                    boxsetId = boxsetObj[emby.constants.PROPERTY_ITEM_ID]
                    boxsetName = boxsetObj[emby.constants.PROPERTY_ITEM_NAME]
                    boxsets[boxsetId] = boxsetName

        # handle BoxSets / collections
        if importCollections and items:
            for (boxsetId, boxsetName) in iteritems(boxsets):
                # get all items belonging to the BoxSet
                boxsetItems = importItems(handle, embyServer, url, mediaType, boxsetId, embyMediaType=embyMediaType, viewName=boxsetName, allowDirectPlay=allowDirectPlay)
                for boxsetItem in boxsetItems:
                    # find the matching retrieved item
                    for (index, item) in enumerate(items):
                        if boxsetItem.getPath() == item.getPath():
                            # set the BoxSet / collection
                            kodi.Api.setCollection(item, boxsetName)
                            items[index] = item

        log('{} {} items imported from {}'.format(len(items), mediaType, mediaProvider2str(mediaProvider)))
        if items:
            xbmcmediaimport.addImportItems(handle, items, mediaType)

    xbmcmediaimport.finishImport(handle)
def updateOnProvider(handle: int, _options: dict):
    """Perform update/export of library items from Kodi into conifigured PMS (watch status, resume points, etc.)

    :param handle: Handle id from input
    :type handle: int
    :param _options: Options/parameters passed in with the call, Unused
    :type _options: dict
    """
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log("cannot retrieve media import", xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = mediaImport.getProvider()
    if not mediaProvider:
        log("cannot retrieve media provider", xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log("cannot prepare media provider settings", xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log("cannot prepare media import settings", xbmc.LOGERROR)
        return

    item = xbmcmediaimport.getUpdatedItem(handle)
    if not item:
        log("cannot retrieve updated item", xbmc.LOGERROR)
        return

    itemVideoInfoTag = item.getVideoInfoTag()
    if not itemVideoInfoTag:
        log("updated item is not a video item", xbmc.LOGERROR)
        return

    # determine the item's identifier / ratingKey
    itemId = Api.getItemIdFromListItem(item)
    if not itemId:
        log(
            f"cannot determine the identifier of the updated item: {itemVideoInfoTag.getPath()}",
            xbmc.LOGERROR)
        return

    # create a Plex server instance
    server = Server(mediaProvider)
    if not server.Authenticate():
        log(
            f"failed to connect to Plex Media Server for {mediaProvider2str(mediaProvider)}",
            xbmc.LOGWARNING)
        return

    plexItem = Api.getPlexItemDetails(
        server.PlexServer(), itemId,
        Api.getPlexMediaClassFromMediaType(itemVideoInfoTag.getMediaType()))
    if not plexItem:
        log(
            f"cannot retrieve details of updated item {itemVideoInfoTag.getPath()} with id {itemId}",
            xbmc.LOGERROR)
        return

    # check / update watched state
    playcount = itemVideoInfoTag.getPlayCount()
    watched = playcount > 0
    if watched != plexItem.isWatched:
        if watched:
            plexItem.markWatched()
        else:
            plexItem.markUnwatched()

    # TODO(Montellese): check / update last played
    # TODO(Montellese): check / update resume point

    xbmcmediaimport.finishUpdateOnProvider(handle)
def execImport(handle, options):
    if not 'path' in options:
        log('cannot execute "import" without path', xbmc.LOGERROR)
        return

    # parse all necessary options
    mediaTypes = mediaTypesFromOptions(options)
    if not mediaTypes:
        log('cannot execute "import" without media types', xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = mediaImport.getProvider()
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    log('importing {} items from {}...'.format(
        mediaTypes, mediaProvider2str(mediaProvider)))

    # prepare the media provider settings
    mediaProviderSettings = mediaProvider.prepareSettings()
    if not mediaProviderSettings:
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    # create an Emby server instance
    embyServer = Server(mediaProvider)

    # build the base URL to retrieve items
    baseUrl = embyServer.BuildUserUrl(emby.constants.URL_ITEMS)
    baseUrlOptions = {
        emby.constants.URL_QUERY_ITEMS_RECURSIVE: 'true',
        emby.constants.URL_QUERY_ITEMS_FIELDS: ','.join(EMBY_ITEM_FIELDS),
        emby.constants.URL_QUERY_ITEMS_EXCLUDE_LOCATION_TYPES:
        'Virtual,Offline',
        emby.constants.URL_QUERY_ITEMS_LIMIT: ITEM_REQUEST_LIMIT
    }
    baseUrl = Url.addOptions(baseUrl, baseUrlOptions)

    # get all (matching) library views
    selectedViews = ImportSettings.GetLibraryViews(importSettings)
    views = getMatchingLibraryViews(embyServer, mediaTypes, selectedViews)
    if not views:
        log('cannot retrieve items without any library views', xbmc.LOGERROR)
        return

    # determine whether Direct Play is allowed
    allowDirectPlay = mediaProviderSettings.getBool(
        emby.constants.SETTING_PROVIDER_PLAYBACK_ALLOW_DIRECT_PLAY)

    # determine whether to import collections
    importCollections = importSettings.getBool(
        emby.constants.SETTING_IMPORT_IMPORT_COLLECTIONS)

    # determine the last sync time and whether we should perform a fast sync
    fastSync = False
    syncUrlOptions = {}

    # check if synchronization related settings have changed; if yes we have to perform a full synchronization
    if SynchronizationSettings.HaveChanged(mediaTypes,
                                           mediaProviderSettings,
                                           importSettings,
                                           save=True):
        log('forcing a full synchronization to import {} items from {} because some related settings have changed' \
            .format(mediaTypes, mediaProvider2str(mediaProvider)))
    else:
        # check if we
        #   have already performed a (full) synchronization before
        #   should use the Kodi Companion Emby server plugin
        lastSync = mediaImport.getLastSynced()
        if lastSync and mediaProviderSettings.getBool(
                emby.constants.
                SETTING_PROVIDER_SYNCHRONIZATION_USE_KODI_COMPANION):
            if KodiCompanion.IsInstalled(embyServer):
                fastSync = True

                # convert the last sync datetime string to ISO 8601
                lastSync = parser.parse(lastSync).astimezone(utc).isoformat(
                    timespec='seconds')

                syncUrlOptions.update({
                    # only set MinDateLastSavedForUser because it already covers DateLastSaved, RatingLastModified
                    # and PlaystateLastModified. Setting both MinDateLastSaved and MinDateLastSavedForUser will
                    # cause issues, see https://emby.media/community/index.php?/topic/82258-retrieving-changeset-when-client-returns-online-mediaimport/
                    emby.constants.URL_QUERY_ITEMS_MIN_DATE_LAST_SAVED_FOR_USER:
                    lastSync
                })
                log('using fast synchronization to import {} items from {} with Kodi companion plugin' \
                    .format(mediaTypes, mediaProvider2str(mediaProvider)), xbmc.LOGDEBUG)

                # retrieving the sync queue from Kodi companion
                syncQueue = KodiCompanion.SyncQueue.GetItems(
                    embyServer, lastSync)
            else:
                log('Kodi companion usage is enabled to import {} items from {} but the server plugin is not installed' \
                    .format(mediaTypes, mediaProvider2str(mediaProvider)), xbmc.LOGWARNING)

    # loop over all media types to be imported
    progress = 0
    progressTotal = len(mediaTypes)
    for mediaType in mediaTypes:
        if xbmcmediaimport.shouldCancel(handle, progress, progressTotal):
            return
        progress += 1

        if mediaType == xbmcmediaimport.MediaTypeVideoCollection and not importCollections:
            log(
                'importing {} items from {} is disabled'.format(
                    mediaType, mediaProvider2str(mediaProvider)),
                xbmc.LOGDEBUG)
            continue

        log('importing {} items from {}...'.format(
            mediaType, mediaProvider2str(mediaProvider)))

        mappedMediaType = kodi.Api.getEmbyMediaType(mediaType)
        if not mappedMediaType:
            log('cannot import unsupported media type "{}"'.format(mediaType),
                xbmc.LOGERROR)
            continue
        (_, embyMediaType, localizedMediaType) = mappedMediaType

        xbmcmediaimport.setProgressStatus(
            handle,
            __addon__.getLocalizedString(32001).format(
                __addon__.getLocalizedString(localizedMediaType)))

        urlOptions = syncUrlOptions.copy()
        urlOptions.update(
            {emby.constants.URL_QUERY_ITEMS_INCLUDE_ITEM_TYPES: embyMediaType})
        url = Url.addOptions(baseUrl, urlOptions)

        boxsetUrlOptions = {
            emby.constants.URL_QUERY_ITEMS_INCLUDE_ITEM_TYPES:
            kodi.EMBY_MEDIATYPE_BOXSET
        }
        boxsetUrl = Url.addOptions(baseUrl, boxsetUrlOptions)

        items = []
        boxsets = {}

        # handle library views
        for view in views:
            log('importing {} items from "{}" view from {}...'.format(
                mediaType, view.name, mediaProvider2str(mediaProvider)))
            items.extend(
                importItems(handle,
                            embyServer,
                            url,
                            mediaType,
                            view.id,
                            embyMediaType=embyMediaType,
                            viewName=view.name,
                            allowDirectPlay=allowDirectPlay))

            if importCollections and items and mediaType == xbmcmediaimport.MediaTypeMovie:
                # retrieve all BoxSets / collections matching the current media type
                boxsetObjs = importItems(handle,
                                         embyServer,
                                         boxsetUrl,
                                         mediaType,
                                         view.id,
                                         raw=True,
                                         allowDirectPlay=allowDirectPlay)
                for boxsetObj in boxsetObjs:
                    if not emby.constants.PROPERTY_ITEM_ID in boxsetObj or not emby.constants.PROPERTY_ITEM_NAME in boxsetObj:
                        continue

                    boxsetId = boxsetObj[emby.constants.PROPERTY_ITEM_ID]
                    boxsetName = boxsetObj[emby.constants.PROPERTY_ITEM_NAME]
                    boxsets[boxsetId] = boxsetName

        # handle BoxSets / collections
        if importCollections and items:
            for (boxsetId, boxsetName) in iteritems(boxsets):
                # get all items belonging to the BoxSet
                boxsetItems = importItems(handle,
                                          embyServer,
                                          url,
                                          mediaType,
                                          boxsetId,
                                          embyMediaType=embyMediaType,
                                          viewName=boxsetName,
                                          allowDirectPlay=allowDirectPlay)
                for boxsetItem in boxsetItems:
                    # find the matching retrieved item
                    for index, item in enumerate(items):
                        if boxsetItem.getPath() == item.getPath():
                            # set the BoxSet / collection
                            kodi.Api.setCollection(item, boxsetName)
                            items[index] = item

        # in a fast sync we need to get the removed items from Kodi companion
        if fastSync:
            if items:
                log('{} changed {} items imported from {}'.format(
                    len(items), mediaType, mediaProvider2str(mediaProvider)))

            # handle removed items through Kodi companion's sync queue
            if syncQueue.itemsRemoved:
                # retrieve all local items matching the current media type from the current import
                localItems = xbmcmediaimport.getImportedItems(
                    handle, mediaType)

                # match the local items against the changed items
                removedItems, = kodi.Api.matchImportedItemIdsToLocalItems(
                    localItems, syncQueue.itemsRemoved)  # pylint: disable=unbalanced-tuple-unpacking

                # erase all removed items matching the current media type from the sync queue
                syncQueue.itemsRemoved = [
                    removedItem for removedItem in syncQueue.itemsRemoved
                    if removedItem in removedItems
                ]

                if removedItems:
                    log('{} previously imported {} items removed from {}'.
                        format(len(removedItems), mediaType,
                               mediaProvider2str(mediaProvider)))
                    xbmcmediaimport.addImportItems(
                        handle, removedItems, mediaType,
                        xbmcmediaimport.MediaImportChangesetTypeRemoved)
        else:
            log('{} {} items imported from {}'.format(
                len(items), mediaType, mediaProvider2str(mediaProvider)))

        # pass the imported items back to Kodi
        if items:
            xbmcmediaimport.addImportItems(handle, items, mediaType)

    xbmcmediaimport.finishImport(handle, fastSync)
def execImport(handle: int, options: dict):
    """Perform library update/import of all configured items from a configured PMS into Kodi

    :param handle: Handle id from input
    :type handle: int
    :param options: Options/parameters passed in with the call, required mediatypes or mediatypes[]
    :type options: dict
    """
    if 'path' not in options:
        log("cannot execute 'import' without path", xbmc.LOGERROR)
        return

    # parse all necessary options
    mediaTypes = mediaTypesFromOptions(options)
    if not mediaTypes:
        log("cannot execute 'import' without media types", xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log("cannot retrieve media import", xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log("cannot prepare media import settings", xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = mediaImport.getProvider()
    if not mediaProvider:
        log("cannot retrieve media provider", xbmc.LOGERROR)
        return

    # prepare and get the media provider settings
    providerSettings = mediaProvider.prepareSettings()
    if not providerSettings:
        log("cannot prepare provider settings", xbmc.LOGERROR)
        return

    # create a Plex Media Server instance
    server = Server(mediaProvider)
    plexServer = server.PlexServer()
    plexLibrary = plexServer.library

    # get all (matching) library sections
    selectedLibrarySections = getLibrarySectionsFromSettings(importSettings)
    librarySections = getMatchingLibrarySections(plexServer, mediaTypes,
                                                 selectedLibrarySections)
    if not librarySections:
        log(f"cannot retrieve {mediaTypes} items without any library section",
            xbmc.LOGERROR)
        return

    # Decide if doing fast sync or not, if so set filter string to include updatedAt
    fastSync = True
    lastSync = mediaImport.getLastSynced()

    # Check if import settings have changed, or if this is the first time we are importing this library type
    if not lastSync:
        fastSync = False
        SynchronizationSettings.CalculateHash(
            importSettings=importSettings,
            providerSettings=providerSettings,
            save=True)
        log("first time syncronizing library, forcing a full syncronization",
            xbmc.LOGINFO)
    elif SynchronizationSettings.HaveChanged(importSettings=importSettings,
                                             providerSettings=providerSettings,
                                             save=True):
        fastSync = False
        log(
            "library import settings have changed, forcing a full syncronization",
            xbmc.LOGINFO)

    if SynchronizationSettings.HaveChanged(importSettings=importSettings,
                                           providerSettings=providerSettings,
                                           save=True):
        fastSync = False
        log(
            "library import settings have changed, forcing a full syncronization",
            xbmc.LOGINFO)

    if fastSync:
        log(f"performing fast syncronization of items viewed or updated since {str(lastSync)}"
            )
        lastSyncEpoch = parser.parse(lastSync).strftime('%s')
        updatedFilter = {'updatedAt>': lastSyncEpoch}
        watchedFilter = {'lastViewedAt>': lastSyncEpoch}

    # loop over all media types to be imported
    progressTotal = len(mediaTypes)
    for progress, mediaType in enumerate(mediaTypes):
        if xbmcmediaimport.shouldCancel(handle, progress, progressTotal):
            return

        mappedMediaType = Api.getPlexMediaType(mediaType)
        if not mappedMediaType:
            log(f"cannot import unsupported media type '{mediaType}'",
                xbmc.LOGERROR)
            continue

        plexLibType = mappedMediaType['libtype']
        localizedMediaType = localize(mappedMediaType['label']).decode()

        xbmcmediaimport.setProgressStatus(handle,
                                          localize(32001, localizedMediaType))

        log(
            f"importing {mediaType} items from {mediaProvider2str(mediaProvider)}",
            xbmc.LOGINFO)

        # handle library sections
        itemsToImport = []
        sectionsProgressTotal = len(librarySections)
        for sectionsProgress, librarySection in enumerate(librarySections):
            if xbmcmediaimport.shouldCancel(handle, sectionsProgress,
                                            sectionsProgressTotal):
                return

            # get the library section from the Plex Media Server
            section = plexLibrary.sectionByID(librarySection['key'])
            if not section:
                log(
                    f"cannot import {mediaType} items from unknown library section {librarySection}",
                    xbmc.LOGWARNING)
                continue

            # get all matching items from the library section and turn them into ListItems
            sectionProgress = 0
            sectionProgressTotal = ITEM_REQUEST_LIMIT

            while sectionProgress < sectionProgressTotal:
                if xbmcmediaimport.shouldCancel(handle, sectionProgress,
                                                sectionProgressTotal):
                    return

                maxResults = min(ITEM_REQUEST_LIMIT,
                                 sectionProgressTotal - sectionProgress)

                try:
                    if fastSync:
                        updatedPlexItems = section.search(
                            libtype=plexLibType,
                            container_start=sectionProgress,
                            container_size=maxResults,
                            maxresults=maxResults,
                            **updatedFilter)
                        log(f"discovered {len(updatedPlexItems)} updated items from {mediaProvider2str(mediaProvider)}"
                            )
                        watchedPlexItems = section.search(
                            libtype=plexLibType,
                            container_start=sectionProgress,
                            container_size=maxResults,
                            maxresults=maxResults,
                            **watchedFilter)
                        log(f"discovered {len(watchedPlexItems)} new watched items from {mediaProvider2str(mediaProvider)}"
                            )

                        plexItems = updatedPlexItems
                        plexItems.extend([
                            item for item in watchedPlexItems if item.key
                            not in [item.key for item in plexItems]
                        ])

                    else:
                        plexItems = section.search(
                            libtype=plexLibType,
                            container_start=sectionProgress,
                            container_size=maxResults,
                            maxresults=maxResults,
                        )
                except plexapi.exceptions.BadRequest as e:
                    log(
                        f"failed to fetch {mediaType} items from {mediaProvider2str(mediaProvider)}: {e}",
                        xbmc.LOGINFO)
                    return

                # Update sectionProgressTotal now that search has run and totalSize has been updated
                sectionProgressTotal = section.totalSize

                plexItemsProgressTotal = len(plexItems)
                for plexItemsProgress, plexItem in enumerate(plexItems):
                    if xbmcmediaimport.shouldCancel(handle, plexItemsProgress,
                                                    plexItemsProgressTotal):
                        return

                    sectionProgress += 1

                    try:
                        item = Api.toFileItem(plexServer, plexItem, mediaType,
                                              plexLibType)
                        if not item:
                            continue

                        itemsToImport.append(item)
                    except plexapi.exceptions.BadRequest as e:
                        # Api.convertDateTimeToDbDateTime may return (404) not_found for orphaned items in the library
                        log((
                            f"failed to retrieve item {plexItem.title} with key {plexItem.key} "
                            f"from {mediaProvider2str(mediaProvider)}: {e}"),
                            xbmc.LOGWARNING)
                        continue

        if itemsToImport:
            log(
                f"{len(itemsToImport)} {mediaType} items imported from {mediaProvider2str(mediaProvider)}",
                xbmc.LOGINFO)
            xbmcmediaimport.addImportItems(handle, itemsToImport, mediaType)

    xbmcmediaimport.finishImport(handle, fastSync)
Exemple #22
0
def exec_import(handle, options):
    # parse all necessary options
    media_types = media_types_from_options(options)
    if not media_types:
        log('cannot execute "import" without media types', xbmc.LOGERROR)
        return

    # retrieve the media import
    media_import = xbmcmediaimport.getImport(handle)
    if not media_import:
        log("cannot retrieve media import", xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    import_settings = media_import.prepareSettings()
    if not import_settings:
        log("cannot prepare media import settings", xbmc.LOGERROR)
        return

    # retrieve the media provider
    media_provider = media_import.getProvider()
    if not media_provider:
        log("cannot retrieve media provider", xbmc.LOGERROR)
        return

    log(f"importing {media_types} items from { provider2str(media_provider)}..."
        )

    # TODO(stub): prepare collecting ListItems

    # loop over all media types to be imported
    progress = 0
    progress_total = len(media_types)
    for media_type in media_types:
        # check if we need to cancel importing items
        if xbmcmediaimport.shouldCancel(handle, progress, progress_total):
            return
        progress += 1

        log(f"importing {media_type} items from {provider2str(media_provider)}..."
            )

        # report the progress status
        xbmcmediaimport.setProgressStatus(handle,
                                          localize(32001).format(media_type))

        items = []

        # TODO(stub): collect ListItems to import
        #             adjust and use lib.kodi.Api.to_file_item()

        if items:
            # pass the imported items back to Kodi
            log(f"{len(items)} {media_type} items imported from {provider2str(media_provider)}"
                )

            # TODO(stub): for partial imports use the following constants as an optional fourth argument:
            #     xbmcmediaimport.MediaImportChangesetTypeNone: let Kodi decide
            #     xbmcmediaimport.MediaImportChangesetTypeAdded: the item is new and should be added
            #     xbmcmediaimport.MediaImportChangesetTypeChanged: the item has been imported before and has changed
            #     xbmcmediaimport.MediaImportChangesetTypeRemoved: the item has to be removed
            xbmcmediaimport.addImportItems(handle, items, media_type)

    # TODO(stub): tell Kodi whether the provided items is a full or partial import
    partial_import = False

    # finish the import
    xbmcmediaimport.finishImport(handle, partial_import)
def execImport(handle: int, options: dict):
    """Perform library update/import of all configured items from a configured PMS into Kodi

    :param handle: Handle id from input
    :type handle: int
    :param options: Options/parameters passed in with the call, required mediatypes or mediatypes[]
    :type options: dict
    """
    # parse all necessary options
    mediaTypes = mediaTypesFromOptions(options)
    if not mediaTypes:
        log("cannot execute 'import' without media types", xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log("cannot retrieve media import", xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log("cannot prepare media import settings", xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = mediaImport.getProvider()
    if not mediaProvider:
        log("cannot retrieve media provider", xbmc.LOGERROR)
        return

    # prepare and get the media provider settings
    providerSettings = mediaProvider.prepareSettings()
    if not providerSettings:
        log("cannot prepare provider settings", xbmc.LOGERROR)
        return

    # get direct play settings from provider
    allowDirectPlay = providerSettings.getBool(SETTINGS_PROVIDER_PLAYBACK_ALLOW_DIRECT_PLAY)

    # create a Plex Media Server instance
    server = Server(mediaProvider)
    plexServer = server.PlexServer()
    plexLibrary = plexServer.library

    # get all (matching) library sections
    selectedLibrarySections = getLibrarySectionsFromSettings(importSettings)
    librarySections = getMatchingLibrarySections(plexServer, mediaTypes, selectedLibrarySections)
    if not librarySections:
        log(f"cannot retrieve {mediaTypes} items without any library section", xbmc.LOGERROR)
        return

    numDownloadThreads = ImportSettings.GetNumberOfDownloadThreads(importSettings)
    numRetriesOnTimeout = ImportSettings.GetNumberOfRetriesOnTimeout(importSettings)
    numSecondsBetweenRetries = ImportSettings.GetNumberOfSecondsBetweenRetries(importSettings)

    # Decide if doing fast sync or not, if so set filter string to include updatedAt
    fastSync = True
    lastSync = mediaImport.getLastSynced()

    # Check if import settings have changed, or if this is the first time we are importing this library type
    if not lastSync:
        fastSync = False
        SynchronizationSettings.CalculateHash(
            importSettings=importSettings,
            providerSettings=providerSettings,
            save=True
        )
        log("first time syncronizing library, forcing a full syncronization", xbmc.LOGINFO)
    elif SynchronizationSettings.HaveChanged(
            importSettings=importSettings,
            providerSettings=providerSettings,
            save=True
    ):
        fastSync = False
        log("library import settings have changed, forcing a full syncronization", xbmc.LOGINFO)

    if SynchronizationSettings.HaveChanged(importSettings=importSettings, providerSettings=providerSettings, save=True):
        fastSync = False
        log("library import settings have changed, forcing a full syncronization", xbmc.LOGINFO)

    if fastSync:
        log(f"performing fast syncronization of items viewed or updated since {str(lastSync)}")

    # loop over all media types to be imported
    progressTotal = len(mediaTypes)
    for progress, mediaType in enumerate(mediaTypes):
        if xbmcmediaimport.shouldCancel(handle, progress, progressTotal):
            return

        mappedMediaType = Api.getPlexMediaType(mediaType)
        if not mappedMediaType:
            log(f"cannot import unsupported media type '{mediaType}'", xbmc.LOGERROR)
            continue

        plexLibType = mappedMediaType['libtype']
        localizedMediaType = localize(mappedMediaType['label']).decode()

        # prepare and start the converter threads
        converterThreads = []
        for _ in range(0, numDownloadThreads):
            converterThreads.append(ToFileItemConverterThread(mediaProvider, mediaImport, plexServer,
                media_type=mediaType, plex_lib_type=plexLibType, allow_direct_play=allowDirectPlay))
        log((
            f"starting {len(converterThreads)} threads to import {mediaType} items "
            f"from {mediaProvider2str(mediaProvider)}..."),
            xbmc.LOGDEBUG)
        for converterThread in converterThreads:
            converterThread.start()

        # prepare function to stop all converter threads
        def stopConverterThreads(converterThreads):
            log((
                f"stopping {len(converterThreads)} threads importing {mediaType} items "
                f"from {mediaProvider2str(mediaProvider)}..."),
                xbmc.LOGDEBUG)
            for converterThread in converterThreads:
                converterThread.stop()

        xbmcmediaimport.setProgressStatus(handle, localize(32001, localizedMediaType))

        log(f"importing {mediaType} items from {mediaProvider2str(mediaProvider)}", xbmc.LOGINFO)

        # handle library sections
        sectionsProgressTotal = len(librarySections)
        for sectionsProgress, librarySection in enumerate(librarySections):
            if xbmcmediaimport.shouldCancel(handle, sectionsProgress, sectionsProgressTotal):
                stopConverterThreads(converterThreads)
                return

            # get the library section from the Plex Media Server
            section = plexLibrary.sectionByID(librarySection['key'])
            if not section:
                log(f"cannot import {mediaType} items from unknown library section {librarySection}", xbmc.LOGWARNING)
                continue

            # get all matching items from the library section and turn them into ListItems
            sectionRetrievalProgress = 0
            sectionConversionProgress = 0
            sectionProgressTotal = ITEM_REQUEST_LIMIT

            while sectionRetrievalProgress < sectionProgressTotal:
                if xbmcmediaimport.shouldCancel(handle, sectionConversionProgress, sectionProgressTotal):
                    stopConverterThreads(converterThreads)
                    return

                maxResults = min(ITEM_REQUEST_LIMIT, sectionProgressTotal - sectionRetrievalProgress)

                retries = numRetriesOnTimeout
                while retries > 0:
                    try:
                        if fastSync:
                            lastSyncDatetime = parser.parse(lastSync).astimezone(timezone.utc)
                            prefix = ''
                            if mediaType in (xbmcmediaimport.MediaTypeTvShow, xbmcmediaimport.MediaTypeEpisode):
                                prefix = Api.getPlexMediaType(mediaType)['libtype'] + '.'
                            elif mediaType == xbmcmediaimport.MediaTypeSeason:
                                prefix = Api.getPlexMediaType(xbmcmediaimport.MediaTypeEpisode)['libtype'] + '.'

                            updatedFilter = {prefix + 'updatedAt>>': lastSyncDatetime}
                            watchedFilter = {prefix + 'lastViewedAt>>': lastSyncDatetime}

                            updatedPlexItems = section.search(
                                libtype=plexLibType,
                                container_start=sectionRetrievalProgress,
                                container_size=maxResults,
                                maxresults=maxResults,
                                filters=updatedFilter
                            )
                            log(f"discovered {len(updatedPlexItems)} updated {mediaType} items from {mediaProvider2str(mediaProvider)}")
                            watchedPlexItems = section.search(
                                libtype=plexLibType,
                                container_start=sectionRetrievalProgress,
                                container_size=maxResults,
                                maxresults=maxResults,
                                filters=watchedFilter
                            )
                            log(f"discovered {len(watchedPlexItems)} newly watched {mediaType} items from {mediaProvider2str(mediaProvider)}")

                            plexItems = updatedPlexItems
                            plexItems.extend(
                                [item for item in watchedPlexItems if item.key not in [item.key for item in plexItems]]
                            )

                        else:
                            plexItems = section.search(
                                libtype=plexLibType,
                                container_start=sectionRetrievalProgress,
                                container_size=maxResults,
                                maxresults=maxResults,
                            )

                        # get out of the retry loop
                        break
                    except Exception as e:
                        log(f"failed to fetch {mediaType} items from {mediaProvider2str(mediaProvider)}: {e}", xbmc.LOGWARNING)

                        # retry after timeout
                        retries -= 1

                        # check if there are any more retries left
                        # if not abort the import process
                        if retries == 0:
                            log(
                                (
                                    f"fetching {mediaType} items from {mediaProvider2str(mediaProvider)} failed "
                                    f"after {numRetriesOnTimeout} retries"
                                ),
                                xbmc.LOGWARNING)
                            stopConverterThreads(converterThreads)
                            return
                        else:
                            # otherwise wait before trying again
                            log(
                                (
                                    f"retrying to fetch {mediaType} items from {mediaProvider2str(mediaProvider)} in "
                                    f"{numSecondsBetweenRetries} seconds"
                                )
                            )
                            time.sleep(float(numSecondsBetweenRetries))

                # Update sectionProgressTotal now that search has run and totalSize has been updated
                # TODO(Montellese): fix access of private LibrarySection._totalViewSize
                sectionProgressTotal = section._totalViewSize

                # nothing to do if no items have been retrieved from Plex
                if not plexItems:
                    continue

                # automatically determine how to distribute the retrieved items across the available converter threads
                plexItemsPerConverter, remainingPlexItems = divmod(len(plexItems), len(converterThreads))
                plexItemsPerConverters = [plexItemsPerConverter] * len(converterThreads)
                plexItemsPerConverters[0:remainingPlexItems] = [plexItemsPerConverter + 1] * remainingPlexItems
                converterThreadIndex = 0

                splitItems = []
                for plexItem in plexItems:
                    if xbmcmediaimport.shouldCancel(handle, sectionConversionProgress, sectionProgressTotal):
                        stopConverterThreads(converterThreads)
                        return

                    sectionRetrievalProgress += 1

                    # copllect the plex items for the next converter thread
                    splitItems.append(plexItem)
                    plexItemsPerConverters[converterThreadIndex] -= 1

                    # move to the next converter thread if necessary
                    if not plexItemsPerConverters[converterThreadIndex]:
                        converterThreads[converterThreadIndex].add_items_to_convert(splitItems)
                        splitItems.clear()

                        converterThreadIndex += 1

                    # retrieve and combine the progress of all converter threads
                    sectionConversionProgress = \
                        sum(converterThread.get_converted_items_count() for converterThread in converterThreads)

                if splitItems:
                    log(f"forgot to process {len(splitItems)} {mediaType} items", xbmc.LOGWARNING)

        # retrieve converted items from the converter threads
        totalItemsToImport = 0
        countFinishedConverterThreads = 0
        while countFinishedConverterThreads < len(converterThreads):
            if xbmcmediaimport.shouldCancel(handle, sectionConversionProgress, sectionProgressTotal):
                stopConverterThreads(converterThreads)
                return

            sectionConversionProgress = 0
            itemsToImport = []
            for converterThread in converterThreads:
                # update the progress
                sectionConversionProgress += converterThread.get_converted_items_count()

                # ignore finished converter threads
                if converterThread.should_finish():
                    continue

                # retrieve the converted items
                convertedItems = converterThread.get_converted_items()
                itemsToImport.extend(convertedItems)

                # check if all items have been converted
                if not converterThread.get_items_to_convert_count():
                    converterThread.finish()
                    countFinishedConverterThreads += 1

            if itemsToImport:
                totalItemsToImport += len(itemsToImport)
                xbmcmediaimport.addImportItems(handle, itemsToImport, mediaType)

            time.sleep(0.1)

        if totalItemsToImport:
            log(f"{totalItemsToImport} {mediaType} items imported from {mediaProvider2str(mediaProvider)}", xbmc.LOGINFO)

    xbmcmediaimport.finishImport(handle, fastSync)
Exemple #24
0
def updateOnProvider(handle, options):
    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = mediaImport.getProvider()
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    item = xbmcmediaimport.getUpdatedItem(handle)
    if not item:
        log('cannot retrieve updated item', xbmc.LOGERROR)
        return

    itemVideoInfoTag = item.getVideoInfoTag()
    if not itemVideoInfoTag:
        log('updated item is not a video item', xbmc.LOGERROR)
        return

    # determine the item's identifier / ratingKey
    itemId = Api.getItemIdFromListItem(item)
    if not itemId:
        log('cannot determine the identifier of the updated item: {}'.format(itemVideoInfoTag.getPath()), xbmc.LOGERROR)
        return

    # create a Plex server instance
    server = Server(mediaProvider)
    if not server.Authenticate():
        log('failed to connect to Plex Media Server for {}'.format(mediaProvider2str(mediaProvider)), xbmc.LOGWARNING)
        return

    plexItem = Api.getPlexItemDetails(server.PlexServer(), itemId, Api.getPlexMediaClassFromMediaType(itemVideoInfoTag.getMediaType()))
    if not plexItem:
        log('cannot retrieve details of updated item {} with id {}'.format(itemVideoInfoTag.getPath(), itemId), xbmc.LOGERROR)
        return

    # check / update watched state
    playcount = itemVideoInfoTag.getPlayCount()
    watched = playcount > 0
    if watched != plexItem.isWatched:
        if watched:
            plexItem.markWatched()
        else:
            plexItem.markUnwatched()

    # TODO(Montellese): check / update last played
    # TODO(Montellese): check / update resume point

    xbmcmediaimport.finishUpdateOnProvider(handle)
Exemple #25
0
def execImport(handle, options):
    if not 'path' in options:
        log('cannot execute "import" without path', xbmc.LOGERROR)
        return

    # parse all necessary options
    mediaTypes = mediaTypesFromOptions(options)
    if not mediaTypes:
        log('cannot execute "import" without media types', xbmc.LOGERROR)
        return

    # retrieve the media import
    mediaImport = xbmcmediaimport.getImport(handle)
    if not mediaImport:
        log('cannot retrieve media import', xbmc.LOGERROR)
        return

    # prepare and get the media import settings
    importSettings = mediaImport.prepareSettings()
    if not importSettings:
        log('cannot prepare media import settings', xbmc.LOGERROR)
        return

    # retrieve the media provider
    mediaProvider = mediaImport.getProvider()
    if not mediaProvider:
        log('cannot retrieve media provider', xbmc.LOGERROR)
        return

    # prepare the media provider settings
    if not mediaProvider.prepareSettings():
        log('cannot prepare media provider settings', xbmc.LOGERROR)
        return

    # create a Plex Media Server instance
    server = Server(mediaProvider)
    plexServer = server.PlexServer()
    plexLibrary = plexServer.library

    # get all (matching) library sections
    selectedLibrarySections = getLibrarySectionsFromSettings(importSettings)
    librarySections = getMatchingLibrarySections(plexServer, mediaTypes, selectedLibrarySections)
    if not librarySections:
        log('cannot retrieve {} items without any library section'.format(mediaTypes), xbmc.LOGERROR)
        return

    # loop over all media types to be imported
    progressTotal = len(mediaTypes)
    for progress, mediaType in enumerate(mediaTypes):
        if xbmcmediaimport.shouldCancel(handle, progress, progressTotal):
            return

        mappedMediaType = Api.getPlexMediaType(mediaType)
        if not mappedMediaType:
            log('cannot import unsupported media type "{}"'.format(mediaType), xbmc.LOGERROR)
            continue

        plexLibType = mappedMediaType['libtype']
        localizedMediaType = localise(mappedMediaType['label'])

        xbmcmediaimport.setProgressStatus(handle, localise(32001).format(localizedMediaType))

        log('importing {} items from {}'.format(mediaType, mediaProvider2str(mediaProvider)))

        # handle library sections
        plexItems = []
        sectionsProgressTotal = len(librarySections)
        for sectionsProgress, librarySection in enumerate(librarySections):
            if xbmcmediaimport.shouldCancel(handle, sectionsProgress, sectionsProgressTotal):
                return

            # get the library section from the Plex Media Server
            section = plexLibrary.sectionByID(librarySection['key'])
            if not section:
                log('cannot import {} items from unknown library section {}'.format(mediaType, librarySection), xbmc.LOGWARNING)
                continue

            # get all matching items from the library section
            try:
                plexSectionItems = section.search(libtype=plexLibType)
                plexItems.extend(plexSectionItems)
            except plexapi.exceptions.BadRequest as err:
                log('failed to retrieve {} items from {}: {}'.format(mediaType, mediaProvider2str(mediaProvider), err))
                return

        # parse all items
        items = []
        itemsProgressTotal = len(plexItems)
        for itemsProgress, plexItem in enumerate(plexItems):
            if xbmcmediaimport.shouldCancel(handle, itemsProgress, itemsProgressTotal):
                return

            item = Api.toFileItem(plexServer, plexItem, mediaType, plexLibType)
            if not item:
                continue

            items.append(item)

        if items:
            log('{} {} items imported from {}'.format(len(items), mediaType, mediaProvider2str(mediaProvider)))
            xbmcmediaimport.addImportItems(handle, items, mediaType)

    xbmcmediaimport.finishImport(handle)