Ejemplo n.º 1
0
def download_addon(client: HttpClient,
                   id: int) -> Union[DownloadOk, DownloadError]:
    "Fetch a single add-on from AnkiWeb."
    try:
        resp = client.get(aqt.appShared +
                          f"download/{id}?v=2.1&p={current_point_version}")
        if resp.status_code != 200:
            return DownloadError(status_code=resp.status_code)

        data = client.streamContent(resp)

        fname = re.match("attachment; filename=(.+)",
                         resp.headers["content-disposition"]).group(1)

        meta = extract_meta_from_download_url(resp.url)

        return DownloadOk(
            data=data,
            filename=fname,
            mod_time=meta.mod_time,
            min_point_version=meta.min_point_version,
            max_point_version=meta.max_point_version,
            branch_index=meta.branch_index,
        )
    except Exception as e:
        return DownloadError(exception=e)
Ejemplo n.º 2
0
    def download(self):
        idx = self.lst.currentIndex()
        if not idx.isValid():
            QMessageBox.show(self, self.windowTitle(),
                             'Please select a language.')
            return
        url = idx.data(Qt.UserRole)

        client = HttpClient()
        resp = client.get(url)

        if resp.status_code != 200:
            QMessageBox.information(
                self, self.windowTitle(),
                'Downloading %s data failed.' % self.mode_str)
            return

        data = client.streamContent(resp)

        dir_path = os.path.join(addon_path, 'user_files', 'db', self.mode_str)
        os.makedirs(dir_path, exist_ok=True)

        dst_path = os.path.join(dir_path, '%s.json' % self.dst_lang)

        with open(dst_path, 'wb') as f:
            f.write(data)

        if self.mode == self.Mode.Freq:
            msg = 'Imported frequency data for "%s".\n\nNote that the frequency data is only applied to newly imported dictionaries for this language.' % self.dst_lang
        else:
            msg = 'Imported conjugation data for "%s".' % self.dst_lang
        QMessageBox.information(self, self.windowTitle(), msg)

        self.accept()
def download_index(server_url=DEFAULT_SERVER):
    server_url = normalize_url(server_url)

    index_url = server_url + '/index.json'

    client = HttpClient()
    resp = client.get(index_url)

    if resp.status_code != 200:
        return None

    data = client.streamContent(resp)
    return json.loads(data)
Ejemplo n.º 4
0
def check_for_updates(mgr: AddonManager,
                      on_done: Callable[[HttpClient, List[Dict]], None]):
    client = HttpClient()

    def check():
        return fetch_update_info(client, mgr.ankiweb_addons())

    def update_info_received(future: Future):
        # if syncing/in profile screen, defer message delivery
        if not mgr.mw.col:
            mgr.mw.progress.timer(
                1000,
                lambda: update_info_received(future),
                False,
                requiresCollection=False,
            )
            return

        if future.exception():
            # swallow network errors
            print(str(future.exception()))
            result = []
        else:
            result = future.result()

        on_done(client, result)

    mgr.mw.taskman.run_in_background(check, update_info_received)
Ejemplo n.º 5
0
def download_addon(client: HttpClient, id: int) -> Union[DownloadOk, DownloadError]:
    "Fetch a single add-on from AnkiWeb."
    try:
        resp = client.get(aqt.appShared + f"download/{id}?v=2.1&p={pointVersion}")
        if resp.status_code != 200:
            return DownloadError(status_code=resp.status_code)

        data = client.streamContent(resp)

        fname = re.match(
            "attachment; filename=(.+)", resp.headers["content-disposition"]
        ).group(1)

        return DownloadOk(data=data, filename=fname)
    except Exception as e:
        return DownloadError(exception=e)
Ejemplo n.º 6
0
    def _retrieveURL(self, url):
        "Download file into media folder and return local filename or None."
        # urllib doesn't understand percent-escaped utf8, but requires things like
        # '#' to be escaped.
        url = urllib.parse.unquote(url)
        if url.lower().startswith("file://"):
            url = url.replace("%", "%25")
            url = url.replace("#", "%23")
            local = True
        else:
            local = False
        # fetch it into a temporary folder
        self.mw.progress.start(immediate=not local, parent=self.parentWindow)
        ct = None
        try:
            if local:
                req = urllib.request.Request(
                    url, None,
                    {"User-Agent": "Mozilla/5.0 (compatible; Anki)"})
                filecontents = urllib.request.urlopen(req).read()
            else:
                reqs = HttpClient()
                reqs.timeout = 30
                r = reqs.get(url)
                if r.status_code != 200:
                    showWarning(
                        _("Unexpected response code: %s") % r.status_code)
                    return
                filecontents = r.content
                ct = r.headers.get("content-type")
        except urllib.error.URLError as e:
            showWarning(_("An error occurred while opening %s") % e)
            return
        except requests.exceptions.RequestException as e:
            showWarning(_("An error occurred while opening %s") % e)
            return
        finally:
            self.mw.progress.finish()
        # strip off any query string
        url = re.sub(r"\?.*?$", "", url)
        fname = os.path.basename(urllib.parse.unquote(url))
        if ct:
            fname = self.mw.col.media.add_extension_based_on_mime(fname, ct)

        return self.mw.col.media.write_data(fname, filecontents)
