def __init__(self, w=OVERLAY_WIDTH, h=OVERLAY_HEIGHT, *args, **kwargs):
     self.window = xbmcgui.Window(WINDOW_FULLSCREEN_VIDEO)
     viewport_w, viewport_h = self._get_skin_resolution()
     # Adjust size based on viewport, we are using 1080p coordinates
     w = int(w * viewport_w / VIEWPORT_WIDTH)
     h = int(h * viewport_h / VIEWPORT_HEIGHT)
     x = (viewport_w - w) / 2
     y = (viewport_h - h) / 2 + int(
         ADDON.getSetting(id="overlay_status_offset"))
     self._shown = False
     self._text = ""
     self._label = xbmcgui.ControlLabel(x,
                                        y,
                                        w,
                                        h,
                                        self._text,
                                        alignment=XBFONT_CENTER_X
                                        | XBFONT_CENTER_Y,
                                        *args,
                                        **kwargs)
     self._shadow = xbmcgui.ControlLabel(x + 1,
                                         y + 1,
                                         w,
                                         h,
                                         self._text,
                                         textColor='0xD0000000',
                                         alignment=XBFONT_CENTER_X
                                         | XBFONT_CENTER_Y,
                                         *args,
                                         **kwargs)
     self._background = xbmcgui.ControlImage(
         x, y, w, h,
         os.path.join(ADDON_PATH, "resources", "img",
                      "black.png").encode('utf-8'))
     self._background.setColorDiffuse("0xD0000000")
Example #2
0
 def GetAddonInfo(self):
     info = {}
     for key in ("author", "changelog", "description", "disclaimer",
                 "fanart", "icon", "id", "name", "profile", "stars",
                 "summary", "type", "version"):
         info[key] = ADDON.getAddonInfo(key)
     info['path'] = ADDON_PATH
     info['home'] = "special://home"
     info['xbmc'] = "special://xbmc"
     return info
def get_setting(key, converter=str, choices=None):
    value = ADDON.getSetting(id=key)
    if converter is six.text_type or converter is str:
        return py2_decode(value)
    elif converter is bool:
        return value == 'true'
    elif converter is int:
        return int(value)
    elif isinstance(choices, (list, tuple)):
        return choices[int(value)]
    else:
        raise TypeError('Acceptable converters are str, unicode, bool and '
                        'int. Acceptable choices are instances of list '
                        ' or tuple.')
Example #4
0
    def AddonFailure(self, addonId):
        if ADDON.getSetting("provider_disable_failing") == u"false":
            return 0

        if addonId in self._failures:
            self._failures[addonId] += 1
        else:
            self._failures[addonId] = 1

        log.warning("Recorded failure %d for %s" %
                    (self._failures[addonId], addonId))

        if self._failures[addonId] > int(
                ADDON.getSetting("provider_allowed_failures")):
            try:
                time.sleep(10)
                notify(getLocalizedString(30111))
                urllib_request.urlopen("%s/provider/%s/disable" %
                                       (projectxD_HOST, addonId))
            except:
                notify(getLocalizedString(30112))
                return 0
        return self._failures[addonId]
Example #5
0
 def GetAllSettings(self):
     settings = []
     settingsFile = os.path.join(ADDON_PATH, "resources", "settings.xml")
     with open(settingsFile, 'r') as settingsStr:
         fileContent = settingsStr.read()
         keyType = re.findall(
             r".*id=\"(\w+)\".*type=\"(\w+)\"(.*option=\"(\w+)\")?",
             fileContent)
     for key, _type, optgroup, option in keyType:
         settings.append({
             "key": key,
             "type": _type,
             "option": option,
             "value": ADDON.getSetting(key)
         })
     return settings
def _get_logger(name):
    global loggers

    if loggers.get(name):
        return loggers.get(name)
    else:
        logger = logging.getLogger(ADDON_ID)
        if ADDON.getSetting("logger_silent") == 'true':
            logger.setLevel(logging.ERROR)
        else:
            logger.setLevel(logging.DEBUG)

        handler = XBMCHandler()
        handler.setFormatter(logging.Formatter('[%(name)s] %(message)s'))
        logger.addHandler(handler)
        return logger