Ejemplo n.º 7
0
def _fetch_update_info_batch(client: HttpClient,
                             chunk: Iterable[str]) -> Iterable[Dict]:
    """Get update info from AnkiWeb.

    Chunk must not contain more than 25 ids."""
    resp = client.get(aqt.appShared + "updates/" + ",".join(chunk) + "?v=3")
    if resp.status_code == 200:
        return resp.json()
    else:
        raise Exception("Unexpected response code from AnkiWeb: {}".format(
            resp.status_code))
Ejemplo n.º 8
0
def download_addons(
    parent: QWidget,
    mgr: AddonManager,
    ids: List[int],
    on_done: Callable[[List[DownloadLogEntry]], None],
    client: Optional[HttpClient] = None,
) -> None:
    if client is None:
        client = HttpClient()
    downloader = DownloaderInstaller(parent, mgr, client)
    downloader.download(ids, on_done=on_done)
Ejemplo n.º 9
0
    def _retrieveURL(self, url: str) -> Optional[str]:
        "Download file into media folder and return local filename or None."
        # urllib doesn't understand percent-escaped utf8, but requires things like
        # '#' to be escaped.
        url = urllib.parse.unquote(url)
        if url.lower().startswith("file://"):
            url = url.replace("%", "%25")
            url = url.replace("#", "%23")
            local = True
        else:
            local = False
        # fetch it into a temporary folder
        self.mw.progress.start(immediate=not local, parent=self.parentWindow)
        content_type = None
        error_msg: Optional[str] = None
        try:
            if local:
                req = urllib.request.Request(
                    url, None,
                    {"User-Agent": "Mozilla/5.0 (compatible; Anki)"})
                with urllib.request.urlopen(req) as response:
                    filecontents = response.read()
            else:
                with HttpClient() as client:
                    client.timeout = 30
                    with client.get(url) as response:
                        if response.status_code != 200:
                            error_msg = tr(
                                TR.QT_MISC_UNEXPECTED_RESPONSE_CODE,
                                val=response.status_code,
                            )
                            return None
                        filecontents = response.content
                        content_type = response.headers.get("content-type")
        except (urllib.error.URLError,
                requests.exceptions.RequestException) as e:
            error_msg = tr(TR.EDITING_AN_ERROR_OCCURRED_WHILE_OPENING,
                           val=str(e))
            return None
        finally:
            self.mw.progress.finish()
            if error_msg:
                showWarning(error_msg)
        # strip off any query string
        url = re.sub(r"\?.*?$", "", url)
        fname = os.path.basename(urllib.parse.unquote(url))
        if not fname.strip():
            fname = "paste"
        if content_type:
            fname = self.mw.col.media.add_extension_based_on_mime(
                fname, content_type)

        return self.mw.col.media.write_data(fname, filecontents)
Ejemplo n.º 10
0
def _retrieveURL(url: str):
    # url = urllib.parse.unquote(url)
    url = html.unescape(url)
    if url.lower().startswith("file://"):
        url = url.replace("%", "%25")
        url = url.replace("#", "%23")
        local = True
    else:
        local = False
    # fetch it into a temporary folder
    mw.progress.start(immediate=not local)
    content_type = None
    error_msg = None
    try:
        if local:
            req = urllib.request.Request(
                url, None, {"User-Agent": "Mozilla/5.0 (compatible; Anki)"})
            with urllib.request.urlopen(req) as response:
                filecontents = response.read()
        else:
            with HttpClient() as client:
                client.timeout = 30
                # print(f"_retrieveURL(): url = '{url}'")
                with client.get(url) as response:
                    # print(f"_retrieveURL(): response.status_code = '{response.status_code}'")
                    if response.status_code != 200:
                        error_msg = (_("Unexpected response code: %s") %
                                     response.status_code)
                        return None
                    filecontents = response.content
                    content_type = response.headers.get("content-type")
    except (urllib.error.URLError, requests.exceptions.RequestException) as e:
        error_msg = _("An error occurred while opening %s") % e
        return None
    finally:
        mw.progress.finish()
        if error_msg:
            showWarning(error_msg)
    # strip off any query string
    fname = os.path.basename(urllib.parse.unquote(url))
    if not fname.strip():
        fname = "paste"
    if content_type:
        fname = mw.col.media.add_extension_based_on_mime(fname, content_type)

    return mw.col.media.write_data(fname, filecontents)