def set_setting(key, val):
    return ADDON.setSetting(id=key, value=val)
Example #8
0
def getLocalizedString(stringId):
    try:
        return ADDON.getLocalizedString(stringId).encode('utf-8', 'ignore')
    except:
        return stringId
def get_platform():
    try:
        binary_platform = ADDON.getSetting("binary_platform")
    except:
        binary_platform = "auto"
        pass

    build = xbmc.getInfoLabel("System.BuildVersion")
    kodi_version = int(build.split()[0][:2])

    if binary_platform and "auto" not in binary_platform.lower():
        custom = binary_platform.split('_')
        if len(custom) > 1:
            return {
                "os": custom[0],
                "arch": custom[1],
                "fork": True,
                "version": "",
                "kodi": kodi_version,
                "build": build
            }

    ret = {
        "auto_arch": sys.maxsize > 2 ** 32 and "64-bit" or "32-bit",
        "arch": sys.maxsize > 2 ** 32 and "x64" or "x86",
        "os": "",
        "version": "",
        "kodi": kodi_version,
        "build": build,
        "fork": True,
        "machine": "",
        "system": "",
        "platform": ""
    }

    try:
        ret["os"] = platform.release()
    except:
        pass

    try:
        ret["machine"] = platform.machine()
    except:
        # Default 'machine' for Android can be 'arm'
        if xbmc.getCondVisibility("system.platform.android"):
            ret["machine"] = "arm"
        pass

    try:
        ret["system"] = platform.system()
    except:
        pass

    try:
        ret["platform"] = platform.platform()
    except:
        pass

    if xbmc.getCondVisibility("system.platform.android"):
        ret["os"] = "android"
        if "arm" in ret["machine"].lower() or "aarch" in ret["machine"].lower():
            ret["arch"] = "arm"
            if "64" in ret["machine"] and ret["auto_arch"] == "64-bit":
                ret["arch"] = "arm64"
    elif xbmc.getCondVisibility("system.platform.linux"):
        ret["os"] = "linux"

        if "aarch" in ret["machine"].lower() or "arm64" in ret["machine"].lower():
            if xbmc.getCondVisibility("system.platform.linux.raspberrypi"):
                ret["arch"] = "armv7"
            elif ret["auto_arch"] == "32-bit":
                ret["arch"] = "armv7"
            elif ret["auto_arch"] == "64-bit":
                ret["arch"] = "arm64"
            # elif platform.architecture()[0].startswith("32"):
            #     ret["arch"] = "armv6"
            else:
                ret["arch"] = "armv7"
        elif "armv7" in ret["machine"]:
            ret["arch"] = "armv7"
        elif "arm" in ret["machine"]:
            cpuarch = ""
            if "aarch" in ret["machine"].lower() or "arm" in ret["machine"].lower():
                info = cpuinfo()
                for proc in info.keys():
                    log.info("CPU: %s=%s" % (proc, info[proc]))
                    model = ""
                    if "model name" in info[proc]:
                        model = info[proc]["model name"].lower()
                    elif "Processor" in info[proc]:
                        model = info[proc]["Processor"].lower()

                    if model:
                        log.info("Exploring model: %s" % model)
                        if "aarch" in model or "arm64" in model or "v8l" in model:
                            cpuarch = "arm64"
                        elif "armv7" in model or "v7l" in model:
                            cpuarch = "armv7"
                        break

            if cpuarch:
                log.info("Using CPU info arch: %s" % cpuarch)
                ret["arch"] = cpuarch
            else:
                ret["arch"] = "armv6"
    elif xbmc.getCondVisibility("system.platform.xbox"):
        ret["os"] = "windows"
        ret["arch"] = "x64"
        ret["fork"] = False
    elif xbmc.getCondVisibility("system.platform.windows"):
        ret["os"] = "windows"
        if ret["machine"].endswith('64'):
            ret["arch"] = "x64"
    elif ret["system"] == "Darwin":
        ret["os"] = "darwin"
        ret["arch"] = "x64"

        if "AppleTV" in ret["platform"]:
            ret["os"] = "ios"
            ret["arch"] = "armv7"
            ret["fork"] = False
            if "64bit" in ret["platform"]:
                ret["arch"] = "arm64"
        elif xbmc.getCondVisibility("system.platform.ios"):
            ret["os"] = "ios"
            ret["arch"] = "armv7"
            ret["fork"] = False
            if "64bit" in ret["platform"]:
                ret["arch"] = "arm64"

    # elif xbmc.getCondVisibility("system.platform.osx"):
    #     ret["os"] = "darwin"
    #     ret["arch"] = "x64"
    # elif xbmc.getCondVisibility("system.platform.ios"):
    #     ret["os"] = "ios"
    #     ret["arch"] = "armv7"
    return ret
Example #10
0
 def GetLocalizedString(self, *args, **kwargs):
     return ADDON.getLocalizedString(*args,
                                     **kwargs).encode('utf-8', 'ignore')
def get_projectx_binary():
    global binary_platform
    binary_platform = get_platform()

    binary = "projectx" + (binary_platform["os"] == "windows" and ".exe" or "")
    binary_dir = os.path.join(ADDON_PATH, "resources", "bin", "%(os)s_%(arch)s" % binary_platform)

    if binary_platform["os"] == "android":
        log.info("Detected binary folder: %s" % binary_dir)
        binary_dir_legacy = binary_dir.replace("/storage/emulated/0", "/storage/emulated/legacy")
        if os.path.exists(binary_dir_legacy):
            binary_dir = binary_dir_legacy
            log.info("Using changed binary folder for Android: %s" % binary_dir)

        app_id = android_get_current_appid()
        xbmc_data_path = os.path.join("/data", "data", app_id)
        if not os.path.exists(xbmc_data_path):
            log.info("%s path does not exist, so using %s as xbmc_data_path" % (xbmc_data_path, xbmc.translatePath("special://xbmcbin/")))
            xbmc_data_path = xbmc.translatePath("special://xbmcbin/")

        if not os.path.exists(xbmc_data_path):
            log.info("%s path does not exist, so using %s as xbmc_data_path" % (xbmc_data_path, xbmc.translatePath("special://masterprofile/")))
            xbmc_data_path = xbmc.translatePath("special://masterprofile/")

        dest_binary_dir = os.path.join(xbmc_data_path, "files", ADDON_ID, "bin", "%(os)s_%(arch)s" % binary_platform)
    else:
        dest_binary_dir = os.path.join(xbmc.translatePath(ADDON.getAddonInfo("profile")), "bin", "%(os)s_%(arch)s" % binary_platform)

    binary_path = os.path.join(binary_dir, binary)
    dest_binary_path = os.path.join(dest_binary_dir, binary)

    log.info("Binary detection. Source: %s, Destination: %s" % (binary_path, dest_binary_path))

    if not os.path.exists(binary_path):
        # notify((getLocalizedString(30103) + " %(os)s_%(arch)s" % PLATFORM), time=7000)
        dialog_ok("LOCALIZE[30347];;" + "%(os)s_%(arch)s" % binary_platform)
        system_information()
        try:
            log.info("Source directory (%s):\n%s" % (binary_dir, os.listdir(os.path.join(binary_dir, ".."))))
            log.info("Destination directory (%s):\n%s" % (dest_binary_dir, os.listdir(os.path.join(dest_binary_dir, ".."))))
        except Exception:
            pass
        return False, False

    if os.path.isdir(dest_binary_path):
        log.warning("Destination path is a directory, expected previous binary file, removing...")
        try:
            shutil.rmtree(dest_binary_path)
        except Exception as e:
            log.error("Unable to remove destination path for update: %s" % e)
            system_information()
            return False, False

    if not os.path.exists(dest_binary_path) or not os.path.exists(binary_path) or get_projectxd_checksum(dest_binary_path) != get_projectxd_checksum(binary_path) or not filecmp.cmp(dest_binary_path, binary_path, shallow=True):
        log.info("Updating projectx daemon...")
        try:
            os.makedirs(dest_binary_dir)
        except OSError:
            pass
        try:
            shutil.rmtree(dest_binary_dir)
        except Exception as e:
            log.error("Unable to remove destination path for update: %s" % e)
            system_information()
            pass
        try:
            shutil.copytree(binary_dir, dest_binary_dir)
        except Exception as e:
            log.error("Unable to copy to destination path for update: %s" % e)
            system_information()
            return False, False

    # Clean stale files in the directory, as this can cause headaches on
    # Android when they are unreachable
    dest_files = set(os.listdir(dest_binary_dir))
    orig_files = set(os.listdir(binary_dir))
    log.info("Deleting stale files %s" % (dest_files - orig_files))
    for file_ in (dest_files - orig_files):
        path = os.path.join(dest_binary_dir, file_)
        if os.path.isdir(path):
            shutil.rmtree(path)
        else:
            os.remove(path)

    log.info("Binary detection: [ Source: %s, Destination: %s ]" % (binary_path, dest_binary_path))
    return dest_binary_dir, ensure_exec_perms(dest_binary_path)