Ejemplo n.º 11
0
 def install(self, id, course):
     # check, and ignore, if addin has already been installed
     if not self.isInstalled():
         # to prevent scam installations require user to authorise download
         # id must be looked up on Anki addin website and hardcoded in command file
         prompt = 'Install {0} add-in?\nUse Help to view add-in details'.format(
             self.name)
         help = 'https://ankiweb.net/shared/info/{0}'.format(id)
         if askUser(prompt,
                    defaultno=True,
                    help='https://ankiweb.net/shared/info/{0}'.format(id),
                    title=course.name):
             from anki.httpclient import HttpClient
             (id, result) = addons.download_and_install_addon(
                 mw.addonManager, HttpClient(), id)
             if isinstance(result, addons.DownloadError):
                 raise Exception('{0} download failed: {1}'.format(
                     self.name, result))
Ejemplo n.º 12
0
        def run(self):
            from .dictionaryManager import importDict

            client = HttpClient()

            num_dicts = 0
            num_installed = 0

            def update_dict_progress(amt):
                progress = 0
                if num_dicts > 0:
                    progress = num_installed / num_dicts
                    progress += amt / num_dicts
                progress_percent = round(progress * 100)
                self.progress_update.emit(progress_percent)

            for l in self.install_index:
                num_dicts += len(l.get('dictionaries', []))

            self.log_update.emit('Installing %d dictionaries...' % num_dicts)

            freq_path = os.path.join(addon_path, 'user_files', 'db',
                                     'frequency')
            os.makedirs(freq_path, exist_ok=True)

            conj_path = os.path.join(addon_path, 'user_files', 'db',
                                     'conjugation')
            os.makedirs(conj_path, exist_ok=True)

            for l in self.install_index:
                if self.cancel_requested:
                    return

                lname = self.force_lang
                if not lname:
                    lname = l.get('name_en')
                if not lname:
                    continue

                # Create Language
                try:
                    aqt.mw.miDictDB.addLanguages([lname])
                except Exception as e:
                    # Lanugage already exists
                    pass

                # Install frequency data
                if self.install_freq:
                    furl = l.get('frequency_url')
                    if furl:
                        self.log_update.emit(
                            'Installing %s frequency data...' % lname)
                        furl = self.construct_url(furl)
                        dl_resp = client.get(furl)
                        if dl_resp.status_code == 200:
                            fdata = client.streamContent(dl_resp)
                            dst_path = os.path.join(freq_path,
                                                    '%s.json' % lname)
                            with open(dst_path, 'wb') as f:
                                f.write(fdata)
                        else:
                            self.log_update.emit(
                                ' ERROR: Download failed (%d).' %
                                dl_resp.status_code)

                # Install conjugation data
                if self.install_conj:
                    curl = l.get('conjugation_url')
                    if curl:
                        self.log_update.emit(
                            'Installing %s conjugation data...' % lname)
                        curl = self.construct_url(curl)
                        dl_resp = client.get(curl)
                        if dl_resp.status_code == 200:
                            cdata = client.streamContent(dl_resp)
                            dst_path = os.path.join(conj_path,
                                                    '%s.json' % lname)
                            with open(dst_path, 'wb') as f:
                                f.write(cdata)
                        else:
                            self.log_update.emit(
                                ' ERROR: Download failed (%d).' %
                                dl_resp.status_code)

                # Install dictionaries
                for d in l.get('dictionaries', []):
                    if self.cancel_requested:
                        return

                    dname = d.get('name')
                    durl = self.construct_url(d.get('url'))

                    self.log_update.emit('Installing %s...' % dname)

                    self.log_update.emit(' Downloading %s...' % durl)
                    dl_resp = client.get(durl)

                    if dl_resp.status_code == 200:
                        update_dict_progress(0.5)
                        self.log_update.emit(' Importing...')
                        ddata = client.streamContent(dl_resp)
                        try:
                            importDict(lname, io.BytesIO(ddata), dname)
                        except ValueError as e:
                            self.log_update.emit(' ERROR: %s' % str(e))
                    else:
                        self.log_update.emit(' ERROR: Download failed (%d).' %
                                             dl_resp.status_code)

                    update_dict_progress(1.0)
                    num_installed += 1

                # Only once language can be installed when language is forced
                if self.force_lang:
                    # Should never happen
                    break

            self.progress_update.emit(100)
            self.log_update.emit('All done.')
Ejemplo n.º 13
0
        return path
    # not a valid local file or directory, so assume that it points to a URL
    return name


def importAnkiScript(download=False):
    """private function to get a reference to the Anki Script addon, installing it if required"""

    if ankiscript := find_addon_by_name('Anki Script'):
        return ankiscript

    if download:
        id = '828860704'
        (id,
         result) = addons.download_and_install_addon(mw.addonManager,
                                                     HttpClient(), id)
        if isinstance(result, addons.DownloadError):
            raise Exception('Anki Script download failed: {0}'.format(result))

        # now can try again to import
        return importAnkiScript(download=False)

    raise Exception('Cannot find Anki Script addin')


# https://forums.ankiweb.net/t/accessing-the-module-of-another-add-on/5936/2
def find_addon_by_name(addon_name: str) -> Optional[ModuleType]:
    for name in mw.addonManager.allAddons():
        if mw.addonManager.addonName(name) == addon_name:
            try:
                return import_module(name)