def run(url_suffix="", retry=0):
    try:
        buffer_timeout = int(ADDON.getSetting("buffer_timeout"))
        if buffer_timeout < 60:
            buffer_timeout = 60
    except:
        buffer_timeout = 60
    buffer_timeout = buffer_timeout * 2

    try:
        preload_timeout = int(ADDON.getSetting("preload_timeout"))
        if preload_timeout < 1:
            preload_timeout = 1
    except:
        preload_timeout = 1

    socket.setdefaulttimeout(buffer_timeout)
    urllib_request.install_opener(
        urllib_request.build_opener(NoRedirectHandler()))

    # Pause currently playing projectx file to avoid doubling requests
    try:
        if xbmc.Player().isPlaying() and ADDON_ID in xbmc.Player(
        ).getPlayingFile():
            xbmc.Player().pause()
    except:
        pass

    url = sys.argv[0].replace("plugin://%s" % ADDON_ID,
                              projectxD_HOST + url_suffix) + sys.argv[2]
    query_add = ""

    if len(sys.argv) > 3:
        app = sys.argv[3].replace(":", "=")

        # Replacing resume=false with resume=true if item is launched from main window
        title = xbmc.getInfoLabel('ListItem.Title')
        label = xbmc.getInfoLabel('ListItem.Label')
        # if "resume" in app and not title and not label:
        #     app = app.replace("resume=false", "resume=true")
        # if "resume" not in app and not title and not label:
        if not title and not label:
            if app:
                app = app.replace("resume=false", "")
                app += "&"
            if app and "resume" not in app:
                app += "resume=true"
        query_add = app
    elif "play" in url or "links" in url:
        query_add = "resume=true"

    if query_add and "resume" not in url:
        query_add = query_add.replace("resume=", "doresume=")
        if "?" in url:
            url += "&" + query_add
        else:
            url += "?" + query_add

    log.debug("Requesting %s from %s" % (url, repr(sys.argv)))

    try:
        data = _json(url)
    except PlayerException as e:
        redirect_url = e.__str__()
        log.debug("Launching player with %s" % (redirect_url))
        xbmcplugin.endOfDirectory(HANDLE, succeeded=True)
        xbmc.sleep(500)
        xbmc.executeJSONRPC(
            '{"jsonrpc":"2.0","method":"Player.Open","params":{"item":{"file":"%s"}},"id":"1"}'
            % (redirect_url))
        return
    except RedirectException as e:
        redirect_url = e.__str__()
        log.debug("Redirecting Kodi with %s" % (redirect_url))
        xbmcplugin.endOfDirectory(HANDLE, succeeded=True)
        xbmc.sleep(500)
        if "keyboard=1" in sys.argv[0]:
            xbmc.executebuiltin('Container.Update(%s,replace)' %
                                (redirect_url))
        else:
            xbmc.executebuiltin('Container.Update(%s)' % (redirect_url))
        return
    except urllib_error.URLError as e:
        # We can retry the request if connection is refused.
        # For example when plugin has not yet started but is requested by someone.
        if retry <= 2:
            time.sleep(preload_timeout)
            return run(retry=retry + 1)

        if isinstance(e.reason, IOError) or isinstance(
                e.reason, OSError) or 'Connection refused' in e.reason:
            notify(getLocalizedString(30116), time=7000)
        else:
            import traceback
            map(log.error, traceback.format_exc().split("\n"))
            notify(e.reason, time=7000)
        return
    except Exception as e:
        import traceback
        map(log.error, traceback.format_exc().split("\n"))
        try:
            msg = six.ensure_text(e.__str__(), errors='ignore')
        except:
            try:
                msg = six.ensure_binary(e.__str__(), errors='ignore')
            except:
                msg = repr(e)
        notify(getLocalizedLabel(msg), time=7000)
        return

    if not data:
        return

    if data["content_type"]:
        content_type = data["content_type"]
        if data["content_type"].startswith("menus"):
            content_type = data["content_type"].split("_")[1]

        xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_UNSORTED)
        if content_type != "tvshows":
            xbmcplugin.addSortMethod(HANDLE,
                                     xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE)
        else:
            xbmcplugin.addSortMethod(HANDLE,
                                     xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE)
        xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_DATE)
        xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_GENRE)
        xbmcplugin.setContent(HANDLE, content_type)

    listitems = list(range(len(data["items"])))
    for i, item in enumerate(data["items"]):
        # Translate labels
        if item["label"][0:8] == "LOCALIZE":
            item["label"] = getLocalizedLabel(item["label"])
            if isinstance(item["label"], str):
                item["label"] = six.ensure_text(item["label"], 'utf-8')
        if item["label2"][0:8] == "LOCALIZE":
            item["label2"] = getLocalizedLabel(item["label2"])

        if PLATFORM['kodi'] >= 19:
            listItem = xbmcgui.ListItem(label=item["label"],
                                        label2=item["label2"])
            listItem.setArt({'icon': item["icon"]})
            listItem.setArt({'thumb': item["thumbnail"]})
        else:
            listItem = xbmcgui.ListItem(label=item["label"],
                                        label2=item["label2"],
                                        iconImage=item["icon"],
                                        thumbnailImage=item["thumbnail"])

        if item.get("info"):
            listItem.setInfo("video", item["info"])
        if item.get("stream_info"):
            for type_, values in item["stream_info"].items():
                listItem.addStreamInfo(type_, values)
        if item.get("art"):
            listItem.setArt(item["art"])
        elif ADDON.getSetting('default_fanart'
                              ) == 'true' and item["label"] != six.ensure_text(
                                  getLocalizedString(30218), 'utf-8'):
            fanart = os.path.join(ADDON_PATH, "fanart.png")
            listItem.setArt({'fanart': fanart})
        if item.get("context_menu"):
            # Translate context menus
            for m, menu in enumerate(item["context_menu"]):
                if menu[0][0:8] == "LOCALIZE":
                    menu[0] = getLocalizedLabel(menu[0])
            listItem.addContextMenuItems(item["context_menu"])
        listItem.setProperty("isPlayable", item["is_playable"] and "true"
                             or "false")
        if item.get("properties"):
            for k, v in item["properties"].items():
                listItem.setProperty(k, v)
        listitems[i] = (item["path"], listItem, not item["is_playable"])

    xbmcplugin.addDirectoryItems(HANDLE, listitems, totalItems=len(listitems))

    # Set ViewMode
    if data["content_type"]:
        viewMode = ADDON.getSetting("viewmode_%s" % data["content_type"])
        if viewMode:
            try:
                xbmc.executebuiltin('Container.SetViewMode(%s)' % viewMode)
            except Exception as e:
                log.warning("Unable to SetViewMode(%s): %s" %
                            (viewMode, repr(e)))

    xbmcplugin.endOfDirectory(HANDLE,
                              succeeded=True,
                              updateListing=False,
                              cacheToDisc=False)
def start_projectxd(**kwargs):
    jsonrpc_failures = 0
    while jsonrpc_enabled() is False:
        jsonrpc_failures += 1
        log.warning("Unable to connect to Kodi's JSON-RPC service, retrying...")
        if jsonrpc_failures > 1:
            time.sleep(5)
            if not jsonrpc_enabled(notify=True):
                log.error("Unable to reach Kodi's JSON-RPC service, aborting...")
                return False
            else:
                break
        time.sleep(3)

    projectx_dir, projectx_binary = get_projectx_binary()

    log.info("Binary dir: %s, item: %s " % (projectx_dir, projectx_binary))
    if projectx_dir is False or projectx_binary is False:
        return False

    lockfile = os.path.join(ADDON_PATH, ".lockfile")
    if os.path.exists(lockfile):
        log.warning("Existing process found from lockfile, killing...")
        try:
            with open(lockfile) as lf:
                pid = int(lf.read().rstrip(" \t\r\n\0"))
            os.kill(pid, 9)
        except OSError as e:
            if e.errno != 3:
                # Ignore:   OSError: [Errno 3] No such process
                log.error(repr(e))
        except Exception as e:
            log.error(repr(e))

        if binary_platform["os"] == "windows":
            try:
                library_lockfile = os.path.join(xbmc.translatePath(ADDON.getAddonInfo("profile")).decode('utf-8'), "library.db.lock")
                log.warning("Removing library.db.lock file at %s ..." % library_lockfile)
                os.remove(library_lockfile)
            except Exception as e:
                log.error(repr(e))

    SW_HIDE = 0
    STARTF_USESHOWWINDOW = 1

    args = [projectx_binary]
    kwargs["cwd"] = projectx_dir

    if binary_platform["os"] == "windows":
        args[0] = getWindowsShortPath(projectx_binary)
        kwargs["cwd"] = getWindowsShortPath(projectx_dir)
        si = subprocess.STARTUPINFO()
        si.dwFlags = STARTF_USESHOWWINDOW
        si.wShowWindow = SW_HIDE
        clear_fd_inherit_flags()
        kwargs["startupinfo"] = si
    else:
        env = os.environ.copy()
        env["LD_LIBRARY_PATH"] = "%s:%s" % (projectx_dir, env.get("LD_LIBRARY_PATH", ""))
        kwargs["env"] = env
        kwargs["close_fds"] = True

    wait_counter = 1
    log.debug("Checking for visible")
    while xbmc.getCondVisibility('Window.IsVisible(10140)') or xbmc.getCondVisibility('Window.IsActive(10140)'):
        if wait_counter == 1:
            log.info('Add-on settings currently opened, waiting before starting...')
        if wait_counter > 300:
            break
        time.sleep(1)
        wait_counter += 1

    log.info("projectxd: start args: %s, kw: %s" % (args, kwargs))

    if hasSubprocess:
        return subprocess.Popen(args, **kwargs)
    return False
Example #14
0
 def GetSetting(self, *args, **kwargs):
     return ADDON.getSetting(*args, **kwargs)
Example #15
0
 def SetSetting(self, *args, **kwargs):
     return ADDON.setSetting(*args, **kwargs)
Example #16
0
def getLocalizedStringMatch(match):
    try:
        return ADDON.getLocalizedString(int(match.group(1)))
    except:
        return match.group(1)
Example #17
0
def notify(message, header=ADDON_NAME, time=5000, image=ADDON_ICON):
    sound = ADDON.getSetting('do_not_disturb') == 'false'
    dialog = xbmcgui.Dialog()
    return dialog.notification(toUtf8(header), toUtf8(message), toUtf8(image),
                               time, sound)