summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes 'josch' Schauer <josch@debian.org>2021-01-19 12:54:47 +0100
committerJohannes 'josch' Schauer <josch@debian.org>2021-01-19 12:54:47 +0100
commit7a1db4de351875bebb4a8e7ffbe6710ad5b518c5 (patch)
tree41fc39a14842780c6ea061b29a00888e8071ffa8
parent075e7e02683d9db1b303de8e8c17ff7da4c62510 (diff)
New upstream version 0.18.0+dfsg1
-rw-r--r--.config.sh1
-rw-r--r--.coveragerc11
-rw-r--r--.dockerignore9
-rw-r--r--.github/ISSUE_TEMPLATE/bug-report.md37
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml14
-rw-r--r--.github/ISSUE_TEMPLATE/engine-request.md29
-rw-r--r--.github/ISSUE_TEMPLATE/feature-request.md19
-rw-r--r--.github/workflows/integration.yml110
-rw-r--r--.gitignore1
-rw-r--r--.pylintrc2
-rw-r--r--.travis.yml57
-rw-r--r--AUTHORS.rst28
-rw-r--r--CHANGELOG.rst142
-rw-r--r--CONTRIBUTING.md49
-rw-r--r--Dockerfile10
-rw-r--r--Makefile48
-rw-r--r--PULL_REQUEST_TEMPLATE.md25
-rw-r--r--README.rst32
-rwxr-xr-xdockerfiles/docker-entrypoint.sh52
-rw-r--r--dockerfiles/uwsgi.ini7
-rw-r--r--docs/admin/arch_public.dot8
-rw-r--r--docs/admin/buildhosts.rst2
-rw-r--r--docs/admin/engines.rst25
-rw-r--r--docs/admin/engines/recoll.rst50
-rw-r--r--docs/admin/filtron.rst8
-rw-r--r--docs/admin/installation-docker.rst46
-rw-r--r--docs/admin/installation-nginx.rst4
-rw-r--r--docs/admin/installation-searx.rst31
-rw-r--r--docs/admin/installation.rst17
-rw-r--r--docs/admin/settings.rst254
-rw-r--r--docs/blog/command-line-engines.rst65
-rw-r--r--docs/blog/index.rst1
-rw-r--r--docs/blog/lxcdev-202006.rst2
-rw-r--r--docs/blog/private-engines.rst2
-rw-r--r--docs/blog/python3.rst2
-rw-r--r--docs/build-templates/filtron.rst20
-rw-r--r--docs/build-templates/morty.rst20
-rw-r--r--docs/build-templates/searx.rst49
-rw-r--r--docs/conf.py18
-rw-r--r--docs/dev/contribution_guide.rst25
-rw-r--r--docs/dev/engine_overview.rst37
-rw-r--r--docs/dev/makefile.rst2
-rw-r--r--docs/dev/plugins.rst7
-rw-r--r--docs/dev/quickstart.rst129
-rw-r--r--docs/dev/reST.rst24
-rw-r--r--docs/utils/index.rst3
-rw-r--r--docs/utils/standalone_searx.py.rst11
-rwxr-xr-xmanage.sh6
-rw-r--r--requirements-dev.txt27
-rw-r--r--requirements.txt8
-rw-r--r--searx/__init__.py52
-rw-r--r--searx/answerers/__init__.py8
-rw-r--r--searx/answerers/random/answerer.py32
-rw-r--r--searx/answerers/statistics/answerer.py15
-rw-r--r--searx/autocomplete.py34
-rw-r--r--searx/brand.py6
-rw-r--r--searx/data/__init__.py29
-rw-r--r--searx/data/ahmia_blacklist.txt16177
-rw-r--r--searx/data/engines_languages.json2848
-rw-r--r--searx/data/external_urls.json156
-rw-r--r--searx/data/wikidata_units.json1006
-rw-r--r--searx/engines/1337x.py18
-rw-r--r--searx/engines/__init__.py88
-rw-r--r--searx/engines/acgsou.py37
-rw-r--r--searx/engines/ahmia.py82
-rw-r--r--searx/engines/apkmirror.py12
-rw-r--r--searx/engines/archlinux.py10
-rw-r--r--searx/engines/arxiv.py27
-rwxr-xr-xsearx/engines/base.py9
-rw-r--r--searx/engines/bing.py10
-rw-r--r--searx/engines/bing_images.py21
-rw-r--r--searx/engines/bing_news.py19
-rw-r--r--searx/engines/bing_videos.py5
-rw-r--r--searx/engines/btdigg.py6
-rw-r--r--searx/engines/command.py183
-rw-r--r--searx/engines/currency_convert.py39
-rw-r--r--searx/engines/dailymotion.py2
-rw-r--r--searx/engines/deezer.py4
-rw-r--r--searx/engines/deviantart.py85
-rw-r--r--searx/engines/dictzone.py9
-rw-r--r--searx/engines/digbt.py8
-rw-r--r--searx/engines/digg.py73
-rw-r--r--searx/engines/doku.py5
-rw-r--r--searx/engines/duckduckgo.py52
-rw-r--r--searx/engines/duckduckgo_definitions.py234
-rw-r--r--searx/engines/duckduckgo_images.py17
-rw-r--r--searx/engines/duden.py47
-rw-r--r--searx/engines/ebay.py68
-rw-r--r--searx/engines/elasticsearch.py140
-rw-r--r--searx/engines/etools.py5
-rw-r--r--searx/engines/fdroid.py4
-rw-r--r--searx/engines/filecrop.py88
-rw-r--r--searx/engines/flickr.py2
-rw-r--r--searx/engines/flickr_noapi.py10
-rw-r--r--searx/engines/framalibre.py9
-rw-r--r--searx/engines/frinkiac.py2
-rw-r--r--searx/engines/genius.py15
-rw-r--r--searx/engines/gentoo.py6
-rw-r--r--searx/engines/gigablast.py2
-rw-r--r--searx/engines/github.py2
-rw-r--r--searx/engines/google.py68
-rw-r--r--searx/engines/google_images.py43
-rw-r--r--searx/engines/google_news.py4
-rw-r--r--searx/engines/google_videos.py13
-rw-r--r--searx/engines/ina.py9
-rw-r--r--searx/engines/invidious.py11
-rw-r--r--searx/engines/json_engine.py9
-rw-r--r--searx/engines/kickass.py5
-rw-r--r--searx/engines/mediawiki.py4
-rw-r--r--searx/engines/microsoft_academic.py3
-rw-r--r--searx/engines/mixcloud.py2
-rw-r--r--searx/engines/not_evil.py64
-rw-r--r--searx/engines/nyaa.py5
-rw-r--r--searx/engines/opensemantic.py42
-rw-r--r--searx/engines/openstreetmap.py8
-rw-r--r--searx/engines/peertube.py94
-rw-r--r--searx/engines/photon.py4
-rw-r--r--searx/engines/piratebay.py126
-rw-r--r--searx/engines/pubmed.py4
-rw-r--r--searx/engines/qwant.py42
-rw-r--r--searx/engines/recoll.py104
-rw-r--r--searx/engines/reddit.py2
-rw-r--r--searx/engines/scanr_structures.py2
-rw-r--r--searx/engines/searchcode_code.py2
-rw-r--r--searx/engines/searchcode_doc.py49
-rw-r--r--searx/engines/searx_engine.py4
-rw-r--r--searx/engines/seedpeer.py78
-rw-r--r--searx/engines/sepiasearch.py97
-rw-r--r--searx/engines/soundcloud.py10
-rw-r--r--searx/engines/spotify.py8
-rw-r--r--searx/engines/stackoverflow.py9
-rw-r--r--searx/engines/startpage.py75
-rw-r--r--searx/engines/tokyotoshokan.py5
-rw-r--r--searx/engines/torrentz.py15
-rw-r--r--searx/engines/translated.py13
-rw-r--r--searx/engines/twitter.py87
-rw-r--r--searx/engines/unsplash.py2
-rw-r--r--searx/engines/vimeo.py2
-rw-r--r--searx/engines/wikidata.py1090
-rw-r--r--searx/engines/wikipedia.py90
-rw-r--r--searx/engines/wolframalpha_api.py20
-rw-r--r--searx/engines/wolframalpha_noapi.py2
-rw-r--r--searx/engines/www1x.py26
-rw-r--r--searx/engines/xpath.py118
-rw-r--r--searx/engines/yacy.py12
-rw-r--r--searx/engines/yahoo.py5
-rw-r--r--searx/engines/yahoo_news.py12
-rw-r--r--searx/engines/yandex.py7
-rw-r--r--searx/engines/yggtorrent.py123
-rw-r--r--searx/engines/youtube_api.py6
-rw-r--r--searx/engines/youtube_noapi.py6
-rw-r--r--searx/exceptions.py72
-rw-r--r--searx/external_bang.py14
-rw-r--r--searx/external_urls.py77
-rw-r--r--searx/languages.py144
-rw-r--r--searx/metrology/__init__.py0
-rw-r--r--searx/metrology/error_recorder.py147
-rw-r--r--searx/plugins/__init__.py117
-rw-r--r--searx/plugins/ahmia_filter.py33
-rw-r--r--searx/plugins/hash_plugin.py54
-rw-r--r--searx/plugins/https_rewrite.py5
-rw-r--r--searx/plugins/oa_doi_rewrite.py2
-rw-r--r--searx/plugins/open_results_on_new_tab.py25
-rw-r--r--searx/plugins/self_info.py6
-rw-r--r--searx/plugins/tracker_url_remover.py2
-rw-r--r--searx/poolrequests.py76
-rw-r--r--searx/preferences.py92
-rw-r--r--searx/query.py122
-rw-r--r--searx/raise_for_httperror.py66
-rw-r--r--searx/results.py111
-rw-r--r--searx/search.py584
-rw-r--r--searx/settings.yml275
-rw-r--r--searx/settings_loader.py132
-rw-r--r--searx/settings_robot.yml9
-rw-r--r--searx/static/plugins/external_plugins/.gitignore3
-rw-r--r--searx/static/plugins/js/infinite_scroll.js2
-rw-r--r--searx/static/plugins/js/open_results_on_new_tab.js3
-rw-r--r--searx/static/themes/oscar/gruntfile.js1
-rw-r--r--searx/static/themes/oscar/js/searx.js18
-rw-r--r--searx/static/themes/oscar/js/searx.min.js.map1
-rw-r--r--searx/static/themes/oscar/js/searx_src/element_modifiers.js7
-rw-r--r--searx/static/themes/oscar/js/searx_src/infobox.js11
-rw-r--r--searx/static/themes/oscar/less/logicodev/infobox.less50
-rw-r--r--searx/static/themes/oscar/less/logicodev/results.less19
-rw-r--r--searx/static/themes/oscar/less/logicodev/search.less6
-rw-r--r--searx/static/themes/oscar/less/pointhi/infobox.less51
-rw-r--r--searx/static/themes/oscar/less/pointhi/search.less10
-rw-r--r--searx/static/themes/simple/js/searx_src/searx_search.js4
-rw-r--r--searx/templates/__common__/about.html23
-rw-r--r--searx/templates/__common__/opensearch.xml10
-rw-r--r--searx/templates/__common__/opensearch_response_rss.xml4
-rw-r--r--searx/templates/courgette/404.html2
-rw-r--r--searx/templates/courgette/base.html2
-rw-r--r--searx/templates/courgette/github_ribbon.html2
-rw-r--r--searx/templates/courgette/preferences.html16
-rw-r--r--searx/templates/courgette/results.html12
-rw-r--r--searx/templates/courgette/search.html4
-rw-r--r--searx/templates/legacy/404.html2
-rw-r--r--searx/templates/legacy/base.html4
-rw-r--r--searx/templates/legacy/github_ribbon.html2
-rw-r--r--searx/templates/legacy/infobox.html2
-rw-r--r--searx/templates/legacy/preferences.html16
-rw-r--r--searx/templates/legacy/result_templates/default.html7
-rw-r--r--searx/templates/legacy/results.html12
-rw-r--r--searx/templates/legacy/search.html2
-rw-r--r--searx/templates/oscar/404.html2
-rw-r--r--searx/templates/oscar/base.html2
-rw-r--r--searx/templates/oscar/infobox.html24
-rw-r--r--searx/templates/oscar/languages.html3
-rw-r--r--searx/templates/oscar/macros.html46
-rw-r--r--searx/templates/oscar/preferences.html59
-rw-r--r--searx/templates/oscar/result_templates/files.html55
-rw-r--r--searx/templates/oscar/result_templates/key-value.html2
-rw-r--r--searx/templates/oscar/result_templates/products.html22
-rw-r--r--searx/templates/oscar/results.html42
-rw-r--r--searx/templates/oscar/search.html4
-rw-r--r--searx/templates/oscar/search_full.html2
-rw-r--r--searx/templates/oscar/time-range.html3
-rw-r--r--searx/templates/pix-art/preferences.html8
-rw-r--r--searx/templates/pix-art/search.html2
-rw-r--r--searx/templates/simple/404.html2
-rw-r--r--searx/templates/simple/base.html6
-rw-r--r--searx/templates/simple/infobox.html3
-rw-r--r--searx/templates/simple/macros.html4
-rw-r--r--searx/templates/simple/preferences.html24
-rw-r--r--searx/templates/simple/results.html14
-rw-r--r--searx/templates/simple/search.html2
-rw-r--r--searx/testing.py4
-rw-r--r--searx/url_utils.py30
-rw-r--r--searx/utils.py568
-rw-r--r--searx/version.py2
-rw-r--r--searx/webadapter.py255
-rwxr-xr-xsearx/webapp.py300
-rw-r--r--searx/webutils.py145
-rw-r--r--setup.py2
-rw-r--r--tests/__init__.py2
-rw-r--r--tests/test_robot.py23
-rw-r--r--tests/unit/engines/test_command.py240
-rw-r--r--tests/unit/engines/test_xpath.py121
-rw-r--r--tests/unit/settings/empty_settings.yml0
-rw-r--r--tests/unit/settings/syntaxerror_settings.yml2
-rw-r--r--tests/unit/settings/user_settings.yml111
-rw-r--r--tests/unit/settings/user_settings_keep_only.yml14
-rw-r--r--tests/unit/settings/user_settings_remove.yml10
-rw-r--r--tests/unit/settings/user_settings_remove2.yml15
-rw-r--r--tests/unit/settings/user_settings_simple.yml9
-rw-r--r--tests/unit/test_answerers.py4
-rw-r--r--tests/unit/test_engines_init.py44
-rw-r--r--tests/unit/test_plugins.py74
-rw-r--r--tests/unit/test_poolrequests.py89
-rw-r--r--tests/unit/test_preferences.py54
-rw-r--r--tests/unit/test_query.py62
-rw-r--r--tests/unit/test_search.py108
-rw-r--r--tests/unit/test_settings_loader.py122
-rw-r--r--tests/unit/test_standalone_searx.py130
-rw-r--r--tests/unit/test_utils.py209
-rw-r--r--tests/unit/test_webadapter.py54
-rw-r--r--tests/unit/test_webapp.py45
-rw-r--r--tests/unit/test_webutils.py65
-rw-r--r--utils/brand.env6
-rw-r--r--utils/fabfile.py4
-rwxr-xr-xutils/fetch_ahmia_blacklist.py33
-rw-r--r--utils/fetch_currencies.py20
-rwxr-xr-xutils/fetch_firefox_version.py2
-rw-r--r--utils/fetch_languages.py207
-rw-r--r--utils/fetch_wikidata_units.py47
-rwxr-xr-xutils/filtron.sh2
-rwxr-xr-xutils/lib.sh56
-rw-r--r--utils/lxc-searx.env3
-rwxr-xr-xutils/lxc.sh7
-rw-r--r--utils/makefile.python12
-rw-r--r--utils/makefile.sphinx45
-rwxr-xr-xutils/morty.sh2
-rwxr-xr-xutils/searx.sh40
-rwxr-xr-xutils/standalone_searx.py254
-rw-r--r--utils/templates/etc/searx/use_default_settings.yml22
-rw-r--r--utils/templates/etc/uwsgi/apps-archlinux/searx.ini5
-rw-r--r--utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket5
-rw-r--r--utils/templates/etc/uwsgi/apps-available/searx.ini9
-rw-r--r--utils/templates/etc/uwsgi/apps-available/searx.ini:socket5
280 files changed, 27950 insertions, 4598 deletions
diff --git a/.config.sh b/.config.sh
index 4eff5f4..f9bac73 100644
--- a/.config.sh
+++ b/.config.sh
@@ -26,6 +26,7 @@ fi
# ---------
# SEARX_INTERNAL_URL="127.0.0.1:8888"
+# SEARX_SETTINGS_TEMPLATE="${REPO_ROOT}/utils/templates/etc/searx/use_default_settings.yml"
# Only change, if you maintain a searx brand in your searx fork.
# GIT_BRANCH="${GIT_BRANCH:-master}"
diff --git a/.coveragerc b/.coveragerc
index 4f50efc..8540bec 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,15 +1,6 @@
[run]
branch = True
-source =
- searx/engines
- searx/__init__.py
- searx/autocomplete.py
- searx/https_rewrite.py
- searx/languages.py
- searx/search.py
- searx/testing.py
- searx/utils.py
- searx/webapp.py
+source = searx
[report]
show_missing = True
diff --git a/.dockerignore b/.dockerignore
index 044460b..3d158c9 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -46,4 +46,11 @@ node_modules/
*/*/*/node_modules/
*/*/*/*/node_modules/
-.tx/ \ No newline at end of file
+.tx/
+
+#
+build/
+dist/
+local/
+gh-pages/
+searx.egg-info/
diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
new file mode 100644
index 0000000..6762783
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -0,0 +1,37 @@
+---
+name: Bug report
+about: Report a bug in Searx
+title: ''
+labels: bug
+assignees: ''
+
+---
+**Version of Searx, commit number if you are using on master branch and stipulate if you forked Searx**
+<!-- If you are running on master branch using git execute this command
+in order to fetch the latest commit ID:
+```
+git log -1
+```
+If you are using searx-docker then look at the bottom of the Searx page
+and check for the version after "Powered by searx"
+
+Please also stipulate if you are using a forked version of Searx and
+include a link to the fork source code.
+-->
+**How did you install Searx?**
+<!-- Did you install Searx using the official wiki or using searx-docker
+or manually by executing the searx/webapp.py file? -->
+**What happened?**
+<!-- A clear and concise description of what the bug is. -->
+
+**How To Reproduce**
+<!-- How can we reproduce this issue? (as minimally and as precisely as possible) -->
+
+**Expected behavior**
+<!-- A clear and concise description of what you expected to happen. -->
+
+**Screenshots & Logs**
+<!-- If applicable, add screenshots, logs to help explain your problem. -->
+
+**Additional context**
+<!-- Add any other context about the problem here. --> \ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..617d81d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,14 @@
+blank_issues_enabled: true
+contact_links:
+ - name: Installation issue using the official wiki
+ url: https://github.com/searx/searx/discussions/new?category_id=32001257
+ about: Ask for help if you are having some issue with installing Searx using the official wiki.
+ - name: Installation issue using searx-docker
+ url: https://github.com/searx/searx/discussions/new?category_id=32001259
+ about: Ask for help if you are having some issue with installing Searx using searx-docker.
+ - name: Installation issue without using the official wiki nor searx-docker
+ url: https://github.com/searx/searx/discussions/new?category_id=32001260
+ about: Ask for help if you are having some issue with installing Searx manually without using the wiki nor searx-docker.
+ - name: Report a bug on a public Searx instance
+ url: https://github.com/searx/searx/discussions/new?category_id=32001412
+ about: Report a bug that you discovered on a public Searx instance. \ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/engine-request.md b/.github/ISSUE_TEMPLATE/engine-request.md
new file mode 100644
index 0000000..1037004
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/engine-request.md
@@ -0,0 +1,29 @@
+---
+name: Engine request
+about: Request a new engine in Searx
+title: ''
+labels: enhancement, engine request
+assignees: ''
+
+---
+**Working URL to the engine**
+<!-- Please check if the engine is responding correctly before submitting it. -->
+
+**Why do you want to add this engine?**
+<!-- What's special about this engine? Is it open source or libre? -->
+
+**Features of this engine**
+<!-- Features of this engine: Doesn't track its users, fast, easy to integrate, ... -->
+
+**How can Searx fetch the information from this engine?**
+<!-- List API URL, example code (using the correct markdown) and more
+that could be useful for the developers in order to implement this engine.
+If you don't know what to write, let this part blank.>
+
+**Applicable category of this engine**
+<!-- Where should this new engine fit in Searx? Current categories in Searx:
+general, files, images, it, map, music, news, science, social media and videos.
+You can add multiple categories at the same time. -->
+
+**Additional context**
+<!-- Add any other context about this engine here. --> \ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md
new file mode 100644
index 0000000..3bb27fe
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request.md
@@ -0,0 +1,19 @@
+---
+name: Feature request
+about: Request a new feature in Searx
+title: ''
+labels: enhancement
+assignees: ''
+
+---
+**Is your feature request related to a problem? Please describe.**
+<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
+
+**Describe the solution you'd like**
+<!-- A clear and concise description of what you want to happen. -->
+
+**Describe alternatives you've considered**
+<!-- A clear and concise description of any alternative solutions or features you've considered. -->
+
+**Additional context**
+<!-- Add any other context or screenshots about the feature request here. --> \ No newline at end of file
diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
new file mode 100644
index 0000000..8821ec2
--- /dev/null
+++ b/.github/workflows/integration.yml
@@ -0,0 +1,110 @@
+name: Integration
+
+on: [push, pull_request]
+
+jobs:
+ python:
+ name: Python ${{ matrix.python-version }}
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ os: [ubuntu-latest]
+ python-version: [3.5, 3.6, 3.7, 3.8]
+ steps:
+ - name: Checkout 🛎️
+ uses: actions/checkout@v2
+ - name: Install Ubuntu packages 🧰
+ run: |
+ sudo ./utils/searx.sh install packages
+ sudo apt install firefox
+ - name: Set up Python 🧰
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ architecture: 'x64'
+ - name: Install Python dependencies 🧰
+ run: |
+ make V=1 install
+ make V=1 gecko.driver
+ - name: Run tests 🏗️
+ run: make V=1 test
+ - name: Test coverage 🗺️
+ run: make V=1 test.coverage
+ - name: Store coverage result 🗺️
+ uses: actions/upload-artifact@v2
+ with:
+ name: coverage-${{ matrix.python-version }}
+ path: coverage/
+ retention-days: 60
+
+ themes:
+ name: Themes
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout 🛎️
+ uses: actions/checkout@v2
+ - name: Install Ubuntu packages 🧰
+ run: sudo ./utils/searx.sh install packages
+ - name: Install node dependencies 🧰
+ run: make V=1 node.env
+ - name: Build themes 🏗️
+ run: make V=1 themes
+
+ documentation:
+ name: Documentation
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout 🛎️
+ uses: actions/checkout@v2
+ with:
+ persist-credentials: false
+ - name: Install Ubuntu packages 🧰
+ run: sudo ./utils/searx.sh install buildhost
+ - name: Set up Python 🧰
+ uses: actions/setup-python@v2
+ with:
+ python-version: '3.9'
+ architecture: 'x64'
+ - name: Build documentation 🏗️
+ run: SEARX_DEBUG=1 make V=1 travis-gh-pages
+ - name: Deploy 🚀
+ if: github.ref == 'refs/heads/master'
+ uses: JamesIves/github-pages-deploy-action@3.7.1
+ with:
+ GITHUB_TOKEN: ${{ github.token }}
+ BRANCH: gh-pages
+ FOLDER: gh-pages
+ CLEAN: true # Automatically remove deleted files from the deploy branch
+
+ dockers:
+ name: Docker
+ if: github.ref == 'refs/heads/master'
+ needs:
+ - python
+ - themes
+ - documentation
+ env:
+ DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout 🛎️
+ if: env.DOCKERHUB_USERNAME != null
+ uses: actions/checkout@v2
+ with:
+ # make sure "make docker.push" can get the git history
+ fetch-depth: '0'
+ - name: Set up QEMU 🧰
+ if: env.DOCKERHUB_USERNAME != null
+ uses: docker/setup-qemu-action@v1
+ - name: Set up Docker Buildx 🧰
+ if: env.DOCKERHUB_USERNAME != null
+ uses: docker/setup-buildx-action@v1
+ - name: Login to DockerHub 🔒
+ if: env.DOCKERHUB_USERNAME != null
+ uses: docker/login-action@v1
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ - name: Build and push 🐳
+ if: env.DOCKERHUB_USERNAME != null
+ run: make -e GIT_URL=$(git remote get-url origin) docker.push
diff --git a/.gitignore b/.gitignore
index e56a575..b1286ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ setup.cfg
*.pyc
*/*.pyc
*~
+*.swp
/node_modules
diff --git a/.pylintrc b/.pylintrc
index 3b4adb2..eb6d500 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -12,7 +12,7 @@
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
-extension-pkg-whitelist=
+extension-pkg-whitelist=lxml.etree
# Add files or directories to the blacklist. They should be base names, not
# paths.
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index f6fa9c9..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,57 +0,0 @@
-os: linux
-dist: bionic
-language: python
-cache:
- - directories:
- - $HOME/.cache/pip
-addons:
- firefox: "latest"
-
-install:
- - env
- - which python; python --version
- - make V=1 install
- - make V=1 gecko.driver
- - make V=1 node.env
- - make V=1 travis.codecov
-script:
- - make V=1 themes
- - make V=1 test
-after_success:
- - make V=1 test.coverage
- - codecov
-
-stages:
- - test
- - name: docker
- if: branch = master AND type != pull_request AND env(DOCKER_USERNAME) IS present
-
-jobs:
- include:
- - python: "2.7"
- env: PY=2
- - python: "3.5"
- - python: "3.6"
- - python: "3.7"
- - python: "3.8"
- - stage: docker
- python: "3.8"
- git:
- depth: false
- services:
- - docker
- addons: []
- before_install: true
- install: true
- script:
- - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- - make -e GIT_URL=$(git remote get-url origin) docker.push
- after_success: true
-
-notifications:
- irc:
- channels:
- - "irc.freenode.org#searx"
- template:
- - "%{repository}/#%{build_number}/%{branch} (%{author}): %{message} %{build_url}"
- on_success: change
diff --git a/AUTHORS.rst b/AUTHORS.rst
index fc2cacc..036ae0f 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -11,6 +11,7 @@ Major contributing authors:
- Marc Abonce Seguin @a01200356
- @pofilo
- Markus Heiser @return42
+- Émilien Devos @unixfox
People who have submitted patches/translates, reported bugs, consulted features or
generally made searx better:
@@ -96,7 +97,7 @@ generally made searx better:
- Alice Ferrazzi @aliceinwire
- @LiquidLemon
- @dadosch
-- @Venca24
+- Václav Zouzalík @Venca24
- @ZEROF
- Ivan Skytte Jørgensen @isj-privacore
- @miicha
@@ -110,7 +111,6 @@ generally made searx better:
- Nick Espig @nachtalb
- Rachmadani Haryono @rachmadaniHaryono
- Frank de Lange @yetangitu
-- Émilien Devos @unifox
- Nicolas Gelot @nfk
- @volth
- Mathieu Brunot @madmath03
@@ -133,3 +133,27 @@ generally made searx better:
- @gordon-quad
- Sophie Tauchert @999eagle
- @bauruine
+- Michael Ilsaas `<https://mikeri.net>`_
+- @renyhp
+- rachmadani haryono @rachmadaniHaryono
+- Mohamad Safadieh @msafadieh
+- @gardouille
+- @resynth1943
+- @Eliesemoule
+- @gardouille
+- @GazoilKerozen
+- Lukáš Kucharczyk @KucharczykL
+- Lynda Lopez @lyndalopez544
+- M. Efe Çetin @efectn
+- Nícholas Kegler @nicholasks
+- @pierrechtux
+- Scott Wallace @scottwallacesh
+- @Singustromo
+- @TheEvilSkeleton
+- @Wonderfall
+- @mrwormo
+- Xiaoyu WEI @xywei
+- @joshu9h
+
+
+
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index c665602..cbe4b6a 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,3 +1,133 @@
+0.18.0 2020.12.14
+=================
+
+Core
+~~~~
+
+- drop Python 2 support ( #2137 #2244 )
+- separate index and search routes ( #1681 ). ⚠️ add & remove your searx instance(s) from your browser.
+- add external_bang ( #2027 #2043 #2059 )
+- add external plugins supports ( #2074 )
+- add plugin converting strings into hash digests ( #1246 )
+- new category: Onions ( #565 )
+- allow searx query parts anywhere in the query ( commit aa3c18dda9329fff875328f6ba97483c417b149a 2aef38c3b9d1fe93e9d665a49b10151d63d92392 )
+- preferences: use base_url for prefix of sharing 'currenly saved preferences' (#1249 )
+- upgrade to request 2.24.0, pyopenssl is optional ( #2199 )
+- force admins to set secret_key if debug mode is disabled ( #2256 )
+- standalone searx update ( #1591 )
+- architecture clean up ( #2140 #2185 #2195 #2196 #2198 #2189 #2208 #2239 #2241 #2246 #2248 )
+- record detail about engine error ( #2332 #2375 #2350 ). Add a new API endpoint: ``/stats/errors``.
+- display if an engine does not support HTTPS requests ( #2373 )
+
+New settings.yml
+~~~~~~~~~~~~~~~~
+
+- ``use_default_settings``: user settings can relied on the default settings ( #2291 #2362 #2349 )
+- ``ui.results_on_new_tab: False`` - for opening result links in a new tab ( #2167 )
+- ``ui.advanced_search`` - add preference for displaying advanced settings ( #2327 )
+- ``server.method: "POST"`` - Make default query submission method configurable ( #2130 )
+- ``server.default_http_headers`` - add default http headers ( #2295 )
+- ``engines.*.proxies`` - Using proxy only for specific engines ( #1827 #2319 ), see https://searx.github.io/searx/dev/engine_overview.html#settings-yml
+- ``enabled_plugins`` - Enabled plugins ( a05c660e3036ad8d02072fc6731af54c2ed6151c )
+- ``preferences.lock`` - Let admins lock user preferences ( #2270 )
+
+Oscar theme
+~~~~~~~~~~~
+
+- update infobox ( #2131 )
+
+ - Make infoboxes shorter by default.
+ - Hide the main image by default as well and set a maximum height even when expanded.
+ - Add a toggle at the bottom of the infobox to expand it or to shrink it again.
+ - Fix pointhi style
+- query suggestion does not keep the language tag of the original query ( #1314 )
+- fix the clear button ( #2306 )
+
+Simple theme
+~~~~~~~~~~~~
+
+- Fix autocomplete ( #2205 )
+
+New engines
+~~~~~~~~~~~
+
+- ahmia, not_evil ( #565 )
+- codeberg ( #2104 )
+- command line engines: git grep, find, etc. ( #2128 #2250 )
+- elasticsearch ( #2292 )
+- metager ( #2139 )
+- naver ( #1912 )
+- opensemanticsearch ( #2271 )
+- peertube ( #2109 )
+- recoll (#2325)
+- sepiasearch ( #2227 )
+
+Updated engines
+~~~~~~~~~~~~~~~
+
+- digg ( #2285 )
+- dbpedia ( #2352 )
+- duckduckgo_definitions ( #2224 #2356 )
+- duden ( #2359 )
+- invidious ( #2116 )
+- libgen ( #2360 )
+- photon ( #2336 )
+- soundclound ( #2365 )
+- wikipedia ( #2178 #2363 #2354 )
+- wikidata ( #2151 #2224 #2353 ) - faster response time
+- yaCy ( #2255 ) - support HTTP digest authentication.
+- youtube_noapi ( #2364 )
+
+Fixed engines
+~~~~~~~~~~~~~
+
+- 1x ( #2361 )
+- answer 'random sha256' ( #2121 )
+- bing image ( #1496 )
+- duckduckgo ( #2254 )
+- genius ( #2371 )
+- google ( #2236 )
+- google image ( #2115 )
+- lobste.rs ( #2253 )
+- piratebay ( #2133 )
+- startpage ( #2385 )
+- torrentz ( #2101 )
+
+Removed engines
+~~~~~~~~~~~~~~~
+
+- filecrop ( #2352 )
+- searchcode_doc ( #2372 )
+- seedpeer ( #2366 )
+- twitter ( #2372 )
+- yggtorrent ( #2099 #2375 )
+
+Install scripts & documentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- install script & documentation ( #2384 #2380 #2362 #2287 #2283 #2277 #2223 #2211 #2118 #2117 #2063 )
+
+Docker image
+~~~~~~~~~~~~
+
+- use Alpine 3.12 ( #1983 )
+- uwsgi serves the static files directly. ( #1865 )
+- fix k8s support ( #2099 )
+- make docker produces clean tag version ( #2182 )
+
+Bug fixes
+~~~~~~~~~
+
+- searx.utils.HTMLTextExtractor: invalid HTML don't raise an Exception ( #2190 )
+- Fix static URL ( commit da8b227044f45127f705f6ea94a72d368eea73bb )
+- Fix autocomplete ( #2127 )
+- Fix opensearch.xml ( #2132 #2247 )
+- Fix documentation build ( #2237 )
+- Some fixes in the fetch languages script ( #2212 )
+
+Special thanks to `NLNet <https://nlnet.nl>`__ for sponsoring multiple features of this release.
+
+
0.17.0 2020.07.09
=================
@@ -13,12 +143,12 @@
- Wikivoyage
- Rubygems
- Engine fixes (google, google images, startpage, gigablast, yacy)
- - Private engines introduced - more details: https://asciimoo.github.io/searx/blog/private-engines.html
- - Greatly improved documentation - check it at https://asciimoo.github.io/searx
+ - Private engines introduced - more details: https://searx.github.io/searx/blog/private-engines.html
+ - Greatly improved documentation - check it at https://searx.github.io/searx
- Added autofocus to all search inputs
- CSP friendly oscar theme
- Added option to hide engine errors with `display_error_messages` engine option (true/false values, default is true)
- - Tons of accessibility fixes - see https://github.com/asciimoo/searx/issues/350 for details
+ - Tons of accessibility fixes - see https://github.com/searx/searx/issues/350 for details
- More flexible branding options: configurable vcs/issue tracker links
- Added "disable all" & "allow all" options to preferences engine select
- Autocomplete keyboard navigation fixes
@@ -142,8 +272,8 @@ News
- Bug fixes
- - https://github.com/asciimoo/searx/issues/1088
- - https://github.com/asciimoo/searx/issues/1089
+ - https://github.com/searx/searx/issues/1088
+ - https://github.com/searx/searx/issues/1089
- Dependency updates
@@ -312,7 +442,7 @@ News
News
~~~~
-New documentation page is available: https://asciimoo.github.io/searx
+New documentation page is available: https://searx.github.io/searx
0.8.0 2015.09.08
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..300349f
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,49 @@
+# How to contribute
+
+## Resources in the documentation
+
+* [Development quickstart](https://searx.github.io/searx/dev/contribution_guide.html)
+* [Contribution guide](https://searx.github.io/searx/dev/contribution_guide.html)
+
+## Submitting PRs
+
+Please follow the provided PR template when writing a description for your changes.
+
+Do not take criticism personally. When you get feedback, it is about your work,
+not your character, personality, etc. Keep in mind we all want to make the project better.
+
+When something is not clear, please ask questions to clear things up.
+
+If you would like to introduce a big architectural changes or do a refactoring
+either in the codebase or the development tools, please open an issue with a proposal
+first. This way we can think together about the problem and probably come up
+with a better solution.
+
+## Coding conventions and guidelines
+
+### Commit messages
+
+* Always write descriptive commit messages ("fix bug" is not acceptable).
+* Use the present tense ("Add feature" not "Added feature").
+* Use the imperative mood ("Move cursor to..." not "Moves cursor to...").
+* Limit the first line to 72 characters or less.
+* Include the number of the issue you are fixing.
+
+### Coding guidelines
+
+As a Python project, we must follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 20](https://www.python.org/dev/peps/pep-0020/) guidelines.
+
+Furthermore, follow the Clean code conventions. The most important
+in this project are the following rules:
+
+* Simpler is better. [KISS principle](https://en.wikipedia.org/wiki/KISS_principle)
+* Be consistent.
+* Every function must do one thing.
+* Use descriptive names for functions and variables.
+* Always look for the root cause.
+* Keep configurable data high level.
+* Avoid negative conditionals.
+* Prefer fewer arguments.
+* Do not add obvious comment to code.
+* Do not comment out code, just delete lines.
+
diff --git a/Dockerfile b/Dockerfile
index 59098e0..3894aa9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM alpine:3.10
+FROM alpine:3.12
ENTRYPOINT ["/sbin/tini","--","/usr/local/searx/dockerfiles/docker-entrypoint.sh"]
EXPOSE 8080
VOLUME /etc/searx
@@ -45,12 +45,14 @@ RUN apk upgrade --no-cache \
ca-certificates \
su-exec \
python3 \
+ py3-pip \
libxml2 \
libxslt \
openssl \
tini \
uwsgi \
uwsgi-python3 \
+ brotli \
&& pip3 install --upgrade pip \
&& pip3 install --no-cache -r requirements.txt \
&& apk del build-dependencies \
@@ -63,8 +65,10 @@ RUN su searx -c "/usr/bin/python3 -m compileall -q searx"; \
touch -c --date=@${TIMESTAMP_UWSGI} dockerfiles/uwsgi.ini; \
if [ ! -z $VERSION_GITCOMMIT ]; then\
echo "VERSION_STRING = VERSION_STRING + \"-$VERSION_GITCOMMIT\"" >> /usr/local/searx/searx/version.py; \
- fi
-
+ fi; \
+ find /usr/local/searx/searx/static -a \( -name '*.html' -o -name '*.css' -o -name '*.js' \
+ -o -name '*.svg' -o -name '*.ttf' -o -name '*.eot' \) \
+ -type f -exec gzip -9 -k {} \+ -exec brotli --best {} \+
# Keep this argument at the end since it change each time
ARG LABEL_DATE=
diff --git a/Makefile b/Makefile
index 2f10c62..4a873f0 100644
--- a/Makefile
+++ b/Makefile
@@ -2,10 +2,10 @@
.DEFAULT_GOAL=help
# START Makefile setup
-export GIT_URL=https://github.com/asciimoo/searx
+export GIT_URL=https://github.com/searx/searx
export GIT_BRANCH=master
export SEARX_URL=https://searx.me
-export DOCS_URL=https://asciimoo.github.io/searx
+export DOCS_URL=https://searx.github.io/searx
# END Makefile setup
include utils/makefile.include
@@ -13,6 +13,8 @@ include utils/makefile.include
PYOBJECTS = searx
DOC = docs
PY_SETUP_EXTRAS ?= \[test\]
+PYLINT_SEARX_DISABLE_OPTION := I,C,R,W0105,W0212,W0511,W0603,W0613,W0621,W0702,W0703,W1401
+PYLINT_ADDITIONAL_BUILTINS_FOR_ENGINES := supported_languages,language_aliases
include utils/makefile.python
include utils/makefile.sphinx
@@ -66,13 +68,10 @@ clean: pyclean docs-clean node.clean test.clean
PHONY += run
run: buildenv pyenvinstall
$(Q) ( \
- sed -i -e "s/debug : False/debug : True/g" ./searx/settings.yml ; \
sleep 2 ; \
xdg-open http://127.0.0.1:8888/ ; \
- sleep 3 ; \
- sed -i -e "s/debug : True/debug : False/g" ./searx/settings.yml ; \
) &
- $(PY_ENV)/bin/python ./searx/webapp.py
+ SEARX_DEBUG=1 $(PY_ENV)/bin/python ./searx/webapp.py
# docs
# ----
@@ -80,11 +79,11 @@ run: buildenv pyenvinstall
sphinx-doc-prebuilds:: buildenv pyenvinstall prebuild-includes
PHONY += docs
-docs: sphinx-doc-prebuilds sphinx-doc
+docs: sphinx-doc-prebuilds
$(call cmd,sphinx,html,docs,docs)
PHONY += docs-live
-docs-live: sphinx-doc-prebuilds sphinx-live
+docs-live: sphinx-doc-prebuilds
$(call cmd,sphinx_autobuild,html,docs,docs)
PHONY += prebuild-includes
@@ -121,14 +120,14 @@ buildenv:
$(Q)echo "build searx/brand.py"
$(Q)echo "GIT_URL = '$(GIT_URL)'" > searx/brand.py
$(Q)echo "GIT_BRANCH = '$(GIT_BRANCH)'" >> searx/brand.py
- $(Q)echo "ISSUE_URL = 'https://github.com/asciimoo/searx/issues'" >> searx/brand.py
+ $(Q)echo "ISSUE_URL = 'https://github.com/searx/searx/issues'" >> searx/brand.py
$(Q)echo "SEARX_URL = '$(SEARX_URL)'" >> searx/brand.py
$(Q)echo "DOCS_URL = '$(DOCS_URL)'" >> searx/brand.py
$(Q)echo "PUBLIC_INSTANCES = 'https://searx.space'" >> searx/brand.py
$(Q)echo "build utils/brand.env"
$(Q)echo "export GIT_URL='$(GIT_URL)'" > utils/brand.env
$(Q)echo "export GIT_BRANCH='$(GIT_BRANCH)'" >> utils/brand.env
- $(Q)echo "export ISSUE_URL='https://github.com/asciimoo/searx/issues'" >> utils/brand.env
+ $(Q)echo "export ISSUE_URL='https://github.com/searx/searx/issues'" >> utils/brand.env
$(Q)echo "export SEARX_URL='$(SEARX_URL)'" >> utils/brand.env
$(Q)echo "export DOCS_URL='$(DOCS_URL)'" >> utils/brand.env
$(Q)echo "export PUBLIC_INSTANCES='https://searx.space'" >> utils/brand.env
@@ -213,19 +212,25 @@ gecko.driver:
PHONY += test test.sh test.pylint test.pep8 test.unit test.coverage test.robot
test: buildenv test.pylint test.pep8 test.unit gecko.driver test.robot
-ifeq ($(PY),2)
-test.pylint:
- @echo "LINT skip liniting py2"
-else
-# TODO: balance linting with pylint
+PYLINT_FILES=\
+ searx/preferences.py \
+ searx/testing.py \
+ searx/engines/gigablast.py \
+ searx/engines/deviantart.py \
+ searx/engines/digg.py
test.pylint: pyenvinstall
+ $(call cmd,pylint,$(PYLINT_FILES))
$(call cmd,pylint,\
- searx/preferences.py \
- searx/testing.py \
- searx/engines/gigablast.py \
+ --disable=$(PYLINT_SEARX_DISABLE_OPTION) \
+ --additional-builtins=$(PYLINT_ADDITIONAL_BUILTINS_FOR_ENGINES) \
+ searx/engines \
+ )
+ $(call cmd,pylint,\
+ --disable=$(PYLINT_SEARX_DISABLE_OPTION) \
+ --ignore=searx/engines \
+ searx tests \
)
-endif
# ignored rules:
# E402 module level import not at top of file
@@ -243,8 +248,9 @@ test.sh:
shellcheck -x .config.sh
test.pep8: pyenvinstall
- @echo "TEST pep8"
- $(Q)$(PY_ENV_ACT); pep8 --exclude='searx/static, searx/engines/gigablast.py' --max-line-length=120 --ignore "E402,W503" searx tests
+ @echo "TEST pycodestyle (formerly pep8)"
+ $(Q)$(PY_ENV_ACT); pycodestyle --exclude='searx/static, searx/languages.py, $(foreach f,$(PYLINT_FILES),$(f),)' \
+ --max-line-length=120 --ignore "E117,E252,E402,E722,E741,W503,W504,W605" searx tests
test.unit: pyenvinstall
@echo "TEST tests/unit"
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..a7ad130
--- /dev/null
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,25 @@
+## What does this PR do?
+
+<!-- MANDATORY -->
+
+<!-- explain the changes in your PR, algorithms, design, architecture -->
+
+## Why is this change important?
+
+<!-- MANDATORY -->
+
+<!-- explain the motivation behind your PR -->
+
+## How to test this PR locally?
+
+<!-- commands to run the tests or instructions to test the changes-->
+
+## Author's checklist
+
+<!-- additional notes for reviewiers -->
+
+## Related issues
+
+<!--
+Closes #234
+-->
diff --git a/README.rst b/README.rst
index 55deb67..318def9 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,7 @@
.. SPDX-License-Identifier: AGPL-3.0-or-later
-.. figure:: https://raw.githubusercontent.com/asciimoo/searx/master/searx/static/themes/oscar/img/logo_searx_a.png
- :target: https://asciimoo.github.io/searx/
+.. figure:: https://raw.githubusercontent.com/searx/searx/master/searx/static/themes/oscar/img/logo_searx_a.png
+ :target: https://searx.github.io/searx/
:alt: searX
:width: 100%
:align: center
@@ -22,25 +22,25 @@ Privacy-respecting, hackable `metasearch engine`_ / *pronunciation* **səːks**.
.. _metasearch engine: https://en.wikipedia.org/wiki/Metasearch_engine
.. |searx install| image:: https://img.shields.io/badge/-install-blue
- :target: https://asciimoo.github.io/searx/admin/installation.html
+ :target: https://searx.github.io/searx/admin/installation.html
.. |searx homepage| image:: https://img.shields.io/badge/-homepage-blue
- :target: https://asciimoo.github.io/searx
+ :target: https://searx.github.io/searx
.. |searx wiki| image:: https://img.shields.io/badge/-wiki-blue
- :target: https://github.com/asciimoo/searx/wiki
+ :target: https://github.com/searx/searx/wiki
.. |AGPL License| image:: https://img.shields.io/badge/license-AGPL-blue.svg
- :target: https://github.com/asciimoo/searx/blob/master/LICENSE
+ :target: https://github.com/searx/searx/blob/master/LICENSE
-.. |Issues| image:: https://img.shields.io/github/issues/asciimoo/searx?color=yellow&label=issues
- :target: https://github.com/asciimoo/searx/issues
+.. |Issues| image:: https://img.shields.io/github/issues/searx/searx?color=yellow&label=issues
+ :target: https://github.com/searx/searx/issues
-.. |PR| image:: https://img.shields.io/github/issues-pr-raw/asciimoo/searx?color=yellow&label=PR
- :target: https://github.com/asciimoo/searx/pulls
+.. |PR| image:: https://img.shields.io/github/issues-pr-raw/searx/searx?color=yellow&label=PR
+ :target: https://github.com/searx/searx/pulls
-.. |commits| image:: https://img.shields.io/github/commit-activity/y/asciimoo/searx?color=yellow&label=commits
- :target: https://github.com/asciimoo/searx/commits/master
+.. |commits| image:: https://img.shields.io/github/commit-activity/y/searx/searx?color=yellow&label=commits
+ :target: https://github.com/searx/searx/commits/master
.. |OpenCollective searx backers| image:: https://opencollective.com/searx/backers/badge.svg
:target: https://opencollective.com/searx#backer
@@ -55,10 +55,10 @@ Otherwise jump to the user_, admin_ and developer_ handbooks you will find on
our homepage_.
.. _searx.space: https://searx.space
-.. _user: https://asciimoo.github.io/searx/user
-.. _admin: https://asciimoo.github.io/searx/admin
-.. _developer: https://asciimoo.github.io/searx/dev
-.. _homepage: https://asciimoo.github.io/searx
+.. _user: https://searx.github.io/searx/user
+.. _admin: https://searx.github.io/searx/admin
+.. _developer: https://searx.github.io/searx/dev
+.. _homepage: https://searx.github.io/searx
contact:
openhub_ // twitter_ // IRC: #searx @ freenode
diff --git a/dockerfiles/docker-entrypoint.sh b/dockerfiles/docker-entrypoint.sh
index 8b4c348..accc015 100755
--- a/dockerfiles/docker-entrypoint.sh
+++ b/dockerfiles/docker-entrypoint.sh
@@ -1,15 +1,43 @@
#!/bin/sh
-export SEARX_VERSION=$(su searx -c 'python3 -c "import six; import searx.version; six.print_(searx.version.VERSION_STRING)"')
-printf 'searx version %s\n\n' "${SEARX_VERSION}"
+help() {
+ printf "Command line:\n\n"
+ printf " -h Display this help\n"
+ printf " -d Dry run to update the configuration files.\n"
+ printf " -f Always update on the configuration files (existing files are renamed with the .old suffix)\n"
+ printf " Without this option, the new configuration files are copied with the .new suffix\n"
+ printf "\nEnvironment variables:\n\n"
+ printf " INSTANCE_NAME settings.yml : general.instance_name\n"
+ printf " AUTOCOMPLETE settings.yml : search.autocomplete\n"
+ printf " BASE_URL settings.yml : server.base_url\n"
+ printf " MORTY_URL settings.yml : result_proxy.url\n"
+ printf " MORTY_KEY settings.yml : result_proxy.key\n"
+ printf " BIND_ADDRESS uwsgi bind to the specified TCP socket using HTTP protocol. Default value: \"${DEFAULT_BIND_ADDRESS}\"\n"
+ printf "\nVolume:\n\n"
+ printf " /etc/searx the docker entry point copies settings.yml and uwsgi.ini in this directory (see the -f command line option)\n"
+ echo
+ exit 0
+}
+
+export DEFAULT_BIND_ADDRESS="0.0.0.0:8080"
+if [ -z "${BIND_ADDRESS}" ]; then
+ export BIND_ADDRESS="${DEFAULT_BIND_ADDRESS}"
+fi
export UWSGI_SETTINGS_PATH=/etc/searx/uwsgi.ini
export SEARX_SETTINGS_PATH=/etc/searx/settings.yml
-if [ -z "${BIND_ADDRESS}" ]; then
- export BIND_ADDRESS=":8080"
+# Parse special command line
+# see docs/admin/installation-docker.rst
+# display the help message without the version
+if [ "$1" = "help" ]; then
+ help
fi
+# Version
+export SEARX_VERSION=$(su searx -c 'python3 -c "import six; import searx.version; six.print_(searx.version.VERSION_STRING)"' 2>/dev/null)
+printf 'searx version %s\n\n' "${SEARX_VERSION}"
+
# Parse command line
FORCE_CONF_UPDATE=0
DRY_RUN=0
@@ -23,19 +51,7 @@ do
DRY_RUN=1
;;
h)
- printf "Command line:\n\n"
- printf " -h Display this help\n"
- printf " -d Dry run to update the configuration files.\n"
- printf " -f Always update on the configuration files (existing files are renamed with the .old suffix)\n"
- printf " Without this option, new configuration files are copied with the .new suffix\n"
- printf "\nEnvironment variables:\n\n"
- printf " INSTANCE_NAME settings.yml : general.instance_name\n"
- printf " AUTOCOMPLETE settings.yml : search.autocomplete\n"
- printf " BASE_URL settings.yml : server.base_url\n"
- printf " MORTY_URL settings.yml : result_proxy.url\n"
- printf " MORTY_KEY settings.yml : result_proxy.key\n"
- printf " BIND_ADDRESS where uwsgi will accept HTTP request (format : host:port)\n"
- exit 0
+ help
esac
done
@@ -50,7 +66,7 @@ patch_searx_settings() {
CONF="$1"
# Make sure that there is trailing slash at the end of BASE_URL
- # see http://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion
+ # see https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Shell-Parameter-Expansion
export BASE_URL="${BASE_URL%/}/"
# update settings.yml
diff --git a/dockerfiles/uwsgi.ini b/dockerfiles/uwsgi.ini
index ecc4b39..398a440 100644
--- a/dockerfiles/uwsgi.ini
+++ b/dockerfiles/uwsgi.ini
@@ -35,3 +35,10 @@ logto = /var/log/uwsgi/uwsgi.log
# No keep alive
# See https://github.com/searx/searx-docker/issues/24
add-header = Connection: close
+
+# uwsgi serves the static files
+# expires set to one day as Flask does
+static-map = /static=/usr/local/searx/searx/static
+static-expires = /* 864000
+static-gzip-all = True
+offload-threads = %k
diff --git a/docs/admin/arch_public.dot b/docs/admin/arch_public.dot
index 5018225..0d82607 100644
--- a/docs/admin/arch_public.dot
+++ b/docs/admin/arch_public.dot
@@ -4,11 +4,11 @@ digraph G {
edge [fontname="Sans"];
browser [label="Browser", shape=Mdiamond];
- rp [label="Reverse Proxy", href="https://asciimoo.github.io/searx/utils/filtron.sh.html#public-reverse-proxy"];
- filtron [label="Filtron", href="https://asciimoo.github.io/searx/utils/filtron.sh.html"];
- morty [label="Morty", href="https://asciimoo.github.io/searx/utils/morty.sh.html"];
+ rp [label="Reverse Proxy", href="https://searx.github.io/searx/utils/filtron.sh.html#public-reverse-proxy"];
+ filtron [label="Filtron", href="https://searx.github.io/searx/utils/filtron.sh.html"];
+ morty [label="Morty", href="https://searx.github.io/searx/utils/morty.sh.html"];
static [label="Static files", href="url to configure static files"];
- uwsgi [label="uwsgi", href="https://asciimoo.github.io/searx/utils/searx.sh.html"]
+ uwsgi [label="uwsgi", href="https://searx.github.io/searx/utils/searx.sh.html"]
searx1 [label="Searx #1"];
searx2 [label="Searx #2"];
searx3 [label="Searx #3"];
diff --git a/docs/admin/buildhosts.rst b/docs/admin/buildhosts.rst
index a727d25..1f6eb47 100644
--- a/docs/admin/buildhosts.rst
+++ b/docs/admin/buildhosts.rst
@@ -67,7 +67,7 @@ to ``imgmath``:
If your docs build (``make docs``) shows warnings like this::
WARNING: dot(1) not found, for better output quality install \
- graphviz from http://www.graphviz.org
+ graphviz from https://www.graphviz.org
..
WARNING: LaTeX command 'latex' cannot be run (needed for math \
display), check the imgmath_latex setting
diff --git a/docs/admin/engines.rst b/docs/admin/engines.rst
index 4d1872d..f1ac036 100644
--- a/docs/admin/engines.rst
+++ b/docs/admin/engines.rst
@@ -1,14 +1,28 @@
-.. _engines generic:
-
=======
Engines
=======
+Special Engine Settings
+=======================
+
.. sidebar:: Further reading ..
- :ref:`settings engine`
- - :ref:`engine settings`
- - :ref:`engine file`
+ - :ref:`engine settings` & :ref:`engine file`
+
+.. toctree::
+ :maxdepth: 1
+
+ engines/recoll.rst
+
+
+.. _engines generic:
+
+General Engine Settings
+=======================
+
+Explanation of the :ref:`general engine configuration` shown in the table
+:ref:`configured engines`.
============= =========== ==================== ============
:ref:`engine settings` :ref:`engine file`
@@ -30,8 +44,6 @@ Disabled **D**
Show errors **DE**
============= =========== =================================
-Configuration defaults (at built time):
-
.. _configured engines:
.. jinja:: webapp
@@ -73,3 +85,4 @@ Configuration defaults (at built time):
- {{(mod.display_error_messages and "y") or ""}}
{% endfor %}
+
diff --git a/docs/admin/engines/recoll.rst b/docs/admin/engines/recoll.rst
new file mode 100644
index 0000000..cba2e81
--- /dev/null
+++ b/docs/admin/engines/recoll.rst
@@ -0,0 +1,50 @@
+.. _engine recoll:
+
+======
+Recoll
+======
+
+.. sidebar:: info
+
+ - `Recoll <https://www.lesbonscomptes.com/recoll/>`_
+ - `recoll-webui <https://framagit.org/medoc92/recollwebui.git>`_
+
+Recoll_ is a desktop full-text search tool based on Xapian. By itself Recoll_
+does not offer web or API access, this can be achieved using recoll-webui_
+
+
+
+Configuration
+=============
+
+You must configure the following settings:
+
+``base_url``:
+ Location where recoll-webui can be reached.
+
+``mount_prefix``:
+ Location where the file hierarchy is mounted on your *local* filesystem.
+
+``dl_prefix``:
+ Location where the file hierarchy as indexed by recoll can be reached.
+
+``search_dir``:
+ Part of the indexed file hierarchy to be search, if empty the full domain is
+ searched.
+
+
+Example
+=======
+
+Scenario:
+
+#. Recoll indexes a local filesystem mounted in ``/export/documents/reference``,
+#. the Recoll search inteface can be reached at https://recoll.example.org/ and
+#. the contents of this filesystem can be reached though https://download.example.org/reference
+
+.. code:: yaml
+
+ base_url: https://recoll.example.org/
+ mount_prefix: /export/documents
+ dl_prefix: https://download.example.org
+ search_dir: ''
diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 93e430b..503a4d5 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -64,7 +64,7 @@ of:
"Param:q",
"Path=^(/|/search)$"
],
- "interval": "<time-interval-in-sec (int)>"
+ "interval": "<time-interval-in-sec (int)>",
"limit": "<max-request-number-in-interval (int)>",
"subrules": [
{
@@ -91,7 +91,7 @@ of:
},
{
"name": "IP limit",
- "interval": "<time-interval-in-sec (int)>"
+ "interval": "<time-interval-in-sec (int)>",
"limit": "<max-request-number-in-interval (int)>",
"stop": true,
"aggregations": [
@@ -111,7 +111,7 @@ of:
"filters": [
"Param:format=(csv|json|rss)"
],
- "interval": "<time-interval-in-sec (int)>"
+ "interval": "<time-interval-in-sec (int)>",
"limit": "<max-request-number-in-interval (int)>",
"stop": true,
"actions": [
@@ -125,7 +125,7 @@ of:
},
{
"name": "useragent limit",
- "interval": "<time-interval-in-sec (int)>"
+ "interval": "<time-interval-in-sec (int)>",
"limit": "<max-request-number-in-interval (int)>",
"aggregations": [
"Header:User-Agent"
diff --git a/docs/admin/installation-docker.rst b/docs/admin/installation-docker.rst
index 340e663..c5f54b4 100644
--- a/docs/admin/installation-docker.rst
+++ b/docs/admin/installation-docker.rst
@@ -9,20 +9,52 @@ Docker installation
:local:
:backlinks: entry
-Make sure you have installed Docker. For instance, you can deploy searx like this:
+----
+
+Docker image searx/searx
+========================
+
+
+The docker image is `searx/searx <https://hub.docker.com/r/searx/searx>`_ (based on `github.com/searx/searx <https://github.com/searx/searx>`_).
+
+Make sure you have `installed Docker <https://docs.docker.com/get-docker/>`_. For instance, you can deploy a local instance:
.. code:: sh
- docker pull wonderfall/searx
- docker run -d --name searx -p $PORT:8888 wonderfall/searx
+ export PORT=80
+ docker pull searx/searx
+ docker run --rm -d -v ${PWD}/searx:/etc/searx -p $PORT:8080 -e BASE_URL=http://localhost:$PORT/ searx/searx
Go to ``http://localhost:$PORT``.
-See https://hub.docker.com/r/wonderfall/searx/ for more informations. It's also
-possible to build searx from the embedded Dockerfile.
+Inside ``${PWD}/searx``, you will find ``settings.yml`` and ``uwsgi.ini``.
+You can modify these files according to your needs and restart the Docker image.
+
+
+Command line
+------------
+
.. code:: sh
- git clone https://github.com/asciimoo/searx.git
+ docker run --rm -it searx/searx -h
+
+.. program-output:: ../dockerfiles/docker-entrypoint.sh help
+
+
+Build the image
+---------------
+
+It's also possible to build searx from the embedded Dockerfile.
+
+.. code:: sh
+
+ git clone https://github.com/searx/searx.git
cd searx
- docker build -t whatever/searx .
+ make docker
+
+
+Public instance
+===============
+
+If you intend to create a public instance using Docker, see https://github.com/searx/searx-docker
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index 5e32d96..589c40a 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -9,7 +9,7 @@ Install with nginx
.. _nginx server configuration:
https://docs.nginx.com/nginx/admin-guide/web-server/web-server/#setting-up-virtual-servers
.. _nginx beginners guide:
- http://nginx.org/en/docs/beginners_guide.html
+ https://nginx.org/en/docs/beginners_guide.html
.. _Getting Started wiki:
https://www.nginx.com/resources/wiki/start/
.. _uWSGI support from nginx:
@@ -189,7 +189,7 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
}
location /searx/static {
- /usr/local/searx/searx-src/searx/static;
+ alias /usr/local/searx/searx-src/searx/static;
}
diff --git a/docs/admin/installation-searx.rst b/docs/admin/installation-searx.rst
index f1d4860..3f8904a 100644
--- a/docs/admin/installation-searx.rst
+++ b/docs/admin/installation-searx.rst
@@ -52,7 +52,7 @@ In the same shell create *virtualenv*:
:end-before: END create virtualenv
To install searx's dependencies, exit the searx *bash* session you opened above
-and restart a new. Before install, first check if your *virualenv* was sourced
+and restart a new. Before install, first check if your *virtualenv* was sourced
from the login (*~/.profile*):
.. kernel-include:: $DOCS_BUILD/includes/searx.rst
@@ -64,17 +64,38 @@ from the login (*~/.profile*):
Open a second terminal for the configuration tasks and left the ``(searx)$``
terminal open for the tasks below.
+
+.. _use_default_settings.yml:
+
Configuration
-==============
+=============
+
+To create a initial ``/etc/searx/settings.yml`` you can start with a copy of the
+file :origin:`utils/templates/etc/searx/use_default_settings.yml`. This setup
+:option:ref:`use default settings <settings use_default_settings>` from
+:origin:`searx/settings.yml` and is recommended since :pull:`2291` is merged.
-Create a copy of the :origin:`searx/settings.yml` configuration file in system's
-*/etc* folder. Configure like shown below -- replace ``searx@\$(uname -n)`` with
-a name of your choice -- *and/or* edit ``/etc/searx/settings.yml`` if necessary.
+For minimal Setup, configure like shown below – replace ``searx@\$(uname -n)``
+with a name of your choice, set ``ultrasecretkey`` -- *and/or* edit
+``/etc/searx/settings.yml`` to your needs.
.. kernel-include:: $DOCS_BUILD/includes/searx.rst
:start-after: START searx config
:end-before: END searx config
+.. tabs::
+
+ .. group-tab:: Use default settings
+
+ .. literalinclude:: ../../utils/templates/etc/searx/use_default_settings.yml
+ :language: yaml
+
+ .. group-tab:: searx/settings.yml
+
+ .. literalinclude:: ../../searx/settings.yml
+ :language: yaml
+
+
Check
=====
diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index 167c300..4a301ec 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -33,10 +33,17 @@ The following will install a setup as shown in :ref:`architecture`. First you
need to get a clone. The clone is only needed for the installation procedure
and some maintenance tasks (alternatively you can create your own fork).
+For the installation procedure, use a *sudoer* login to run the scripts. If you
+install from ``root``, take into account that the scripts are creating a
+``searx``, a ``filtron`` and a ``morty`` user. In the installation procedure
+these new created users do need read access to the clone of searx, which is not
+the case if you clone into a folder below ``/root``.
+
+
.. code:: bash
$ cd ~/Downloads
- $ git clone https://github.com/asciimoo/searx searx
+ $ git clone https://github.com/searx/searx searx
$ cd searx
**Install** :ref:`searx service <searx.sh>`
@@ -64,3 +71,11 @@ If all services are running fine, you can add it to your HTTP server:
- :ref:`installation apache`
- :ref:`installation nginx`
+.. _git stash: https://git-scm.com/docs/git-stash
+
+.. tip::
+
+ About script's installation options have a look at chapter :ref:`toolboxing
+ setup`. How to brand your instance see chapter :ref:`makefile setup`. To
+ *stash* your instance's setup, `git stash`_ your clone's :origin:`Makefile`
+ and :origin:`.config.sh` file .
diff --git a/docs/admin/settings.rst b/docs/admin/settings.rst
index 8b1cb8d..985c16f 100644
--- a/docs/admin/settings.rst
+++ b/docs/admin/settings.rst
@@ -9,6 +9,7 @@ file.
.. sidebar:: Further reading ..
+ - :ref:`use_default_settings.yml`
- :ref:`search API`
.. contents:: Contents
@@ -16,6 +17,19 @@ file.
:local:
:backlinks: entry
+.. _settings location:
+
+settings.yml location
+=====================
+
+First, searx will try to load settings.yml from these locations:
+
+1. the full path specified in the ``SEARX_SETTINGS_PATH`` environment variable.
+2. ``/etc/searx/settings.yml``
+
+If these files don't exist (or are empty or can't be read), searx uses the :origin:`searx/settings.yml` file.
+
+
.. _settings global:
Global Settings
@@ -23,97 +37,123 @@ Global Settings
.. code:: yaml
+ general:
+ debug : False # Debug mode, only for development
+ instance_name : "searx" # displayed name
+
+``debug`` :
+ Allow a more detailed log if you run searx directly. Display *detailed* error
+ messages in the browser too, so this must be deactivated in production.
+
+.. code:: yaml
+
server:
port : 8888
- secret_key : "ultrasecretkey" # change this!
- debug : False # debug mode, only for development
- request_timeout : 2.0 # seconds
- base_url : False # set custom base_url (or False)
- themes_path : "" # custom ui themes path
- default_theme : oscar # ui theme
- useragent_suffix : "" # suffix of searx_useragent, could contain
- # informations like admins email address
- image_proxy : False # proxying image results through searx
- default_locale : "" # default interface locale
+ bind_address : "127.0.0.1" # address to listen on
+ secret_key : "ultrasecretkey" # change this!
+ base_url : False # set custom base_url (or False)
+ image_proxy : False # proxying image results through searx
+ default_locale : "" # default interface locale
+ default_theme : oscar # ui theme
+ default_http_headers:
+ X-Content-Type-Options : nosniff
+ X-XSS-Protection : 1; mode=block
+ X-Download-Options : noopen
+ X-Robots-Tag : noindex, nofollow
+ Referrer-Policy : no-referrer
+
+``port`` & ``bind_address``:
+ Port number and *bind address* of the searx web application if you run it
+ directly using ``python searx/webapp.py``. Doesn't apply to searx running on
+ Apache or Nginx.
- # uncomment below section if you want to use a proxy
+``secret_key`` :
+ Used for cryptography purpose.
- #outgoing_proxies :
- # http : http://127.0.0.1:8080
- # https: http://127.0.0.1:8080
+``base_url`` :
+ The base URL where searx is deployed. Used to create correct inbound links.
- # uncomment below section only if you have more than one network interface
- # which can be the source of outgoing search requests
+``image_proxy`` :
+ Allow your instance of searx of being able to proxy images. Uses memory space.
+
+``default_locale`` :
+ Searx interface language. If blank, the locale is detected by using the
+ browser language. If it doesn't work, or you are deploying a language
+ specific instance of searx, a locale can be defined using an ISO language
+ code, like ``fr``, ``en``, ``de``.
- #source_ips:
- # - 1.1.1.1
- # - 1.1.1.2
+``default_theme`` :
+ Name of the theme you want to use by default on your searx instance.
- locales:
- en : English
- de : Deutsch
- he : Hebrew
- hu : Magyar
- fr : Français
- es : Español
- it : Italiano
- nl : Nederlands
- ja : 日本語 (Japanese)
- tr : Türkçe
- ru : Russian
- ro : Romanian
+.. _HTTP headers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
+``default_http_headers``:
+ Set additional HTTP headers, see `#755 <https://github.com/searx/searx/issues/715>`__
-``port`` :
- Port number of the searx web application if you run it directly using ``python
- searx/webapp.py``. Doesn't apply to searx running on Apache or Nginx.
-``secret_key`` :
- Used for cryptography purpose.
+.. code:: yaml
+
+ outgoing: # communication with search engines
+ request_timeout : 2.0 # default timeout in seconds, can be override by engine
+ # max_request_timeout: 10.0 # the maximum timeout in seconds
+ useragent_suffix : "" # informations like an email address to the administrator
+ pool_connections : 100 # Number of different hosts
+ pool_maxsize : 10 # Number of simultaneous requests by host
+ # uncomment below section if you want to use a proxy
+ # proxies:
+ # http:
+ # - http://proxy1:8080
+ # - http://proxy2:8080
+ # https:
+ # - http://proxy1:8080
+ # - http://proxy2:8080
+ # uncomment below section only if you have more than one network interface
+ # which can be the source of outgoing search requests
+ # source_ips:
+ # - 1.1.1.1
+ # - 1.1.1.2
-``debug`` :
- Allow a more detailed log if you run searx directly. Display *detailed* error
- messages in the browser too, so this must be deactivated in production.
``request_timeout`` :
- Global timeout of the requests made to others engines in seconds. A bigger
+ Global timeout of the requests made to others engines in seconds. A bigger
timeout will allow to wait for answers from slow engines, but in consequence
will slow searx reactivity (the result page may take the time specified in the
- timeout to load)
-
-``base_url`` :
- The base URL where searx is deployed. Used to create correct inbound links.
-
-``themes_path`` :
- Path to where the themes are located. If you didn't develop anything, leave it
- blank.
-
-``default_theme`` :
- Name of the theme you want to use by default on you searx instance.
+ timeout to load). Can be override by :ref:`settings engine`
``useragent_suffix`` :
Suffix to the user-agent searx uses to send requests to others engines. If an
engine wish to block you, a contact info here may be useful to avoid that.
-``image_proxy`` :
- Allow your instance of searx of being able to proxy images. Uses memory space.
+.. _requests proxies: https://requests.readthedocs.io/en/latest/user/advanced/#proxies
+.. _PySocks: https://pypi.org/project/PySocks/
-``default_locale`` :
- Aearx interface language. If blank, the locale is detected by using the
- browser language. If it doesn't work, or you are deploying a language
- specific instance of searx, a locale can be defined using an ISO language
- code, like ``fr``, ``en``, ``de``.
+``proxies`` :
+ Define one or more proxies you wish to use, see `requests proxies`_.
+ If there are more than one proxy for one protocol (http, https),
+ requests to the engines are distributed in a round-robin fashion.
-.. _requests proxies: http://requests.readthedocs.io/en/latest/user/advanced/#proxies
-.. _PR SOCKS support: https://github.com/kennethreitz/requests/pull/478
-
-``outgoing_proxies`` :
- Define a proxy you wish to use, see `requests proxies`_. SOCKS proxies are
- not supported / see `PR SOCKS support`.
+ - Proxy: `see <https://2.python-requests.org/en/latest/user/advanced/#proxies>`__.
+ - SOCKS proxies are also supported: `see <https://2.python-requests.org/en/latest/user/advanced/#socks>`__
``source_ips`` :
- If you use multiple nework interfaces, define from which IP the requests must
- be made.
+ If you use multiple network interfaces, define from which IP the requests must
+ be made. This parameter is ignored when ``proxies`` is set.
+
+.. code:: yaml
+
+ locales:
+ en : English
+ de : Deutsch
+ he : Hebrew
+ hu : Magyar
+ fr : Français
+ es : Español
+ it : Italiano
+ nl : Nederlands
+ ja : 日本語 (Japanese)
+ tr : Türkçe
+ ru : Russian
+ ro : Romanian
``locales`` :
Locales codes and their names. Available translations of searx interface.
@@ -139,9 +179,18 @@ Engine settings
api_key : 'apikey'
disabled : True
language : en_US
+ #proxies:
+ # http:
+ # - http://proxy1:8080
+ # - http://proxy2:8080
+ # https:
+ # - http://proxy1:8080
+ # - http://proxy2:8080
+ # - socks5://user:password@proxy3:1080
+ # - socks5h://user:password@proxy4:1080
``name`` :
- Name that will be used accross searx to define this engine. In settings, on
+ Name that will be used across searx to define this engine. In settings, on
the result page...
``engine`` :
@@ -152,7 +201,7 @@ Engine settings
Code used to execute bang requests (in this case using ``!bi`` or ``?bi``)
``base_url`` : optional
- Part of the URL that should be stable accross every request. Can be useful to
+ Part of the URL that should be stable across every request. Can be useful to
use multiple sites using only one engine, or updating the site URL without
touching at the code.
@@ -170,7 +219,7 @@ Engine settings
is described in the file.
``disabled`` : optional
- To disable by default the engine, but not deleting it. It will allow the user
+ To disable by default the engine, but not deleting it. It will allow the user
to manually activate it in the settings.
``language`` : optional
@@ -188,3 +237,68 @@ Engine settings
A few more options are possible, but they are pretty specific to some
engines, and so won't be described here.
+
+
+.. _settings use_default_settings:
+
+use_default_settings
+====================
+
+.. note::
+
+ If searx is cloned from a git repository, most probably there is no need to have an user settings.
+
+The user defined settings.yml can relied on the default configuration :origin:`searx/settings.yml` using ``use_default_settings: True``.
+
+In the following example, the actual settings are the default settings defined in :origin:`searx/settings.yml` with the exception of the ``secret_key`` and the ``bind_address``:
+
+.. code-block:: yaml
+
+ use_default_settings: True
+ server:
+ secret_key: "uvys6bRhKHUdFF5CqbJonSDSRN8H0sCBziNSrDGNVdpz7IeZhveVart3yvghoKHA"
+ bind_address: "0.0.0.0"
+
+With ``use_default_settings: True``, each settings can be override in a similar way, the ``engines`` section is merged according to the engine ``name``.
+
+In this example, searx will load all the engine and the arch linux wiki engine has a :ref:`token<private engines>`:
+
+.. code-block:: yaml
+
+ use_default_settings: True
+ server:
+ secret_key: "uvys6bRhKHUdFF5CqbJonSDSRN8H0sCBziNSrDGNVdpz7IeZhveVart3yvghoKHA"
+ engines:
+ - name: arch linux wiki
+ tokens: ['$ecretValue']
+
+It is possible to remove some engines from the default settings. The following example is similar to the above one, but searx doesn't load the the google engine:
+
+.. code-block:: yaml
+
+ use_default_settings:
+ engines:
+ remove:
+ - google
+ server:
+ secret_key: "uvys6bRhKHUdFF5CqbJonSDSRN8H0sCBziNSrDGNVdpz7IeZhveVart3yvghoKHA"
+ engines:
+ - name: arch linux wiki
+ tokens: ['$ecretValue']
+
+As an alternative, it is possible to specify the engines to keep. In the following example, searx has only two engines:
+
+.. code-block:: yaml
+
+ use_default_settings:
+ engines:
+ keep_only:
+ - google
+ - duckduckgo
+ server:
+ secret_key: "uvys6bRhKHUdFF5CqbJonSDSRN8H0sCBziNSrDGNVdpz7IeZhveVart3yvghoKHA"
+ engines:
+ - name: google
+ tokens: ['$ecretValue']
+ - name: duckduckgo
+ tokens: ['$ecretValue']
diff --git a/docs/blog/command-line-engines.rst b/docs/blog/command-line-engines.rst
new file mode 100644
index 0000000..746c9e4
--- /dev/null
+++ b/docs/blog/command-line-engines.rst
@@ -0,0 +1,65 @@
+========================================
+Running shell commands to fetch results
+========================================
+
+Previously, with searx you could search over the Internet on other people's
+computers. Now it is possible to fetch results from your local machine without
+connecting to any networks from the same graphical user interface.
+
+
+Command line engines
+====================
+
+In :pull:`2128` a new type of engine has been introduced called ``command``.
+This engine lets administrators add engines which run arbitrary shell commands
+and show its output on the web UI of searx.
+
+When creating and enabling a ``command`` engine on a public searx instance,
+you must be careful to avoid leaking private data. The easiest solution
+is to add tokens to the engine. Thus, only those who have the appropriate token
+can retrieve results from the it.
+
+The engine base is flexible. Only your imagination can limit the power of this engine. (And
+maybe security concerns.) The following options are available:
+
+* ``command``: A comma separated list of the elements of the command. A special token {{QUERY}} tells searx where to put the search terms of the user. Example: ``['ls', '-l', '-h', '{{QUERY}}']``
+* ``delimiter``: A dict containing a delimiter char and the "titles" of each element in keys.
+* ``parse_regex``: A dict containing the regular expressions for each result key.
+* ``query_type``: The expected type of user search terms. Possible values: ``path`` and ``enum``. ``path`` checks if the uesr provided path is inside the working directory. If not the query is not executed. ``enum`` is a list of allowed search terms. If the user submits something which is not included in the list, the query returns an error.
+* ``query_enum``: A list containing allowed search terms if ``query_type`` is set to ``enum``.
+* ``working_dir``: The directory where the command has to be executed. Default: ``.``
+* ``result_separator``: The character that separates results. Default: ``\n``
+
+
+The example engine below can be used to find files with a specific name in the configured
+working directory.
+
+.. code:: yaml
+
+ - name: find
+ engine: command
+ command: ['find', '.', '-name', '{{QUERY}}']
+ query_type: path
+ shortcut: fnd
+ delimiter:
+ chars: ' '
+ keys: ['line']
+
+
+Next steps
+==========
+
+In the next milestone, support for local search engines and indexers (e.g. Elasticsearch)
+are going to be added. This way, you will be able to query your own databases/indexers.
+
+Acknowledgement
+===============
+
+This development was sponsored by `Search and Discovery Fund`_ of `NLnet Foundation`_ .
+
+.. _Search and Discovery Fund: https://nlnet.nl/discovery
+.. _NLnet Foundation: https://nlnet.nl/
+
+
+| Happy hacking.
+| kvch // 2020.09.28 21:26
diff --git a/docs/blog/index.rst b/docs/blog/index.rst
index 04d9535..689739a 100644
--- a/docs/blog/index.rst
+++ b/docs/blog/index.rst
@@ -11,3 +11,4 @@ Blog
admin
intro-offline
private-engines
+ command-line-engines
diff --git a/docs/blog/lxcdev-202006.rst b/docs/blog/lxcdev-202006.rst
index f9ca3c2..b8d470d 100644
--- a/docs/blog/lxcdev-202006.rst
+++ b/docs/blog/lxcdev-202006.rst
@@ -81,7 +81,7 @@ fork:
.. code:: sh
$ cd ~/Downloads
- $ git clone https://github.com/asciimoo/searx.git
+ $ git clone https://github.com/searx/searx.git
$ cd searx
The :ref:`lxc-searx.env` consists of several images, see ``export
diff --git a/docs/blog/private-engines.rst b/docs/blog/private-engines.rst
index 796f0fc..027cc3d 100644
--- a/docs/blog/private-engines.rst
+++ b/docs/blog/private-engines.rst
@@ -7,6 +7,8 @@ enabled engines on their instances. It might be because they do not want to
expose some private information through an offline engine. Or they
would rather share engines only with their trusted friends or colleagues.
+.. _private engines:
+
Private engines
===============
diff --git a/docs/blog/python3.rst b/docs/blog/python3.rst
index 5bb7f1c..1d2cfc8 100644
--- a/docs/blog/python3.rst
+++ b/docs/blog/python3.rst
@@ -54,7 +54,7 @@ If you found bugs
Please open an issue on `GitHub`_. Make sure that you mention your Python
version in your issue, so we can investigate it properly.
-.. _GitHub: https://github.com/asciimoo/searx/issues
+.. _GitHub: https://github.com/searx/searx/issues
Acknowledgment
==============
diff --git a/docs/build-templates/filtron.rst b/docs/build-templates/filtron.rst
index 83a4518..79b2543 100644
--- a/docs/build-templates/filtron.rst
+++ b/docs/build-templates/filtron.rst
@@ -7,11 +7,11 @@
.. code-block:: sh
$ sudo -H useradd --shell /bin/bash --system \\
- --home-dir "$SERVICE_HOME" \\
- --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+ --home-dir \"$SERVICE_HOME\" \\
+ --comment \"Privacy-respecting metasearch engine\" $SERVICE_USER
- $ sudo -H mkdir "$SERVICE_HOME"
- $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+ $ sudo -H mkdir \"$SERVICE_HOME\"
+ $ sudo -H chown -R \"$SERVICE_GROUP:$SERVICE_GROUP\" \"$SERVICE_HOME\"
.. END create user
@@ -23,16 +23,16 @@
.. code-block:: bash
- $ cat > "$GO_ENV" <<EOF
+ $ cat > \"$GO_ENV\" <<EOF
export GOPATH=${SERVICE_HOME}/go-apps
export PATH=\$PATH:${SERVICE_HOME}/local/go/bin:\$GOPATH/bin
EOF
- $ sudo -i -u "${SERVICE_USER}"
+ $ sudo -i -u \"${SERVICE_USER}\"
(${SERVICE_USER}) $ echo 'source $GO_ENV' >> ~/.profile
(${SERVICE_USER}) $ mkdir ${SERVICE_HOME}/local
- (${SERVICE_USER}) $ wget --progress=bar -O "${GO_TAR}" \\
- "${GO_PKG_URL}"
- (${SERVICE_USER}) $ tar -C ${SERVICE_HOME}/local/go -xzf "${GO_TAR}"
+ (${SERVICE_USER}) $ wget --progress=bar -O \"${GO_TAR}\" \\
+ \"${GO_PKG_URL}\"
+ (${SERVICE_USER}) $ tar -C ${SERVICE_HOME}/local -xzf \"${GO_TAR}\"
(${SERVICE_USER}) $ which go
${SERVICE_HOME}/local/go/bin/go
@@ -46,7 +46,7 @@
.. code-block:: bash
- $ sudo -i -u "${SERVICE_USER}"
+ $ sudo -i -u \"${SERVICE_USER}\"
(${SERVICE_USER}) $ go get -v -u github.com/asciimoo/filtron
.. END install filtron
diff --git a/docs/build-templates/morty.rst b/docs/build-templates/morty.rst
index 4a5d1f2..092f9f6 100644
--- a/docs/build-templates/morty.rst
+++ b/docs/build-templates/morty.rst
@@ -7,11 +7,11 @@
.. code-block:: sh
$ sudo -H useradd --shell /bin/bash --system \\
- --home-dir "$SERVICE_HOME" \\
- --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+ --home-dir \"$SERVICE_HOME\" \\
+ --comment \"Privacy-respecting metasearch engine\" $SERVICE_USER
- $ sudo -H mkdir "$SERVICE_HOME"
- $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+ $ sudo -H mkdir \"$SERVICE_HOME\"
+ $ sudo -H chown -R \"$SERVICE_GROUP:$SERVICE_GROUP\" \"$SERVICE_HOME\"
.. END create user
@@ -23,16 +23,16 @@
.. code-block:: bash
- $ cat > "$GO_ENV" <<EOF
+ $ cat > \"$GO_ENV\" <<EOF
export GOPATH=${SERVICE_HOME}/go-apps
export PATH=\$PATH:${SERVICE_HOME}/local/go/bin:\$GOPATH/bin
EOF
- $ sudo -i -u "${SERVICE_USER}"
+ $ sudo -i -u \"${SERVICE_USER}\"
(${SERVICE_USER}) $ echo 'source $GO_ENV' >> ~/.profile
(${SERVICE_USER}) $ mkdir ${SERVICE_HOME}/local
- (${SERVICE_USER}) $ wget --progress=bar -O "${GO_TAR}" \\
- "${GO_PKG_URL}"
- (${SERVICE_USER}) $ tar -C ${SERVICE_HOME}/local/go -xzf "${GO_TAR}"
+ (${SERVICE_USER}) $ wget --progress=bar -O \"${GO_TAR}\" \\
+ \"${GO_PKG_URL}\"
+ (${SERVICE_USER}) $ tar -C ${SERVICE_HOME}/local/go -xzf \"${GO_TAR}\"
(${SERVICE_USER}) $ which go
${SERVICE_HOME}/local/go/bin/go
@@ -46,7 +46,7 @@
.. code-block:: bash
- $ sudo -i -u "${SERVICE_USER}"
+ $ sudo -i -u \"${SERVICE_USER}\"
(${SERVICE_USER}) $ go get -v -u github.com/asciimoo/morty
.. END install morty
diff --git a/docs/build-templates/searx.rst b/docs/build-templates/searx.rst
index 5cb70e9..fe82ec3 100644
--- a/docs/build-templates/searx.rst
+++ b/docs/build-templates/searx.rst
@@ -64,11 +64,11 @@ ${fedora_build}
.. code-block:: sh
$ sudo -H useradd --shell /bin/bash --system \\
- --home-dir "$SERVICE_HOME" \\
- --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+ --home-dir \"$SERVICE_HOME\" \\
+ --comment 'Privacy-respecting metasearch engine' $SERVICE_USER
- $ sudo -H mkdir "$SERVICE_HOME"
- $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+ $ sudo -H mkdir \"$SERVICE_HOME\"
+ $ sudo -H chown -R \"$SERVICE_GROUP:$SERVICE_GROUP\" \"$SERVICE_HOME\"
.. END create user
@@ -81,7 +81,7 @@ ${fedora_build}
.. code-block:: sh
$ sudo -H -u ${SERVICE_USER} -i
- (${SERVICE_USER})$ git clone "https://github.com/asciimoo/searx.git" "$SEARX_SRC"
+ (${SERVICE_USER})$ git clone \"https://github.com/searx/searx.git\" \"$SEARX_SRC\"
.. END clone searx
@@ -93,8 +93,8 @@ ${fedora_build}
.. code-block:: sh
- (${SERVICE_USER})$ python3 -m venv "${SEARX_PYENV}"
- (${SERVICE_USER})$ echo ". ${SEARX_PYENV}/bin/activate" >> "$SERVICE_HOME/.profile"
+ (${SERVICE_USER})$ python3 -m venv \"${SEARX_PYENV}\"
+ (${SERVICE_USER})$ echo \". ${SEARX_PYENV}/bin/activate\" >> \"$SERVICE_HOME/.profile\"
.. END create virtualenv
@@ -118,7 +118,7 @@ ${fedora_build}
pip install -U wheel
# jump to searx's working tree and install searx into virtualenv
- (${SERVICE_USER})$ cd "$SEARX_SRC"
+ (${SERVICE_USER})$ cd \"$SEARX_SRC\"
(${SERVICE_USER})$ pip install -e .
@@ -128,13 +128,30 @@ ${fedora_build}
.. tabs::
- .. group-tab:: bash
+ .. group-tab:: Use default settings
+
+ .. code-block:: sh
+
+ $ sudo -H mkdir -p \"$(dirname ${SEARX_SETTINGS_PATH})\"
+ $ sudo -H cp \"$SEARX_SRC/utils/templates/etc/searx/use_default_settings.yml\" \\
+ \"${SEARX_SETTINGS_PATH}\"
+
+ .. group-tab:: searx/settings.yml
+
+ .. code-block:: sh
+
+ $ sudo -H mkdir -p \"$(dirname ${SEARX_SETTINGS_PATH})\"
+ $ sudo -H cp \"$SEARX_SRC/searx/settings.yml\" \\
+ \"${SEARX_SETTINGS_PATH}\"
+
+.. tabs::
+
+ .. group-tab:: minimal setup
.. code-block:: sh
- $ sudo -H cp "$SEARX_SRC/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
- $ sudo -H sed -i -e "s/ultrasecretkey/\\$(openssl rand -hex 16)/g" "$SEARX_SETTINGS_PATH"
- $ sudo -H sed -i -e "s/{instance_name}/searx@\\$(uname -n)/g" "$SEARX_SETTINGS_PATH"
+ $ sudo -H sed -i -e \"s/ultrasecretkey/\$(openssl rand -hex 16)/g\" \"$SEARX_SETTINGS_PATH\"
+ $ sudo -H sed -i -e \"s/{instance_name}/searx@\$(uname -n)/g\" \"$SEARX_SETTINGS_PATH\"
.. END searx config
@@ -147,16 +164,16 @@ ${fedora_build}
.. code-block:: sh
# enable debug ..
- $ sudo -H sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
+ $ sudo -H sed -i -e \"s/debug : False/debug : True/g\" \"$SEARX_SETTINGS_PATH\"
# start webapp
$ sudo -H -u ${SERVICE_USER} -i
(${SERVICE_USER})$ cd ${SEARX_SRC}
- (${SERVICE_USER})$ export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
+ (${SERVICE_USER})$ export SEARX_SETTINGS_PATH=\"${SEARX_SETTINGS_PATH}\"
(${SERVICE_USER})$ python searx/webapp.py
# disable debug
- $ sudo -H sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
+ $ sudo -H sed -i -e \"s/debug : True/debug : False/g\" \"$SEARX_SETTINGS_PATH\"
Open WEB browser and visit http://$SEARX_INTERNAL_URL . If you are inside a
container or in a script, test with curl:
@@ -167,7 +184,7 @@ container or in a script, test with curl:
.. code-block:: sh
- $ xgd-open http://$SEARX_INTERNAL_URL
+ $ xdg-open http://$SEARX_INTERNAL_URL
.. group-tab:: curl
diff --git a/docs/conf.py b/docs/conf.py
index 1f79e4c..d6fde9b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -10,6 +10,7 @@ GIT_BRANCH = os.environ.get("GIT_BRANCH", "master")
from searx.brand import SEARX_URL
from searx.brand import DOCS_URL
+
# Project --------------------------------------------------------------
project = u'searx'
@@ -28,15 +29,15 @@ exclude_patterns = ['build-templates/*.rst']
from searx import webapp
jinja_contexts = {
- 'webapp': dict(**webapp.__dict__)
+ 'webapp': dict(**webapp.__dict__),
}
# usage:: lorem :patch:`f373169` ipsum
extlinks = {}
# upstream links
-extlinks['wiki'] = ('https://github.com/asciimoo/searx/wiki/%s', ' ')
-extlinks['pull'] = ('https://github.com/asciimoo/searx/pull/%s', 'PR ')
+extlinks['wiki'] = ('https://github.com/searx/searx/wiki/%s', ' ')
+extlinks['pull'] = ('https://github.com/searx/searx/pull/%s', 'PR ')
# links to custom brand
extlinks['origin'] = (GIT_URL + '/blob/' + GIT_BRANCH + '/%s', 'git://')
@@ -48,11 +49,11 @@ extlinks['man'] = ('https://manpages.debian.org/jump?q=%s', '')
#extlinks['role'] = (
# 'https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-%s', '')
extlinks['duref'] = (
- 'http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#%s', '')
+ 'https://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#%s', '')
extlinks['durole'] = (
- 'http://docutils.sourceforge.net/docs/ref/rst/roles.html#%s', '')
+ 'https://docutils.sourceforge.net/docs/ref/rst/roles.html#%s', '')
extlinks['dudir'] = (
- 'http://docutils.sourceforge.net/docs/ref/rst/directives.html#%s', '')
+ 'https://docutils.sourceforge.net/docs/ref/rst/directives.html#%s', '')
extlinks['ctan'] = (
'https://ctan.org/pkg/%s', 'CTAN: ')
@@ -81,11 +82,12 @@ intersphinx_mapping = {
"sphinx" : ("https://www.sphinx-doc.org/en/master/", None),
}
-issues_github_path = "asciimoo/searx"
+issues_github_path = "searx/searx"
# HTML -----------------------------------------------------------------
sys.path.append(os.path.abspath('_themes'))
+sys.path.insert(0, os.path.abspath("../utils/"))
html_theme_path = ['_themes']
html_theme = "searx"
@@ -99,7 +101,7 @@ html_theme_options = {"index_sidebar_logo": True}
html_context = {
"project_links": [
ProjectLink("Source", GIT_URL),
- ProjectLink("Wiki", "https://github.com/asciimoo/searx/wiki"),
+ ProjectLink("Wiki", "https://github.com/searx/searx/wiki"),
ProjectLink("Public instances", "https://searx.space/"),
ProjectLink("Twitter", "https://twitter.com/Searx_engine"),
]
diff --git a/docs/dev/contribution_guide.rst b/docs/dev/contribution_guide.rst
index f9e9569..cffbbca 100644
--- a/docs/dev/contribution_guide.rst
+++ b/docs/dev/contribution_guide.rst
@@ -87,7 +87,7 @@ In order to submit a patch, please follow the steps below:
- Add yourself to the :origin:`AUTHORS.rst` file.
-- Choose meaning full commit messages, read `Conventional Commits`_
+- Choose meaningful commit messages, read `Conventional Commits`_
.. code::
@@ -117,8 +117,8 @@ Translation currently takes place on :ref:`transifex <translation>`.
Documentation
=============
-.. _Sphinx: http://www.sphinx-doc.org
-.. _reST: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
+.. _Sphinx: https://www.sphinx-doc.org
+.. _reST: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
.. sidebar:: The reST sources
@@ -141,6 +141,9 @@ Here is an example which makes a complete rebuild:
live build
----------
+.. _sphinx-autobuild:
+ https://github.com/executablebooks/sphinx-autobuild/blob/master/README.md
+
.. sidebar:: docs-clean
It is recommended to assert a complete rebuild before deploying (use
@@ -156,9 +159,21 @@ changed.
$ make docs-live
...
The HTML pages are in dist/docs.
- ... Serving on http://0.0.0.0:8080
+ ... Serving on http://0.0.0.0:8000
... Start watching changes
+Live builds are implemented by sphinx-autobuild_. Use environment
+``$(SPHINXOPTS)`` to pass arguments to the sphinx-autobuild_ command. Except
+option ``--host`` (which is always set to ``0.0.0.0``) you can pass any
+argument. E.g to find and use a free port, use:
+
+.. code:: sh
+
+ $ SPHINXOPTS="--port 0" make docs-live
+ ...
+ ... Serving on http://0.0.0.0:50593
+ ...
+
.. _deploy on github.io:
@@ -182,4 +197,4 @@ needed git add, commit and push:
cd gh-pages; git checkout gh-pages >/dev/null
Switched to a new branch 'gh-pages'
...
- doc available at --> https://asciimoo.github.io/searx
+ doc available at --> https://searx.github.io/searx
diff --git a/docs/dev/engine_overview.rst b/docs/dev/engine_overview.rst
index c3c81ff..3562ca6 100644
--- a/docs/dev/engine_overview.rst
+++ b/docs/dev/engine_overview.rst
@@ -18,6 +18,9 @@ engines. Adapters are stored under the folder :origin:`searx/engines`.
:depth: 3
:backlinks: entry
+
+.. _general engine configuration:
+
general engine configuration
============================
@@ -49,16 +52,19 @@ offline boolean engine runs offline
settings.yml
------------
-======================= =========== ===========================================
+======================= =========== =============================================
argument type information
-======================= =========== ===========================================
+======================= =========== =============================================
name string name of search-engine
engine string name of searx-engine
(filename without ``.py``)
shortcut string shortcut of search-engine
timeout string specific timeout for search-engine
display_error_messages boolean display error messages on the web UI
-======================= =========== ===========================================
+proxies dict set proxies for a specific engine
+ (e.g. ``proxies : {http: socks5://proxy:port,
+ https: socks5://proxy:port}``)
+======================= =========== =============================================
overrides
@@ -128,16 +134,19 @@ The function ``def request(query, params):`` always returns the ``params``
variable. Inside searx, the following paramters can be used to specify a search
request:
-============ =========== =========================================================
-argument type information
-============ =========== =========================================================
-url string requested url
-method string HTTP request method
-headers set HTTP header information
-data set HTTP data information (parsed if ``method != 'GET'``)
-cookies set HTTP cookies
-verify boolean Performing SSL-Validity check
-============ =========== =========================================================
+=================== =========== ==========================================================================
+argument type information
+=================== =========== ==========================================================================
+url string requested url
+method string HTTP request method
+headers set HTTP header information
+data set HTTP data information (parsed if ``method != 'GET'``)
+cookies set HTTP cookies
+verify boolean Performing SSL-Validity check
+max_redirects int maximum redirects, hard limit
+soft_max_redirects int maximum redirects, soft limit. Record an error but don't stop the engine
+raise_for_httperror bool True by default: raise an exception if the HTTP code of response is >= 300
+=================== =========== ==========================================================================
example code
@@ -256,7 +265,7 @@ latitude latitude of result (in decimal format)
longitude longitude of result (in decimal format)
boundingbox boundingbox of result (array of 4. values
``[lat-min, lat-max, lon-min, lon-max]``)
-geojson geojson of result (http://geojson.org)
+geojson geojson of result (https://geojson.org/)
osm.type type of osm-object (if OSM-Result)
osm.id id of osm-object (if OSM-Result)
address.name name of object
diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst
index 62cd0a9..699729a 100644
--- a/docs/dev/makefile.rst
+++ b/docs/dev/makefile.rst
@@ -68,7 +68,7 @@ Python environment
``source ./local/py3/bin/activate``
-With Makefile we do no longer need to build up the virualenv manually (as
+With Makefile we do no longer need to build up the virtualenv manually (as
described in the :ref:`devquickstart` guide). Jump into your git working tree
and release a ``make pyenv``:
diff --git a/docs/dev/plugins.rst b/docs/dev/plugins.rst
index 6add975..16262ea 100644
--- a/docs/dev/plugins.rst
+++ b/docs/dev/plugins.rst
@@ -30,6 +30,13 @@ Example plugin
ctx['search'].suggestions.add('example')
return True
+External plugins
+================
+
+External plugins are standard python modules implementing all the requirements of the standard plugins.
+Plugins can be enabled by adding them to :ref:`settings.yml`'s ``plugins`` section.
+Example external plugin can be found `here <https://github.com/asciimoo/searx_external_plugin_example>`_.
+
Register your plugin
====================
diff --git a/docs/dev/quickstart.rst b/docs/dev/quickstart.rst
index a4fc897..bb9f4d6 100644
--- a/docs/dev/quickstart.rst
+++ b/docs/dev/quickstart.rst
@@ -4,129 +4,52 @@
Development Quickstart
======================
-.. sidebar:: :ref:`makefile`
+.. _npm: https://www.npmjs.com/
- For additional developer purpose there are :ref:`makefile`.
-
-This quickstart guide gets your environment set up with searx. Furthermore, it
-gives a short introduction to the ``manage.sh`` script.
-
-How to setup your development environment
-=========================================
-
-.. sidebar:: :ref:`make pyenv <make pyenv>`
-
- Alternatively use the :ref:`make pyenv`.
-
-First, clone the source code of searx to the desired folder. In this case the
-source is cloned to ``~/myprojects/searx``. Then create and activate the
-searx-ve virtualenv and install the required packages using ``manage.sh``.
-
-.. code:: sh
-
- cd ~/myprojects
- git clone https://github.com/asciimoo/searx.git
- cd searx
- python3 -m venv searx-ve
- . ./searx-ve/bin/activate
- ./manage.sh update_dev_packages
-
-
-How to run tests
-================
-
-.. sidebar:: :ref:`make test.unit <make test>`
-
- Alternatively use the ``test.pep8``, ``test.unit``, ``test.robot`` targets.
-
-Tests can be run using the ``manage.sh`` script. Following tests and checks are
-available:
-
-- Unit tests
-- Selenium tests
-- PEP8 validation
-- Unit test coverage check
-
-For example unit tests are run with the command below:
-
-.. code:: sh
-
- ./manage.sh unit_tests
-
-For further test options, please consult the help of the ``manage.sh`` script or
-read :ref:`make test`.
-
-
-How to compile styles and javascript
-====================================
-
-.. _less: http://lesscss.org/
-.. _NodeJS: https://nodejs.org
-
-How to build styles
--------------------
-
-Less_ is required to build the styles of searx. Less_ can be installed using
-either NodeJS_ or Apt.
+Searx loves developers, just clone and start hacking. All the rest is done for
+you simply by using :ref:`make <makefile>`.
.. code:: sh
- sudo -H apt-get install nodejs
- sudo -H npm install -g less
+ git clone https://github.com/searx/searx.git
-OR
+Here is how a minimal workflow looks like:
-.. code:: sh
+1. *start* hacking
+2. *run* your code: :ref:`make run`
+3. *test* your code: :ref:`make test`
- sudo -H apt-get install node-less
+If you think at some point something fails, go back to *start*. Otherwise,
+choose a meaningful commit message and we are happy to receive your pull
+request. To not end in *wild west* we have some directives, please pay attention
+to our ":ref:`how to contribute`" guideline.
-After satisfying the requirements styles can be build using ``manage.sh``
+If you implement themes, you will need to compile styles and JavaScript before
+*run*.
.. code:: sh
- ./manage.sh styles
-
-
-How to build the source of the themes
-=====================================
-
-.. _grunt: https://gruntjs.com/
-
-Grunt_ must be installed in order to build the javascript sources. It depends on
-NodeJS, so first Node has to be installed.
-
-.. code:: sh
+ make themes
- sudo -H apt-get install nodejs
- make node.env
+Don't forget to install npm_ first.
-After installing grunt, the files can be built using the following command:
+.. tabs::
-.. code:: sh
+ .. group-tab:: Ubuntu / debian
- make themes
+ .. code:: sh
+ sudo -H apt-get install npm
-Tips for debugging/development
-==============================
+ .. group-tab:: Arch Linux
-.. sidebar:: :ref:`make run`
+ .. code-block:: sh
- Makefile target ``run`` already enables debug option for your developer
- session / see :ref:`make run`.
+ sudo -H pacman -S npm
-Turn on debug logging
- Whether you are working on a new engine or trying to eliminate a bug, it is
- always a good idea to turn on debug logging. When debug logging is enabled a
- stack trace appears, instead of the cryptic ``Internal Server Error``
- message. It can be turned on by setting ``debug: False`` to ``debug: True`` in
- :origin:`settings.yml <searx/settings.yml>`.
+ .. group-tab:: Fedora / RHEL
-.. sidebar:: :ref:`make test`
+ .. code-block:: sh
- Alternatively use the :ref:`make test` targets.
+ sudo -H dnf install npm
-Run ``./manage.sh tests`` before creating a PR.
- Failing build on Travis is common because of PEP8 checks. So a new commit
- must be created containing these format fixes. This phase can be skipped if
- ``./manage.sh tests`` is run locally before creating a PR.
diff --git a/docs/dev/reST.rst b/docs/dev/reST.rst
index 906a0e9..9633787 100644
--- a/docs/dev/reST.rst
+++ b/docs/dev/reST.rst
@@ -1391,27 +1391,27 @@ The next example shows the difference of ``\tfrac`` (*textstyle*) and ``\dfrac``
.. _readability: https://docs.python-guide.org/writing/style/
.. _Sphinx-Primer:
- http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
+ https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
.. _reST: https://docutils.sourceforge.io/rst.html
.. _Sphinx Roles:
https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html
-.. _Sphinx: http://www.sphinx-doc.org
-.. _`sphinx-doc FAQ`: http://www.sphinx-doc.org/en/stable/faq.html
+.. _Sphinx: https://www.sphinx-doc.org
+.. _`sphinx-doc FAQ`: https://www.sphinx-doc.org/en/stable/faq.html
.. _Sphinx markup constructs:
- http://www.sphinx-doc.org/en/stable/markup/index.html
+ https://www.sphinx-doc.org/en/stable/markup/index.html
.. _`sphinx cross references`:
- http://www.sphinx-doc.org/en/stable/markup/inline.html#cross-referencing-arbitrary-locations
+ https://www.sphinx-doc.org/en/stable/markup/inline.html#cross-referencing-arbitrary-locations
.. _sphinx.ext.extlinks:
https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
-.. _intersphinx: http://www.sphinx-doc.org/en/stable/ext/intersphinx.html
-.. _sphinx config: http://www.sphinx-doc.org/en/stable/config.html
-.. _Sphinx's autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html
+.. _intersphinx: https://www.sphinx-doc.org/en/stable/ext/intersphinx.html
+.. _sphinx config: https://www.sphinx-doc.org/en/stable/config.html
+.. _Sphinx's autodoc: https://www.sphinx-doc.org/en/stable/ext/autodoc.html
.. _Sphinx's Python domain:
- http://www.sphinx-doc.org/en/stable/domains.html#the-python-domain
+ https://www.sphinx-doc.org/en/stable/domains.html#the-python-domain
.. _Sphinx's C domain:
- http://www.sphinx-doc.org/en/stable/domains.html#cross-referencing-c-constructs
+ https://www.sphinx-doc.org/en/stable/domains.html#cross-referencing-c-constructs
.. _doctree:
- http://www.sphinx-doc.org/en/master/extdev/tutorial.html?highlight=doctree#build-phases
+ https://www.sphinx-doc.org/en/master/extdev/tutorial.html?highlight=doctree#build-phases
.. _docutils: http://docutils.sourceforge.net/docs/index.html
.. _docutils FAQ: http://docutils.sourceforge.net/FAQ.html
.. _linuxdoc: https://return42.github.io/linuxdoc
@@ -1424,5 +1424,5 @@ The next example shows the difference of ``\tfrac`` (*textstyle*) and ``\dfrac``
.. _ImageMagick: https://www.imagemagick.org
.. _`Emacs Table Mode`: https://www.emacswiki.org/emacs/TableMode
-.. _`Online Tables Generator`: http://www.tablesgenerator.com/text_tables
+.. _`Online Tables Generator`: https://www.tablesgenerator.com/text_tables
.. _`OASIS XML Exchange Table Model`: https://www.oasis-open.org/specs/tm9901.html
diff --git a/docs/utils/index.rst b/docs/utils/index.rst
index 13914af..ada78ce 100644
--- a/docs/utils/index.rst
+++ b/docs/utils/index.rst
@@ -16,6 +16,7 @@ developers.
filtron.sh
morty.sh
lxc.sh
+ standalone_searx.py
.. _toolboxing common:
@@ -40,7 +41,7 @@ Scripts to maintain services often dispose of common commands and environments.
<lxc-searx.env>` use ::
sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/filtron.sh apache install
-
+
.. _toolboxing setup:
Tooling box setup
diff --git a/docs/utils/standalone_searx.py.rst b/docs/utils/standalone_searx.py.rst
new file mode 100644
index 0000000..557c4b7
--- /dev/null
+++ b/docs/utils/standalone_searx.py.rst
@@ -0,0 +1,11 @@
+
+.. _standalone_searx.py:
+
+=============================
+``utils/standalone_searx.py``
+=============================
+
+.. automodule:: standalone_searx
+ :members:
+
+
diff --git a/manage.sh b/manage.sh
index 424d64e..78571e4 100755
--- a/manage.sh
+++ b/manage.sh
@@ -39,7 +39,7 @@ install_geckodriver() {
return
fi
GECKODRIVER_VERSION="v0.24.0"
- PLATFORM="`python -c "import six; import platform; six.print_(platform.system().lower(), platform.architecture()[0])"`"
+ PLATFORM="`python3 -c "import platform; print(platform.system().lower(), platform.architecture()[0])"`"
case "$PLATFORM" in
"linux 32bit" | "linux2 32bit") ARCH="linux32";;
"linux 64bit" | "linux2 64bit") ARCH="linux64";;
@@ -120,7 +120,7 @@ docker_build() {
# "git describe" to get the Docker version (for example : v0.15.0-89-g0585788e)
# awk to remove the "v" and the "g"
- SEARX_GIT_VERSION=$(git describe --match "v[0-9]*\.[0-9]*\.[0-9]*" HEAD 2>/dev/null | awk -F'-' '{OFS="-"; $1=substr($1, 2); $3=substr($3, 2); print}')
+ SEARX_GIT_VERSION=$(git describe --match "v[0-9]*\.[0-9]*\.[0-9]*" HEAD 2>/dev/null | awk -F'-' '{OFS="-"; $1=substr($1, 2); if ($3) { $3=substr($3, 2); } print}')
# add the suffix "-dirty" if the repository has uncommited change
# /!\ HACK for searx/searx: ignore searx/brand.py and utils/brand.env
@@ -136,7 +136,7 @@ docker_build() {
# Check consistency between the git tag and the searx/version.py file
# /!\ HACK : parse Python file with bash /!\
# otherwise it is not possible build the docker image without all Python dependencies ( version.py loads __init__.py )
- # SEARX_PYTHON_VERSION=$(python -c "import six; import searx.version; six.print_(searx.version.VERSION_STRING)")
+ # SEARX_PYTHON_VERSION=$(python3 -c "import six; import searx.version; six.print_(searx.version.VERSION_STRING)")
SEARX_PYTHON_VERSION=$(cat searx/version.py | grep "\(VERSION_MAJOR\|VERSION_MINOR\|VERSION_BUILD\) =" | cut -d\= -f2 | sed -e 's/^[[:space:]]*//' | paste -sd "." -)
if [ $(echo "$SEARX_GIT_VERSION" | cut -d- -f1) != "$SEARX_PYTHON_VERSION" ]; then
echo "Inconsistency between the last git tag and the searx/version.py file"
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 5a1ca15..d0d1ecc 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,19 +1,24 @@
-pallets-sphinx-themes
-Sphinx
-sphinx-issues
mock==2.0.0
-nose2[coverage_plugin]
+nose2[coverage_plugin]==0.9.2
cov-core==1.15.0
-pep8==1.7.0
-pylint
+pycodestyle==2.6.0
+pylint==2.4.4
plone.testing==5.0.0
splinter==0.11.0
transifex-client==0.12.2
unittest2==1.1.0
zope.testrunner==4.5.1
selenium==3.141.0
-linuxdoc @ git+http://github.com/return42/linuxdoc.git
-sphinx-jinja
-sphinx-tabs
-sphinxcontrib-programoutput
-twine
+twine==3.2.0; python_version >= "3.6"
+twine==1.15.0; python_version < "3.6"
+Pallets-Sphinx-Themes==1.2.3
+Sphinx==3.2.1; python_version >= '3.6'
+Sphinx==3.0.1; python_version < '3.6'
+sphinx-issues==1.2.0
+sphinx-jinja==1.1.1
+sphinx-tabs==1.3.0; python_version >= '3.6'
+sphinx-tabs==1.1.13; python_version < '3.6'
+sphinxcontrib-programoutput==0.16
+sphinx-autobuild==2020.9.1; python_version >= '3.6'
+sphinx-autobuild==0.7.1; python_version < '3.6'
+linuxdoc @ git+http://github.com/return42/linuxdoc.git@70673dcf69e705e08d81f53794895dc15c4920b3#egg=linuxdoc
diff --git a/requirements.txt b/requirements.txt
index 5d508d7..51a4fff 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,12 +1,12 @@
-certifi==2020.4.5.1
+certifi==2020.6.20
babel==2.7.0
flask-babel==1.0.0
flask==1.1.2
-idna==2.9
+idna==2.10
jinja2==2.11.1
-lxml==4.5.0
+lxml==4.6.1
pygments==2.1.3
pyopenssl==19.1.0
python-dateutil==2.8.0
pyyaml==5.3.1
-requests[socks]==2.23.0
+requests[socks]==2.24.0
diff --git a/searx/__init__.py b/searx/__init__.py
index 2f3ebfc..9bbc7c8 100644
--- a/searx/__init__.py
+++ b/searx/__init__.py
@@ -15,52 +15,25 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
'''
-import certifi
import logging
+import searx.settings_loader
from os import environ
from os.path import realpath, dirname, join, abspath, isfile
-from io import open
-from ssl import OPENSSL_VERSION_INFO, OPENSSL_VERSION
-try:
- from yaml import safe_load
-except:
- from sys import exit, stderr
- stderr.write('[E] install pyyaml\n')
- exit(2)
+
searx_dir = abspath(dirname(__file__))
engine_dir = dirname(realpath(__file__))
+static_path = abspath(join(dirname(__file__), 'static'))
+settings, settings_load_message = searx.settings_loader.load_settings()
-
-def check_settings_yml(file_name):
- if isfile(file_name):
- return file_name
- else:
- return None
-
-
-# find location of settings.yml
-if 'SEARX_SETTINGS_PATH' in environ:
- # if possible set path to settings using the
- # enviroment variable SEARX_SETTINGS_PATH
- settings_path = check_settings_yml(environ['SEARX_SETTINGS_PATH'])
-else:
- # if not, get it from searx code base or last solution from /etc/searx
- settings_path = check_settings_yml(join(searx_dir, 'settings.yml')) or check_settings_yml('/etc/searx/settings.yml')
-
-if not settings_path:
- raise Exception('settings.yml not found')
-
-# load settings
-with open(settings_path, 'r', encoding='utf-8') as settings_yaml:
- settings = safe_load(settings_yaml)
+if settings['ui']['static_path']:
+ static_path = settings['ui']['static_path']
'''
enable debug if
the environnement variable SEARX_DEBUG is 1 or true
(whatever the value in settings.yml)
or general.debug=True in settings.yml
-
disable debug if
the environnement variable SEARX_DEBUG is 0 or false
(whatever the value in settings.yml)
@@ -80,17 +53,14 @@ else:
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger('searx')
-logger.debug('read configuration from %s', settings_path)
-# Workaround for openssl versions <1.0.2
-# https://github.com/certifi/python-certifi/issues/26
-if OPENSSL_VERSION_INFO[0:3] < (1, 0, 2):
- if hasattr(certifi, 'old_where'):
- environ['REQUESTS_CA_BUNDLE'] = certifi.old_where()
- logger.warning('You are using an old openssl version({0}), please upgrade above 1.0.2!'.format(OPENSSL_VERSION))
-
+logger.info(settings_load_message)
logger.info('Initialisation done')
if 'SEARX_SECRET' in environ:
settings['server']['secret_key'] = environ['SEARX_SECRET']
if 'SEARX_BIND_ADDRESS' in environ:
settings['server']['bind_address'] = environ['SEARX_BIND_ADDRESS']
+
+if not searx_debug and settings['server']['secret_key'] == 'ultrasecretkey':
+ logger.error('server.secret_key is not changed. Please use something else instead of ultrasecretkey.')
+ exit(1)
diff --git a/searx/answerers/__init__.py b/searx/answerers/__init__.py
index 444316f..97e7e58 100644
--- a/searx/answerers/__init__.py
+++ b/searx/answerers/__init__.py
@@ -1,12 +1,8 @@
from os import listdir
from os.path import realpath, dirname, join, isdir
-from sys import version_info
from searx.utils import load_module
from collections import defaultdict
-if version_info[0] == 3:
- unicode = str
-
answerers_dir = dirname(realpath(__file__))
@@ -36,10 +32,10 @@ def ask(query):
results = []
query_parts = list(filter(None, query.query.split()))
- if query_parts[0].decode('utf-8') not in answerers_by_keywords:
+ if query_parts[0] not in answerers_by_keywords:
return results
- for answerer in answerers_by_keywords[query_parts[0].decode('utf-8')]:
+ for answerer in answerers_by_keywords[query_parts[0]]:
result = answerer(query)
if result:
results.append(result)
diff --git a/searx/answerers/random/answerer.py b/searx/answerers/random/answerer.py
index 2dfb088..d5223e5 100644
--- a/searx/answerers/random/answerer.py
+++ b/searx/answerers/random/answerer.py
@@ -1,7 +1,6 @@
import hashlib
import random
import string
-import sys
import uuid
from flask_babel import gettext
@@ -10,12 +9,7 @@ from flask_babel import gettext
keywords = ('random',)
random_int_max = 2**31
-
-if sys.version_info[0] == 2:
- random_string_letters = string.lowercase + string.digits + string.uppercase
-else:
- unicode = str
- random_string_letters = string.ascii_lowercase + string.digits + string.ascii_uppercase
+random_string_letters = string.ascii_lowercase + string.digits + string.ascii_uppercase
def random_characters():
@@ -24,32 +18,32 @@ def random_characters():
def random_string():
- return u''.join(random_characters())
+ return ''.join(random_characters())
def random_float():
- return unicode(random.random())
+ return str(random.random())
def random_int():
- return unicode(random.randint(-random_int_max, random_int_max))
+ return str(random.randint(-random_int_max, random_int_max))
def random_sha256():
m = hashlib.sha256()
- m.update(b''.join(random_characters()))
- return unicode(m.hexdigest())
+ m.update(''.join(random_characters()).encode())
+ return str(m.hexdigest())
def random_uuid():
- return unicode(uuid.uuid4())
+ return str(uuid.uuid4())
-random_types = {b'string': random_string,
- b'int': random_int,
- b'float': random_float,
- b'sha256': random_sha256,
- b'uuid': random_uuid}
+random_types = {'string': random_string,
+ 'int': random_int,
+ 'float': random_float,
+ 'sha256': random_sha256,
+ 'uuid': random_uuid}
# required answerer function
@@ -70,4 +64,4 @@ def answer(query):
def self_info():
return {'name': gettext('Random value generator'),
'description': gettext('Generate different random values'),
- 'examples': [u'random {}'.format(x.decode('utf-8')) for x in random_types]}
+ 'examples': ['random {}'.format(x) for x in random_types]}
diff --git a/searx/answerers/statistics/answerer.py b/searx/answerers/statistics/answerer.py
index 73dd25c..abd4be7 100644
--- a/searx/answerers/statistics/answerer.py
+++ b/searx/answerers/statistics/answerer.py
@@ -1,11 +1,8 @@
-from sys import version_info
from functools import reduce
from operator import mul
from flask_babel import gettext
-if version_info[0] == 3:
- unicode = str
keywords = ('min',
'max',
@@ -30,21 +27,21 @@ def answer(query):
func = parts[0]
answer = None
- if func == b'min':
+ if func == 'min':
answer = min(args)
- elif func == b'max':
+ elif func == 'max':
answer = max(args)
- elif func == b'avg':
+ elif func == 'avg':
answer = sum(args) / len(args)
- elif func == b'sum':
+ elif func == 'sum':
answer = sum(args)
- elif func == b'prod':
+ elif func == 'prod':
answer = reduce(mul, args, 1)
if answer is None:
return []
- return [{'answer': unicode(answer)}]
+ return [{'answer': str(answer)}]
# required answerer function
diff --git a/searx/autocomplete.py b/searx/autocomplete.py
index 00a9f95..fbe634a 100644
--- a/searx/autocomplete.py
+++ b/searx/autocomplete.py
@@ -16,19 +16,16 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
'''
-import sys
from lxml import etree
from json import loads
+from urllib.parse import urlencode
+
from searx import settings
from searx.languages import language_codes
from searx.engines import (
categories, engines, engine_shortcuts
)
from searx.poolrequests import get as http_get
-from searx.url_utils import urlencode
-
-if sys.version_info[0] == 3:
- unicode = str
def get(*args, **kwargs):
@@ -41,22 +38,22 @@ def get(*args, **kwargs):
def searx_bang(full_query):
'''check if the searchQuery contain a bang, and create fitting autocompleter results'''
# check if there is a query which can be parsed
- if len(full_query.getSearchQuery()) == 0:
+ if len(full_query.getQuery()) == 0:
return []
results = []
# check if current query stats with !bang
- first_char = full_query.getSearchQuery()[0]
+ first_char = full_query.getQuery()[0]
if first_char == '!' or first_char == '?':
- if len(full_query.getSearchQuery()) == 1:
+ if len(full_query.getQuery()) == 1:
# show some example queries
# TODO, check if engine is not avaliable
results.append(first_char + "images")
results.append(first_char + "wikipedia")
results.append(first_char + "osm")
else:
- engine_query = full_query.getSearchQuery()[1:]
+ engine_query = full_query.getQuery()[1:]
# check if query starts with categorie name
for categorie in categories:
@@ -75,32 +72,32 @@ def searx_bang(full_query):
# check if current query stats with :bang
elif first_char == ':':
- if len(full_query.getSearchQuery()) == 1:
+ if len(full_query.getQuery()) == 1:
# show some example queries
results.append(":en")
results.append(":en_us")
results.append(":english")
results.append(":united_kingdom")
else:
- engine_query = full_query.getSearchQuery()[1:]
+ engine_query = full_query.getQuery()[1:]
for lc in language_codes:
- lang_id, lang_name, country, english_name = map(unicode.lower, lc)
+ lang_id, lang_name, country, english_name = map(str.lower, lc)
# check if query starts with language-id
if lang_id.startswith(engine_query):
if len(engine_query) <= 2:
- results.append(u':{lang_id}'.format(lang_id=lang_id.split('-')[0]))
+ results.append(':{lang_id}'.format(lang_id=lang_id.split('-')[0]))
else:
- results.append(u':{lang_id}'.format(lang_id=lang_id))
+ results.append(':{lang_id}'.format(lang_id=lang_id))
# check if query starts with language name
if lang_name.startswith(engine_query) or english_name.startswith(engine_query):
- results.append(u':{lang_name}'.format(lang_name=lang_name))
+ results.append(':{lang_name}'.format(lang_name=lang_name))
# check if query starts with country
if country.startswith(engine_query.replace('_', ' ')):
- results.append(u':{country}'.format(country=country.replace(' ', '_')))
+ results.append(':{country}'.format(country=country.replace(' ', '_')))
# remove duplicates
result_set = set(results)
@@ -116,7 +113,7 @@ def searx_bang(full_query):
def dbpedia(query, lang):
# dbpedia autocompleter, no HTTPS
- autocomplete_url = 'http://lookup.dbpedia.org/api/search.asmx/KeywordSearch?'
+ autocomplete_url = 'https://lookup.dbpedia.org/api/search.asmx/KeywordSearch?'
response = get(autocomplete_url + urlencode(dict(QueryString=query)))
@@ -124,8 +121,7 @@ def dbpedia(query, lang):
if response.ok:
dom = etree.fromstring(response.content)
- results = dom.xpath('//a:Result/a:Label//text()',
- namespaces={'a': 'http://lookup.dbpedia.org/'})
+ results = dom.xpath('//Result/Label//text()')
return results
diff --git a/searx/brand.py b/searx/brand.py
index 91d2ab3..d71c57d 100644
--- a/searx/brand.py
+++ b/searx/brand.py
@@ -1,6 +1,6 @@
-GIT_URL = 'https://github.com/asciimoo/searx'
+GIT_URL = 'https://github.com/searx/searx'
GIT_BRANCH = 'master'
-ISSUE_URL = 'https://github.com/asciimoo/searx/issues'
+ISSUE_URL = 'https://github.com/searx/searx/issues'
SEARX_URL = 'https://searx.me'
-DOCS_URL = 'https://asciimoo.github.io/searx'
+DOCS_URL = 'https://searx.github.io/searx'
PUBLIC_INSTANCES = 'https://searx.space'
diff --git a/searx/data/__init__.py b/searx/data/__init__.py
new file mode 100644
index 0000000..55a254b
--- /dev/null
+++ b/searx/data/__init__.py
@@ -0,0 +1,29 @@
+import json
+from pathlib import Path
+
+
+__init__ = ['ENGINES_LANGUGAGES', 'CURRENCIES', 'USER_AGENTS', 'EXTERNAL_URLS', 'WIKIDATA_UNITS',
+ 'bangs_loader', 'ahmia_blacklist_loader']
+data_dir = Path(__file__).parent
+
+
+def load(filename):
+ # add str(...) for Python 3.5
+ with open(str(data_dir / filename), encoding='utf-8') as fd:
+ return json.load(fd)
+
+
+def bangs_loader():
+ return load('bangs.json')
+
+
+def ahmia_blacklist_loader():
+ with open(str(data_dir / 'ahmia_blacklist.txt'), encoding='utf-8') as fd:
+ return fd.read().split()
+
+
+ENGINES_LANGUAGES = load('engines_languages.json')
+CURRENCIES = load('currencies.json')
+USER_AGENTS = load('useragents.json')
+EXTERNAL_URLS = load('external_urls.json')
+WIKIDATA_UNITS = load('wikidata_units.json')
diff --git a/searx/data/ahmia_blacklist.txt b/searx/data/ahmia_blacklist.txt
new file mode 100644
index 0000000..a5c0e82
--- /dev/null
+++ b/searx/data/ahmia_blacklist.txt
@@ -0,0 +1,16177 @@
+0008f4726e2b9231c09355c0a176cd94
+0010e177f6fcaf499b1fd518046bde0a
+00140a082312f66c426de5ab0c2c6f1c
+001489ecdac8b8535f34bb65a678003d
+001a850b58240b760ed5c11821e64c7b
+001ae86997f8b3e517a46101df2ac85c
+002ab3c58ec26372ca8a16fa04097fad
+002ca30d3c48fbabec4748d1ad8af561
+002f82d895d220051c5c490f07bd2f06
+0032223cd6dad0feb29a6b93756702e8
+0034e8102d0d6b44a12c44940e79e134
+00367fbc39135a28347dd17f25095cc4
+00373196a4e80c24a520f08ed7232e30
+00377cce47784d49d250ec1527dd4c6a
+00392e810e7825e5a7e4635795626015
+003b7721ad5fd66dcb70ca66a554abe4
+003ed39119889b93fae7751c2ceb7756
+00433ed7d7fb1e659de569635558a96c
+004545573381002c98edbea07ecce710
+0048dcde7e2afd2cecaf7ef7180780e0
+005058cec8d6ffd3bad1c749a25589bd
+00538809ed5fd00a7d145dc536e105f1
+0053df1dd38b0a7e373723ad6c7307b8
+005aa0ccff6de439ab4e4e628b0552af
+005b7acb3c2804853e576aa65663bdce
+006764d9f710c03932876234851ffc7c
+006ed781ea4e05accc7727a58316a67a
+0071f5068289a6770bc9003c9fbfd393
+0073818c20b2c7709056e3196ade537b
+007a73add89e3bf5f8ca180184604ebb
+007b7aaa348d99204dd924ab0aac069c
+007be7c1c38f6f29f77b65ade2b01853
+007c3df36f8cb0a35ad4fba7c28128e9
+008749e26c3566eb70bf1d0f256e3746
+008b0025d3f03ace831c0967fa4a0ecd
+00918e625657958bbd5fd08d9a931c93
+00928ba6bd24dfd87e12a830900a6d89
+00929b53c605e9e50cc818f382be038f
+00998ecf41af4b2a4fd0af9b905ed429
+00a3b612a567540163894169b78b9c92
+00a7f2d21c6cba3ae02422401d49e317
+00a8568b63b7ea784fade52085ae5655
+00a89d2291d1a3a1fd160391ad9f1139
+00ab0106dbd7b42b47f63255b76a434f
+00ac1a128b2451206b2a7c6366352c8a
+00ac3bce80bdc994f303941bc803cbb8
+00b14adf176addf980b83eb857673d2b
+00b3176aa3ac9172b3d113d8090a1b96
+00b7238d24ffb710abf3f4d212b02a9f
+00b72c5fe0695156463eeef0b4dd08fc
+00be79509dd4be21c2cfb20e2e54c0e4
+00c25827b5c5bb282ada3a46a9dfed37
+00c5e3a334cae51709c9b98dc24274a9
+00c97286685afa91fdcc05d08d8f4d31
+00d297325d32283a85a11d966e70efa5
+00d9c83b0193b470637606b8815d95a0
+00db9d9ff9641feacad14176b6586954
+00e4fc981bdf5b7f387212199de7a93a
+00ea4e2e7fd07475f866efde3cf29de5
+00eb1e933df9aadc281fddd853cb9f32
+00ee964a85a4c681ccab861fc6ee46d7
+00f25e838a6fb5bac49fbdb3ded92b6b
+00f3dd2bb0b34bd96b426884ec5dccad
+00f729b27968913438fbf67691ebef6b
+0103f981ed3351e36a12d29d631224f8
+010669db7b90458bbeb4e3de4c758f78
+01067d8a4b16ec2466200c3e41961c1d
+010890ddb4c62da8dfc174e418437722
+010ea7123ee29776e390dba449cf9c5c
+0111f597eb58bcf715807b568b2a1d86
+0116d303c7f51659900e56455e80ddd9
+01177c79cc4dae823970094c4822b6fb
+011b5d530cece0a3fed2f1bee9a12256
+011b68d0c9ab00ec43883d0dfe754975
+0121791d3a0ab2b6f9adc85a7c3320ba
+0124cf594fd210d06a81cdc31ed01e20
+012a10e3befc963ddc87c63ae2911878
+012b36bdf7cd2f6c7cb60b0b3210f10c
+012ef974f5b4f307d168e00ada58cf96
+01323cea7c44f7d4587d83d7e9fe232c
+0134e00e8fc6faa9f87978e1a370f4c2
+013ac9677cc3e343a9cd7586fa4d6d7b
+013f07dd014e877dbdfc4942501bfa64
+0140a817d177b78dd06c98b8fe0e370a
+01440f491db052b0b7918508ed138362
+014460c8ab46c9bfbf64c740f5fe9768
+0146656b5d8c4dc633d1759356bca217
+0147327639f2821d0b306aea87c2724c
+0148b20eafc7a4822fee536c4532ba3c
+014be60ea314977759a3a853595f06c5
+014f5d09fca09a8e6345e1b34935b0f1
+0152f35578dde3a1458508c41983c049
+01580f129485a12c915ca506c3deacbf
+015c1a46929d6ac6262acb4adddb7ed8
+015e18e7f2207ff2c57a0328ef004880
+016791d8701640b7e50386542bd163d3
+016c737fe17e910ced778c22b5af045a
+016e99308792440162960f48ceb67b30
+0179b2d47ad553c109a77735fd9e54b5
+01814982bd45481f313f7cf0d8c3c11a
+018189b5fa04493aca3247bfc3968d48
+01855839a423395a888e944413028d60
+01867f9ffa48aa3e2fa7c8946b490a54
+0187eb3cf5945eb53c507b5b96b4cda9
+018a97ef1b4b8750a08606b7582409a6
+018ecf2b8adb2004617eb9198283d809
+01972455883dfb10b1a86a4007b9fe94
+0197260635dfec18d1ca367413fc20ff
+0197fd936ab57b542a4bffceaeb2d6f0
+019800ad8b7a58ef8343a510d9d54981
+019ae64ed14fde2e9c6fe55e801357e0
+019f1e59e2117a07a5fce9f4fe7a7f58
+01a3fea2e45541449e96bbc4471751ab
+01a57bfca99842bd8dc6d00ff6faa6cf
+01a5ab9e2080b00ccae00dbd13191428
+01a7074d0c29e85f1529d72a092813ed
+01a83de81a34db23d8e1d0cdedb226d7
+01b37288412b4e4ebfc90351ee373887
+01bc3e531b68b8062dfb0cd50dc15e73
+01bfce0b47cda6c4a61241a611273b43
+01c083cbd068d0ff1bcc2a958f315e04
+01cc4e13d5ee5c25fdb3397ee2adfdd6
+01cc73f7be6c6a09f1405f21f737a364
+01ce4cd52aee7cbb98badf2758767ef5
+01d2bebc3d521bd49cec1dcf1807b861
+01d7a435a48346581dd44056dbc4a25a
+01de33bd940fd991fd55563d692a74a9
+01f190140af9becde6cf1dd01fcd459a
+01f35948fc5b1c422459113ce786ac79
+01f82627d008e50411e8f8d37f6a4bb3
+01f8f2e292f9b9f609f3c673cfcf57e7
+01fa008eedea8531f2751786ba49bc82
+01fde11f5c7e9b1a3b1287bfbf4ddcaf
+01fe114f85a3cc6d5e288f694b937fc2
+01fe443b6981e7c3c0076c25332ac9ad
+02003740b7bdbafad9d20cfb8b2b6068
+02052e3fb4aa7d9f93e31c0202f2bd7e
+0207c80eb3b4ebfdf06d619add983134
+020a26bd2e07e1340068bd2bff420d3b
+020b298a8af3f8e80374d2b4731f2db7
+020baf4cd4ebb620c88ba26a47e5758d
+020d29b1642d553e9c64d00c0266f47d
+0213d8700ae82035cd1525a57c3432ae
+0216b34293cbcc4eb3345075ed2b4ace
+0216d286551434f146b10a800cdf5e32
+021fb404d2afa572658675418dc47ed8
+021ff20738a9f49a07cc856ddeb345b8
+02201937de04b0f44384fe7a8729a097
+0221620b6f7a6c8f2b8c22654f1ffe00
+02274c10044d60a4660b04aee89d38d5
+022d50338ff9502a3dfe34c4928b0762
+02338f61dcfc26b78eab670857683d91
+024001dd4847513158cb5d67c4705f9d
+02400fd7eb58ed95cebf6b8ab9175ef6
+024092c599ecb95abc003f43b66d6364
+02437a3d51e6c1b33bb2e3fe8be5e472
+0245236428a5f036063fa6e2aa1564ef
+0248b116ecd91230882a25a65f7faeef
+024b09b9bcdad171018d70220d049c4b
+024d79f57506a4994bd3cde30b2a998b
+0254b35a80052f596d30ab7178812f11
+02596da0d2e5bd18f5ebb3515a6ceb5f
+02626cc7d639b8dc20f48f3b33eafc7d
+026610e8aa4fc1d2ff0b85d1fc79352e
+026de3a3fa1dd7181d4a29b97e72951b
+026e82fb0d650f67f0f4d42da0a9b3bb
+0270944e29ee36dde8b6b3c398666265
+0270be0aef7214c7fdd44aa854ee0989
+02738c2e465f764b8e75ee36af205dd6
+02773e6376cf04b3bd5bc557edb207cb
+027d2c8a0f15afab38f185a661a12f63
+027eecc41e1513c7575cf9219ac0c6a7
+0282005216f5690127570b6e0385d2d4
+0285a8152235d46839b4a0c08d58ddc5
+02869dc869b7982e80932addef97a26b
+028b4bade611761182500c53fe013c67
+0295fe6011a3b7ac80cc51f05554bc21
+029c585134b256ada1805781fa7465e3
+029ca164b97ce75db17ebd4386cb5db9
+029d5c9f1228d06216d16298d78ba2a0
+02a4ca50cfdefb81ace51b2ad30260f2
+02aeef5bfd58cd9fbaf9097e9e8f3bb0
+02af4dfb97c81949a862b03d8d2149ab
+02afb5d8f2c11ff7aa3de2dcaf583e5d
+02b19e3221162da19b50051d02c5248d
+02baa92abe8ef70246ac2527f606f432
+02bc6589cc54fc58e0fbc9e7890b101d
+02c1a70284bce340a698b49ef2263933
+02c22724fdeb1b77c9d83d171517aa80
+02cc2dc5875b7e4e3bb3dbfe1f1a8bf8
+02cff67f28c0f923d7fab7ed20e38335
+02d464bfce934d7a4f23959210003aa4
+02d65237c6b53642be70ae35f4eac928
+02d97ffbb6c69e4c5cec27c1e88c36fb
+02d9e81803b1f68819b7748968bbec3b
+02dd748d15607f2cf394a197db7c7bf9
+02fd522aa48e7dcf9e4e2e4bae71722b
+02fea5bcf3a85e295cc07c0408b95783
+0302b771ec58fdbd233e755c4cfe8192
+0308813ea03b46ea48b2187dfed16267
+030ad459eeef5d137028921bf7bbd6a4
+03144298c092883fc02701c6cc9fa9ed
+0324bc6c45bc6ff68c114f4120ca2169
+03257a5e60aeafd8c7f1966d68e871cc
+0329e624df2ccdd66803e4b5c65a7b8d
+032db122dc5dbc4cffbd0e3b627130b8
+03335b010c7d458cc1008c4649162849
+033ab26e48d62c0251b1df6c407bf8c1
+033e1cf13d22f08baf70113e58087714
+033e72b4f869a11df6c077f921de9396
+03434cd052821f38f0484ce86bc46ba3
+0343ccf85d3ab5d5d5be9589ca7628dd
+0347a3b56554a8b8b0c0ebc1ad46bfb4
+03483bb949079341515ca85794a96c29
+0348b7fda21987cc79fcfe51d6265dfd
+034c2f1f7b2cf830a3477b30b44590f6
+034dc7d908e4d6582b50698e87a259b8
+034f7318933d53dde4870df4fd2b12c3
+03535813c7d9215a1e2cbdddb84f044a
+03542701b5fcdee9e7397fb9ccb84a5c
+03584f44c383541682bf18f1515ba348
+035a2b6c907eefb47fd3517b3482938f
+035e7485b58fd1a001ba9d8cda365a6d
+03639f51d8804bd28b9e8ece1d33e0a4
+036c808021b0379f47406d112719ec8e
+036d1e83796b3e1d632ef251e03197b3
+03752147841942c24935dcadfee218dd
+037f34b93a0d81bdef370fd37441d8ec
+037f9d59c641e87901b0f7a7c78ac293
+0387f0b61d7ee8b06ce1e813ee5f5b0c
+038817c32d55abfd4a6ce35d9259ecbd
+0388920c2ddf0d31a417418f7df2c03b
+03896d7272c21e66203fa7a53143d8d5
+0391efae80f8bd00fca9a6006a32752e
+039df073fe5630a13e3e47caa45362a6
+03a1b57ccedac923faed4913416aaf1f
+03a57237b8847605a332d502610513c3
+03a6ce5a60cdd6a6878433604bd991d1
+03ab57e67b5f1d145e1e2f93ccbd9cce
+03b37e35f3f818e4e7f5b08b3ea1ad76
+03b4737fc62c890b4f6e220b72ad8004
+03b482f6d191a55f3456f8e90a777431
+03bc20b2b401d4def45a310edbc747a4
+03bc87ad3ae40cb88ffdc2ff5adc3635
+03be7b1606d04df6d4e7faf5ad109016
+03c48f6b9404ba17da9e48cc8a7a3f4d
+03c77bcf378f59624520c87183e950b7
+03ca271a52b63a7888227cd11cf2f7e2
+03cfcfd4126cd702cfed51bf541bec41
+03d193490d7a271d97730280dd9d2abf
+03d1a0c15481b13250bfe51b9de1807f
+03ec33ed97a4b5af1b6147fe966771c9
+03edf07e831e59422f6d33cf7c0df622
+03ffd1dfb83547396200e3f325f40904
+0400c08ee9a0b422b72fad8e0bba3015
+040c8e1e8fe3614ccfd121cce82a4ee6
+040e38baab0dc1ce2136f709119f67cf
+040f3af5be9a1e069a32cac204b0aa55
+040fda88fd07087d9935f4921a443a93
+04100a501a59f302dec6860a2601421e
+04110020a853e508f8fd4726cf46ff48
+041cfc66a6ebd50a143773ad38a6d180
+041d0bdd135ef4287e8198ae4a8a5cb4
+041e45c7c93c0c051c46dcb4e792ee8d
+041ff214a5779ace005c6a929deacc37
+04277eacdda67d927146e32bf7334bf9
+0428da0dbaf654291f3bb753a145a199
+042f8bfd44379a99f6ee109bc3ea3298
+043061594cbc9a4ebcaedd79575b47b2
+04392289403b44b5f15a1d4f37113baf
+043d3eee7d90f8efffa564f423ee4b8b
+043e4f61fb03b8355dd5515dd5acae62
+043fdbc4e305130c6c060cc93bd746dc
+044341ac655e93291393d626d57d90d2
+0444523ef1998b3ae0a3ea5d61ba1f8d
+04460fad5a0807ff7c4404240eb17f4d
+0446dc34fbc9f798158407db20ee43c6
+0456b3e9dbedda1513e4254cbf9732a3
+04590b9a9bb24aba744397c5ed9e3cf1
+0467d22a8a078447fef6ca92b3696040
+046ea1bc9b9697c1b74d5b407a68f911
+04718655e06dc9f3b44e05a26d3e02d6
+047657fad1a0e1ecde91aa8dc903ddec
+0477a428cf5591d0fcee466f56e84ff7
+047b34e5a9c8aa0b9b158eb69151e3c3
+047efcfc8548d2f93ba0d0f86cf2490e
+048655817dfd90ec0c83bbd9503eeedd
+048940101fbcb90f903cadc03790518e
+048f2f9712cf9b671becb1aa3fead3a1
+04901c1b8ee03233f1504890a33a1e61
+0491c53f1c57de44616d6c5380c39b3f
+04933d16ff025b0fd5f236156cf0f1fd
+049be8cda07b90c4516286375a57aaab
+049f0f07992ec5e755855fb1e0562bf2
+04a155448fcab81d12e842fd2243fed7
+04a627438a4333145b3c17185f601244
+04a8c4a47cd42c27d03327a71656a56f
+04a9e724322752867c6fa81394f7cdab
+04acb9a09ad3dac9c3066100e38851cb
+04accf86ad1c7e85aaae70f11db1b492
+04adf6a990aea9bfa6c07d7c1601c3d5
+04b0511f96dcc3d763bbfb6964319cb6
+04b29e460c3838fa2234681600e0aef8
+04b67be655edcd4ab46f1f0cd001a04d
+04b6fea9b19f438922ab3cf358ba5b64
+04b893f9f75d53598213c5bf15dbd688
+04b9dd20b4a5d9673d1cce9488a82a65
+04bb52555f0b61172dfa652f3dbff0cb
+04c018f727c08111a155c842c6e689e5
+04c144edc8aefe30041c65cce13afa40
+04c3b6cc04321791d93fc33a2dd4e99a
+04c82086d33f38b06240e247285052cf
+04cdfbc6d2f6de1c08cfe30e4bc2d32f
+04d0ff2f92ccca0403337db573f17408
+04d3e6bbd0ba917977caa1df23427760
+04d579338f042a5c6035cb044046e514
+04d8159d73072b0cc91ddeee8e29b0f1
+04e231a78948bc752ea8cd53cc09766a
+04e679fd9dde3fbbc5bd0913228088fb
+04e8e9c9feca5fa86ed60dd2953efe75
+04ea261384887ef6e8f5602a9a24a024
+04eb8f5cf86a40e68317b5751c0480f3
+04f2c6f496de3110fa2021598dc7dce2
+04f3a71a99a1db79c0f90caa67ff2f83
+04f4357e7ca3413b627b1d1e5e9fac97
+04f5fec56796218b247a2ae982bd5a49
+04f6501bf06dce3c6794364647246b4c
+0503eb131e712a38b5ea49a834002d8d
+0509271c25c2ccc40a47d139f485e442
+050d55200a22cd6ab9e1cf2a29577451
+0519739b082a5ce83e8d8f8f5483df8f
+051a0a23918f86b44126a412094b2174
+051ea70f37c7c634b8360a3a65e97702
+05232121fadc31451ef7e7195fc9dd6c
+052a95fe99fb2f97809d96016a99fe52
+0539e166b066e708d94e02d4094598cc
+0540ecac6fe8a4fac8dd71a7338a159c
+0540f8e1b015d066a807ea28e54460fe
+0544aa3f14ea2e08b6a0564d7255f543
+0545cde86e1960b0d65e5524dfdb77a5
+0548a9443138d2ac97eab4026721cb38
+0549d54b2e7c305fa6b7cb5e1eead3b3
+05600a454c17ac66045de113dbcaaa0b
+05608b4aa4af64fccff5c391070b7a95
+0564aa25224fcde6f7ce48804b026826
+056bdfd7b59da8d1e7298d5a2d7e49e1
+0574ddd3cc1094c06b9fcf696e7ab10d
+0579b7267e32af48471cb65fa55efc20
+0587ee74310b646b011c25ffbb2fe913
+058ab46a35a1978c967df2a7071c452d
+058e3c07cffd4d782bbc343821c97dee
+058fb39d7471427200bcdf9315604dc5
+05922ddc93eddd17d6073fb8b3fd4657
+0592c7f13feb763cce719659bf6a8660
+0592e14d97aca207440298396d591c8a
+0593253d57ba0d34242209a157b4ed96
+05964f31946a5df2287e21b1c340025b
+059dc8f5fc944dd8817c7248bc348804
+05a218b4ae7618224e393ed460e2bd38
+05a65d00a8c57a4650b8f3955a817400
+05b43ded340b78e681499dac34454a20
+05b4bb66c1be417cdb1839be725c155a
+05be06d93692bc3b82873530eaf38d5e
+05bf5f1e721b6f9c39ab1e260e246df3
+05c30f415bb3d75776ad9c0294f0dc20
+05ccc18c527fc7af31c2edf24494c491
+05d92e0762a5d11e946efbda219a65d6
+05d9bf848c4f25c2615037cd5650537a
+05e0fc7a820fd7b491a33f89be770202
+05e1f2e770bb212b053224eb8be0fd67
+05e4687d127af6ecd1579b124bac4a37
+05e65f8d6e1cbdd4adde86f33f2e23a6
+05e8b64a7ff78eea9c2ea1d4dd054c44
+05f10c99070cb8775b18c6357b5d2a31
+05f138de7edd1bb6ef3989f7e7ab35b8
+05f672dfd0b0ca09cddd507321a63b80
+05f6e96c3e1388a453a2c9df8c52ca05
+05f718d1056fbfac17fa9420fc2fe15d
+0605d2da74d44dc6288ee8db8d836801
+06068fca0cb5a0e0cc218ee8b95c74fc
+06116c06efeae75ff867a0d313a7b832
+06137cdaaaafdbbaf6950fc896bb3cd7
+06208cd60ecf6f556f9247ed515ccd63
+062391f4f45dd37708dbc6060f9b4c5c
+062bf40fdb52c2b94b8b77e38c982064
+062c72ec1afbcb3b10564a758bc96d9f
+062fb2dfe27618d5fd8510ee00ab12fd
+063e97813dcd1f9a258ca4a9a9a2b9db
+06467ad3b06a7e8d4445937f3ad92d52
+0647e59ea78c2082ecbffea826581ff6
+064b4623340ab59429f3a208ab5793d5
+06511790169b5d8d0ba16c402cd9820b
+066695c6b8d7fadb15cbb1b9a345a677
+0666da3578df09a9bf0f680bc43fc780
+066bb32fc53b81636c16f0efe3ed4b7f
+067415e1d91068c399b17667d7f28fc5
+06770110943ada17e9143baa7e43133c
+067919bcc9409139ffc6bd407ba77e1f
+067e8b2c9451070109a6dd46fb55ee66
+068544d4b84f96163f6961d04285b1fd
+06868ec175f1a7f63bcbcd75cd52021e
+068dfeca71ed47d4783d078911f6f68e
+069094b274dd168e46b1c57e5be5ae69
+06909a9f73551ad54892bdd812866d46
+0691ed2d375ca749d22854b49a430782
+0696b5a307cf7c4dc30e059428c36e42
+0697e6acfa92c23ae507a26082d89ae1
+0698417a52978698b08c6228d4640f86
+06a796a09ca594b32b2e5f945bd259a8
+06a9957912e28cd5f08aeaf492ed3bfb
+06af48fa2d0baec9d268aba5911ec82f
+06b6248da8e3f7555697af8d573d73d6
+06b8477effc8005b84c1a24c4e2d2793
+06bc8d51ced82454e662d3d96ead59e1
+06bcd7a02c11fa069278c65c4d4f0727
+06c721e0c2a92f26619b300cea253920
+06cc76c699fd14bce9b8df18b08f5372
+06ce313c3b8b81038263de735b21ace4
+06ce812c979494a286b41babfab1563b
+06d2b655473bc0faf36360f4e8d129cc
+06d52208bdc568504e84008bd25606a4
+06d8fc5b98128beccb77ff50b91af202
+06d9d53e2cd4b9364360bdebf199a729
+06db2d33258eaf878215280e5805c0c2
+06dd5bb14962b16f55f7da69b72328a3
+06e225aebf17a2d1bcf399015678b2b3
+06e33afe6cdc5dfae60416c18262c8e7
+06e5d47522b3e3be03b69bea930043c1
+06e66f87421788567bd8e1778248729a
+06e884bd9247f7e82763f87d48cd7f1a
+06ea755431b6ac1b86ead981b157be6e
+06ed97c9afc6d00b1cad3cc8a707adc8
+06f14011d2e2c8e114447c06eaa58fbe
+06f56b7bae870aaea0ea1ab49cbe207c
+06f58000dc7acf5b03c7cf37626d2bb2
+0700210a31892b7d21c91808cc143cd8
+0701befc6f0a0210ff3ef161338c66c9
+0703b25d809e575b27f0324764799ceb
+0706ffd0494fcb7794f5e864e02fcf9a
+0707454c84e71cba27196ffebe49e019
+070acbda93c9ed024c94ebe7a7bcfdfb
+0717450f6dfffda33819185809708ccf
+07181afb625b07b655612dc97259c0e8
+071e441f2b19555fa57e4071b82ad0fb
+0725dab2dc6f5e71aae63e069e0cf0d6
+072ac3fa0b1bc2ac4dbd6617f26a2bf4
+072d539e33e2c215409ea1429f6271ba
+072df47b55b4b55e36581df68c85c7db
+072e1c774685118318e7bff18f0cb5ac
+0732e3572fb688d035d2eaa0a5c70ebb
+0739d96da7101e4beda064ac896e0d8d
+0739fa4c66341622bedeb92e37a1ef4c
+073e40a1f561dcc708ad0ed2ae6a5ee1
+0740b04cf0ed9d00cad574222d1f8f41
+074241af8a0490f43832a1c66112ed3f
+07424d3e5933262a691315419a28462e
+07434e220d0f1cdac5984b53eec3cf6e
+0745508ee3ef74318c229f93020992bd
+074914f1b33b6411404bf9ef9d6e3fd3
+074bbd5f050c4362eff2803c7755e3dc
+074d258ca6750e452e242fd525300c42
+07539928a2fdf1fdbfc77d4e2d832944
+075e876da2d6a5ab6f99f4279a0e3bfd
+075fa01a439798e8fdd575b3521c1565
+07669c5a075494cc9f8467037ab4c5ee
+0768777d1aea8ae7f8b58f99a634206c
+076a87895cffbd1926f463837de01942
+076a9fe26f75500add3a997a11a9fbda
+076aee2945c566dd0d1f9e6fb5ee4385
+076be6ca26665ff52a83186c19da4945
+076fc0d4b2d93ea5b8f49d5a4347bc5b
+077191f6bccc6e004f852c8b30a630e2
+0774ff2f696d3d3807ee8c4c5dc7a18b
+077dfdaa83d31b5964765d734d163e80
+0782016d0c643ead91f958261606479b
+078913c35cb77670b1505b6f0cefc504
+0789379206806e82d02562957f21812c
+078d86c7f587f450a77815b3c4438f4a
+079209ac4790abebde3f8c83da2e84cb
+079277eb78da04ac53561d9c1f27cb0a
+0792d25a8c9dfa2e98f82c88eba21d05
+07938905a8c4929bdd02d6388dcffbac
+0796292449b67030d59903bc4927921f
+079d7d250c9ce7541b7f991fca73a130
+07a3087ba3b43cbbe6513b50aab72253
+07a32080ced64fabc51cac78763f8a97
+07aa9387af3cf3c7f0040ff44dd9bf13
+07ab1525ba7acb140a0a148521bd96e2
+07ab344174250779fc068289c982d346
+07ad7d64b4a58ade444d5bf536293110
+07af1b7f9523698984f14d5529127862
+07b5417584913ffa1d34b70785dcaae2
+07bb0741f3a34b7f13dd7b37f84c8e6b
+07bb47fe5b36bd2794d4c7b472d69f61
+07bc2aa0578dc9d26dfda23840daee05
+07bc61eef7d11906209d411612bbcfec
+07d865388aa721874d2e59155d67da9d
+07da155f5b679c64db6b08b0b88770d3
+07db784b509078fb0b00c5080afd2b95
+07e629fc2615045100772d76afd37f46
+07f2969d8e633eaba7953faf8584f7fc
+07fcf1348e9e83ffbd2cba4d6f90bda1
+07fddff595828007bf34233a92ac13b7
+07ff70a690e4bc62bcec745187a32d4a
+0800128d8a224b87ab2c356f8b178473
+0801199a333b7c2fb4250cf907d94388
+08029e1a629c040a9357ed1d1f4b44cb
+08035fb2855cc2d97e25422508edf989
+0806ca6d476fd125103bf9acf94e93ff
+081106d528ddbda7a3640f1eeb6f0e31
+0815c9b87e2333772c123636737455c0
+081750113426a0eb8ceead9cf1df33fa
+0818bbae37e01c5a5430b5e0ba32a864
+081a74595f90ee12c9885f8022919351
+081ef7c9f54a5bfafa31647d98bb84ce
+081ff06cef942d61e7fe271b99ac8a18
+082562d517e166ff4f23b7a3cad7ea24
+082a3dd92be3fc581a6b5d7edb4db308
+08311ac4669d9dab261af739153d8dbc
+0832426dbc556aa505f8fcd452b111ee
+0832dd7b163a804bbb400f531398f2c4
+0839f525be96d19646ddc6ee31bf879a
+083ce25d6a509f9f462442806278177d
+08437ae79ce1ddd3685e01d2bc17cfa8
+08468e827f44bc49741ae3672e7e4d62
+084e3bb4c493bfa5eee2968c00bbaa48
+0856eb1b42005f9aaa8d9ed02a4b56f5
+08581977f69f411700dff19c9f5694b2
+085aec5ec69d1e257de4e4e6ae1d5479
+085ecd2037d9637e90fed043f98b0a77
+0863c18ede44f82650056833b79ac8ba
+086765ebf69c91c379f4c90aa3de176b
+086c23f66e6a00ac43387eac58297d8e
+0870a2c7afbedc6deacc18e0f082756c
+0884a0f375d15fa52b1cff208fe3891a
+0886aa63e1f392af5e37d6e58374d847
+088a8c3eba0e1db3a7d9534fbaf2e7c1
+088b2c8de9a9302891abaaf8af28043d
+088b9f58361b045193ed8e3a8acb7f5b
+088bf444eebe451e524e0fb196a8c464
+088fe186746b8e1e11f847998ff169e0
+08912e8d365146f6c6ca19a89de89389
+089ab7fc9a1518bcd064f5676398d808
+089ddb0c4e8ddf3307ebf8382ff6427e
+089e4b855c0206e25448d12135dff5a3
+08a04beb76a35c0b002a783a8377aafd
+08a0ec8386d809d6e6223a9cde792167
+08a1c98822c0fce33a468c8e6244e72b
+08ae06ac2584b4ec28c7ee99185b6d59
+08b4c315947d17490354a74bbc41e904
+08bc72ff8e9f3476c6f18c73efed3b3d
+08c15b037637db9a12d6ed64546f6bcf
+08ca1183b0544f178fcd40eb902cba2d
+08d0ca5f0a5f64f0edf2dd5904f4dd51
+08d180589c3bdd44c027c6e157a59008
+08d2a34b9ea1d0ad7118f47c64af3e8d
+08d7ff0b7beb367a004c425a81d18f9c
+08dcf4b82fc648f3fa691246435542e6
+08e3a8a969fbb8d82aded8aaf62bef75
+08f20abac62e82f22ed5c2a09035f316
+08f89912c40bd7901c5063d7c6853383
+08fd02e3dd10863359dcc361a3c711fe
+08fe4c8d3ae05cffcb731e29ff1c6e1c
+0904f2e8a8a469a5c38d690fc1e03de9
+0908b495d39975bf684dadd276e6482d
+090b022871ef8b904ec75dc9b111c381
+09113509e91b7191716c6ab3438149b2
+0919c5fe197e6298745719e148eadf09
+091c400b924b641962aa59b6b609452b
+09227c6d01fb39bb4390e379f600d16a
+092ecf19eb8dfdf89fa61c38af501dd1
+0930438aa7674780b62dcf099e0d5e33
+093c464b2993a4d26fb7c63832ba8c2f
+093cd513443d447a8d7fe533f93cd27c
+09414753a458b4f3bdd0300ce713a24a
+0941e1658fff83e62dbf090bcc1ceedb
+09441092353cea254fdc0700735dd97f
+094ab9a2f50eb82ebf0ec88cf39abe36
+0950989cd5ea851e5149f1e6f14ccba0
+0951dde966c7ea3cafef6c2b0de77dad
+0951de59ae4133af0e2bc20e38c32d99
+095aa78342b166beaaa70fef97fb9607
+095cdf3a6cc64141bdb8379318ecf620
+0962ad3aff935223aa4c75b61fa792b0
+0963d0f60ef92704971353bf70080151
+096741dbabb7d8a36dd76e83774139e7
+096d384ca4aa91838196c54da291bdb1
+096f364d89a2330e434246252f8f775d
+0971cf04d4d8cac734a59bfe4e8d975e
+0975443b3eed87e4ee8f0409f5f3df3f
+097b47950d953d08d75b4ff7587a661f
+0988d9a2a055e330355df038e5898d5a
+098d8db5a8664154940a3b748fd1adcd
+098fc7270dc6e7c07d9dcbbaf33d0b8e
+098fddf9f57bd2bab5370f6365a24aa7
+09900de1a1267991ab9a72333e87904a
+0991d5dc055f327ddd8ec2a3a1ab6c6a
+09942e65bdb4bfdc2ccecb2e1f9c04b8
+099fa8f893d58361766e427d02e058d1
+09a2de78a057b11d3e826dc9530bd25d
+09a6114739e3c116a1efdd235ec5cb67
+09a9c9e3ab00b7ba8a99a6c2ac3d9713
+09aa0d674db01e6853d2dc00146fde82
+09abc2ab7df0a42cac48e33a348c46b0
+09ad5062470c699577c936fdad6c9716
+09adcae379c1949405f89677553cb546
+09afea82a038e42edd663d77f40c99c0
+09b616f4c4ebe56e3ad19d99061aca8f
+09b79aaf00b690556cd774b9ff4e3362
+09b80d7108a15ac8b6021a5cc4a75f15
+09bb833d28e2093890eeec2a919b13f6
+09bbb2af26d1705af39eab119b338582
+09bc6e9b4bb8b14f81a30ebff96cd618
+09c5ea56e887a4bff31889a7ea18b60b
+09c7bf695173a0bc079046ea5c77dac2
+09c7dce017c60fe0b840d3681f24e26b
+09c8f372965087322fea42c4df91f8d4
+09c933ca1e8e94afecfe48f54514486e
+09ca7b82432e6748849a1f4573c6db38
+09cbdf32e63e1d0d26007ac37d7fdcb8
+09d087393bd0649e21eb045e3b2beb55
+09ddeaa0bc16d528bc8b44a41a20277a
+09e07f311951d1dd404107081d02380d
+09e13507ebdec887ca9a84a4e144a7c8
+09e1df4b67a36423a3d2d435fecf0ac2
+09eb2931b9573e7b1ea2303a5d09a0f2
+09f3702e9f4954c15477efb6d27cd457
+09ffface5278341d4aaf2b2bf80cd33f
+0a02dcca2cc6783b6bc0c5d7bf317fa5
+0a0497c6a3fd4aa18c58c1c7753bdc93
+0a0547c5804955993753bdd7480627e1
+0a06bf1d3b5c84a3e63a443746d16394
+0a06ffa089e56f463e564b90df709090
+0a0be5b85e0e3c91a0981e613bb20e4f
+0a0ef04a14bf38524b36624d2a3c1337
+0a1232acde436a52dcb2e645df45f920
+0a19917a52402917cabecada7cae83f7
+0a19a549a9e0f206e467bd738228ff9e
+0a1cf347976b37c7169f2caf49202b96
+0a1d7c682eccbf088c8953d77c1ba199
+0a24b9acace735db35badb7c363c8b67
+0a25bcd7ec874651b629df44483a2965
+0a26fb59db44a817c7d6a1d7666db219
+0a334adcb90d3377a3f59798e2ce1f51
+0a34802fe17fb87f7d91f647fb62c6b0
+0a36133325855783ea8eaef4bc259d51
+0a3719b282c7299777c1697302484d0b
+0a3e893236fd57a394ec71d418984170
+0a4199d7b5694f36dea4aad2c3f47fed
+0a469bbe55b8801b6580eb3a5a40c38f
+0a4f57cbc4df95e6e0ff8fa56b6aa6e4
+0a50eddf543178726358c07c2b034ea2
+0a543c193e708d8148a622f0ca6d0058
+0a58bb4e9c869625a53abdb9b4655b17
+0a63eefae5ffa7553cc17610083162d0
+0a65932359c3aa6bf1ac193f4fad8b71
+0a67459edeba9638ad5a2da13d6da6de
+0a6d53c04d62d42570f460a6f6283240
+0a6e27a691780835cabb089311ec0f25
+0a6e95df7cca7001aea1c40dc9e3832e
+0a730b09454fe391cf373ef34002b74f
+0a7806c555a70976ce5d4ac68a5d18ce
+0a7aad76799ab6a3563c59f7e341cc70
+0a7bed806fa74c05cd03adddb8b46def
+0a7de060adf4be59403efa8a2c2b5fc9
+0a8131ceceb4e34ed068c8889b9eca5e
+0a869a5ee6bba473166cc015f487d5eb
+0a86d7cee84b1c74c6836b5435d4d184
+0a8c3a73bf669ab7a319c2935de39c0a
+0a8d4a78c16206937dc1543764e663a3
+0a8f9e9a42d50771a6d02f262326e58c
+0a909df0710cd2847032808e1960538f
+0a947b496bc5667ea1f679291c17937c
+0a95e3417712d42b2e4a916129587e46
+0aa125fb2d7720d719f866b1502e8ea7
+0aa220c8834b48055307ebeabb4f4fae
+0aa5e4fe100119d91d15d9bbf7243d10
+0aa73c719eb1967f2a6b7e8c4bab15ff
+0aa866a384250d8ec81af9a7fd788efb
+0ab3804308d9ac41c611c41dc1769c24
+0ab6beedfa035ceccaa9eff727bc5a8b
+0ab92b332b23b5519260039eb100cb0d
+0abc7f847192dc406e6b076d3ebe1fe9
+0ac0cc9a1c7f03da19929f3d86273d4f
+0ac508b362663c641fce9105107ddb81
+0ac7bfb6d3d3c11cd667655aa5bdcf19
+0ac8020d4b6feafadc1d05ac1c230b18
+0ad0287c8e78d10c4e3f3d2c2f7a03a8
+0ad0d8bd137e833c1aca656de8aa11e7
+0ad43a62e74c0c4f51f025a9c01a1c02
+0ae0ea5d0fe28a33b1b2a04cef3ae6c0
+0ae1cb1b249bc92710d870116588ce9f
+0ae26b6e2f3bd3d82739d822fe8d5ed0
+0aeb066404549a91de6e9b03f0b56edf
+0aed7decb3a85be9349009fe1855fa16
+0afc5b415809876089482692afeaea94
+0b0346ce8972761ab8d70c33dd2b99d8
+0b042781aedebfac15ab459bec9791e5
+0b06ba1bb85730a590b5d6b81806e217
+0b06d324bd2613bd2faf250848b9b205
+0b0db59aa7441843447435ab7710238e
+0b13309d3efb217b346b80a693c02946
+0b16afa2bcc7c9ba016095d71ff7d698
+0b172c57e20b0496574a05ae715ea138
+0b19018fc9bad3f67544cdfaacf2e162
+0b198f4715f0fe3d0e26341927a74421
+0b19d18d3e51274e4f6463912ddabef7
+0b19f29a72bc09882e52f665a7a5f24c
+0b21c031c7505e2e5564cf415700360d
+0b24de38c8ddc8a94901840571bb9225
+0b2ab129e154df85e1ef572ae6972d62
+0b2be164b8795610b012c3a62f98a5ec
+0b30a59f92f36ee78f652bf9e2731d41
+0b324eb6e182de50033d60d51cfa99aa
+0b35a79443c5d6aff0d1149a865be5a1
+0b376011b24f94bb9704b7413dfc842e
+0b40028a51fba9b3f3669b36c3895e64
+0b442aa6dbe3299a2c315721d23b4fc2
+0b4b8c4b7f3a7b9e204e6524ee043063
+0b506f8508bfdcd2a94a1cdbb6a86abf
+0b51036353765cb7d620fe9535f3f84f
+0b52d83733c975490471610d585ff239
+0b53bc66e3016620ee4b3af927d44637
+0b55ffedb2eb824fbadb9b7d73cd9373
+0b5e429c0d43cd1a824ac74e275293f4
+0b628f79584671a1468b731263bc5758
+0b63f89e16443a3329299e697962f23f
+0b653d7255312b2cc1e64adb356b41ef
+0b666eb89c21ac69b2d933a54946b23e
+0b667328f139701101f7fd459736f7af
+0b6840b8e665eb17b26dd47c568419b8
+0b6a031c6d05387696ead854371d25e2
+0b730b5dd3bbb77202229704ca98452c
+0b75062d1fbb782cf6f4abccb85a64b6
+0b75b8ac78fc955346844ffa451d95f9
+0b7737e09d3adc5b000b5dde43da7841
+0b7e6bc018ac51a920e5a905e87ad3c4
+0b811565f042a8dd6ef0943d344a7cb6
+0b82dabd3df8171ab7b29463f7a10f62
+0b96552eefaab9e31da4d067458bed57
+0b96a5290a55d9e13a0902304aa3830a
+0b9c7884d88e3fde417c4f7a218ee9e2
+0b9f02ae3a2d7ad12f0b267d0ac5cf48
+0b9f02b807998ff4d0fb2cd71b2c2f92
+0ba1d968975ad74c1eebdfd1fa137d2a
+0ba739acd34d11a5d543c2f512257466
+0ba918b2e4ae5a50f6fe54bb6909683c
+0ba9d1affb34139d41a4cb170728d0d9
+0bad721d9972d7d9b00d2f3571df8965
+0bb0c3d9639c83f817c9ed17200040d2
+0bb37a7f32bcefa49c6209b6cfd0937b
+0bce5c42a39d2e960e7f9d262db42ef8
+0bcf20f6233c20e877e71e5d3b1519d4
+0bd081f954e2002edc25caa206ab72f1
+0bd3a43392b90f7d41f091db3e5484c8
+0bda4266222fff5a2b85904a3c947383
+0bdb6b35d0471146383b699f630326dd
+0bdcc29ee516de7aa307bd7dc75f442a
+0bddff61793eeefd08ce666ef9de6152
+0be3b5402135bb0691a051b61964c667
+0be5947c00058cccd78a86f12882fb32
+0be790f0456b41840d04d17c31908b1b
+0be9758097779cd514cbd4def5ab0986
+0becc9780c18190a4ef4de97524d06d5
+0bfa51a39c0f135d4a3e1c7e3c5fc06d
+0bfc33bd0f8a28812fdefa831228cad4
+0c060c98eeafec2386d9bed805a72f2f
+0c07661043022cc8d8cb4936f6ed58b3
+0c08bc325565ddeae398c5bbe237b163
+0c0be3b19f5ebcc311bc9b06c9609285
+0c0cb0acdef4a63e6b88abfb204b58d5
+0c0dc06b800bb971ae87210568aaf4c0
+0c0e282cb411cfe115ab5535378a0e1a
+0c1066d0a1c1918b62ae4d2657c6298e
+0c10b316ba7bf29316e47b169b9ccb14
+0c11b6392e4fb9c2984facdfec2b327a
+0c14ab1ded7689f88e2618218a33c10e
+0c164219da269fbcd54c6bd2067ab7a6
+0c1859192d3f9cf0a23f385666e15183
+0c188e683bc169cb6a23c09194405929
+0c1b3e35cd0c62d01deec40cdf8d308a
+0c1f8c87e46e101dddda9c0b972555dc
+0c2544f754e6d37207ddf7eff16464c6
+0c2e75fc8d15ba9861666c197fd9ff8c
+0c365d4ae6d17190335a52bab9aa091f
+0c4b63b996cfcf760ef96df6e94e828e
+0c5375203369e0bf639809f0d0e82817
+0c569cb69826b55e5db8707fb1cce4c2
+0c5af4988f4651aad0c12e7efa40ac41
+0c60d10383e626af487ee609e80a2384
+0c631a68d067f434646f1b8a3270e43e
+0c64875e5d45226e1a592aa2bafce152
+0c6505c9bd49a2879eee1a73334e2b46
+0c6ba9b3c049d8a8dc24bb310f3971cf
+0c6d0fcac209882711a1b08d798e341b
+0c712101f41262675b63c893a288399c
+0c754bf9872b7a0b5f87b7818cbe456a
+0c809e865a6f66ef2725aa5602e2b4dc
+0c8879c58380563f25c02cef57a41e24
+0c8ba5a43423184143cd2264a3dc2280
+0c92475e9795e94979c936db5cb0c183
+0c96a0c1440c18d0a5f07d5268b4b672
+0c99f194044366e5000933867250f5cd
+0c9bade4536fb12087e92055a7d75ed4
+0c9ccf82c0acd274ee74052d5d96b56f
+0ca08ebf1d050a505145df8c36931ddd
+0ca2a5e5e81d0c0b6509fec16badb8a7
+0ca44e0b32275fb360efb954bd0e29c4
+0ca582d5e5c0f650aaba4661589cb17d
+0ca8e3f13411dce0d88d27a3bed5ed13
+0ca9f52d39136f30dd15291e090cf06f
+0caff2425e3e7703c8a2514ff6ce7b84
+0cb2b1eaf15603d5586313de8a2fc57e
+0cb311aa28ba7a088d026043f8d1d763
+0cb54c766ce827132320fdfed1eca362
+0cbe264402f9c82eb5b61241c5e2fe6b
+0cc05000008985599340cc87f5d1b5bb
+0cc41001512935aec942d1766348446f
+0cc78daeac83e97e557d2015f8b3b97c
+0cc87829681460790e2c90d8c2582bba
+0ccabb436f87e1cca31553dde65c12e9
+0ccc0625f04f77b5ba39c441ba039f17
+0ccd993eaea261ba56301aaffc4a81c1
+0cd103dcbb2a3774d4918b48e06b916b
+0cda81de698cb75bc2337e2229f7c5ec
+0cdd90bba7b5422ecfb59eae0f1fdeb2
+0ce0750d3d153b57be78ab9dce57c043
+0ce0818ba457a181b56bd1fde803b029
+0ce2e16ba90bfe64dd12c4a4b726dda0
+0ce365c24e6587f7cd3ea0ae0875e570
+0ce544a6dee68c7442fa536a1704f069
+0ce7193da57ed84ed405eb4a5bd031cb
+0cef86f948db0353c36e42dfafcfcd87
+0cf13c813c2a5cdcbf744e7d4fa293d0
+0d026654316328c989c5703b96268bbb
+0d043c7b19b46f6d8c16177562f2216d
+0d089889c7894526987d8c624961a29f
+0d0c0c6f6fa9476b898af05a698ca285
+0d0c27351b2c8da3fe3a85f05c18c623
+0d21ca02239ce0119f1cf3f937ef38c8
+0d2938459f80e9535d1a2ce9770d4515
+0d31748dd012bfd45f543f6e25a7dbe7
+0d325484f4c13489acae6d3b7d762c61
+0d37767ce986889d6641fa06293854c2
+0d380e7e0790f512a4066014af8ac82c
+0d433178b005d63f5c8a182f559661b7
+0d4537ce9283f497303efda887933126
+0d462f5ca83059c8c0fbfbde4e4c341d
+0d491382c717746600d77c5988241b3b
+0d4b6eb2e4e960e96458ea0a9a9d89c7
+0d4e4da299f63a42b1b2ea5c633a5494
+0d57285519452c46dbd447e9b98addc4
+0d5ea69ea289e169da701ba5039b7e05
+0d6194dda4c13bdd2af3f8af6874a65a
+0d634f81f9b3d21b619ac7ac44a77a16
+0d637b2e645e630cf9ec356abc012e40
+0d6483789ea65ab7fa8aa689f2905443
+0d655778ca6b82231cee6e7aadc962d9
+0d68d3281f711450e3a93e6464534356
+0d6c8cb94799222cad0b3294c60d6282
+0d6ce06655da03213089e1f0e8790fe4
+0d7139e85d5aedc57a3c066d406934be
+0d727d070aa9966ac7bcb7a45e6b9a48
+0d760e38c1ea31f81ac929939a62c054
+0d77a2d0a4756ea008720f5cb697b836
+0d7b234a93a9bc1cadf118e3bc0eb3b9
+0d7b950cc82f90aacf9e3a01b5ff54d3
+0d7f70f4bfae8ae1f2d74a4b21fd2af7
+0d80f597a9bce794c3c322de3ee090a0
+0d8f59b30c9d507dbf2a87229e790a7a
+0d948cdead4ceeb51f08e4fde057d60e
+0d9616162f3447bc2772e9bef721a84d
+0d9bfc675171b7512f60e2180ac27900
+0d9cc323adcd7912d1b232cee172f5a3
+0d9d153a19ae89a62edf4a7676d999f1
+0da1144d92633c1a03b8f2f9f5626d19
+0da69c29a7a4e3ac1d04495b5a6de834
+0da88b948d8e1deb0be142a2d2766f8f
+0dadaecd66fcf5c3825ff4b1fa47c65d
+0dae9e80c1851f7f0506d2c5a8ed73e7
+0db5d869063fed7a970f7e72d02873d6
+0db950f044696feb380809529fe4609b
+0dc0549924709113303d05250bc53ca7
+0dcdafab2352b39ab88ffd8128a3134b
+0dd094dd9a37d13ea5c3485989c39e36
+0dd1a13a96ac99da74135c30ba94086d
+0dd27af4feea046484d7edc6d654790e
+0dd58e24193d8ea33e68146eb23acbcd
+0ddc50e50b86a06dc5b20e639b49412e
+0de353641b08516df8b601b2828fa4f7
+0de7484b954b75f1a045165d0c29f66f
+0de7ba720cfe4b39d3b439445a2c9df4
+0de7e98b0d1ba576650c1b1f47758f8e
+0df014529780a8e3ef65cdb4e0be8286
+0df5ebb3ba07dab03b08664ddd03b305
+0df84fac86e058bb2ae958028be35618
+0df97919d01bd980c358e7a3c571e3ad
+0dff6538e8458ae1afa246f972cfe65f
+0e02f4271f5c0789577710ff017be6c7
+0e125d9ba1e7135a226e523bad5ed59e
+0e156170caaa5b098a2fa664c23e61a8
+0e1770494214b19d9c7e3de2c05fef5b
+0e1c308f09373cf7d83e70c7b8f5d81b
+0e1e40a16950ebc250d5cf24b1b16a52
+0e210164523eb617bce3a5bd93b567ed
+0e28e85962a24d8dcdf8a49767f15235
+0e2b009a910411939857c50fd7149be5
+0e2da7eaa888fca0ce22f118de6628ac
+0e31ad11b4f016108bb4b4031dc5fa67
+0e34f3254da8588061021d3fb133717b
+0e46bb9f1ad35e36b1b7837bf1783d95
+0e4d72d9df1f05bd3c16c6d97f033886
+0e4fc91984e6c2312a7aa403399d53f2
+0e547ea85d52997e7e093166fd688e31
+0e57175070bf78cc0c1e0e5d909acc05
+0e5724341a22d1d39f15ad427431993a
+0e58ec4ec9eb8286b53b44319c3a9442
+0e5941408cbe6ad145187923a89285f9
+0e5b5dfab8df9a5ff3882b2af697026e
+0e5d6e2bde4aec6a6e4cfbea09c1099b
+0e5f13fe179bf0e09b8f341e5f48c0be
+0e5f24872ccf98b41cf41fda358a3e1e
+0e669729c272cf6d00351861b5b6ea37
+0e736a09e9986ffc6e70b1a1751b4b44
+0e78348146c815da304ba1ac4cea89e6
+0e797f413e0a82f0e140121afb1a5b59
+0e827827cfc9e9e638317fe7b5aaaba8
+0e8683e7bc393f6a1a867ce21cc85546
+0e86e26592010a4963c3bd4ce7b5765b
+0e88dc134e12605c30ec51d5960fc1c0
+0e8b8a661598539a506764f0437cba94
+0e8d8f63596cd608c32da08feb8a56b8
+0e8df75411ba4637b64252f450526870
+0e8e8bf30e198984cfe11b0626d730f8
+0e8f36a9ef75bbcf52ec481c7197aaff
+0e8fb81dda7816cd550757ac6030791a
+0e90bf9465ce433e985b730bec9c97b5
+0e9676bf477a871e2526b9a3f99aee67
+0e9f2d3bef56a7e97652f38f88590434
+0ea55695fbbb4646087f89d21db8cb2c
+0ea8a38a926d56ab3aa669a5298bda4b
+0eb24e59eb5b9a513739f363d6b315e4
+0eb6a5f3b9529ca81c9dcca81c5cec66
+0ec48493b211080c8c8bcb4f3d381cd3
+0eccf0f3d0c1d6a3c6154cfbca3f0245
+0ece627667ab62b0bd3a8d345f488a16
+0ed19f0cf12ed6346512331c057666cd
+0ed5edac05f7cf40fc7ebb71d8238eb9
+0ed84c68e73e52ed46d4a9b4628d3b54
+0edbbee4e101c0244f0244c235f8cb42
+0ee71c4f9c342865813426d1d2c00b8a
+0eeb3af04852f25165d5a66303e611e1
+0eef3663b2177162ef2a24da7a0b781c
+0ef32cdcc2a9bb9cfb1c8f645f3e6abd
+0efa937a3c26711df681d70addc6c10f
+0efe9f0c4b5326699e1ba0eeafd82a2b
+0effe69bdc61c3352c76643f4a2e6be9
+0f07edbc8001026d4418485cf7d30e0c
+0f084321f75e82a07bf2fdcc639bec02
+0f0a1925d6ddc2a70ad46b8468c3a141
+0f0b063bf82e1356e5d05cc9b4214b26
+0f0c80cdb642a17a3c252d08c4f79785
+0f0eda8fbb72a5e561633cb7e6946475
+0f13bd262e7e30828766d7b35fa17f1a
+0f17a4d379d0581352759f09e7d34e1e
+0f1bc9b3c7c58b52553eb8955d2a0f44
+0f1dddcae4a97d15da0a02eef4b6e8fe
+0f233507357d545890de3692eb102165
+0f26277935ad7cc3d6042d98ef0eb44a
+0f2edefb17adddf309028c9de43fd8f8
+0f2f1246781ca625a3bf7b9dda7d1d26
+0f2fcc14d0ad6124fd975f62326334af
+0f3067572f21f76a043e7b42e290c4ea
+0f3102326411a0ae1b29b6b344dc0d7a
+0f361974ab831b65df64cae2ce3f821f
+0f3801db65893fe3fc6097f378c5bdba
+0f39f0155f32c6b709c183e8e1819b12
+0f3c314ef0c2e3b40eb4ea32948534ca
+0f3dd4b2b97236e9b44e6c029796d7ab
+0f3ffab0266a3737656da83aa96cb7a8
+0f41742d11a9f0aa8bac715e305bdbd5
+0f450c11d33a1e550e16513639a2655a
+0f46ef50485715aa306f0192e6de7702
+0f480eda91f4f6e357e3f6d15148c913
+0f4aef21c3dacd6cfd76135c481b695f
+0f4b688ab5cdc2a3d05ba9d3b6a8ff38
+0f51cda6bb5fa57d1fc8819f71cd917e
+0f56846ad3af9de20e09f7974aaba7e8
+0f5c0d76db59cf0a4cf9a9993035adfa
+0f5cc9b0af3bcf3ab24ddf2ddbcf7848
+0f694ea96523802d3aecacbbf900d482
+0f6d0acd80a1aea135f052454c3a2568
+0f7248e691f8401eec164e36f50f5837
+0f7290ab380f0913c1888b2501444ce2
+0f78e33fd3a5ec0961e241f99b251ade
+0f7ccc8ec1b11ef3d1f3530688a42fe7
+0f7ffe43653f89fd68a9ec7de71e4eb3
+0f8337488ae733dffdb793ddbe938b29
+0f843476ea15266b09a0238bc68b9049
+0f880699c627024a53c3a2d56c75103e
+0f88c6a569ca9b2fcd374a30949dbe42
+0f88d8dd26bfb06566d620d948801726
+0f99db2b7a5695cda1cdf86583a8b01f
+0fa1b7090bfd992ec9140b152851e7d5
+0fa268c92338b27b7da0b9a85d775061
+0faada83c84d2e19ac8e4743a4dfebfa
+0faccfc399bb3a63b9c1ebbf942e54f4
+0fbe06ddb8e3416aac63fa8b4fcda858
+0fc2ce4e86a1c636980f874fecc00938
+0fca7d178cfbd75a1936c47b83bc30b9
+0fcc66697dfe2e8050b0a934c1141092
+0fcc8069c0e8d9cc8f3a45cd3cc1c226
+0fcd88b93a783e8da024984898719d38
+0fcda1b2506043a5d951dacd64b9f45d
+0fcdb49e090d9ff8830cc6fae07010e1
+0fcfce2f4323f70877fb289c263226e3
+0fd42ea0b57287a4c6901c0a30a67646
+0fd78679937e17bc38fd166795ba483e
+0fda526bee0d1ad217d53fba68a0adc0
+0fda627d40584896a1199a1d9b110947
+0fe0a44d748de5df478988510b512aa6
+0fe25de3422190b0f760c439c0e052b4
+0fe3df008a2982e716b9403bd6487256
+0fe4483817a6fd1ff7c48ff689c93882
+0fec9e6570ac779d7c8b8d0e0122d271
+0fed82f6f74bdf8384493db0c7671156
+0ff02af53f8f13b2a60e68b08d82f554
+0ff2c9c047011f191d5804f686331310
+0ff7b3c1a3e9dbac555f5d0699527a11
+0ffb26d29a23cb71cf0ed64cb53698d4
+0ffbed1cdda7c40e39acf2fa5fc68c3b
+1001305fc0cabacaa99cab32ecbb57c4
+10078dbc57f60d59072eb36fd420ced0
+100906e33016af6ce1c670b0166c0353
+1009a7cd5e03024ad2921603d3233f86
+100a826c6ffc8644d03f279b1cda3bf6
+100be512e608ebb8a96861d003fd12d9
+100ca3579fe90422d14ba15f108d1a60
+1016cd94bde0643533c4013e7e2a675d
+101714eac8a241ce06e02521f3609580
+10181ecef7a5a35fa47e42433e79a5ac
+101c04899b115b8ab2742705288288f1
+10205324a44f7919152890ec351ebe93
+10212d06b7c2c6011624bfbf68098ebc
+102e843e300a601258c16dde33e88fee
+1032aba198dd5b86fc6aeb6a0c943fbb
+1033daa4dc4cb82fe24f6157165a0525
+10399a21ad01bcb6df26c4108839990f
+103ea9048c56a5d0bf54965f6f29e20d
+10429c6e838a19729542fa22f5ce3625
+1057295610e5cbc904074183aa168d09
+105f49e991ce8ae8f2e8b8a6079c687b
+10608cec96c6e2ff666223e7498ba740
+1068143256e7fcdc7ce24bc5e3ba92b4
+106b378fff1a4a9514e1e47be5d03eca
+106fc3ead7856d5d8fd45942f342c7e4
+107bb7cb2c8d5e497366de3bab2b277c
+107e824cdb7aecca3334aa8d0f37be3b
+1083ae8361c36e482ac2972e91e94295
+108469397de416e5dd83d39c4b23b5dc
+1088550a31645ca76b6fc00e4a3db18a
+10894d6b0acf1426a6d0254037399f33
+108a6006f113f0ea3147ef5a51c77030
+1092448650d608ff173afc701d482c31
+1092b158485fca75f40b4f6a14142c1c
+10949f67e8182f1130812489f0f9d678
+10953298b4c50836fc6044d036c1bfa1
+1096f4480b05634699c1b8ead3505bb6
+10a93be5e8225b84a67a0f92f5c6fb6f
+10accfd982d8eadca835ecd8dd8ecbe9
+10ad35dd7e53d6dc0fbf6ca867bfde0f
+10ba518561fa25ba99023681bf6853e6
+10bad937f7c4a608823fb4b02763c647
+10d00e9e72874137d4c678d4bfe0eb1f
+10d66bd9d0105d529d725b52147f19db
+10dfbdc086682198b244a8830412eaff
+10e5aecaa113e019450876b6aa37ba56
+10e6541e72f84aaa3458e5b1f4c15814
+10f1a36f1301e293a64f59fb7755be82
+10f36ed1c0859b2172511517313dec94
+10f94cd5c8a9f942173b3e8091ef0410
+10fc5488af8bfbd440f04af11f1bccca
+10fd1043393f347d937844e4f7e0b296
+11056a7a40fea6844e4551b87c8dd989
+11057a4ab71cd7caaed54383f9866e06
+1109a6e6b79d2a6d40f926a29ec0bc48
+110d551b984759326f46e6effd06fd35
+110d6867f3f9e443b2bc391836196266
+1113691aaf2b966f77c7ee81abc01818
+111bcb1f9843aa7b90051c9cc418b12d
+111e73222aa3d699b05716c3d5d3e591
+111f1aa2235cdac22b83fef964a82a3d
+1121ba5d884fdfb8d2b49f06ee9faac6
+1124043baa1c4cef2046522753c0bf6c
+1124c4345681d53b3cc51bd9dea25a48
+112f6104588d393db0ba5b1446de8960
+112f95975f05e195ee79a29b3244ce95
+11379eb9b2d4be9f88bbb4ad9ba66843
+11383a4f6b07012c43d81df60fe31cff
+113f0d67f8f8eb8e803c4a1784698af5
+113f8166fe3cbaa763583daa349e10fc
+114395d2576aa3cd1714e3ea168a27bb
+1143cc566294ae910d1c4c9e7e7fc5c6
+1145886091b199a2837bbb67cb3c9225
+11517d554441e7e5281e55602406bcdd
+1153bb7bfbfe5cd592a712d020f90902
+11559a4e1f0b4a40665091ec7070e660
+11597e8094494755622df12cc9215f9d
+115bcd62a8f5c554a8e9dea9c6164b86
+115d3bcbab3ad1c10b4e0695b85d3ba7
+116277ae4017505ba017ee9e38a48e83
+1166571df4bf038219962b0af06873c3
+1173d9b3539090aa76ea43763c3a54f8
+1174760872ee045ec13570ff261f5d41
+1177ee8d3cf58151f26855decb87448a
+117c7121c9d179c81727835daf0469da
+11825f0c778767c4edac04da84a55045
+1183d806b73ab05d71bf0f1e36d61b57
+119617e80751cd2917a931e71ad96421
+1197b413c0a5ca464f5a8f31018d2d41
+1197e4d7a711f01e55df77dba9c36099
+11999aa9de849e73f59f90190551a5f4
+119b7ce99c777bcb8ef630ab41ae5453
+119f0ec6cc41973dce1ff9e793d2cc84
+11a2eead415f1d5fc48f324a07c03d3b
+11a528946249869507cfcc3f5db67b14
+11a962c4b608358ef1d4eb837eb80b34
+11acffd459726f72aa9c061af7adcd85
+11b17a6adaabc1df315c3bf0a4e7be40
+11b1fa687db65526bfbd1648bd36abe1
+11b4f0e06ad027fe519be0a66e8e2f1c
+11b76eae548ed79cede3592c6bd50c30
+11bdd976861834ac9025e5f7681bff6e
+11be0d0ce8a1ca91041f8a7ff991022f
+11bfaa40f390a20c5b1d0b2a08a95726
+11c258da2996684bb06c6d79877a8550
+11c479ecb5642ac01ce3eef75916adf7
+11c489bc06dffc7e526233c0f7b5e867
+11c6e64505d16d8f28703c049b2dd923
+11c72cc08f344d75d7a8cb3893e92dfb
+11cf970fd27bdf42cfe379fe06bd55d6
+11d2012c1028a12246946893cf808587
+11d3ad285bdaac42e33a01fe0b0d0366
+11d47070343043e1abf20a8fa2c73b30
+11d7ab551796a9923c2fae5199584b9e
+11db6ae6137ed18f695cd6a9a333b2b3
+11deac2627b0bf1e7c81c4f026464dea
+11df5de877a48379b0f11611dc571e42
+11e6297925095f809e1019e649c73a43
+11f41d781c031cfa9d8728f1b93e037e
+11f7bcb4f885dbee02e53d17924de9dd
+11f80bb140dbf38b6dcf63574aa0eda6
+12026eb357b3e300dfb6482abe259021
+120432a3bc6603feeec5d0d3b704f26e
+1208d34d9e9995e83ca1ff6650e81a1e
+120bdb07b34c6597742c090cabe63d8f
+120f301d851fb26862a35cf3f8576e31
+1213c1e4e9df3ee5d13ca7eeac7eb5fe
+1219b28926238df6e1ba11abdfc4a52e
+121f4d8eed1994dd2cfcaa1511a064ae
+1230c3eb87eebcf7151d5b700467db1f
+12396158edd29d2337bea0392c44c3b5
+123a21f254a8908de84adf24705dbe50
+123f6d7cc10ff9816c998eae660f01dc
+1247402305775be71cd7e2fba51eb65b
+1247dcbf6a4b474dd2399f1a179c9309
+1249d735aa53c98af0cb629e993ff8c2
+124ba142f0d94f8281e865a09e35582b
+1250af952e10eb41d0b464f259358b40
+125f9ee0547f854764f2351664b07638
+1262d6717e4c56e13687258435e591e2
+126345495dbb65717225289702942812
+1265814c354605c17a7d514a8b969d4e
+12670c8d7b1608f535779a0410b6f135
+12670d69c78ff47edebd42edbfd7eb76
+126cb41e3cae5b0bb0b149459ecb54aa
+126edc72591b20cec30af20ff365f7e2
+127474dca128768f867589b714cf387c
+127707e65caa351159a4e22cea3726fd
+1279da5bc23bb63e4b4206f64279df75
+1280def9bbd502c005892b80c09536ea
+1281f131fb36462564d49b92e36ed593
+12871c2ddb304f196a2127b67d6dbf37
+12912bf30c7ed273f553ff3b01495b57
+1297c88edd5a7496e750ea1db855e519
+129864aa15d10feebb69a4aa4ef9d917
+12a31409ef8760344fb16798551a8610
+12a77b697957ea2c133a31ce19dcb63b
+12ba035390e11882b70764044a2da62d
+12bbf898ca9d0bee089408bee5540d34
+12be5eb2b9a5fe40f928501662dc1334
+12c7c32e72a46fbf7af11c352afeb230
+12cfda304e51c98e4b312a05d558556d
+12d377322985e4eb110064dff4c7bd56
+12dde9d976637b4c76c95d27d375bd79
+12e01fee72de518f1c153709035f276d
+12e14838734a6138fad1f1b41dedc7ac
+12e2a374c368ce27e9dc81995f02d511
+12e380ca9cd6f88dbb9d0bfc32ae2dad
+12e488090acff250a05d7bee2608f781
+12e96adfb0bed9724eadd952048cbf2d
+12ef1bed8e44d1383884cebc5f7a31f6
+12f3033d00cad371551a0bffd9bf7c8d
+12f65b36c0b646124a6d85e1ae94ad46
+12f8f084b3be4ef72978c51d4efecbb1
+130001313ced605c946ae0e14a33e3fa
+1305036bb5070cdb2bddc34c16891dbb
+130bdabb875443ffe9aaeb37db150313
+1314e38a699a0efd3c7fef462d5a16b6
+131658a51a64170089ea8abc020d5358
+1317a6e3f2942772cc82f9cfc9ba7432
+131881688c24aeb84662f1bf4ffa6f34
+13201c16878e3ef9a97eea2e859e05f1
+1322c8e97f4a68d957c3513d5d57769e
+132368fd5de0bdfa83950b5726018c98
+132582e45cf266570aacf9311900a13c
+132b943c9af8b67d80d251cc6934fc83
+13302ab045e1caead15bf7b07ad74c89
+1333b1aca22be3e0c03a6d25326a69c4
+1338cdbfca3fc8b7a30b66e855d4179e
+133cbfd181f450dadaca2b31666bf58d
+13403f4b12366b0d5b74a2b046dc279f
+13440b79a087752161169627168b0cfd
+134f971d504b1fd1cdca10c1f2f8423a
+134fe57cb2cbb5bc7b2c0221847ef8e6
+13502156c7da5961ce94e194f90f8fa8
+1350a1e32c9a3e0474991e3a24cc3ce1
+135573b557221ecdf1a9d893ff4ad83a
+135ae5d4d6b3b2a34f86478862a6878b
+135c92ae0a7933eb24f685d4f4ffe6d2
+136416f89dc644c844558dda59162007
+13671d538df10681fb4a6bd3f196fa3a
+1368b418d08d979fd0ad4bf82ffbe2ff
+137012cb7eb02a4000b8e74d9da1b599
+13725042c309c9d7a8a9bf138399d66e
+13750782190adf5bd887f2b0262d9295
+137a3ecf7c08944dc0aefed82ae5a2ad
+13833267b90d16a7b73cfb69a6f9e25f
+138672e70d760fe9a01d8606cbc461cf
+1389ac1cc5537693689c1168d771b917
+138a802f4751610beea1d0b3229e5588
+138b803ce1ebe0d658924024369d65de
+1399c930687a8335b2885f0f558a9719
+139b8eeb57d114d4354c4355b4915479
+13a371e69f5ad1444e21cd2257fc9f01
+13a433e30b7937656541fc4b6d20a3a9
+13a990f13447b552a57e86d5a7ab9a65
+13abb1447a9a517d1559aa6ef7c1bef9
+13abc932d1a744f71038d45ddc88673d
+13afd099a43e91dbb8d6c1c2da5391fb
+13b530aa7222ed177cf134b5f7d4d8fd
+13b835f43e880515fe4d00d82ec4d879
+13b906fc74c9b65e1ddc92dc8cd4745b
+13c308a0c10631a72dea575208930781
+13ca63f638ffbeda82970d4c2895fc70
+13cc674232435e22462275038cd55362
+13cc6e999717e8c0e5fc8def220bec54
+13d26abbcd4c895bf2f7b4a2e4be4389
+13d4fce1a5538165405a72c80fffcdac
+13d89593a361ecbb484b2b8ad2cf9912
+13da703cc8cb20c6b9da59d6cf775e6e
+13dd6a5e2ed55080b70caeb0b5c8b88d
+13e344650d5cfb4ab99448a215427e95
+13e966d32d34630fd997294846626345
+13f26e38b8d28e7208294ddb0b76eb1c
+13f5921de3c9709de899f2b7226094fd
+13f8cd49949d92f1ec5a857b8bd87e3c
+13fad78df24e2a5709adde62e403e01d
+140265066c1cb7a2408b0251c1292ef3
+140369d27e96feff02891f6abe9522ee
+1404c41dec97d4128d0f07dd6770f022
+140bc726205d1e2d395d522999c13951
+14109687a006f0ebaf143d451d5cfd91
+1411017bd6e69eba5f4b44c7d705a1ef
+1412a50b30c93d7d4a9bb01d2fb4d4ad
+1413a48c20e9973d75ed28e20d4f5d01
+14190622eb28c4e0481ebb8b4ab3750a
+1419c14bd28bdc9066150811791db025
+141f9d78c9003576ccf090c3ff761a22
+1425fb223c5ada047bdd7a17e8ebafbc
+1426511b083e72177477f0fd6fcfb99c
+143592cb82c7e8aa8a485bb9e45daf8b
+143677189012c4a983edda0b56fc773f
+1437a13ec545e8f89d803a7e05fd4b28
+1437d7f6d43002bf9a563e48a2b028ec
+143adf4bc02ddb5b2b61b7d9d2564479
+143c8b50a7fba0927f72a0e4986b6dc7
+14421442d970bc8e8358bb20b614017d
+14434a8fe8ddf027fd30c468178e3a50
+1443c4ce7292bba9367d19de4fdd929d
+14467ba6dfd0953ea3ec446774115177
+144697347d9759fbc7acd9b19f3d6543
+144c9d148333bfa751279d3834828496
+144cca949a0e0b1e58877a53cdf8ac1f
+144f9eca5598e6393999d827ba074ec1
+1455a351ba9405fe99814a0bc38358b2
+145c2c96cb11b838fedbb7dc66d0dcbd
+146b45212f4aff1755933ff730812f90
+147c9d7a91b0258a01258488e1862d33
+147f437dded2dbc2dc458cfa5e17b21c
+147ffe6a6749cfb559edbb17a4c2ffae
+14826e1b8488906c054548b5f180eefd
+1482ff8a742104dca59b7e3109e47c27
+14838e15c6709927853da3d178bf59e4
+1489a125597040774edf209a82405a75
+148fe26dda41d088a90fe27d1c98cdf3
+1490ef903406dd9701d9b6a5d8024158
+1492053a57056db6f8f9a6ad25db7b05
+1498ca4f00d462c7808747b3e78b5208
+14a64cb438e64f7d03d384a70d398e38
+14a80a0eb327049a5a7036d28e8ac553
+14a87dfe76db97843adbdb0aec7486ab
+14a8c9fa5d1ffaaeb32201982081854c
+14a8ead15bf9cd19b208caf56e858b09
+14adbce250383c02af7340566843439e
+14affbf151c62afc246af74cc0f8375d
+14b16159dc1a6701488da930a30051cb
+14b44fca2a57d193d21315d39f537970
+14b53e202d452936540e488280aa4b2c
+14b8ef1285611e75ac0595dafd927c29
+14baced4c2cc51a5ab4012e4df3814fa
+14cb0a2c18b7606ca37e323f95744eb4
+14da0bcc720e18333647503872a68a5c
+14f8b6ba8f5e01b7e86114cd1e7b3cee
+1507644a1f768138471019a124d38819
+15087594c3b0e52c8d54f5304cc7ec3f
+150daba20b4b365421fd13b12a8134f3
+15132a39a8497af69f16d8b33ca6bd68
+151810620f57e96fd73b48c529a3b6b1
+1519b36be935a03c82aeedac12ec598f
+152628fdf0aa47b40f16441cf5ffd716
+15295701bf03c51d4326d38d0c0fcce6
+152bbbd64bd4767054ef4837f0aaaaaf
+1531809569a06e06bbcea67a491e936a
+153185b470ccf434780f90d6dfd60811
+15339e4028ac0d116c2c29af8987717c
+15362c8acc6bb1aa0b043f3b97846458
+1537e4ad6d713f2d9d8ab58a2d5f47f3
+153b025233b2aec538f53c49c92f1e5e
+153cff860211b3e48c595fc1ef3cf78d
+154128bb87684bb1af24a5779a8780ca
+1541b37529db87ae97ae35d6f3230d51
+1547ee6924920ae7dc03573d95c84a63
+154aa7b39beca044db4643d55913f1d3
+154b7fd72c9968ecf854351bb2b72326
+154d1e2de882f1fdbb1973ed48cf29ca
+1550f4f78825b8bdb13e995cb993f069
+155665b312213cfb4cdc1809690c8ae3
+155b055dc44952f5502674905dadb8ab
+1560b3940ccff4d7ae0559e22e8d2200
+156370c58c2d8bce4f3f9046001f3442
+156457e770c85542cdaefd61733999ca
+156da521cc8d497709dc332a3bce058e
+15721bb8e99ad3c6b4dc096681c69cb2
+1572b34c6d902fdf50aceeb444c104ed
+15771e5d104294424d7def0206c4a076
+1578e3aa63787355b804278210f88ce7
+1579daed36e2674c89f8ff0ec5785b4c
+157e1a5e77058749a0bdbddb7e3e30ce
+157f7dee58295642e4bac3f91784d41b
+1586ef2d9ce8a60c1239e8267dd4792a
+15888fbf65a9f1b116ffe2ad88951a2c
+158aeb242136ece5e0870b45c49c7510
+1596cf0f7c7db667cd240e03f3a4263b
+159845465bac31f8269433b900cf944f
+159f45a5bc0d401d55c7b00977edf1f9
+15a665434ced6dc3e0e741f3a4bcb2ce
+15a8d74ce500df5615740d2a3e6365cc
+15c18eb03fc9a648fd9762dffba372e5
+15c45c01e0ec5542fec5091a531e4fd1
+15c9a3737668b270fe74dfca822d10f4
+15ce2158c0dbe7ba8dec3f51c535d4a0
+15d3cdf1c738824eb056d817ee43b5da
+15d438fa351dd07309b8e8a6a9efcbd3
+15dbfa4f4df1685218d9f58cde2db2f8
+15eb32ab38693571438d0b3388e8c0af
+15eb731fedc0462c6ef3d705099afd2f
+15f3ff26f9bc8e910959adf9c06741c9
+15fd6bdfb9abe078ef1838fef53c8056
+15ffb2ab3af03e027dcb98f42e93471a
+1604abd91db4e383bd2a5b0ce3ca55d4
+160654e90aafd0bf77f4acaa849bb6b5
+160d78801519d9055bb6f67b83b95697
+160efdc4011e235b7240fe4a7405a9b0
+160f3ac60f6671a4d1a44b5e811228b5
+16173f15f06e07ca3756aa418215004b
+161b8ba4b43c23af6a4c616dc441806e
+161d036be3d34feedf8e421dbb55fc50
+161da74169b57166a7201a491e341de4
+1626d84a2e833f3ce89078219a8350b6
+162799e90a77c34fd6035b182acf44f8
+162a68c9f0855386655ee02c58eb3638
+162e333bf9bc0a7be18fa3a1bdae4a79
+162efaedfbeb5eed177beeee401b0f96
+16346e70163a293a2ab03f4f9a6981fd
+16350304ee08ff39ce93eeddf78481ef
+1638dcad3d17e8e5fd39fd7f14a8c2ce
+163c5e48acc01167233fdc11e88f24fd
+163d5b27932a3a71e85d1d5fb5b4cc96
+163e04705261b4e147b5b75214a2ab47
+164401c43ca6f0e04f4b5d6b75675bd3
+164b90dc96bfa01e3e62c05ecc957896
+164d19eef54fc4ebe43c13dcef481eb9
+1650c4fd9b6a5ac59b2b08305c0bc477
+1651b1946d8f70df1b33856f17e566ed
+1657595806a3080e6610ec81a4e26aa0
+165a10b0e9e7c0383e6a92b9b4ca83dc
+166b9fb4135668f87b88e7a628d72afd
+166e80813d5bae80cda52eaab899d93c
+1674da3770255f4f9747b190c4231703
+167b0ba18e9e4b6315b61a45a90aaa40
+167dd8f520e50227992177ce73d0ccd3
+1680cc81cd05398808f935d4fa92a769
+168405070e66394fabf683df83b5142d
+1686b7f11623e34c0b3c329d21dde6cf
+168854535092a71089bf6e6b756aef80
+168b87963c4cfea3366ccf2f1c29d916
+168bc2d8b296b2a0e68d3b0249b59b3f
+168de987686731aa5e286c195773d997
+16926ae2808b7641d15bea6dcbe4e511
+1694e0dce621f88a1015428d4b2c149d
+169828a16daf9588e2a2cc1e53952acd
+16a21566b7500403cba299c5bc1777a0
+16a257cae989eb5ca11b381bb3457bad
+16a34258f35b011549abedd88783794c
+16a9e250bd21588716e040b2bea9a0b8
+16ad0bbee41a266472301e9c24a159ca
+16b411b6ac66e08fab2e802133ae5b62
+16ba117d6e0ca109e47994522eb3d950
+16ba73e2438afaa1126c4674722adf6e
+16bc3ad7a3a9907a224d155b8c974035
+16bc877874ed8caf02f7dd406692bc46
+16bd1986e5d3be72916faaf1af577934
+16c51e5074d2fe489be90920ee8fefb0
+16c655d1470b1cc0b6f36cfae3a60776
+16cec97fb27cd6b86c3bd0edbf0aaecf
+16cf18cfc4100368395763141c332493
+16d098f6cadd7771099e5da5eaf623a8
+16d18b7d97d0e20f865cd29094a2fabd
+16d2847e2d7389b4ec52cd05e8accc7d
+16de208fd4017270da4d8bfac06d4ec9
+16e49c6dc0d4f40bd462ca8d1f9b3e00
+16ec3bc5bb030defdc3cfe85fbb1277d
+16f1dda6837fc0057318d2a171a6da56
+16fdb8e82fa2777b4fcd52ad037fd17e
+16ff468abc517442af901de06a035a7f
+1703d79fcf1e16958d69f92b4e9e0fa1
+170f631d7c7e611889d38fa6ce44e3ab
+17131a6929fe2541760f5f5e7d69e876
+1719dbde647e658d4f7ea3b2f35fa41d
+171a0814698c7e8fa2d890544143ff27
+1720713886ff4ba7fa04c1a97def968a
+1725650f77a713f860766ba22c15d916
+17287e67922d9f23a657d6834e3ec420
+172b0a0c079cde02cbbe16651fd57ff8
+172c46fe07d782e2ca49f5bd398b8174
+172e88e363438471be93b2bf996272c6
+172ee161b2d2fdd2f8a55ad960e573d7
+17332fb7b388d3ea1ca0fb8d3e00610c
+173969db55601854c9a01d48087c8dd4
+1742156f90b313f703947f1564b4b200
+17421db1da34bb7af83d712c1cce34c5
+1742f826ae6ae0350548e84a8fa9ce91
+175b421ff245894f2ff54cacd138d706
+17615f40afd0291bd471fd7f066519fb
+1762f6dfb5a659145c65238406d5cdbc
+17707376046a2b17f66b30f571e76fd8
+177c24702979be311fd74c1e2b373c18
+177f24f33529cca0708befea74693dc9
+17803f1148ea883641b9c4e96f4cfcae
+178076f0cd2668c1a19e460fd1bb2453
+1786e1e5dcd5a8c5685b0525d93e19fd
+1787ee1fb4afd597dcfcce3d549bde31
+17887d28284474c6224e6b07ad2a3bcc
+178aaa66599d744f416099a5dff36bb8
+178c4a76eb34640b1cc53c9d3eb5fc44
+178e28e5d80ade2ffc49afd48c4600f4
+178f238fdcb8eab7ac8a3badd7f81962
+1792ae1c220d60771b29e47391eac1aa
+1793acfca93b8f383cdb788168e9fa3e
+179576f743f3cb628a0cdf3444d73815
+1795bbdd1aad8d8ed5d1f5b19a828f01
+1795eaa4c817baee58ea099bc752c371
+179e091ca936927772463eb22bc8df8b
+17a63ce5addd37f85c9a40dc4ccdfabf
+17a8d978474fda69b1e4ea99029cefbc
+17ac9c171d92848138b92eab39721394
+17ae1457d0a54bd1ebd74b786bd74581
+17b01e3f8e23a7e3814152489a87c3cc
+17b6aab771131cb7e07164b5453e83cd
+17b85ede3124dccea9fa36e476d18c07
+17c6541f029115cde6c5fc13bc1f9946
+17c7f71d4a7980177e7ff1b13ec4fc1d
+17c874dba69f0a67aa1bf9aee7538d5f
+17cc714e7b341f196acccbd37b7176a2
+17d67aa0b9bfeef7c85526eace1ceda9
+17d8dd81c78076a1cfa6d7cd7a90dba3
+17dc4d80abaab61218522d947a22ccc3
+17dda51aa78a5f03328561b716dda609
+17e0b6fb412720ddcbef59af5cfd58b7
+17e3fa62d575d242452c5063a38662fd
+17ec633977e91f0e96fdc713a499ceb6
+17ef3da9a794fb25f059e8fcaf895790
+17efdb11ab0d4dd6886d660fd9c37d43
+1803288c0ca7c15c046cf6e4dfd34ef1
+1804af9ef5724726509c35733afe00f2
+1805599f3644e85eb61f5088cb5bde64
+1805dbeb723c2e4ba646ab7c51827b86
+180da230860d49513f134adb28fa88de
+180dbc03b80475589ac6581181e082bd
+180dcfda05a3982f933d7094e5726e6f
+180df21db7881c77d01e6a8c10ccfef3
+18106a7102d0891150702c7dff9c5431
+18156fdc4bee4c39bd4b9897a8c52be7
+181a1d279bd5b6a967f855c1c23b1aac
+181d72902b0583970801f58c0b48ef0a
+1823896127491d5329dc5cba7f7748cd
+1825ab263f59879af4166e9c9438b9d3
+182c1a1ee7d51988209fd100ea4e6c31
+18312801be5f072f1229abec0aa6c3f7
+183a8ddf77b7c51719ea237c917f34cd
+183af19306f11248d5611c4502819c5f
+183b584c4366fbae04e264227db8d57e
+184166c21cd997a8bac69a9b787a833c
+184bfdd3aa4119a2016693d6e2049827
+18536a5ce7522bbed131daf75e16896f
+18570799043de652241ea35ea2b9576a
+185ebc9f05dc1e452349634ce67bbf0a
+185f96bc26dd88494593800856f946fe
+186092b63126e09ba178fa04cb9adfb5
+1861aacab14559d0918ca809e2dfe677
+186a58dd6a30a634edef45c791598f57
+186d0979c5213ebe9d19fcf539959db9
+1877417a3f887d1df90854c7d81a8100
+187a15a1e1786e07b1923354eb0ba960
+18816d625e5ca8913ec24bcb2928f983
+188c59a02e4e8ca7408594fecccfd10d
+1890ca9024ecf1e921e6fc37a41ea2f2
+1896c58883bea94fd477654286c1150c
+1897586300d7aa4fb06850e982e96bce
+189834b3a461cf73c36c88da18cb4225
+189db1a5dd298d7f865e3560a279e151
+18a3815ff20567c0aad6b4c73aefb7ce
+18ab15a9d35ae0dce67d2acc90ce298f
+18afd2cc721bbe772c84b2650cbf9400
+18b03fd564fcbc5e1daeb30864d170ca
+18c01979395fd21aaa1afd30b1fecaf6
+18c0cc78c51c63b2c74241c584d19aad
+18c5413cb8158c421e6029c0f7f79111
+18c5b1418410fa1211704271fc70ee71
+18c5c1ad2cb0ec25154852f0868b4ed8
+18c63ad2c9700cd068c2417e80ad9cb0
+18cb8cbd24b6f13cdc235b29aad97921
+18ce1eb58f8c3c8c1082f8bbb5a27f87
+18ce329c0f3438528ba85c785a7fa98f
+18cfffc3bbcfe78c41409460ea16bc2d
+18da3dcfaa7db1074a36859346069f2b
+18da4e60f5b621a721c9f90247911342
+18db9cfcbef320b40e6fb89a9b034bbb
+18ddde4568c661bb38f04715f9016221
+18e2a0cf77667d81beb0b1e002f36255
+18e6e01ab534218902d91940ed943cae
+18e9f101289de43de9068bc89e758686
+18ecdedf836c0d37f33c169915c1384f
+18f35f49a2bba9c775c3f002ca5668e3
+1900a8f9d7434cb34a09ac3d66147bd9
+19017e667bddf9d2f2091dc0afd29d90
+1902b2464d9b891d6768073f9d9d834a
+190360c42290149ba02180faf16a375f
+190438e79f64c4dd643d48dc0400225c
+19089039f06140c106b099e80294f132
+1909815e1536eb7407d31dd252ba2d21
+1912d9392f3bbe63a771a8f4c31c6896
+191876a58129c9483e17519d19c3d758
+191c21d50fb34bccea75a9f5e0085e2e
+1926a6fcc250e6f7d4b22109424537b9
+192911af7fb2957c61f45d94b3d7b053
+192c99e05318b118af317d6d1253aeef
+192ccd2bf255fdbc832fc964ca0c396e
+192d4fe87bbbf20ae0b4e5639ef67e7c
+192ed1822c339b11e09a6a36e4dc45bf
+1934663bcf5c19c18cc1bf04a10ce240
+193dc416c4e5b5fa6a8116ed7c86f681
+194272625a93c44d07899561222e41ad
+194bb396b76510eda54dd12c853802a2
+194cf236c5fa6c6d71346ba98ef2b797
+194e41e4f4f4d64e8a8719c636a0a20e
+1955b59f64ffa9c32ff702a8638fc0a2
+1959c245eff356824f2fbbc5623ffb54
+195a4130b747ef50ee69052e6e134adb
+195cc6401325aeb3998b559f2a0220bd
+195e7ab75343cd74f189756242033fea
+1962f538c584b381d5066ed669e6b832
+196e078018412bed69b9233ca6cb0db0
+1974494ba4e1c76ca572743a61c84a82
+19772a3101a1a5bd19d0d1ae4856cedc
+197a593a9744727341ad4ef449ff03ed
+19845cab3fc6f8f2d7db6cbc7c443de9
+1988e0aae55d4ca1f51706149fd0fc9c
+19902069923ef3e93b5ad54535ecbef4
+1991872163abe6df9500e18c2d5bff2e
+1991f9431fa7498b37d2d1539794ce63
+1998846cc14a849f802ec3183093ca76
+19997b58a81b807e054e0fb466ced908
+199d36a4a8764626d01eda0ac9bacf6b
+19a0c21daa8f2a26a2cd3bd3e1bc2b84
+19a377539be0e8b60fb5791be5985121
+19a5790851ffad796cbdb8199fc519f6
+19acc4e18058f122ec9b7633986a0237
+19aec2fb6e254373f08d4025e61fa328
+19ba5bc118a0293ec3b3293c7c5b1614
+19bb48ead70293f636e3cbc733cfff36
+19bb7c8748b4c21e13d5c6c7e98ba2e3
+19c1207538456454633c4c909befe3c8
+19ca46dde9ba5371d98c5548f88905b8
+19caf2332c229daf71260d7c41696cc7
+19d9ba29ab67d5bae320ba050a79f91c
+19ddd135ba48848513e1d18f0f61bd4c
+19de5e4a710ce7a2c9bd23068f1e214e
+19e2bebe5e05d67c34fab2f752f94229
+19ec68da2e744f0b17628f3e45b1a436
+19f3c7f25f0f36f54b7cd2868cd420e1
+19f6102477fc6004da7865dea8234a79
+19fac42a1160ca4875a65834aa011a73
+19fcb848f7f3d01f823e6411f3a1c4c8
+19fd7efaf11e8703b6cf436d7b250469
+19fe8db85e32328c58cedb30c07cc9ee
+1a01c67ab5e1e6c9dffb85ea2cde993f
+1a0254aeea56ef866d3b93eac02c3360
+1a044a9f10165a4ab603e6d37475f736
+1a0c0c367e4460c92fe1313c535d3f2e
+1a0f75092f4a1abd59067bc2cfc38156
+1a13d05607dbe47a0b743c69cca03c70
+1a169f0a2150f5ab8bfc02a2d9acca46
+1a1778c0f5e59262825fb3617a93a674
+1a187efc5cfabfe0c6c4adc0a4b2f847
+1a1bea0bfb0ebd7205e4c9fbb2cd0890
+1a1ea60abca15d6cf161416bf7f96156
+1a297f2fa67c45d4401545471e094993
+1a2cf37cabb44379dccb3f84a4adf420
+1a385044b5ba443dbb80659102fa6311
+1a3a93dd9edf2f6c365951c76c8e4145
+1a40756fdc2db70639cb022804924ee9
+1a47ab0ac3256b384127af5d0908c34c
+1a4e2113fd1a1c6271e0f55a9ec5c8f0
+1a5e134b2176584cc633ca7e3aa30d71
+1a5ea3c9b56368e5b22906569e050b1f
+1a5f3f298cc32b343b38d73ae9a4b4f8
+1a65cd1ae88eca0a13924dbd2f0f0292
+1a6c04adba6f524daae4b8137b178dbd
+1a6fa34ae2caa439db47aa2d5b90425f
+1a71e80eca75511850d7e2c592c0548f
+1a76cb0c61522ab412f073428ca9a8f4
+1a7adf7afabcb46eb94fa53b7d27210c
+1a7ce9a33f1bac4ffc564cd8fe8242a0
+1a7fb060f907fbef4f7b533d51be0528
+1a8356399356c12f4acf334fab63940a
+1a94a0cf974e2df1aedd6ccfcdff9449
+1a95429edefbf7281402a0ada795746e
+1a97c27b02647ded320e7940fcda44b7
+1a9d47f566abce3aa4f413357d7f8709
+1a9de67ae1b021261a4a38e230a80a41
+1aa033a9cd258b166ff38abbf3ae54dc
+1aa84a5f4432a1661a5b473f5526fc83
+1ab069898a2937047d73490f99df05bf
+1ab59403628e422131eefc856b022e55
+1ab639dca03c7c4c87c8e10c9af59ed9
+1ac3e703d0718eb0650da68f80d3de6b
+1ac71ef9aaab8df4cc3c9d7af0955c28
+1ac7d388c0360162b37d0b3459912ddd
+1ac7eb9d3b6f6f00f14afb83636c1dc5
+1ac92eec816e47d71593cada5d499b88
+1ac9452cd4a9c7f861edf63c7862f416
+1acaf1b0a5ab2ead73f1763e822834cc
+1accf87c2605c915579c98ff7042dc11
+1acfa360f69e7a8647100b77ae5deeec
+1ad2bfc1873dbd2e1f08cf392f70aea4
+1ad342b64aed9975aacf1d049868a363
+1ad48594361aa150481b5d05d9a64ffb
+1ad7b78fe59a95e7c37f10783ba9cf91
+1adfda5ca096dbad40fa9bdd70b78d57
+1ae074fff7107c76a6a6da7a4b5f28db
+1ae22dfddff25f404fa51bd876e69c4d
+1ae4fd14d6c7783fd3399a8eb96b4a5b
+1ae91064c5c9feb1c60959215f718fe5
+1aec334259366b484628612bec1e561d
+1aece368e684a29f960be6eed0d12498
+1af0026e7782fe287ff1ff952727e62f
+1af0992adf8f1afa92ec197da728c404
+1af1a7d4a8f4a38eb911054da9f7a5c4
+1af2e9e9b32d871f56cf5600896a51d3
+1b0fd04acde07874ef708acef5dab48e
+1b13936f76437dc9dcc0f4aa0b635ef9
+1b14ebdd1b605232207e6823907f07ad
+1b1841b7ad588ce3a0db13591dd8257a
+1b1dfa23dcbe3fd21ee09755e311f933
+1b1f2d53912a7e1d8bfeaad27cd3c846
+1b204bf3f15338447ee4bd6bbafcf6a9
+1b235d919cfccf21f02fc9700260edd2
+1b283340ae51e0355717ab0ba757cc05
+1b28b0d954d32b0368c7e237dc5bca81
+1b2c987f2d479c1ed2a6c8127bc31fe5
+1b30d86924d267c98490944d853efabd
+1b3136218d9e2149c98fa525a15cf905
+1b33793abca22da965835fc6dd1d5c26
+1b34b47a399c0d6d5a8ee25c81ce0e1a
+1b385097ba5788f9e8c4587aebac41b2
+1b3cacdc72cfb7d138775b4a4570ed75
+1b3ef39821ecaf051e342dd03c912cb1
+1b46b439e42b350335494156001622ab
+1b4fd7148d75a31bb0339f44059a49e7
+1b4fe7e47b5f57818a5419d747c56f74
+1b52fd835ec549fd28e8eb5564e77b5d
+1b55ad0f9f08f47c88d9f6f2746f7207
+1b5b63cb2c425cce9138d297e7abf547
+1b5feceb0c23b752c1b95af98babbc51
+1b6039811d8fa688205942c74d462c4c
+1b690ffe6531701026ea9fa9f4b37ec1
+1b6b4bf1173c88f2239d492f9488fa86
+1b7416bb290b9c0b79032616ecc1ea67
+1b853c23773ac4561e18abed9237ccef
+1b88b41602236b6f974e1eba0e81f0c6
+1b8c70346132d9192f2f0481c6b4072d
+1b8ef3c117caa2db9c9db567bf0ead10
+1b919afeb0c276d77929d6fc786b3b1e
+1b9688a9eca2f3ee4f93dc62de5e1421
+1b96f7357b58e1f56a8f73cd96732b0f
+1ba0587a640df64c7b7d6dba6570dd35
+1ba1c66a3f531ef8c3662ebe84e38ce2
+1ba94b5ba0cec79f0d1be9e5fa57b94e
+1baae12a117a5dfedf40990d32d6d63f
+1bae7027eef30291a99486be148999e1
+1baf3d722a6c7bae89dc451862e60095
+1bb3747f571b3a1333838f9a2346c054
+1bb806d3c499ef2acfb75029e3d3cd84
+1bbfa2e118096dc92cf2fd5c50d385f6
+1bc152b63df7db6220f4555d5e3c51c1
+1bc2de3e1c5db99546ad364247915581
+1bc53eebe47666218db66a17876a60d9
+1bc6d9134ece43b0c97116a1a55b6456
+1bc72125feb6c441a6d395623cf56288
+1bcb4c5ee51202b5bd6bc7ebb9a337a9
+1bd31e13319b3f381fab316f1af82fe6
+1bd95d8d69a9100b567070e64cc5e8f2
+1bda7881dac87f39cb37fea119cf1cef
+1bdbc6d43098f58b03a075eca06acd6d
+1bde2a672af7cb1f63f0db89d5c489d9
+1be224ce01786481b8e0ebe3a4d946ff
+1be3f8fa534948e1ebab7960bf17ec15
+1be86e9f3f5a4c2709395cb0ee9cce4b
+1befdcd830978db67982ca8e72ce5569
+1bf4db8b3b586f04b54ad6275f2385ff
+1bf725e66e16541ae0554c00748e267f
+1bfa1f3f2740f7aa14ef878683b241ad
+1bfde0f2f2f4beca74c92a5f383fa987
+1bff523507a91da1f736ecd1375ace47
+1c056ddecf128240376d17e61d9e07be
+1c05a35ce5fce0c8f6ae76b2aaffd571
+1c0d127999b8d722c509d67a7d8033dd
+1c1684bb2f563e35ffc0956c38fae5db
+1c1f87b36710b936f8e35a627add2970
+1c232736d64f5ddc442ba1a034817884
+1c2362cf32af486ae9319fe3e48ffffe
+1c2875e9e1c0b1d544fc4453c6cfb688
+1c2da2ade9dd899e1903dc5bf30df5e0
+1c3352ac3bbcc3e8cf07c9324e5b849f
+1c37c2212d44fd6079810f5fa50b4903
+1c37f30fe26f64c56241bb1143c4923c
+1c398b221f95964e38853677b94f9632
+1c3eddf025bfbc5ce3bdb37cc9df247f
+1c497d8285caf445c52794b6398aaf6d
+1c516d0ff1dbf512a0768dc247abd6f4
+1c5269fc85197c035bd801a606bf9055
+1c551b3670c8e72e28751a67de1bcd13
+1c5594d6c9db3432bde9efd85dbdfa80
+1c5729c939d2063b05d9a72a1b27e201
+1c5ef6ccd1e1a2d6f7350436bb0e1245
+1c5fac493f96a637d4b0c9feeefea6e8
+1c618506efa9e8ea391aa26875367df8
+1c6413b734ac7d14cb2addbe77feb632
+1c65b34377434277e505efd75fa75969
+1c6ba63c761a7a045526bb475bae8589
+1c70b2f4a393f5e86041637b0c31ec6d
+1c710c2c983dd83979f4f3deef18ca46
+1c715da6de84525422b3096c170dee26
+1c7b97ad8c276ae98389ed284358d062
+1c7cbd22af7ced0364329e9cb5cba809
+1c886f2bf65aaf547a6ffbdef9aec99f
+1c8cf4d4d88d53bf15e99a9da76a57fb
+1c8d25785d3007f90b358915aa4d1385
+1c8fbbe22c8752befeceb140a81103cb
+1c9105247c94d44c450a587ae06e6b37
+1c919e5a1e92b3c1d60a9642893e197a
+1c922914fa6cecdef19b9b9edd99fa4f
+1c95433068f16677ee11bb629d6a9a99
+1c960239783e4d3b64fec91d68d0badd
+1c9ac3437e7663be7ed6b43dd01f9495
+1c9b7bd554497d2676178fae8055506f
+1c9d33faf8d714dc4b28269f8adade1b
+1ca40d41f385d8fbdf0733523d5fb34e
+1ca9c427590bb10dc904f4ed435446d1
+1cb01450c502d3616e28381eaeadd802
+1cb1eb1827f5ed15651da7ee49d0a43f
+1cb249dc7d64a73ec89028c8cc87dfc5
+1cb2605f2eaa29fe585e301148c8f3cf
+1cbb3cfd12e92a59a97302365a9e61c6
+1cbd10d74af38249f3de290787a89c4e
+1cc2b8da4df808ed5959ba717b8a92ff
+1cc61a9cbdad1b3c29c8d3d08913e47b
+1cc82340d9f296b33e8d367de663ab7d
+1cca1fd853689c7826d13076e11e4431
+1ccb62c495f377772133c0c9674141f4
+1ccd3174253b38c5151a5fc14a2976b0
+1ccf15ee04a2091ea33c2b0c74b75f85
+1cd49b31b147cc07ad8f64d1e3a3d270
+1cda86ceae26b5507188994d94519cc6
+1cdc8aa61c730aec3cf03955ca00bc9f
+1cde52aed850784ffbdd1f8bdd74d6bd
+1ce0495e034c795ce76a0fbe2fd236e0
+1ce9471ffd1d5ca0d64e2ace51bb898b
+1cf3a97b0cefd7cc2126e7f58d3c1f70
+1cf633948316db9c050900edbb44ea97
+1d03362dee7f9c248d13a60c238d9be2
+1d093a549c4cffa9b472eb63fcf54acd
+1d113f1a32f4313fff0f6aac13c4575e
+1d15dc7f35d3e44a01685a123be846ee
+1d1c564c7540e0e9ba5eee1b487a28c4
+1d1e5b5369c5734f2ffd7f971b4812fe
+1d230f76154536dd1f68ab2f9d94553a
+1d251fd31f0ced1e680e97084329d84d
+1d25762d461e3432f5f5e781615aab16
+1d28843db641dd89ca75b98d70b75c7a
+1d2b64367fc03f3647c7aba242e5a4da
+1d2b6b00d8438aecf98c0a12838cd04a
+1d2c7d438cc938ca3d6c9ab3db455869
+1d2e923d5aec3f551f1af6687d91bd06
+1d2f1fa013cffa1c95f4617f01ce96a6
+1d3390f3dd4d30eaf8b1795b40db4b89
+1d3b6e555acf6adfe9cb5c7390df5926
+1d3e18d89b0e3617b20e0060c7a1fa0d
+1d3ee6aaeb36b1b6973bb6d074d3209e
+1d41a6eb4f9e4783b2d13cf2d6da6950
+1d42e861a607f14727b5af1aeeea9b71
+1d49727300e7f19e60121378a351a87a
+1d4b649158da2ee150cd332bc2f2ae6c
+1d4f5084c9eceb6a63a6a74a3f7f8b42
+1d54f169f4edcfa01f4ee7f7b1287a89
+1d5a6f8c98e54b657cc89c2c344bb03d
+1d5da16226e76fcefe39b336834f0628
+1d6424e25837d4afb6cbe38b0e7be785
+1d64433723d1be38c672659fb40d7eca
+1d67faf81e82e8b73ff431536e43a761
+1d6b0fc08c47553a03d0c7e78faef0cf
+1d6db69c4426fccde44471cf5dd99953
+1d7184ab8fa4a330f79a0f254010d49f
+1d75c3e5c64d36238067934a6b5bd8be
+1d8e8854d2e87238703ce83f6f7f605e
+1d92a0dec1df93b48ddf595638bfd586
+1d93724eb1e515074b1651114d9245d8
+1d9724b750b0fa2c2a34f6786f07b639
+1d980f1cbfc30b9eea016167dced928e
+1d998d315d622aeaf10e54d3585fd681
+1d9c43b62fd15c4f4b0430498564f8d0
+1d9f1afbe0793fdf9dfb526c4675ba1f
+1da0ff3e8fbee38693012dd6c906e947
+1da3d71c665b0aadacf8dadae4d4b0dc
+1dadc146b6bc628c1d66c7071e2ec5ac
+1dae85d53e737bd8a34a948f265aedce
+1db342d235729696dcafd2eba41db532
+1db604f0b901a211af979cf531431c84
+1db912bc2322243cbef3eeabd1f854d8
+1dc99153261feaf63cbf5dc9f782ce86
+1dd24ba91686e38ebaf24820e2cc732e
+1dd70d8cf4f829bf9eab4b941b03917d
+1dd724add6282109fe6c7650595a7a73
+1ddb0ff48979909c18123eeee432ae59
+1de18c012861b7bcf4334866cf41fbf0
+1de33607694a3b9be33343ff34f6b291
+1de362cd4f99288848567602d594cf8f
+1de7e5d935426fe821f311d88e8e0058
+1def12276fe95a74b8345b116a3e71b2
+1defc991dfd4c163e8add52ed6eb2367
+1df269120c7a5cd845673260c80e2ab8
+1df7211a6486e05f99d5a7e2142fa3ab
+1df7c7ff22544e1dac957ce8779753f8
+1e0297bee028ac32910301155e420786
+1e093a09a24e806386acfcfac608f429
+1e0a5a5dbb2041e52ea407ba4f533dc7
+1e0f80e16ae145434f8c411a8e2025f4
+1e10e07eac2601652f77c516469045f1
+1e1328cc61b477c6c7c0ad130f1c561e
+1e13889a631af76f8770b148f3266c1a
+1e14bb61524e306e8e4d95ac6f804141
+1e217870c90d609341159a213004b0e7
+1e21d46852ff85c1c99badbf74e937a1
+1e28476e99f08ffaea9957a6e81a40a7
+1e2a5dd5dd581d7f697d049aa6cfffe4
+1e2d473d061b25cbacadc8e81b2a737d
+1e2debbf7325a740ffc65ec5b98058a9
+1e3742b2b5b20008e07594c897b991b0
+1e3f07e2a69769822afd77b8515f13d3
+1e3f9f5412761923a856b28ed16bc5e3
+1e41a9c821208f4af7a957e3812ad77e
+1e43313fa63d297fd45f96477a83354f
+1e47574802305db99ae224a79458d9e9
+1e4ac082845408c34dcfda95d2c3853a
+1e4c3b9aed6f93702643a6fd87cd2867
+1e4d7ab62a626d55209af7ada24749e6
+1e51638207155f1b9574f6b635b5a5cf
+1e553bb9b50526a9c59f30dae9eae9e7
+1e5a03571a2f8f8788096f06f5c3f683
+1e64c4d325bad7977fc39ea0d37e3c73
+1e6d9ca2170432cb1be1ae70c656440d
+1e6eaaa5ff3c86fe6af62693a6d68fe8
+1e6ebe60d467d8d7334a5cfbd9d88c40
+1e7114a14275a88ec288dae33b2a7f8b
+1e7974041443baf74d83e32c3d60321e
+1e7b229623af66dab3543e37427ff9c3
+1e8551fb74cc4bf894b97069ce846660
+1e877ee8ee6c5b8de5aad7e7cd53669e
+1e8a719dde67b68bdd2e68cc285b862f
+1e9ad85ff554cc626e469f94efd8c0db
+1ea168ce8ed19889dfb190487cd9a2b6
+1ea16a31e720d66a4409f4d7f8343655
+1ea4c69fe4572133e7e1e438d8a29c56
+1ea682506f577e3b1c0dcd67243b13f1
+1ea731e70b02b7f5c3ac9bf856c6b2dc
+1ea8bd89f2c3bf3c77f4f264f5059f12
+1eb2160221cd2c9bb9785f8f0820b5b6
+1eb5735e68b5a5d0be1876a336904235
+1eb7af774d4ce0724c4c52433b02f358
+1eb801b4833ca880ffd79949a4ac4551
+1ec2cc2dbc77269e3ce151c3517f8a43
+1ec642f8fbb3456c881930c8275bffc7
+1ecd01f763f5b00d23308d24369896ed
+1ecf976041ca155272ab6ae013dfc8ea
+1ed7d04497aa7dee308afad340cd490e
+1ed8225193b382030853e6e9c376cdfb
+1ed8817d5a9c63e14e3b6205a0cc8bbe
+1edc8beb63090ac1b4e22ed775019438
+1ee226479d8e600cd32246f8ef35b53b
+1ee3404f0f9d964a1a30303b1577a0cc
+1ee50286387f81a0edb45a071809b6d2
+1eefa30a405be0d9a62d0dad0ef5723b
+1ef1a84f486dd0825bb3daf6cdfbc28e
+1ef23cd90c7c02d7fca0a65572cd52ee
+1ef6198f69484acec70746c6fe730c2c
+1eff1a13b0b03b30a7193bad98ec22a7
+1f03f58d9c5fb2b2bd8aa7967687df26
+1f0cd802a6a3eebc4441d53aff8b794c
+1f0d66b723edc83d56ebf38444c7c8e6
+1f1100bf4e86aa6af9dca73aaa190813
+1f160194fab2a1c32d025cdd751fea50
+1f190c7faf3962f1f33f6cea47f6ad0c
+1f19ccec4f55a3ca8ce06b2faf7fd68e
+1f1d15890a5645b58dd16c7a60bdb1e8
+1f25672951d3b2383024840476676a09
+1f2b24f2abc92d254ba82069ad8166f5
+1f2b87fc650ff9f6c8498231e59c34a7
+1f2bf3145738bb3ce94d61911148101c
+1f34b0e8947185a35569e678a7059b3f
+1f3a2b11291a4c47678b4b59ad951c29
+1f3c7724d1e6a1206e8f4842d260a9a9
+1f3f99b3ff42831d0c28361e6a93d74a
+1f40bc7eeb38233be81f9ac2245ebaa5
+1f43a543b36eea7593ab5949c94e6b85
+1f4607ecfa8af6e93f86a2619ba0836f
+1f4dea72fddf6c9b2b54129ae4abc37a
+1f4f8a4343328d083f652e1da31790f3
+1f500adfd6fefb72863290ac89b76492
+1f51e18cd6402138cc6482f53a4e09ff
+1f53cd4a9882d09d7166287fcef76a60
+1f5a6ba0281ebc175462f82f7c873364
+1f5cebbcb3842e618f74c5170a002e06
+1f5f8a866bf173f8fd0f8da0dfc8aa0c
+1f61cf632a076ecf0d9dff5fb8b86434
+1f627025040b65fc663602cd2164ea24
+1f637206c6e9fa14759fd616458db31f
+1f63b3d3a035fce7dcd90d800a589b33
+1f653e87ef27690ee6860680bfffe413
+1f65bb2eb5e57613e896db20992ef190
+1f6e99b3e05a25685eb3c782252b4166
+1f70917b1306deb9799284727f17ec02
+1f87a2b3107ab515439bee42f107ba84
+1f8cf64d9e7bf652ca0385b2783ebd2a
+1f95f0035781c0a8238260c1f2cbf19a
+1f9787a30dfbff8dd79243076ce83920
+1f9a3554b9fb62157c3b777cd71588df
+1fa1a959ed32118bb46b295cfb9bb90f
+1fa239fa417be0afa0743b70f3c9432f
+1fa353b33d1295bf35ce85d3b4f3b7b1
+1fa4bc65b1a3ab94db7dc47509bfef0a
+1fa4d9e192fbb1f457731596d69aa1d4
+1fafecbe6340890b1441b90ce823246d
+1fb551ab1ccea36eab643176b69ea606
+1fb7238403e1fe0ef5799ec4a3a70c6d
+1fba69c6f45869d5d4a51a95ac9e4ac1
+1fbe3bd6ebe509280e8038319de6e2e8
+1fbea2b833320d83c6dcb3846a4cbfe2
+1fc4fb26f96dcee4c6ef5e2217bc8b0e
+1fc56d40897af19058724fbc04b6a1b4
+1fc636ccbf7a8371cd2951150b8942b8
+1fc888f07db1f2dec62d51a6d2f8d59e
+1fc99ffaadd93941fdfbbff16145ec39
+1fc9dd836297f81c95b956305fc13799
+1fcf9f7ebc0bc0081d54875439dfdec9
+1fd0dc463a6a66ee1061b0cc71300175
+1fd7bb7305292bfb8353984ab8d3878c
+1fe21ccd74e6a0438954d315c3361987
+1fe24480b788af42cb93dd86cd31718d
+1fe25d4cf3901de469c6f131cb96b53a
+1fe65f4d6573e6c88114fc41b2b25568
+1fea5eeedd5f3b330dfe8e6b323bf42b
+1fee62b9a5de5b338546bba878a11de4
+1ff183725fbd56e3e3415a790b6626b2
+1ff694e808e1086f0750f54e91f1a89a
+1ffbc23bdde457759a482961ffc5bf13
+1ffcba1bf87a28d40f48efebfd54c7df
+1ffe2349801d5ed45c7d0f8125271b19
+20006027dc6c8e874a6e3fce657d33b4
+2000af510b463c1b8d01f2f57af431b0
+2000cac5beeb011000593f38d5a58d84
+2001a35d123e58891736e86a483c35b2
+20119de668caf13b4e0c1abb9a434c63
+2013ffc0383bbb71c3caff9975934979
+2014d22b3bbf71676a58ce850a47ba5a
+20189ee29f02bf6b281771df2109d07c
+201a8a1c94752c7b8b2f9330ccfa4e86
+201e8ce91cadac3cfc11c556770cc992
+20287b9ae50d85e4289f91edcdc70c7a
+202a8b4b8c12a4922c4fde3fdb71adef
+202ae6f0e145e8a9323db327579477ba
+202b99966e26b61d4c68f222af0435a5
+202bf0791ecbd1f881cea7afa2398fb1
+202bfef6e601913de679a21048a6de91
+202c14124dcee02f37f7b91e095c7c30
+2034b9fce7627cfea69eb1f3fa163295
+2038239340f5e02b7979e318a88f0427
+20392ef9b7ac54af49f27b23b44da158
+204640a0cbd1456de2b632d1a89c1e12
+204cf58d7534d47b943bac5420707e89
+20524d35be88c5f4ac49ad675f9a62ed
+20677be66874e0c1f63257fafe99d66b
+206aa967e88dd8371103ee7aceaf1f7b
+207945ca6be6e1250831830c63b4dcb3
+207b50f4d1a482000024099ffe686e46
+207dcf29afec40dd80fba9e5aeead9c4
+2087861fdbc74a62ccde714ef4b46e4f
+208834852f0ce19041587a3c76dfcb0a
+208dad50e08591c182bf4449ad24469a
+2091975f600da36cd57572961dfdba15
+20929e5ba39b5addbd75f860117c08ca
+2093238a89860a433f2af0ac0825430f
+209664647ce54c6792d499f591fe3458
+2098a79019f7a7d232169f542c66f212
+209f00a4ef5ef0b9b643e38ca95cc8c7
+20a0bc583849c3637ec32fdace214ccb
+20a2159e2ca038a849c724c24f82fe86
+20a2303a3828632199cc218a7fd6e7a5
+20b11650ba84f35c485b95289a513eb6
+20b1ca27e7fcce228155e2568b1e68df
+20b5d00d0250e34c3b92273ebe8d9eb1
+20bcb5fc662f506641d99d185110c334
+20bf562f3ef2efb63d53e524993499ad
+20c27ade1612e6044b4152a14711a5b5
+20c3c7b77fb89ca2fdade9d3b91256b3
+20c8d783df09b7732205eca0ab61a290
+20c8ec4638186311588ac1a2b6d936a9
+20ca76a6cbfc04553902d48cf19e7ce2
+20cbfe99d235ed8aaa3aa5d5b6653a1c
+20daaacf2a1570fdb14553a1e7869d4f
+20e68e032260118f213f74a0c0abbe70
+20e8bfd020534a60e9a7b8a62798bf11
+20ee76ee11fbb081da4dd41f16b7a294
+20f044289d25841df804ffae7ec190ea
+20f1c10ecca57ec94722f185fa45f27b
+20f6071143b69164a6b12677afae0e0d
+20fc88dee0139afdabf910be234cd9b3
+20fd5e32bc2c8022d34200ba63f0a12e
+2108e841862596ed50f7b6e2233fd318
+210b2b57300d6e8e729c06bb469cb6d5
+210df0a0d87bb812040d2f61a6035863
+21114a4f5159b4b1fd5731eaca04aadd
+2114e088488812306d0bc211bc050300
+211506fbdf67f649425bfa0569a8431f
+211b9e4f47535e543814e2e5eba60553
+2120271f0a6f66fb686ce9c35e1dbd10
+2125d4b48f02a9984d36b6ab17e2fae9
+21287a3dda3971fad56f4cb8ac3ab843
+212955c95f5366584176ff1320998748
+212f68898a926e495429ae787c5453c8
+2134e86794d48e03fb7d65da6722a03d
+2139c380d775578fb2315f9248e5416a
+214375e016d7bf4109b0889950457434
+214a9f7a71e5deb2b0610861a0d0a24e
+214dee30e7f6cc4b78a40dc82759686d
+215378aedc529e3c4af22303e4820821
+215623634aa18fbe210143d40f46d7e5
+216581cea881b54ca9411cfa37eac543
+21678afa4d522c95b2272df06daf7af8
+216b96058100dd6eabdb01c3b125cd2a
+216e509baa68b228b4c1a08f59aa6b37
+217ada5f778f2bbf79f5c435c6e64336
+217cdd275435adfa7d5a311e8d2b9c93
+217dd0132d1fc622f462ca2476ae4f8e
+217e40cd1cc773cd75c09645c0c56556
+21888c33351e700c47971aee32f6fd0d
+2189d511732a866643a2d6ebabc1d5bc
+218c98bac9546cafc3b835359051540a
+218d6494fa219fdd72b4d562bce2e29c
+219124cf8022941d7ee78f8c22d0aa69
+21958742cd2eb467cf2f8147119426cf
+2198438db54488af0f8d56480d11859e
+219b8277e8edbf8760bdb395323cc23d
+21a122d35e941d21e3f157a90f19a648
+21a49dbd01826abf1c14a25c2d452922
+21aaa995601b5430fff898241729a97b
+21ac935c1e90efe82e29d4713f4258db
+21b2f3c098346c497e57f631b3e2a107
+21b4c2fe3abda803ba7e938e3251542f
+21b6e2cf428705d1f2a7c45a97eb59fd
+21b8691eec484a1001ef6c6b7c45babc
+21b9175529047f2b6fafef46ec00dc88
+21bc8dc0685268e1124924d95e5466fa
+21bd604ceebe9a5410a25d4e37d93c23
+21c085993782c27f9ca675209e409391
+21d0653e8bd58449fa888ba0d2d98cbb
+21e6f9149b06ee1b5185156380169109
+21e80a2fdaa30a135fcb92806d6e1c04
+21ee76d14ffe533f6d094aeacec69a76
+21eec9a7ef37089db1475dbc7b22c34c
+21f2fb1f4a7c3b61e6dc8a8263d8bb1b
+21f985d0dbf405faa5ab43c4e4f318d6
+21fa0eddfd39e36c9ce64c9ff3505709
+21fdab3ad11e3a57f5ac0bc55bcafb6c
+21fe3567e12b0f4bb422ee217dcc6f1a
+21fe408e243c4d89357e7273123eeb60
+21fe68b0bdf74908309d3048a86a2b4e
+22052ff6682868fb0fba1b97e05588e8
+220e79b001eced4cf4de996641b9ed98
+2211287b67cfa7868c55fc07903a21f1
+2214e0beafd685ffd286f79685f0f94c
+22154821c566fe562648395306d622b2
+221643eca3ff5d873b67196a13d91b9b
+2217f79a2dac0708b4cbeea21ee81d1d
+2219cdce6baade40e8ee43123a6131ae
+221c1fecbbc6b8dfa49ec586de2fdf39
+221d8ca56ac8e54f9d75c87fde6deb8e
+222056fd4dec1f4b22d757b3834d9668
+2225198a13b239fbb012bd89da7cc400
+222e9a7fb5071f2ad6cfc7c76de459e5
+222f8b97cf325940c705412675a9a8f5
+223f0e442f50a4d7204b13199549159b
+2240e134d4d0c1eb22155bbf8c57b923
+224d340f88f64bc782dc6195dd81470d
+224ef4476dab2bf8c623c76734fd5e11
+22512044abb7f990d1db39f654c17468
+22550557f6d912a1dc24b76171a3c77a
+2263504422424e691b628f5858bc3b2b
+2268e395bef9d1170245f4f33a026e24
+2269332a1325b2938133feb36ea57eae
+226ffe618db53256a7b8c0fc34f8a05c
+22740457727943a12c7ef53001ae6da4
+2274c6a0d62095ceb2414c27122652f5
+2278e1a9419084995dae3ac27977433d
+227b02ff44e8098029d0994a2f043d92
+227f818ca054d208b9d70807c65f015f
+2288df92a27c1c51ee4efd3e940b79c0
+228a41bfeb27a9f6304e57a09f436771
+228c2bfd7e7a84ad85759059c13af446
+2290d64f65bdc47a0f617acc3aa33ade
+229158404252922cbbfddf3abd60cc9b
+2291f5e5d05d6e0ff78bde9981917918
+2296da092377ef367555af29d1622121
+229b265edc8358a792c842edcaef61f3
+229f4a25da60dece022517ea70a8373a
+229f546ccec9c50ceee168388023d923
+22aaac41e92058d14897d664036a3d50
+22b33495e4a6ebfc1d8fee0736fcb59d
+22b3ee236c7e1cfdbb3af79c79164e98
+22b4f78210d4b2e9991745e484d903b9
+22b796888ca66a235782c3e50ef3f2ea
+22baa8398201f13e019f1853ed79844a
+22bec97dc2c9a956320fe329d029ed2c
+22c849455dc39bf6fedfb81e6b487033
+22d018577687fc844448a5e9f65dbb88
+22e3cefc994d6054b9f102876a5a0453
+22e76b335f3039b491adb374962eabb1
+22e9b04e730e5fc914e0958f3552687f
+22ec716f28470f8952922cef79e4a609
+22fe5a4eda2412c42751cd5f8dc80957
+230096bd2ba463869ef3e6e15b5d693f
+2307ad67acf54c1110d41b4a309dc3a5
+2309ecf0c318c55ef73e7346f042f442
+230aca79247840cf72b70d92f82f2ad2
+230d98dd558ba1f2a193801b17a9129b
+231046f3b83fcb83a6439c9be18a349a
+231277ff5bdab895bf0934dadf1030f2
+231d06c898b565e6d8854c8c9fe8027b
+231ff12c2b315cb59a0fc90f4e346bf5
+2321913d6b504211533cc99dae150ba5
+2324de7ece513677ef8769e422df313e
+2325cc2f6550bf5ac897d079c9dfb3ac
+232bc0fa11b44c18555a0fb9757eb50e
+232c67121a2b1c7044e331209b39ca7c
+232e27a74986d1a245bf908419607ee8
+2330a838ad55f65de8ba7f2b7bc6489e
+2332042a51ba1caa1313b3a914d36c5d
+2332122208b7f8566813837e09e4bc28
+2336eabe3868a237398eb22c546640b8
+233719391657d784007800775b692ea1
+233b86753b509906f1bc9b1faaff00f0
+2340c7ae12c6d23f5e84f65870b966f5
+2340d34efca2d569665032ee663cb7a7
+2341bc76717c9ce1ffb9dba9e446cfdb
+2344b1599a04b45d20b8600b1b5305a3
+234773cf5087d444ad1b09fec68345ce
+2348c9e38775cdd42dcf51b8d7fec516
+234b9143642dfd7df3bb5e72e474cf3e
+234e09fffd188d48f9a013e0a6303946
+234eb78491c23bdc64bb3133a7a243aa
+234f08969319176deefde79b2d7960b9
+2356d6c05dcdce0c76039d2bdc7024d3
+2356e0d0ac855d1365b8f7fe175412dd
+235765941c8999bebbad7b7f78dfb283
+235871d4919be9469e57a48271e4bda5
+2359fda8884a019cfb68e5289f091b56
+235e0f64ea33828dfec1990c97f79036
+23705cb8c890dd647f958df0076064b8
+2372c8fe17572313daacd18947ec4892
+23769e3bb710fe8c9e16b22701238b7c
+237ada752ff3f04bb5874a60e1b4d5e0
+237d93f523e67aa31a494fade3f728ef
+237ed7529cb7692d070bcf6fcc2e4aa8
+237f93eb24ecc133ad949b1981519952
+23849de25e9c080f4b7e047faf7540ac
+238da06e9e1879f89c1b06f8c61c52e2
+23919b4a7638dce6537df1f214b4bc00
+23962194679fd78d420f13955756b863
+239af04a3def39f2f9c1e790beb728af
+23a33686b8f36307233be5fcdddbf169
+23a9870df3c7f5b0ba64da7803c61cd8
+23b05da5869200bfe16e0dbfc1e8dc52
+23b29c1d5a283c225310a366ba6edcfe
+23c247ef7a64371abc0d4a810ef9ee62
+23d1f4fe676a67b0e184d7653ba2dcc2
+23d78de66a3c576fe6f00e93c0505e5e
+23d861429a02b66ca2c59c97a47af9d1
+23e60ddc3be83b07fcdf6ee97a10549d
+23e739991c702cdd38c3146b6c4bd47a
+23e7a20d056f792c3749721cfd551bc0
+23e94cecd2cff4e82bfb36831bcdee08
+23ea42669ba1e40e96285b41c33f53be
+23eb4bf051d35d3bd5c94feb0af40512
+23eea01a8c1e678069947c8acd8cf2c1
+23ef80dcac18eceaaa64e2c937af82f2
+23fd8b158ad0ab97a69d72a2a544a227
+23ffd9a6683edd6e2c34153878587811
+2400080184703f7a90f30cd0cf0ad4c6
+24018266bfbf05093d9b6c99f6205a14
+2403e6252c232ff10e6bb07f367c7137
+24064dfa420cf1b66ded2334456743fc
+240a3df83d58bdd0f09eec7152d023dd
+240a7cfc0de4bee6b78b2164ecc8d876
+240c7e24c2c855a32952c18b1b086f7d
+24132957781be21ff42129bd749f9fa4
+2413d77dabb12ba80341258c067881ae
+241499d31420c56d57a643ee290a1841
+24167fe798a2b4d48ee104a255858703
+2417638409591fe1407ea5933bd955e8
+2417b3032f8eb8bd92eda138b124cb1b
+2422e50110c67f3d05c8f4420439f99d
+24282147d4b97323dc28458e18df2d59
+2433fb0553e870759061fb786bf2a539
+2436c5893c9a61fa5cdf345e13683f79
+243b55026041008fc711d7a85d682005
+2442c062162fe5e2d179bcaae129a027
+2446bddad80663a13682dff5051a758e
+2447965b2f1a5cb02df387c1fcae8d58
+24481c8d08314c7eb7613c12d84181fe
+24499e15186e6cbbcc79384d50048b0e
+244babfbce81aebf2dad6785ea443bdf
+244c6be6f3b24282f91b004a94601e7b
+244d7920546453eb317d06954ee03524
+2450696dfb28ead7c76bb60a3857807f
+2450cb19e43294851f93d2a404a6d55d
+245237f5d3144d4f7ec1bf1c170680c2
+245bdc8f03002ce59dd6a6dd9718bda1
+24634b6e8a6daf7ce4a9f04991c0adfa
+2464d6f38b5513fe587f99873eae6454
+2466d807b163b60d902e58ba45ded1d1
+246bd6d1e23a7ebd2a9dafc3585b2d04
+24752f39435cd3bc4ca81ca6f880a5a8
+24769b7789cfeb91cbd62fbf75be18a8
+2479c5ac4eaf9e0394b284392287d5b0
+247e9a62a1740a9a9a679a1bc4d56c6b
+24833dde6c3b9ae7ba4025599f14925d
+2485174cc1ba3bd948209bb72d340d22
+24876ce2ce6610ed139060433e0de03a
+248a75f425c62d826a8118a459cbc72d
+248dc1e5046236c4c4a6ef6cba0db19d
+249611542af9cc822f4123467e75aa92
+2497c18164dfc8e66277d94d16480a68
+24a1d1ebdf4b8c321b8aab77a503d4e5
+24a623ce9b635314bcd2d07b0a9ac65f
+24a9417837ddfc191f1f38ac1a7865ef
+24ac2a2a4e76423706e71f7b2acf2274
+24ad66e7db37fa5bc0ef3ff7b526848e
+24ae0e243269dcab8a2609eb5513630a
+24b3b8f755c3e5232ec86db982b84e99
+24b7a6e30dcfbf4f590ef27c47f09c15
+24bd5c494d80749c8ad839e9a815fa38
+24bd893098dab55bcbe8f818119b794e
+24bf10bfe00046e04db4f0f85df9e0db
+24c6c55ae6fec8295195e7b4ad4d09fe
+24c8b10945df5e8feca5adfab9854913
+24cd8e084c1e718c158ccaffbfa5d42a
+24ce3a4a3f695b096ae9c6d443737b7f
+24da3462eb9a3d96630ee47e81501783
+24dc58bcca3ab882cdc7507d9a32fe5e
+24dcfc8579edbfad2769bca53cbb09b7
+24dd09b7fad3d6fda2a9d959ff7694ab
+24dd9fff52647a767660da78a9357f84
+24de69b459a9188ecb2ea3b0259385c0
+24e24f8bf4ae7546f5769bd5d77e00a0
+24e2719b7df9f01451bd08a2a6a40405
+24e9d4d99623e716d737018c8c7c4a67
+24ea033cbee64fa0957d73ea7b41ff36
+24eab75a3c370da7da792b351e18961a
+24ee4add0962e3022d8148b7a2c80a92
+24f07b7f8d40ed98e9951610651a6751
+24f0922f413eefabd68dd4fcdb3d80f9
+24f106498a98fbeb132c45f99b40a48d
+24f20bcd80164b0f648406deceb700aa
+24f35bd3c67b5b2db6236d4bb2721439
+24f916e65f03be6d23d9e0a78bc657aa
+25032fefc3ce694c6af6dfe1bc08186b
+250381c5933b06f042297845efc0dbd2
+2508dddbcb5c8e0572ab44b990ada201
+25122c0c6444bc6a06a9e49a7a2a912d
+251a873981de9f79bcd13151ca26a454
+251fefa2995787fb8258e70a8c42c025
+2530507e587cea9af52bb1556ee72e3e
+253a1d66ad90844143dd5e0fb9c26bce
+253a525a91ccf1585d4e5c63e7e74a7a
+2541f4c4c3f94cf767832ffb81a68227
+254558e7172b54103ba7762edced490f
+254f9f1fe3cae4afd92ee7d3a14620df
+255255ad411a3f0f4317902829096d39
+255552617fea2189b9d8cf554931a35a
+255557afb1c78300fd59c743a70e00f8
+25555ed899b93275d70bbeedba521c17
+25582af4677d15cd4d2f5d8585d784f4
+255b7415738d9f0f6da59174155b567b
+255b8e7c14594478abb20423689cfe23
+25607e362f476000d31d85a1d16b80d7
+25628789f0d42c31b2c73d4b8b9e0e8e
+256491631e5ccbbafb0af67bde86ac6e
+256a69c9aaa7f4a5ddc0450edd41cd2b
+257404ca1346cea3e9cb0c4654ade32e
+2576d0a2e4dd55bac8475af9c25df988
+257c3007e4e5c788b508319e7858e4d2
+258639812f8cd09ced7091c296c949e3
+258811ab96b6278eee0a44c8cb65ce94
+2588b7a4d701fb3e009b8c525d2039c0
+258d492746f7f699b7fcfc98443dc36f
+258dcfde5fcb7e0384bf20a472e08f49
+258fda8b168674f29a48111f746cc9e4
+2598f8824fb7b655fb2f393f4967bbf5
+259dbfe47d47a232865dc2c5dae3f1b8
+25a44ec98436a44b19a0476124ad8977
+25aaaff5641ecdd6c34771bcc4b06a7f
+25ad65e1cde09c9d9e4d2375a0f1b6cb
+25bb264e9cc643ba6c5438d448a15605
+25c5b5c7a19a6b654d5fbee1c951efba
+25c8ab525979112e3adfe727bf7c28b1
+25ca459ca305e48ce063ff543a3b6752
+25cba26095cd6d80c3b31c0dd497f7d8
+25d31c2fd58946a759ffa3240f9f006d
+25d40b80e855c7f3cf05fdad772535de
+25d5ca0e3202c2203b3369121c92280d
+25da801e322704450461c999bdcd707a
+25db28637a0da4d2d6c178e32554d1ba
+25dc69cde0007f08217879d0e92c3c3d
+25dd342a7a947460daf1524f68b53442
+25ddba81ccf287c6fa1ba586575afb8a
+25e67d139b32bf5e219cddc63d25401b
+25e7b390f241de5ae2b2a80caab4966e
+25e8e8a5a06ac2fa4a2ed2baa73f69fc
+25f052d952f70c50e2de114f93095e99
+25f193fafc725dc4eddf2e48b7836e61
+2615f035989060746adde494dea956b2
+26169ffba3b71a4c39caee0e4eeb84b4
+262b14ae12214bc4ff5f7794c1a806c6
+262b6a5b98680b2f1fcdf47e184e5b53
+262cae3fd2845956882a0d815d73bd70
+262dd50d0752f1232e50dfca6da0a949
+26303a2fa9e2eb1edada178e1393ba65
+26306dcbcd70900872e49284df850636
+2631175a7b045a37f47968c1468a11e1
+263163056b30d3bdd958bdc486521d6a
+263220f7267a6273d33ae77ef65ee82c
+264001400ec2643cd03cbf138444da04
+264e08626ad8accebe4af504ce469a74
+2651453c24b42b645de817a05f48824c
+2655f16ce76a6870a47a7b8bc77e8fb3
+2657bbedb4082a9fd3ffad475e1ae7c7
+2659431f9edcc9eef4e91f87e050b382
+265ee3126e7ed3ad7416d8fae7b09a12
+2661811c5fc3d0f84a2d2335e90f4be5
+2663d9be4f2a14c32c32710c0e4143ec
+2665bfef3edab07e30904e6525afd278
+26691294c9672056234895cb29fb7b88
+26692bea5ade0d84570e1838ae993dfa
+2675ae9470fe7cbe2aba675a6fcac86f
+26764e6b14ec4787fcffb43b4bbd9b5a
+2679b0c03597ac280fcc788161166d9e
+2681147467b16a0f9a3bbf7bd614f7f5
+2682dda97ab9399532881925b2767230
+2682f0738a2a391b09646d9b6408dffa
+26877848be535edfe4e57fad2ccaf961
+268c0b8c9d87d718e91065a69f8b4911
+268c3eeec2d6f8ac045ec48fb7ea3b7b
+268d1339477f2b4de6d8728438a6487c
+268fce392976505b67f65cb72ae77cac
+2694b272e257dd253866791ea5b2ba1b
+26a271fd0150db3cce8113f160a8eba0
+26a8b09b10f0ac1f150e11ac8bcbe808
+26b13c68405152386fa925fdfe90b179
+26b35d5f6f934ab81d87cd3173bf1499
+26b44d8be07fecd13165efca1b8c8ec5
+26bea74092fb0200907d81436ddddf4e
+26bfc6416e8596bdf502056cfe842c2f
+26c2a3bae5f215140bdfbb0d7d94377a
+26d12b44fd97f44cf597116729b18ae8
+26d3cc420e3c294f2182dd3061722fb3
+26d55ef9a2eb42ae9da7cdf7a2008db1
+26dc6c0ab36f8ee64b1f4b2adccbd1d9
+26dd7dc1fe14cfc29ea1b29534206690
+26e1649cc1c607166b1f96812dabf7d2
+26e2f3b3894c24d7355843de06f3e741
+26e33b964900f3450585de422539ae70
+26e3c08095f9cc6f8d46d55ccdf3cd8d
+26e7c888ed87a301ae545b722f47617b
+26e98276419ba68c74f1a36ae7b45553
+26ee5963aa9a1cf857bc54c14643805e
+26f82bb1c9a0b6d88c30f7d8bf746900
+2705744d12738fe0aebe9895a6c3379f
+2706b7693d728e893cd742e6c5455eec
+2706d9bbd6832367cf3e5834093ae917
+270b257dd4ca3e62b0309b3a6a43212d
+2712657c73761f47bbf8f02a18a43b38
+2715b7ca7352ce1b3af0b0a5b72d0de1
+271b81ddd186be40072cd540fc14d9bd
+271f7da74838dfac3ad02d3d862691b7
+2720acd23bb67537dde491280ccfdd8d
+27246f9451475e08fb66c42d26a03dc8
+2727f1ca3a617794aae4a969d4605357
+27283b3d3b57b0c16af83c4a357e3e9e
+2729d1655e8d028fcfe83cffdc98849b
+272a8051d8c069abf1d8e7b5b73cb593
+272bf9fb980fb3046328e70b51f8fe69
+273b2a858151b4a9c238ab092e2562ac
+273bc07a65dcd3a2d0576670513371e6
+27411bb34d1c7c0a84f14954340fae54
+2746f2e565e5de8f06272a4b8194a818
+274d7ec4d7a320bc3ab7ff1882bec2a7
+274ff2f8363b7d695aa0cc4be4d2c56a
+2750a70f7b54f95670f1335a6db4a9b7
+275aaff213496338e9dd19e538308a04
+275bac599e3cf6d6307d794b5659c381
+275bf842a584120132495a0f490f45ad
+275d129c58644722c031e3bdfe4a2ad5
+275ea105519ebf9247e93d42b54a5ddc
+2771d3b1bc3f32604fcd4c2f803f9e6e
+277d73120ced94b4ced1ce5699976656
+277eb4751473c94e3bcb313a9c607322
+278391d2b52ca4d4372b3f5cfc559c78
+27892033e4b8b4cfd3b1f4d2ec4fb948
+2790c3bbb5d4a35df9557fa1c31f204c
+2791a12e6cccc055f22fe825c31edcdc
+279bd598b22d7529e08b305c482cc043
+279dbba3f48c40bfa58d356c3c89aea7
+27a18f94b88f1f9e8f3d9744a2038fae
+27a1ea7d978dd7735c6c89cdfc0ccee4
+27a3106157bb138184d42926d4a349b5
+27a5b97a28fb09fad98ad0a6539379e6
+27a8101a9d61b1e9de28ff11a8c65cbc
+27ac0e4198f057030e712ec12a8ea9f2
+27ac12db5919636bf3154f9ae7300ecc
+27be87281c0cbe573cb50d8dd2e06392
+27bf9314949f532a8a185ad1804131fd
+27c3cdeced267df8f71071a884ef86fc
+27c57c453d88cf3e9b82e9078e2f9227
+27c624352b65780d0825016292d2ea4f
+27c847363e42cc4d3ec704171f6e7c3d
+27ccd61ae219684f2d82cd6ee659c215
+27e1571623eb2584e9decd03ffa7c687
+27e35e26a10846923ba50389feb6d52f
+27e459c7966b24e5d764464b0537cf62
+27e8b8a25c7778169b98395456f0b6b8
+27f08f01fb23a91f344219b5857d83f6
+27f340d1d1892daf716df0f69ba4f83f
+27fb29af88e4c1fb7a318fa9c8bb7bdb
+28021017aca172b4b8a467a7bb411510
+28046e2c0aaafac3625ee6165a2046ed
+280e65ac830e5c36df50ba1c45e85f10
+2810f4ed6228adda8c7f6b1ac8622c14
+28112ae8e36843b3e15a703608ccc519
+2813474b9ea7adc5605320c667bf4ae9
+2815b9305245194a4ef04479ad84904d
+28185ad43c5e4bdfc69b569a3de56033
+28219c3ee8661ccd951b80ab48acaa7f
+2822cc3cdc03d998e929fe0b4333eb5f
+28272f2ef5e41cb28266726793bc0eb1
+282979f66fbf53996ad511881a1b60cb
+2829abaf662901591b43f99ee4f51b38
+28313f401322c8c1a65697c0c527c398
+28323673060c7d786d8213f80f9bc8b9
+2833bde063f81e1651d066d2eaa751aa
+28393b1acbb84ed19ab965341daa5729
+283ded9349b57de1cb44d9350af8bfde
+284086bfdcf781d0a464e2ea7decb395
+2844e31409c734fb83d502723cad6d43
+284b5a83a122502cb9a5bd1740c28f65
+284d774e619c125a14177e42e81e4fe2
+28594d96a7e3805d7ae54ac85704dfa0
+285c16ccdb48dcf9aae9f766a8434610
+285e7f89c2653bf230bdc02696d0e77f
+286091ed30da0c7b77c9d2732c0db5d1
+28642291fedf80e854801f3328ff72e1
+286771e88711e66eb4f801c50f3d85f2
+286a57cebf40f39f75ba950bcd2ec2b7
+286f60a788a6c8b4b632b703c1645217
+28716bd82dca139ebc17ee388f029106
+28749a42163d3edee68ae55c7e61bd41
+287560099f12f3f7907280e402fc20b6
+28772dee5b8bd2b3d1e7d8d45aff71e4
+287c2fc0df8a468cd28c23e6c4e9fde6
+287f5940146f369bcfb149c266233f4b
+28820a49d6f6535bca25f260eb4f8df1
+2888d117f39632251f1817d1863b1b3a
+288d8400d5f457124d980373ffb533f7
+288daf9a6bc30ebfb55de1de07575cdd
+28923c92980c77a0ecf4edfe36a354bc
+28944ce2047e683d4d7a2d6fc0af0244
+2897f6a64d1213373105272033ddb983
+289b591ccdb085a5953285590cd31dcb
+289f18321d8ba6f54b82d0edcfbcbe6d
+28a4e294edaa4a6a3e2536b020675898
+28a8529cad3a28c26ec20f9d8acbf2f9
+28b7314e966befb05355023e12d6462c
+28b9a17827dd56ff2bce79d79fcb1ded
+28bfe14dbcb1afc2b5b20669588102a8
+28c9ea493c7d79aac77e94a15c757579
+28d610caef84bec33a19c864b4b14bf9
+28d79b60fb216c681e608d7a2966dba7
+28e1d834e441875ef520e1ff147a7b24
+28e210ae4101e6dd931a8d7909ced98f
+28e38af2f154332cac7bb1f06c2a3648
+28ebf5cac545d356e3fd3344387935b1
+28ebf70627158a3ee800cb3db9f48ca9
+28f02eb7926ddd24fbacdcc1e6f8ed3f
+28f3d12853f3a11b5308b513c31048ff
+28f59569916c19d7b164ba518e8b3a15
+28f74313f443a400308d463ef55fc7b1
+2905b004864d92207399605aae2ede90
+2909ddc74f66711115b22f81a7cee159
+290ae49f5ad1413556f0a20e3809605e
+290e0bcae7f26505820e5486e49aec8b
+290eea53a38d0966582663f12c491395
+29112f1fbad82a1fc398ab39af6b1900
+29114c861e2c6c1cb3690527f540da53
+2925f27909f08cbd91d256f000deb832
+2928dfcda373fd32875740f2f8b0386c
+292c873419648853276bb1d6470fb3ba
+2932dea4fc0afb6389fdc295e1ae6573
+29398a74c97780a2de31f2d892e52ebc
+293ae1845b27a38a63fded8e586ae631
+293c413d6524538306c8607398a173fb
+2944c8423c63f57a5733862cd312512a
+29468277b20b304aa17563869fad3b15
+29480b3563bf77652a92fc28c0f552be
+294ab43bff03956d3ad3251c645bbbbf
+294ade6d7b88a34b0db9662313c9163c
+294e70b1e3daa2ee44a9edab5f6b418d
+2950f89069ffd8d0531bca45ba4c5c91
+29516e0f135894a8257b75dd2c454316
+295427900bd77fa811e86b37bc8bda43
+2956b6ecdbd9c2974cf6a2d644889958
+295761e29a79bc18b89bc36d7e8f9987
+2959f06f3cebdec14ea2c5a88f09ecc4
+296515cbd46daca0f17fd1f7269332f4
+2966ac1205dbed991ee29c74bb7ff804
+2966c0f7e3acd67849da85779d1736ea
+29720832ba0d2a099f762803bd87e21e
+29766efc5a9d8bd7582b88529aa687d1
+297a5cd892836e02e02e75973e63ba0b
+2981b24a8ed5ccad8e5b77aba148a65f
+2990a982f0cf642fefa482c42ef27e95
+2991949b2a31c16b203a167be1c860fe
+2992f639ca33b74fa7b1704a8baf4e8c
+2993d7896c87fce65dc63cce52813bbf
+2993ee29e7594a37201a56c234f966ff
+29abcf4037998d0fccd4f31e7e260afa
+29ae3e8447f5b050b2be530db71609c0
+29b12655f8152067f058ad0f3a811419
+29b17e7519c831f893141f9542c833dd
+29b1fdf3bd9335e15b32f8d340fd0bc9
+29b2ae7b5d886e1fc0ead35e4e961e76
+29b4ac2f2e50627666d9db25b2b8b187
+29c14698398d9b4c9b762b45d75457d8
+29c180f6134f915643462ab5b3ed709d
+29c76624bc7c285acbfb69431c6d39f1
+29cf6c9fbe1dc84756bc1e156a3991c5
+29cf87a883a757be07aa893bd0dd678c
+29d4edb5e7b4f66e34a077142be4de65
+29d5abbf7b17ba018f8dabac4d63e60b
+29d8d1cb9c736293e765633c7d293a1b
+29de000170d2b1775c8898ff9aa708d8
+29e2cabcc23e73771e5db8da12f43b3d
+29e778b9cfe28cee9761d99b853c0b34
+29e8abbbc20f45e4a782840d4406b86c
+29e9c9aab1f226b44582a34b735c707e
+29ea392228fc2948f16d7f1ae1341a03
+29eb23f8f6d1db96342aa53f60912132
+29eb984d80340769870ff6deea286f8c
+29ed472a9736824c7d6040bb5043cb78
+29ef7e3e4592692fee84235712a603b1
+29fd6639ca9813cd680da5b6a9ed009f
+29fea0396096069a878fcab69b5005f4
+29ff336611641d962b5f1f6ad5254080
+2a023056e0890f785930bb1de6e775b4
+2a08857163c8b75dd22fc8799f83b6d7
+2a09038299e7a378f9535bb8d4215d98
+2a0e92bcc3f07cd1f68031fe8b21c4e1
+2a0edc9305cc16b928b8a4af70606ece
+2a18eab9f0c7d3fec2b1f666d96d72df
+2a2a1300871101fb39c48d0f572f0d52
+2a2ad2ca000851f07abf5823fbf327c4
+2a2d5190f4d626fcc8f3cfdf4a466f9c
+2a2df60a2e8e36f9c2e8e8ca5e408c40
+2a2f46c71390885363233839f01ac437
+2a32473b2cc50c371c23bbef61c6bb64
+2a3305649d8290803545bc61f4aadade
+2a3422316d930af63f6dce35c0612cc7
+2a3aaff72d4e4913aedb16f5ce5995a6
+2a3ab9a00fdd851278efa64d4df3e6c5
+2a3d0ca259ca626d36cb7d21f15da549
+2a4a2fa6abd1fee5b1763397dd67dd78
+2a4a63c56011d5414d177015907702a7
+2a4b1d376ac19df8524de9bcaab969db
+2a55e80264390ffffe397e1c0990c96a
+2a58debbaeeca30a29885337a72dd458
+2a59d1a99d9359790e57347354450f47
+2a619d84c777b4f786c7e72e4b0f9179
+2a6a04a5041cb025142eb697213b25e9
+2a7223396cb6b34996311281fd7be9c0
+2a729611f39d14d7a50d1e57063f9fac
+2a821bddc7636d69c4e3a8e14228c5be
+2a935ea55c7eb4a3262fe7ef05df2300
+2a94ca5e41290bbb7ecaf44d0efbd6aa
+2a96ce99e35d367489cd25fdbed3daf6
+2a9e0d97cc99e424eb4edce35080ebf2
+2a9ecac5ed7a516924c7ae58a2ee7977
+2aab49224e7be37817936dca4bee5aa4
+2aac8d6f8d78a800efa51ec3490847d6
+2aad92bd5bd3fc88b69da5283ec30d04
+2aba2b39cbf1fefe999667a410778717
+2abe82bbd8d32619ec0ad3fb0161e630
+2ac59730f0190ae55d3190920309ca1d
+2ad1d36e6f83e601513c476c5023e6c4
+2ad2a9feca6f045abfc5023c1300bf65
+2ad66171d913a813a92f7bad05768ae9
+2ad770e144c41aa45b44437ab9a92a2a
+2ad812271143c00a2f5694a804d22301
+2adb6370d8f5a5fea229a36194a89803
+2adca13c297a086a2bef248e50b34995
+2ade9115f354a6b7ebb3b2da17ef9bd3
+2ae3cd72ec01bd301a04d9c6129a26d3
+2ae3ecb46b2529ed43313faf996c3478
+2ae7cc552b34847e6ce8a42f4eb7fea2
+2ae9b15fc0ecada94d7cafd461a0e1b2
+2aead96252eb7d801d77e704cbfc6e8f
+2aeed25550ece65b93594b767f39b74c
+2af0d89294ffbb7d21162ab99ce17886
+2af25a39e19edf54a4f97b548482b0fc
+2afea65f6046085b40e2b7c3101bb21a
+2aff5f5053d631642eafcbdb4e3dd6a8
+2b08f9c3a6521a66194e0d396237b3f7
+2b22044a4f386e119790e8542664574d
+2b25a3e89f9182c7dc257767920ea7f6
+2b283d049c4d8fee28712730d72d9d88
+2b360db7f7bbcd813a40a26ac21771a1
+2b38be89f1ea60c792f8d5f2a75e3e86
+2b407f20df57dcb45ae68e2269cf3253
+2b414e626d3369a3cbc09db841e4ad75
+2b4696d15fc7614fd77e08060c11f52d
+2b4705432991e4569c37330467f1bf9d
+2b4d0afa254ead986408be00b7f4e64a
+2b51d23ef1a88398bc4623495fa19fc9
+2b57db5b5d39ecc648fef97ecb3a8b2e
+2b5ba6024f3cb6cf1e27685794e191de
+2b602ea30d165c9b4c1caf10037a31a0
+2b62270f7734fff4e7c678628415e50b
+2b8426928861c8139e2a27d296147ee0
+2b8675c2e525423eb858f0f437d53cf1
+2b8750099dd03aca6bae07545a5e9152
+2b8eeb41c39213c12f9247f21ca078f1
+2b936c06e1ea5bf07f6901e42414cd44
+2b953c81fe9fea51998b506d962a6ef2
+2b989b1306e18793015972134df6ab32
+2b9b1e8d8137b2ef9230f248a0ee46ec
+2b9b920d73092b83ca3112ea994dcfb0
+2b9e70cc118090e1a23e9fcf03eafda8
+2b9f37d878b2b817b317e026aedb009b
+2ba0174f07c1e38eee5c0c0bc44e9cf2
+2ba2dac14cbd28a02fbce180306a8b05
+2ba3f29944125f5d970201547722ae58
+2ba6b432d703f60d1912836348895c66
+2ba9dbb117b353f228c791a246fc694f
+2babe0d112a662c3db7951a2133e2966
+2bafbba911db6cb8d7bb46e133eba619
+2bb2c218eadea506dcfb65a396e47a9b
+2bb61647df445ba0d5503f4f16eec8dd
+2bb6343786315f1064988c6ef60a73e8
+2bb9c0aa9859b984d3fcb36b1bc5edaf
+2bbff074614338515c1dfd7764cc0314
+2bc104cdb97c8aae6392fff4602afa45
+2bc494dfd1a98cd6fb58c1c6d0bf0ead
+2bd09f8023736fe21eeaee1286fa36df
+2bd4b7ad70c35bc8d7f872d4adc0abbf
+2bd623037b0b987dc9be3076465cc986
+2bdc0aeee682b4d82414724ce7afa7e2
+2bdcb7cab60f6fd9bbda4aa850cf5827
+2be09c2e61b63aba07b5bea92c190d14
+2be2dd5ad04f87fc204b7cf6f630620f
+2bf00b52f1ff389aedd7c83ecf58110f
+2bf54fda87808eb490b0bb642664fb46
+2bf5bc6ffe94ca002b71e7fbaf31e662
+2bf6a3a5134b2cc7eccf28cc845f21bd
+2bf7069e0a401d0b3893af97f681968d
+2bf91ec9421a077577a796de32b424a6
+2bf99c263052d69c7d7473e088780aa6
+2bfc467a6955aa464be3892c644c387c
+2c0335d219bdc9bccd89e17ca3670f4d
+2c03a8a365976977e92f6d0c8d80244d
+2c06507cc2cf050056d8a42df0b940d1
+2c0f461230aa9448d60806b450afda5a
+2c108205dd7d80cb4350f4fc37b86168
+2c11080b640c4bf022f919b5a7b8d7cd
+2c1b1e6eb30950cf8171e36101da9d8a
+2c1b565043ba98542847fb051da3fe92
+2c1be22be32e18d8c3656cf2e44f92d0
+2c1beb65b541a94fd5d5981bdade52d7
+2c1c9fd7e45e04aab69409c1ef9a8e30
+2c2dbc42260a273909e67bd3ed9a3ba7
+2c317cbd838f6b6477b8412eb0e286b7
+2c335e4b849da52d33d48d7ecce7cf8a
+2c35ab8eeb437b44e5d07e5d31052feb
+2c3753c8b6731cacc7bda77a5de26220
+2c378789cd6bd90d65f83bed7ac561f2
+2c3797b7f89e0e8d7f5ab196f3772fb7
+2c393af500b22b476dea236dfdb01769
+2c45d73234078affc9e0e08f32791821
+2c556066b92e3022622e4e2ee38ede9e
+2c67d429e3d6384c163147b4754ef671
+2c6b9ccce0b5ee8c8c3823e3e86995a3
+2c6ce4653e886aee7c9a2364363f0c86
+2c78a6f8db5742a722e414757168cd17
+2c7fb74cb998b1026c96219bacc5947a
+2c83cebfde8dfd8ea697cf8ae72ada88
+2c86880e44117c4bf68e20e95036ef1f
+2c879d499efc567a8edb6f07c1f0c02c
+2c8df90ac2248ba8396dab6fea7a82d7
+2c920d747d8a705a1226dfdcd68e3b20
+2c92e80bd1e8f4cc9550f0c8479bdfbe
+2c94c84b6c3b34672a9136f945e40aec
+2c9d054317fdb277c7b1bcba6a73cb75
+2c9d86ead9c6c144db0c548833851113
+2c9f95db3c7ba3c4e226ea84a8859cb8
+2c9fd37bbb9762a2ffb7843b11ed27a6
+2c9fea923dfbb4e98abbd242f8500c83
+2cabf425035a89bb69f9309f89bfe33a
+2cae0df600e3fd618ed2a51c3b1e99ab
+2cb0a7626df3e725bd5440b64d29f4ce
+2cb1a6e9e8b70fb2268826d3c8bc235d
+2cb203b4c5bcb917449cb90e8b05083a
+2cb75e0f9b5eacef2859838e8e4d5a4a
+2cc09bb4ef5fc8e1eb7019ff18d03467
+2cc19a6366c2fe7ec62c2474f39b0b30
+2ccc5113095de4f946b900598a75d13b
+2ccc625da3e05524d8ac0cd049b8e3f9
+2ccdcdb65f4802127012b617ded4fd62
+2cd732100172620a6a04ed5eaab0890a
+2ce0c0e8d4343b728f0139c667274f37
+2cf17eba57606a8fc943795adbf80e8b
+2cf74703c0f4e5e88fa9e3ac0e3d7118
+2cfcbb282bc039defe22524e75f00c80
+2d038f58de83385739846d037531f12c
+2d05179316185319a254ae07fcb4c3ca
+2d087616106ccc3147cd472ba627ed23
+2d0da2c89c779c5d3cb8200fa220c28b
+2d0dc2b22c154d87b66b10c7e03fae06
+2d0e2c33675b519fb4f876f1ae1e1617
+2d108cbe2c02960356bfc25049afd225
+2d127f0e553f212986b3645d9fc638de
+2d14eebe65f5ee717630251561d171ba
+2d19e09a5848c47819f8d9bad74bae38
+2d1fbe352ad1e6fef98b13a306c0afb8
+2d21197ff9697b3598564ff2ddee73f0
+2d24ec1d204ed02a25d2aefd6b9e1777
+2d25c7fdbffc54006a6637d8aec7e1f9
+2d2631e17aa89e444e28b5f7b8fd9584
+2d28eb20647d4a84257484ed14a891ba
+2d28fdc698790e77a6fb4e7794c1c942
+2d29ae875fafa74e879e9bf3c31c2d7f
+2d2e05e09352f911df28eca9bcc8f46e
+2d39a7e23cc1e979aaebfe2f4af077cf
+2d3c843c0f420493d980e8904c5e68f7
+2d3f173418c36b889b231c5e718c1b64
+2d416779226b7f1a5c986f4d5d36b104
+2d43e81d9f7e14df872a77e649baedd3
+2d4548ae105b37f91802ea9660e66e2f
+2d46ac73fe4b37fbe7e1d2dba7118152
+2d489006d499b10b94b6f2dacdb664d1
+2d5508ad54959b041d8df6094833e973
+2d57c7fc1219eeaf8d3731cffd4c22fb
+2d591b8a0e51d577690d5e28e4aaaaa0
+2d5a4ae4fb0bcd6803918d5f6b3cb8c8
+2d6b82ff18ed6f3c0246d15dd0fa4655
+2d6c8de6b7cc362524cd10ff0cdc92f9
+2d6e182c8490ad0c0d48b584703525ce
+2d722eb49e0808f934ad74fe82157ac8
+2d74bd3f8186d705f8b8c4c1eb9c3bd6
+2d79a8f96863371c063f6087ed0d2502
+2d7cfd76b0d0cc2812808801e53a0fcd
+2d808561c772b205b05ed0b63f4f859c
+2d81e6af689d2472e807638b4e78222f
+2d853ab8e6aaa8e0c0857a82c5e81e1d
+2d9176b2491938dc518270966ccedc7e
+2d98985dac1d0bfebc830f2529bfebd4
+2d9aa50069b483848fe0d5aa71083817
+2d9c7b238554c3d95da1de0a244b8123
+2d9ddf60ac7abf311b072262ccc56132
+2da2cf7d5cd03bb86f94c24f75925351
+2da5cb0eff92d2bd17583246af03041c
+2da8d88bac68055cff7a52a8b8ace321
+2da908f9ac33a5c80f55aa246367a23a
+2db0878fdeb05b9878c351302528050e
+2dbd05f29c1938588c9998d194d604fe
+2dbe12a82542a2af060dc3517e5bc538
+2dc3dbdadd09244a747527308ed185ca
+2dc5bf2f7d83e97a0dcafaf5a8cbaa63
+2dc6c3d0783e8793e180c44261399964
+2dd2e8f6d97b9c290868c5b0c333bab6
+2dd3d63327fff6ba5d9623b9d89c6d11
+2dd70be960480bff4510ddf9e59e23b5
+2dd811df9c6874ddee85cb6a6175f713
+2dd847d7151fa73a39414f47f19ae3a6
+2dda9c87c4ba7cdaf1b12dce5f0ec4e6
+2de0328905a58b4fc16e77641458542c
+2de0853efa57a02edd6aef83e8268771
+2de3355388ae770ffbd8f9c3a72a9c58
+2de723630f060901e7b58f0c191f2bec
+2dea65a10b41f163a63b288b70d6e17f
+2decd5e5cc20316d88d44446cbb0929f
+2df119899886870c0ad65898bcf47d4c
+2df1e36bb9029d65c5d3771639246ea1
+2df38d901a084ad9a9035de4ef999f4e
+2df90d27370d679463b48b411eb6d3a6
+2dfa40f48e03f87c0cf7cde98f9d08dc
+2e0599a6cc8bcb4724724e68eaccaf11
+2e0a2ec7c805db7af8874f96011fca5e
+2e0afa00261d875963d514f672ab4dde
+2e0ce7114d9e9ddba278c85c14e74faa
+2e114aed45e6462bf56789a792426c22
+2e1646fba2336d1573d070263f6e3cf9
+2e17ad6372c31a697e047464b315b0b6
+2e1c20277931e4fc68aab2b90872fec2
+2e2654c5530edf4939623fc1ac5b8e01
+2e3527733a74071bcfe3a789f2842285
+2e373f43aeac8990d38d265e61a498fa
+2e3887849f159253d1e89d66667f9532
+2e3bf670aeed38a95af96ca49d49a552
+2e4ba54bc4ffc889824b0464aa0817df
+2e4cb506cde919f7d8f6143333764d42
+2e4cc638df49b3196aa5a627490788b5
+2e52c371cc6c1cfbef92836e08893b14
+2e57ef4d969ef87d4bd3f2724c9e1950
+2e5897cb031de4455468e3cb5e5ca7f2
+2e59476d633032320b310e42e12c5844
+2e5c027e431caf0c579b84b329c06e85
+2e618b4cc8121763527c43065e7eef47
+2e6568758009c3105156041fac890c1b
+2e66f56c815e10cb6be480a642a0cd7c
+2e673868504f9df3474ccd87b50bc656
+2e6b00f0152f28af4b86eed0cea8895b
+2e6e99b0983ee033632523d218c86630
+2e766332ddbeeb078e3aa5814adc7a8b
+2e785a113ac3fd5f54030870a12c00d3
+2e7cf4534e1a6ce1dcc3fd62a21bbec4
+2e819292e3ad53b277f88fd2d34b37d4
+2e893d9657300f780a4447863ce5c86f
+2e8faff11ebf0731bf5ca871daf06b9d
+2e92fc4ef7d5f2d453f1f6a2334d7fa0
+2e94ef3a88a064bc216efd0cc0c8d3a4
+2e97547500f35f3d7f56b9fc490fe6ca
+2e9b66b8dcf9a443723e78a19a3b2941
+2e9cc4a352ee4294736d18e131057f20
+2ea009fff04f7b432eccb4d3c100b871
+2ea6f0836d143772771baf0a031ff74f
+2eb197786539a6f580145187f1257106
+2eb3f9f115e8c157ce375be88dbff859
+2eb53225da743be8ead3487a1f07db06
+2eb6de024c6023e7b5e968a47fce1f76
+2ebb57ae3ebcbdf46afaf1e515326df2
+2ebb7113401900efd89d27b4b4fc1a55
+2ec1f8af92723b47febf9e675b4f951b
+2ec77bed5b3e3819bf4b20602c01b480
+2ecc3c58524cb7ee88724febbd03e3a5
+2ece2bf5c59dd1f748c8534b19bbb85f
+2ed0c3ef9f37073f5030ce73cc88fa36
+2ed326f26aac3f9c5f9550f1322e20ed
+2ed7b551cfde4283770f5efc86034dc2
+2ed8d8bec6707001203fa9ce8feccde3
+2ed9b5172ed95b6706d507a401009494
+2eda81246d7916e5f54e5fa3789eb1e8
+2edba639e025ea903b4760b6331317e9
+2ee217435e1838cb62241f8a324cdc5e
+2ee25c778e9e74a954b9f6977da9830c
+2ee4ba66c616084a2ba9baae384dc8e1
+2ee50f22118f33232051b6190018b9ed
+2ee6dcd4ad4740411bc7ea0d5c08c35f
+2ee9de63e39d8744240b6b103e27296e
+2eeaad7198b640ccd9a343533e2e7058
+2eefff6c58ae677411262cb79b2d7978
+2ef46e599ed03d25b7344a40f6777896
+2ef6e24f760c9fd4005bf21249e1138c
+2efbdc290a37816cd74b73ec9bf3b84e
+2efe8f87acf78ea4655337bd5348efd3
+2f052d957f7d44210f86261a55bb868d
+2f055a9e6c51892d2b29ff53528a1bb2
+2f06d67a8f8013952f2788ddb9ba296e
+2f06f34ca6c1cab6917e82241515e178
+2f0b94eefbc4e4713f2d4548d631cc28
+2f0d1c6b3d9e8210fc27fa3815edf1ab
+2f10e9d0eba55e17af159606665eb4e1
+2f1397a8a66e2efc790776703dc0c722
+2f162fd2d00a2cb3959b37cd732a4adc
+2f22110c34662c4fdf80bd273d2c46b5
+2f2572b575b8ecafd16370f930bed3b0
+2f2744cd8d032de2f73b7b55447636c7
+2f27f2f290ecb0f818236443e0d7662a
+2f28e98dc0420cd3be9b075e0ff1a210
+2f2ac717b9e92edb70b6fdd882654969
+2f2cbabf80a331e7cc4ed32c4d124256
+2f2d38ef28f69c02c6d51907a28a2ffa
+2f318ed61dce9b6816281c038488e3f6
+2f3860af671bc1592cc4ccccda43ae9a
+2f3b8ec36f532cb88b3b05d992e1ed95
+2f3d3d1e096ee47bdd2fac9bdcfa4d18
+2f42b71a001b93924bb885cf29e055b3
+2f4e5ca01d04a50c91acc22732235cb7
+2f4e7a1d991d884113efe43eab9c27bc
+2f50c2a810f0c1a60320e491e4b2d05b
+2f53e42d8eabf10ddc98a6529a8a61e3
+2f680745a0476c23154bb4840e3e60b6
+2f688db067b92a5f32742aa57af26e66
+2f7b0fc80de1b3cd151d6c858003f720
+2f7de40c00bc2c3ac26af9a7e1f09692
+2f7df3bdaf3e938127854034527e3931
+2f89411b3fe38ea818cc85682678b28f
+2f8be8082f45da5663f9469fbb77dbc1
+2f8fc53fd4e05369bccaaf037329f8d0
+2f940fa97832571c9e99f72d2419ed14
+2f943e8900b7f867e60b275ea1b6b855
+2f958cdf39f0adb6b7149a40ac2dd04d
+2f98afb5a7b7c7af013b1ae50239c0e0
+2f9f36e3e9c34041d25fe7b12a19c0ad
+2fa27383a22c2dedaaed7085d76d60ba
+2fb0137e0e4292b071b680df14ba2f41
+2fb1396f807dccfc1a956086a493c69a
+2fb5705a7a569cffd71f955c4fbc9390
+2fb64d661c0466290843f07b89f294e9
+2fc3307a828e885b3673e46626f0d806
+2fc49bd2e58de22f783b1945a0c43479
+2fcbfa84cbcff30177cf0df649e14224
+2fce8adefe9f0066a805557e8d4256ae
+2fd235eeb33435cb8de4fb57a607c1f5
+2fd4a4fa2cb67f29871a154476027614
+2fd7cfede746857c9ae82850daa770d7
+2fd927da3ef7e7fa2916ba807d590f43
+2fde0db465be5df32fce7b1ab3bbd090
+2feae3488dd9062950dc4a81ba986bc7
+2ff1e62a192ff56015af810b29064a07
+2ff7c9b205c706bb05b0f6582e230dd3
+2ff944dbf6d4176ae19c5333d25981ff
+2ffbf863c35ce438bf24017772817a50
+2ffdf8e004637650df48892f3e4f5661
+2fff28091befa4bd799c19adb68a88e6
+30017ba1f6bd892590eaa7b7e7c23ef1
+300b50cc132194c6c0f0b6e021a5324e
+300be4046f1f16cef6e713a4bf61d0fe
+300e62c6504eecd256994c56c5674da2
+3014705b256de967a69bedfa5487cae7
+3018d1bac8748b68340051771737bff7
+302c5e3f52ea1796ff5c4b4ee473c604
+302cb8dced187ee9b257778c8d642aec
+303122b30d511b6146151f9ce8dd1f09
+3036808661f4fb5f7053a0dd83c6b100
+303ff964ae168be6f8fbef34bb4a5ec1
+30451fae74165af2035080ab28102453
+3046b084630016263dfd40a2277fd9b4
+305000f9af933781e294cf712d549880
+3052acdd3939853b9926094d73290710
+3057772225a858f1c15e13a286c53c9a
+305fc523e90ab538543a2267bc189e2e
+30709345a12ca073f9153083945cf9dd
+30730117496ffd12e1f3d7c3cf6630df
+3073c6c43aadbcc6b9ae7b43694e25ff
+30765006b9cc4a17e55f8351633cc7ca
+307b6861ec2c33fe36a0ce1a6ff2763e
+307e529effa4d4b73014a9343e4a196a
+3083073deb2a4a1859c43e3ca13df5f9
+3086ab03babf17d0c61dc50c0b5d78b5
+308770d1e98e355d9df45583b2748b5c
+3089e44c22d1751cff5a646021c2ecd8
+308e3a397bb48c7cd1dae0a3962b2bf9
+3092fb40cb4544973187f350dd610383
+3093cb66cf28a993882f149d0d60185e
+309c2a4595305860fc4d7e850afdccde
+309e6827f7e2f1aa577dff4480a5e74a
+30a47b6be5705c45581a3cb34a4b2702
+30aab146ce00a97bcf54103b8a79d354
+30af6ec768c24824a5a129272fe5a15c
+30b3499a4c805ff0c9bd91122a16c9b8
+30bb43932a47a9b06510401fb58591bb
+30bf7cc634433e64f0229d6560fb75e0
+30c1c91f8e080d4a90e01294fcac0fb8
+30c28c08b133c649a8819a4b43c70501
+30cd3a88a4cf921f52382336d9de8eef
+30d50ac0d5ac7887007f3a771fe17584
+30d5172abfa33c439035ee0f297f413b
+30d7b59fdfbc71fadb55839455fb28f6
+30dbab96c2a256b9351930674e41dcf5
+30e5e74972d6402ceecc043f653a6073
+30f33ab12439c55f96a3f54b96eed127
+30f4662b3e9097887b45bdb8928674a7
+30f4b2897cbcdf51d984cf811997de7a
+30fdac64aa82107151cd0a46fb8de584
+3103884b11c754dd558d3cd5993070e2
+310e2462a106c3cc66d5b5595f1d057f
+310f7bedbedfcf7743fa418b49639e0d
+311d0ba7baf4ee3ebccaf347c25f5d7c
+311faa78873f752a9d8c18685c3f904d
+311ff64b872c2ecc7eea5c51fd3b0e06
+31229a551a2e1d7682228f074ae262f8
+312c083bde5928b960421c090ccf0116
+312dfa86f468e0c4d0b58977eacb4e06
+313118aa5280818f12889e9a50cb5544
+31364e82559fbbe21317bbcd43c79c57
+313a0c372174f69e9bd7341b14aaa09c
+313a35614c525d18e3fa17ef69cf50a2
+313a9b779c136b371259043cc755fcff
+313b11c980ab1e7617e76fb68b9f66ff
+31408d311df8fea0cb6843cae64bdc54
+31454b5c0d93b859a5ce99e545af8841
+3149d2312ee16b2343bd3a1dca4916d5
+314a039b5dd47fd2fd71c955e8118155
+314ee01d4c149eca8f8efe68d4afa6d9
+3157b68fd0e9a50481a9ef1451a86526
+3157dc736dfc5361029f67b6c144e30b
+315b26d79673d11760d42934198a6471
+316019a760a7e6d275a10e07af16a9e9
+3160cd7c0a67d6d8f424e81c375fac48
+316436643dee9ce3cee6dba2629b3469
+31657ac7f759c0c97a1e6637e187d029
+31663875b29c6abf1eecee0f2da03f2d
+316cd494d3370ab4c8b91720e32458a5
+317339ae51c4eb8dff6c0124a4d33fb6
+317b4a200dd974a5a79f6cfd974b94cf
+317f125d0ffe193a666508f0177bd461
+31849d44e121c56bda4b2739504d5c97
+3189bb574cefdde2c3b35a4ec9869191
+318b94d32aabdec9a8531347f58c8f27
+318cbec3953192ab9ee892a35c42d5d9
+319094cdd5a69a7be601ac706f01405f
+3191fa7e3b4fe580e2513b57f88760f3
+3199d25800a2849fde9bd6898ad2928d
+319badc0cae279a243b8bf667e70e59f
+319f1345e14e1e348d4929aa1e8ebbcd
+31b5ae3dee7e807212ac16d648f16e94
+31b617b6ef67eabe69fcab6cf3b73973
+31be90e344ac398ec3e0f921256735d7
+31c0aa0ae4f9cdfa501920a64455babc
+31cb2d32c77a8f90d32b0987d1e4069e
+31cd99a43da4b97052c5fadde57dddf1
+31cf2598fa69dddbfbd01146e5e963b3
+31cfe32afac7c4cb57a2826dd7798bce
+31d2a5d4299fe6ca862155ead1cf67b6
+31d51ba660e9336ebad0f157845d9e97
+31d784e7e05706a93cd67e6ce0d75a30
+31d9d798f4348bf5b9ecf8af3a3b69e0
+31da3d7304e52e33aeecd79e156c1c24
+31da9385d6787ac12ba3257fbd7da273
+31daf39d2abef27ac0bbc47524fe010d
+31de02e80018b719d02888460b83ec9b
+31de914a66869a04ed186a78b3dce1ab
+31df43afb9f8b5b858b48e4adc9ce4d2
+31e2d3bc32cd48e8f09c622974836abf
+31e3fff180c6c4f05614617a21097bd7
+31e44328b007fba20bc0940c66220133
+31e578bb3c08c6a0c616040e62d01096
+31e9ba55f561c35b9cf78a7c1125fcda
+31eaf85ccf9059fc76a562d9dec806da
+31eb5e87ef98571284dbf96df776f52a
+31ee0051b63f74c6925fc5b18a72a570
+31f0802978f58ea275fdcae4c73668f0
+31f147eac0fd1b51a66f5a98d7f4d413
+31f521717ea7d2d31058fd9f21db573c
+31f9b113f40a43af8152e7f1c24ab614
+31fe9140b2aa3076f429687d2f53682d
+320450e5684e9a6032e01fd0f0080a4a
+320a84d9ca581274a423cd7a3261cbc4
+320e8d414c479681d22dde47a1636a0f
+320f37ee55d76509053e2777b46c2184
+3211a705485e6b1d086b0f7f5eb14527
+32127da99983a2c440646b24595ebaaf
+32134e82fb2039a03f60fa565f16b294
+3214dd9d134293affc80e6880a8359b4
+32151e7ddc96b7e389edf6a4e0568f33
+32175dda8d28e3e0654bb75b77390b8a
+3217f18b0ddf1c879d9779f7821cb51d
+321e619085498a07705c470f58c43d15
+321eed276cf793cb42065150987d1e31
+322665ea8ec2db67ac2fd05fbe721f5c
+322ccd7434d8b90a79fb90d1746d9bef
+32336c8b864a6c7efce9c554bbb57a48
+32346f200668890964620f4d652ff86f
+3236c48210ffbc9fc91a2840b2384973
+323bffd361074397a9070f71a48b8d94
+323cc9fd8b82402f1588f7d020f8d12b
+32436cf47e61c43f3f248db4f40b41e0
+3249f5fd0cf1040ce2ab929576cbd22d
+324eb2eb38ebc11d31097673819a8f0f
+32519e5b810a99c0b6c7d7b59a4b85db
+32523230727d74d55fc0a62630cd05fa
+3252fc002e3269b594a70e03a886af89
+32563766ed07f305221ff3e59dc23cb0
+3256bfc839f1964541fc4efdaa5bd3ad
+3258b3a03136974c68261caa407bb2f8
+325e03478bde90548687155468e45869
+3262ba0f965a8053a6d15fcb67b26fb1
+326b2d4394cc91ec99ca07bea91d15ec
+326bdea0e5d48b3988dffe6d63f5a369
+326d0de4f7d63b27325f6f72fa076d52
+326f35d07ac1ab5353c5fb77fe6cb690
+32709c582d95a03d55ba6ea405c38fd8
+327355afb50e011aae4206e341e22ae4
+327a4a9285a2bd602cb7934be5fba00a
+327bcdab1f5defcbfa13890c8e4f0f40
+327bf349e3f32cd017b4fdd26d8989f8
+3280af1e343828bead37e644d6ba6c98
+3289696f2629286538a831eec3b4b802
+328d63970376adc4f079b09e489ee9db
+328f43e51d137d481a13723b9d9a52c2
+3299b8cb2ea307063a063cd75d833a3c
+329f4735b43dcdba001cd882629e5c02
+32a267d1c8a5c65008c01b30bad3d9c0
+32a3a7a92bba6e016836210609e7471c
+32a44bb33af90c3d4754a555888a27d1
+32ab242e8771e5c19dea1c61db24ce8f
+32abfe85c0ed01d6c5f18cc8c390d470
+32ad8968696dfa749d42c39f4c3e3a98
+32af1a6a310d18b80a0d487e87692bce
+32b51a88c730352458ba593d51426591
+32b5633959628235ae1dcb7ae1a37a5f
+32bb59b96b6bb1133fd48c17d1bbcb32
+32bc2051a57c264decc4f803720bd266
+32bdff6427d67633f8cf9c23e1495c40
+32ca4c74bc4cf19f1bc50793f1f914af
+32cfca4d3466c474331b2cc084f89531
+32d7c149692b51e0bfcbad0e13c2216d
+32d8c7498972c708f657b5ea38bed2e1
+32d9199a8b557a9b371b8c3322655e9a
+32f513f23650c2abc3e4f421c1d92ec8
+32f75e6866cfdcf3a0a067ce98db97ab
+32f9145ffd182aaf960d6e140bc94923
+32f9dac77034a9f4e4431f1160b87cde
+32fc4f3ac0d8574e54d2dd33e4c91e46
+32fce047cd81f4af4a39fe0f5dc8a596
+32fe1e82d833c2d4db66d542678b94ad
+330221c57712d67473f17ad032c2fa47
+3303be51fb901cb081afa70dbd4c0ca8
+330a79f103675ac3148228f1408b4f67
+330b984d7cb8c92fa32ead2a37a528c4
+33133ee7d85cef5ea5d27c3574ecfb33
+33146e74e7869e9b060e295ad7895523
+3314ff4e720fe2320b6c507b87d01719
+3318c7d48585d50352486d43a7f46bfa
+331ac29ff553fdc664fae5af1dd460ab
+33230d3ff03d411448a373882bbfe532
+33236ae69414ccf9e0be82148389a675
+3323fcc198d46f22381f2ff143050c4a
+332a8ecdb3bcb4bfd099a7fe35118d79
+332e53e8a1fd1eae872ba75adecc466f
+332f3482252a5122a5a6604618ec86bf
+333170f29dfe2e32bd98c3a49dced2b6
+3335a4a7f27946adbd96bb9fc6a6ca02
+333c526db0b6a5912c81dbee238501b9
+333da009ec5a113fc8f8d01576c3f222
+333f2bedaa8d3096d58ccfdec6ffe670
+334000a4cfcd4595cde1ac01a12b4ffb
+334e99958b55ce7b931230601574253e
+334fe89d5a5de4ba8965ac06e46bffc9
+33514d4c4f3d3b73bdc152115f0d1b3a
+33556e941af79d0a2f5383649bedaded
+335763c8a9be1b6b228965fb0df1131d
+3358cbfd250319d409abaf77ba10a529
+3359a38a402c407dc3c579262e3927c8
+335c1c22df4b662ae3405c0c84ad70c1
+335dbd5fb8c5f7270e73ddf6ee324b0c
+3361febba205c25cde05712be0881fa1
+336d8685be44ea171d2d5cc4ead3bd8a
+3372140a235444b9bce067bc1df63d86
+337a130b4c877e9781491b989f376630
+337aa44b96609568b6e32d15ef0f452a
+337e28285800ab9752302771edf2cc8c
+337f1b169e71a647d18f7203543610e0
+337f6c37f179ddd735b6fe168ede284d
+338893ba1c9c78f5fafa260d2530ac96
+338f8d6a9b598df45f36f252f55726f0
+339b9d8a59b17ed961a613c2a53d80a8
+33ab0c9986c4360792c8d22592b42b66
+33ad3523d90a1e28e5e402b9ecc3a292
+33b6f24cabfd01aa968dc83789dea5bc
+33b86df896e5571ff912a720966c9f4b
+33ba312306f0e2f7440857459a7fba44
+33c07caecdac9d80cac5b60669a34053
+33c4453fdb220e70eebfc0522e825ded
+33c8ee82a06de02be208af2591608394
+33ca8ea189eb2933fcaec25521f9099e
+33cbe825a19f8d77d4306de647a0c8f6
+33d30382ab9e0e2166549b25bf97f31b
+33d44675ab979a4d431c0b708449cee8
+33dff8be72089407f1f199e12703775e
+33f0665e59b906b10fe4841399589973
+33fc64bc9f6ee2637927a7e65cbbb114
+34053d381376369c88bddee412bb5c8f
+340c3ecf3de437289a9ae45697e0f2ec
+340cdfda2ac0d5d409b995b6ddafb52f
+340ddfed4adfaebde97dd533c0b248fe
+3410a6d7e04e34638f9c6c248630767e
+341469ecd2716d4ce77c18287b30d230
+34188d8436066552803795f193ee0c4a
+341ac10e38ad7950358de024568de462
+341bf8e089f6b2c43767a32f3ab25aef
+3420c0be36a54f6dfc04030c2099bcfe
+34297f5dcb42ede8df68716fdaf8280b
+34307456f8751502aa773c8667804207
+34397a71c2ebacb9ce7009fb2e3d150d
+343c520361903f4ba4b29fd0339769bd
+343cf6d64d66efcb58e8b8d13efde47a
+344132abc716ccb3ad96b1a8f9a49078
+3442f8577f4e65a15d3ef32192620ed8
+34442ef4a4461ef9443c24e12b9a00ac
+3448acb3d2b9f115cf3b9609f8413e69
+34496a9b1455e370827260a372d5b7db
+344e6bbae54e0835c5d8733be51156a1
+345221abfa7f6e3a1ac605d87b3c112d
+3456c8e1714c4fd98504f6b8fb292387
+345bb776cac061bd8019104b34d66950
+345c4dd5afaa57db809710e08e3ed9b9
+345c61e5cc080d6a4e3f4f332d86df4e
+346b8b348c6e8878ea8aaba58cbecc97
+346cc320ad855b4a6e78062810c0e9fc
+346f1e9db30918b2d1b8161cf9255502
+3470e90c47e89354b9a121ed4b2642f6
+3472a535b1d82588ef80b46b2abf5829
+3476e613ccca21690f66a17cfdc098f3
+348046578c5ea1ccf774fd2a83e64cee
+348152de59d1cc3b76e9955fbdd5551f
+34828006616d8662f52dfd80705b6ba9
+348324bc782aaaeeb8691cffc73f252e
+349ae66e9eb6320d358f87f948ea9c51
+34ad4b3ca22d8e78fa9fd16d386cd546
+34b1bef6f9cd2f36d0ad2e81c35b2e10
+34c4197fedbc180333c613bc74e60e64
+34cd54ed6788c94d0ee11dfba9bde4a1
+34ce66321202e0b7754d1e6fa6b945f8
+34d3c2198d4529906035d5666ecb0f55
+34d42ece079714faee7684e8486c4a09
+34db46bd4f8be9c0a8a940e705c57923
+34e05418b0d52e86249ab421f7c63699
+34e063f33e5ac96462ef244650f275b2
+34e1bab7711b1fa5a14cac97bd551c9c
+34e6d9febc380a19281ae663b6cb43f7
+34e9c1278ed8dbc6992e65c64f7e4a3c
+34eb0da7234fce20f108f9ce3712775b
+34f44e454ca0eadb7d9c91c43b23d9d9
+34f573363a5a1f945fd21e3b4ceab061
+34f9172b3eb5aa03545df510efb8bac4
+35053aeef556830c6749976336e2cdd5
+350578682dd9e664275c0eb49b1ff79e
+3506d92035030627dc893297f98496e1
+3507fafb4336bd5a17b0b7a886157dd6
+35088e3a64feb9fc8d13d789c20c7b5d
+350a24438b70d9b36ec3793c4326e112
+350accae2f68949905b3d25fde8ed97d
+350c398bfd3b267468dd60ca564491ce
+3512386c52a629b4edc5f495c96ef43b
+35128f4d255694e11a3b6e850dfc150b
+351da229e46656801ade3868ab68033e
+35262d96680866967722a810dff20941
+3531e183e4f880842762edcc2b649f59
+35332e0307dad58bad0650270f458671
+353385894840237623351e651d8c53bb
+353420c46978cf6f1fa2783dbfe5707f
+3536d5e5dd9b3875ea2d5618fc4d33a2
+3545453ae115ac90243418a89cb0cc48
+3545e0e5c1cfd1151cc87afadb78ddf7
+35468945cd3dc153b842773f9b1bec48
+3547a296f60b101fa83d70bbd5af7357
+354a3360f8d251c1325e0c07da19acc9
+35591414ad77cce9a68f44f79cf58d1f
+3559b54d3d55192e8ef41d33dd7b15e8
+355ad347f48a9528bdf54dca0254f11a
+355becc4d8481fd9a4b49dc8ea4fe108
+3565e14e1b295e6720683de39b1a4b06
+356af386a34daa1a4ad45d0600da3b47
+356b1463c3629377fd436d73e9f7f9ad
+356b7de6ab9db6e3328475d2543b74db
+356ead603654677198c3b05f61cf7a51
+356f54e4c38c913172cc5844c6a4d661
+356fb4ffc25aefb229b02c560912a14c
+35807b369ba6dd10455eecaf704d8dd0
+3583e55dbecd0c43bf46ae82c6ee8a58
+358580a3220a1fe1ae769d5a0b4e4f3e
+3595b555b52e4d284bae533a060dfb46
+3596d8cbb6383688d394e225f01db680
+35a399f6528fc280d3ab7423365a14eb
+35a50e7b3c35ceeaa432394862dbffa5
+35a6db5f924f9578edb20721729c88da
+35afdcc297f4b7050cf3b31d1e44534c
+35b0bca7b0d655ac188e5714de5099b7
+35bf2349ff6f24a34afa59c5567c36bc
+35d022dbf924569bdbe6c4bb21c925eb
+35d4340555c8792c541113fb626c7d83
+35dcd2f62a4359c2963823e3333aacda
+35e9a009803269c38adee89aec8f301e
+35f019cc4cfbd0bb398c8d80c9cda06d
+35f0e53bb283be5158bb00fed20d5fd2
+35f248c1518410144baa08924adc273c
+35feeb2f6ba92f1f5d7fd63674c3e4e8
+360334d7161e40c632f1137a7c4719fb
+3603d243177dee839081ec395b97b933
+3605da9bb07dafebdf800c5ffccdb22c
+3607db1b7c85b04483908aac8db4b189
+360ba6e92df37cba57460c88a0ac607b
+361cb7688ee9be369d62e7da4c57d7f3
+3620249907cbe1733751a4c820be402f
+36280b9d3f248f471ac6d1d0a3bb7b52
+362a08a514c962e04b742fe77cfeb6b2
+362d4eb46651184290b228d5a12bf65f
+3635995bef6c0c4868befa276f9604d1
+363ac4e99e74c4cf27e739fce9cb972e
+363b4ea76953b5941525546fcfcc66eb
+363e9f7d68f0f28fe8acd73ad8c84425
+364150bf99fcd801c2daa2bc3732d40e
+3641b94a2ab4ceb5b525c448b7333a5a
+36422c0231a049f9579927ba455a01ae
+3642afd134c3dc23b28531e8dcfbbcc8
+36431bd73da450cde5581e1e97193d1f
+36443f5b40be48a83a015eaabf46897c
+3644647c5bc413d130e1d1cc36c7e197
+364591cf543219a812c3bcbab729bc15
+364abccd40479337d04cd97d6c9bda6f
+364b1abf0abca92176a3413369409985
+364dbc58dd13a9d54b77a7948e0cb34c
+365b61584d9d77dcbf95d2b4b41db6f1
+365f157e4aa5354e10b1b4fee0d3effe
+36667c3bfa67c037166a5d4d2defc803
+3674c7ef5d6244b6641eaa0ff184cc6c
+367688e7770c0fb91ee0e1e8cbbf4281
+368274af83f344e74ec9212c659c409c
+36864a972ed3f19794e76d3679a83107
+368b9e89160b08fde99a90ba417f2164
+3690fdf54ebbbb26b77e2cb1ee8cf2c6
+3692f2d90adfdd0bbce24a3804803815
+369521f3d87bf056017f1f00d7dfed3e
+369b2283114a1170063f0c6405d9f5ca
+369c98b34082d5923a9e38d65c26a472
+369e95cc8a2fe5d4cf9cafb60bdc7dcd
+36a0badbfec7273e4a88688e50d1ae63
+36acacfc08e197db581d129ca0313178
+36b07189dca1530f15a804d7374cf8cf
+36b0b4dc1761a921a1d1ac3ae76575b8
+36b1e0d1aadeeadbb9d3cda1da02b48c
+36bbec6ccd6051de12e98a721884d9a2
+36be1e1e147dd7ade760ac9bc17bb0a4
+36cf14d72da46d182ceb165d1fa8a282
+36cf48d4f5d7a1d067bad4fb23d472e2
+36ebd20570c6d499e8a87ed2955bc28e
+36f0f09c84bd785dae5388fc5f36afd6
+36f634b240d904281999663f6e72717e
+370916fbc46d2681b8fcee7d9f4f8137
+370bca6ad73f42aec0b062bb87ec7d13
+371810c25e8147f5998f329bec527e32
+37268a02cbd52a7484552cf2646cc1ac
+3727ae623d27428ebd14013d0953d290
+3727d8682aa4ea256bf438117a7b8162
+373b4c36c80eb44320c2f8dddf4f7ecd
+373c150f5d844d37aa15a799b3cbc143
+374b908afc29568fdf716e19e2ed0a83
+374bf55c045b86220477a6119f17445e
+374df6b225de599e57fd764601e2e8f8
+37500659a18c1bc7aa06c60ea3639c76
+375052811c6d5b537c8780d763daf373
+3750842d15cac2e0064d1c9786ec03fa
+375c92acd79d52c574d0c2ad96d2b02a
+3760157d02865e8dc852ad2c16c4dd4a
+3760fef726c326cce48bac97d35aee8a
+3762a42809e79134d2a598b89a07a7ef
+3763392e529da1680b9f85d6198a583d
+376a10ec872bd19832a528044e708f39
+376cf9759983fcce7651384a807fbc27
+376f87522455ad5efec17f5c9357c40c
+3770bbfb07fad1f468b7fa9f130cc95a
+3770d0cb0f19cd8b3ccf4d668859c84c
+3775b5e2fbd916b88817df5343fac400
+3775f2eaca2f0ded7d9f779a42967d7b
+37789b44c7dc5153d3b8f42606a66e9c
+377a3e1be5e7751287ec22bb049ff44f
+377af71243d44c37ab3d8ab932ff948a
+377dcc7326523250a8ac4ed2c118349e
+3780f8c8785012f08ba28a7ea3d5afff
+37864a8687a5f167922bf0d86ea8caab
+3788328794c563f6a6d579b4c081cf0c
+378e6df2d3aac77b5d2dfa2165b74e44
+378fac22e64cc6237cd9d8dbfedd993c
+378fc0f343214cf1c4ac1ee1d15bf59f
+37909eb4df0ad182d28432caeb737f62
+3790dfa7ed15c540f96042c10c9bb038
+3791651de95a29ea9c1c2cf46b391da9
+379b4ef9cdcb22b8ea715853c6e737b4
+37ab65da231844186767c43803b0cc10
+37b2bb56533684c3642ed471d2185af9
+37b69abae8b5f216eeb9406911a8c93f
+37b9bbe4126f2dcd62e7273ff4478ec5
+37bbc06467e4157a48100a463bb9179b
+37beb6aeba28a6eff94835140a146bb1
+37c0800df62cd02e2f5377219c7b63bd
+37c1370573c6f1a0c935e1faf1b172c6
+37cd24035875dc17bf559ff5b963bfb6
+37cde8321b0b09adb056b9b174e8c3b6
+37ce9de96e85700e3a2ef9048b19fa1a
+37ced5ae39492dc5b6ab467e6498055f
+37d03f7f5a83e21f819d7f423da43f70
+37d29d64da02cd3a68ba1db46eba0a0b
+37d6e7cde57cf69996525c5949b29ca5
+37de99211282c476af174df0489289ff
+37df76f4ac6d83800e9932efaba690a0
+37e2249d03673d894d3beb67707cc2ff
+37e6cf2da160e7e68118bc98f6b7da0b
+37eae5f8d9ab516ab9e9e1ab85b86053
+37ecad7e162aa3b87b4211caf96fb11d
+37edfa54e732182ad321dafe3ca41334
+37f233ac5ce8d1e67a47057e78d9df9b
+37fa13987c274e6eddabe0b5d094dec3
+38067295d5ed9ed031933c0b6612aef7
+380871a1bb3bc2aed4b9f0a5c58524e9
+380c5e79d89b743c1ad09657d903b4b3
+380e92ecc06d063e8ef13cbe0df9b679
+38133035f10368a6a8e8b8d63dd937c0
+3818e5b55fb95308ff880d576c8e5db6
+381cc900e19dc17808eb818c90a81f0a
+3823ce333c2b7cdddfe5f9d677f26555
+3827a69a77d517f3c56220917425a3a6
+382854aa49127e8c5a40e6c1b89bb78d
+3828d02a8cd30483a8a9a1cd7969e1c5
+38311e31ecc76b3728ccd7b3dd8470ed
+383400e4f6d738ef993cc40b90e2950b
+3835374303556de0c2b6ee27c92ce931
+38432202be381ff7cdb9919ec4dcc6e5
+384473d074c85f4172806c42a1385041
+3845b7dc41356cc6c919d4441795dd17
+384a4eb0276214a6e24c2a7691064876
+384d314301425860404d40c30b1bba21
+385241904542b3cad90d7e70a56a4781
+38534288c49ccec0de9f89f8b9ca48c8
+38543f7a736332c1a595592d1f85280f
+3856ed405143f735c46f2efe85eabfa6
+386087bd84c21b3eeaf1d00ba186bad8
+38641bbe6aa7167add462a4e7c6fb1df
+3864530cff742baa812897fd8a848028
+386a2aad056e6d4a8b08a26f606e6563
+386d68e56ea66ebed95b4cea96b9dc20
+386df2f7450c639751c7539626bf3256
+387214738a9afb518321e0435a04c760
+3873e85963c3259bd3b84598d4f05794
+3877ff6a9df884464f134b056c3f187d
+387902a941657e9e1fea0c8d29def463
+387c868995c8a3b52b07e65209303b78
+388a6932bfbecb0fc10ffa55d4b0e1c2
+388f0d22a1b18ec5b6946df7215d8a36
+38953244e27777b89725a88c412acc91
+38a09d6ccc58e0683ceb75fed652b7ac
+38a2d86626977e611511705ca967ee0b
+38a34700119534da26c3e41efb9a6e95
+38a428ec47c89636cded57deaba497b8
+38ad99be123c02d3b8742c9a1fa5f6c2
+38adeba05c1d947b0b1ddf67566630dd
+38b1bb9fce45c2e2ba62c50b08385a3d
+38b9005a721bcc3816954f506b1ba273
+38c0933d114b36106dfcc5ff16aab35b
+38c473be890d122c98b9063e8d8fa68e
+38c4f923fd5d47ae0825466d93c4e2f3
+38c7e359c5495d5964b0c06e59cfa94c
+38cc528fd90a474c59627ff7e64ea08b
+38cf4edb1b7e3f90d208b32dc3293b85
+38d127c4fe00088989e39e779f7476f3
+38d8b0b84fafe6762d7e91b796a56f4e
+38dc848dc1d81fa39e73a9be9f97f1d3
+38dff965761a9f0f175b4135eb213b59
+38e0e5e826b875558d681b76f005b30f
+38e1de88ad8f9440ff885733f9667030
+38e64f17ec4f741e62a27030f42c0432
+38e6a28485336794c806df4f592f4cfc
+38ebdf40e1ad9d52fdcb08b90217de61
+38f50d4824385cd373926ef19f81d02e
+38f955e48333493ad96aafd14bb83c09
+39002a69a2f63706b5d579d8f564db8f
+3902daa847c56393618cfcbefdef04df
+3903a24714365f12dd9d9cc4c80e475c
+390792baf48c261728408df31f0244b2
+390e41b4170ff3fabc9abd1e2ebdd410
+390fd9c3bb478dfda6535cbec34901c8
+3911288eb8a975bd292555793f569812
+3913217f36c53e992cf01b97663bf247
+3918a7c7543bc6386cf2ce3da8933019
+3923ba158cd062f13fb363d9655e83b7
+3926b631de3e6a5721cbe6b58b7196e6
+392742a8e5865b229a4d96ff0a7c8b6f
+392cb94fe709675930abc29dc23d5185
+392ffe1dd7324d08d8295d45a5b99842
+3930b4510de2f1f8e130ac49908fde07
+39316b98ab699b1a416845c2670b5703
+393334c1bfd010d1b0151477ba4f8839
+3936b7b3a5a824d194686f886025026b
+39388b572d0e31cf1f7579fd48fd366c
+393f35196c96fd32feb01df0deaec14f
+39407f705a6902ec237fbb8cbff49d1e
+39422d454984ec0c166b6651ee81b62c
+39495df25e58468098f41ffe2d39265a
+3951355a60149f6ab15144855b2a5c9e
+3954ddf7e429173a5b4e17f4e0ee91c8
+3955ce9e3ea1f1b65a788d0b8b8c49c2
+39564351f1f1c406dbca8111bec945e7
+3956d65ab58114160ef4b31d862f18e3
+395f84c655f8c74f29a1b923507a85b9
+396ebfe1ae0daae18cdc1b05576236c1
+3975197a696ecf1995462811c0881594
+39770e8c679763f4607c6b206b8a3150
+3977ac20e8e7d188da60bae181e2c986
+3981479dbefbed4fa4aa5f2b4d54edc2
+39859a4ce83c7f0639c8da3b39a5f099
+3985ee38df2628379e3129545fb426d4
+3986f1040d1ac5be99a0837bee2c6ff5
+3995484da1f00d031aef6ca4408a22e9
+3999d7641d3fc150517940285ef33fb2
+399b31552f81eba91c77e43ef329e51d
+399fb160d5286fc2b69806eae1e59bae
+39a00217fea3207cd155c5174a44963b
+39a075c22d2883c45ec719c850990754
+39a191c572927c09f5472d5f75a1103a
+39a2882a0c4ed5e0fa11347bf2f0dba5
+39a345cc9fe610601b4d9d0066fd71ca
+39a7111bc91f027c2f51858c12228737
+39ac4187ff50379b4a5ca9558f8d0231
+39ac6e9d65ba069fee21b4604193657e
+39b022445fab87c2d8e00e4e1c79ed1a
+39b2897f34e8e2bd1e2a24f4794a148e
+39b7dfd4dab614763837b4ea7b1ff475
+39bf94acffbf40e1700a3d99ff1c0f94
+39c7d06a37aff7c848ba601dbb254475
+39c7f46d23e9aab70ca7ce612c855273
+39ccb06e496a4fac62fa124f83315e77
+39e33443f7cd308d81fc77c6e11b1bb2
+39ec45f9a0df1bb1228569f1bf16c640
+39f063decec0186efc32bd4634c0aac5
+39f16ba5200148227c0f60f9d62d36fa
+39f7024a15c13be5e8d88cb756b788d1
+39f743cae6ad6899a545a58a7202620b
+39f82a50c59cfa338ec6b38f313e7868
+39fce25f77ac0c5e4e07bc7806a6f30b
+39ff6881656172f2227e149420338224
+3a0271aa440de6bfe7bb1e290445091a
+3a09a0d6353bd6f1a0da46ba11deeec8
+3a0ff6c99d9d88c1061c5cbfd07cf4b0
+3a173a67ec6c77d2efb2bad964b6a2ff
+3a211f24cfe276e5ce189859d7f06af3
+3a26ecd8a19a179d0fc04af27128a406
+3a2743147b4372ebb65700a1be5209ad
+3a27bf17e85342a7133da5e8ee042e4d
+3a3084a9dc688ccc919b36694533e1b2
+3a31425c4afbc90ea28f9fe971b84ecb
+3a31a6ee30d322f75496d81698567eab
+3a3ad495ca040056e0fa2d8ab23f2da0
+3a3c4c1deadbdbbf26f602b0ead446d9
+3a4c962f7f68551c848b015d0ed89ed8
+3a4edcc8444f2b7375562b5d4ae0cf2d
+3a5b072d08e3ca77a44e7e2828571634
+3a5f418b41b15a3ffe6cf1eeb93cc00b
+3a5fe76ddf59d0ac8b1549fabbe8caaa
+3a60725ae776e3d7017469566650ef8e
+3a68747232efc550da0ace090987a76a
+3a6afc21828452d53e2ac1aec5a452c8
+3a6bbc4b226e81fc580bca6efd2dafc2
+3a6ee9d5e11e67c3d816f1ed8272b753
+3a6f925489d4dbb7a063af9d99400d68
+3a73dc9f458b9994355d79b275fdff6c
+3a73ec18a174bac2a1d580ea86ccd6e7
+3a78a5125cee5cba31500ae5ac3566e8
+3a80f4e044a01c70b0bc4e6027a6975f
+3a8346fa0f3d6c5d2a890a4d8e12be09
+3a848ce943b4dc20656f5f33bdf68759
+3a89811098f139825b98026da5f8bd41
+3a89e8576769f8a4a740dc1dc8338f0a
+3a8e23e2e0daa10ee1332ce1acb03fdf
+3a92b6926d67c622f663388ba6fc61ed
+3a9367d48778a157184388e47e43b0b6
+3a9e88c9be7cd74556868c0a82c81d47
+3a9f3e8cd12c5c86ddb098eadd69f249
+3aa6669452dbd6184cabeb67da46aeb6
+3aa6e047b6a695d6548e1ace434ec5ad
+3aa6f09a58afffe81f1c6fd391b6e7ca
+3aadb657236762da5b3e8a7ae1e85d53
+3aae840def45ea99e5e92a87f29a7476
+3aaf520570c69dbd3333917b059cbbae
+3ab3316e3c5f426a46961f7a065d6a03
+3abc409eecb30bee30a1a88a09699e4f
+3ac667af4b31c6580f390d02404d5e57
+3ac91a8cfaa9047d04845b167c87b04b
+3acc5f464286d8ebbb1c66b59e975365
+3acebc8ac46a6846faba3d2602256699
+3ad55af032fde133b721f98ec4300f9b
+3ad9a9e5444a23d5a5be99e88d14eb8f
+3ada0216319796f276ec2908b79ec2f6
+3ae403c69edc4d50abba199e93b39f87
+3ae790eb4ed906595a4e989d0dcc2830
+3ae8888372f0562991d958c78e7a0a80
+3af14b98295d0ca1f39c67b89ea2c1f3
+3af1a9370928dca6d28476a9c7587f6c
+3af1b86d1baaa8a13f624e9cab9eec07
+3af7289bca97fa75ea0b82c97aa73a5d
+3af8e8af7d71f5d39ffe6b047461ec04
+3b01083b32d1c9c4310c9e79b1ebeea1
+3b01e76a1c5e85bcd1f29e4b2275e848
+3b020c3907351b55b0ba558dc099849c
+3b05c7d9dccc31342d888de0d7ff4d9e
+3b0674a7d7a4be60b84d289af9e531ca
+3b0689437757c1b265ac8caf2be06c72
+3b08b7bb2f31bf537c13541f8887b312
+3b10c3b758685326fb8b0fa9dabad5b5
+3b141d5ef3d926be4a8dc49a82f91ebe
+3b14ed56a08209bd12b90c252f41e0ef
+3b17c0bce5f6c3fffe5d15713713990f
+3b1dd23289ce9a672de7b295ccffadc3
+3b232a0288353f9118c1840ab82bb85e
+3b2daa82154dded54cfb2d238d74d3ea
+3b334f7e730f7d835b0d59bdc862e627
+3b33823c69a3266982927254f7bff94f
+3b47db796b8d1ed64610fe7c919d57f7
+3b49f390693e5fb48b7e7f170519461f
+3b5492065772b0019aa2e58c3946ffce
+3b7631dfe2b8da532a4ee836f11d7872
+3b7952a65f91c133b66cdc0d15bac0cc
+3b79c08a1f5d1c836fa0afb1c70a16d6
+3b7ccf5cee33a00c51403085b66bb978
+3b82d8e20e63c42a2325a541b594b32c
+3b84f182f2d812a527f5be84125e1195
+3b8dba4b8bd0cc6a155187959b9f9917
+3b905a970cbe626caf135bca4fcef131
+3b92eee6edebfdc56483d87d98c465e5
+3b96abf0fcb55c771453e00d38000d12
+3b981c901e89d141283cbedfa5133136
+3ba198515bcdcd00d6eeaf152cae0c77
+3ba1b67db727736b3f29592302cf9014
+3ba29da298559fd022f3c4aa2c1015b4
+3ba7c64766592892a406d8e44531cc96
+3bb0d1c4c83e0d299ac2077d704ecd05
+3bb12c27955e2502a8b2571849056b81
+3bb3475e0f17051240b50dd3aa1dd277
+3bb3c85b2906531c261a20be2a2dc3e6
+3bb540bc22567f94ab2bd27f78a02d07
+3bb665ed6bc7137f6eb9ce3de733d788
+3bb785035c254f6a07d55f7f8b26b575
+3bb7ef95925d41876cb215c255f35036
+3bc1aa4203f9d63726a752406f2313cf
+3bc3187c3f76fbe15ef53264b999676f
+3bc37415c58707da8856f69281c0c5e9
+3bc73dae5adb310087470d3bb8f4653c
+3bc77b482b24186b82b551fabe9116a8
+3bc97705932dc2e81f1863598ae19389
+3bccf8638e604d7b97851f18fdb47f1d
+3bd016a11100db8a4928ade809a1e56f
+3bd4778c7b81b92271a8de5eca1dd7b6
+3befd41df2c9dc22564f111c622e6abe
+3bf3af3718d13123da499cc4af7a71a9
+3bf8bc79b7274a32e7f64e27ff8d3e49
+3bfa5b7f0702c3cc848b69df7818e25f
+3bfaf898ebbf008de767202186a61b48
+3c0389e02a3fbc6068684a8b584fa160
+3c0401ab0915581a6d3d4bfc936d8df4
+3c05854c39b99367751fb54a0dfe0c0e
+3c06e5c2de1ff0b9da9325797ba5d2f4
+3c07d6eaa59bca6377b38e90b7fabe3e
+3c0c0221b94b9321eee8cf74c9b98be5
+3c0d45d1f9edff689b0e7859d368af7d
+3c0d7a5f141a3b7bb8ba8a92adfd5e0e
+3c0f1bc1da419bcde6774b726be27e19
+3c168f0739056be2119650fec2e1b3db
+3c1cb62516603b26b2fcacee10b44c61
+3c1e4e03db5149a75b2ec6f79329f5ab
+3c21a7b882fd269c14a0aef55db11e7d
+3c21b61595618b17310f0018251aaa12
+3c24c6a7e0a8de3f8a90bff56406ccdd
+3c26a85f432a7069c4590d6d9e88eb44
+3c27522c464ba187ef8ab84869bb171a
+3c3693cdc74e0d32df84fe5a68a526c0
+3c38414ddeba374fb47112da2224750a
+3c3ec6e3424574afb2d9cfd3c863e070
+3c44a80e992c79802741a886a9d34d07
+3c4753a919e7d091343e4d49b131b1e7
+3c4de3178831c953408a4f61f3a94654
+3c514d7968f5f80cadc2f234cba205dc
+3c5c2a8f06b2c0b6eed6ec8146b5bdb9
+3c5c6156c4b343d4c75fbbdd549c3386
+3c6cdc2609d1a30f67f0d132fb0ef69f
+3c73a531e940b286bb46becd82087664
+3c73ac1a7a07d3869624bde75ce1610d
+3c856288a877721b34e68b0c12c8fc69
+3c884677aa9835e40a8458d3d87707eb
+3c8f0ab6ae19460bae98ad0b4258965c
+3c91989db0f0226cc0ca739f55fe2416
+3c94039ccab68c13233e615a0c0501b4
+3c9672f0b5dfa3edccd02e9a0e9a4a67
+3c996a0c1a72893f6429aaf1356af42b
+3c9ce4a5043276b21632da6308f4ed4c
+3ca45aa948043b665bcb061cc664230d
+3ca5adaa1a0b1c2059e7fe7da1b1da29
+3cab39fe34fa1d84ae0206a99a94e349
+3cc92fd751e64a6c5cdc90932cebbb2f
+3cca6c4a1435fc95f083895fd04bd320
+3ccbdb16a577fa870047883417490568
+3cd8fa6321d0df5960d8b2005afb9bc5
+3cdb5704ac0f167c86a41c45e0738ba9
+3cdbee13f550873cb8cc8066d5fc46fa
+3ce386941f7abad624f45b99352353d2
+3ce4f9832617c71b39d7164ac517f7df
+3ce7ff071e0df17efa0e5d17966956cc
+3ce8ec1b963585ab6dbe781aa976da36
+3cea5406163b5f2e2a56d64b106ee482
+3ceac7d9f30d846ef28cef1328dfaa5e
+3cf0728fed4a63b547b2f892f7e86a10
+3cf4a6c9027cd027d6f05675235c9864
+3cf653df8e4fcbc2609ff610feed467a
+3d00e670d35898f37365a5d7d0a8f3e0
+3d04eae10e1c0ad86000f1e7824e10c1
+3d087b670e492a7dac9d0b4b0a5d8c7a
+3d0bfd35d6fbcbb1d52b2fed1569fece
+3d0df6f16ac447a782a5ab55808412d5
+3d1952122716fcc229e9551f6e69f8d4
+3d1ce390a81e034a6bcf31be98a79e5e
+3d1d5642e200b9c873d55711e48fc365
+3d2834f5e417f68c61f9ad9cf71f2ca0
+3d3782074dd04f55c3dd47ea728b9cb0
+3d3de3721af68032c502270d55e26b3a
+3d42d0f4224340c239d8b630496a1ccd
+3d43385a5fe890d9dd8adc3418ba2c73
+3d4800eac042513f0e21cab2c9cbc964
+3d4abc82204f8abb9ab08133a50cf8af
+3d504ada6a3b5903886be22d1eb022fd
+3d53abf677190ae71c595be4e56bed8e
+3d5eb68c8fe71ca5e514241317f3ab85
+3d5fa0e244f6efe36d55a07c4cee3a34
+3d63f65b8a6818fc308d96752226cc2e
+3d6a0685eeffe1de2b66c7657bbfb0df
+3d6a5f25a5a44d545d500e7036c3f23e
+3d71a80ac34dd222b9da883b75601050
+3d77a820d1a5faf3705c17ab7b81ae67
+3d788ea4215854207262556cbb4ddc73
+3d7ed579aef715bb1bf95ad3de8da089
+3d865bdc8080f835f04af56eb440657b
+3d8f499cb34c90240bd2cc00d58f055c
+3d99b44bd87da556a1c596a7ab28b9a5
+3d9dae3e25aa1019496f004cf1c5aad3
+3da09c0d774787306f2856a0daa21e5e
+3da3bfbc73c859eb79cd704154f923ef
+3dadf9a64746f368c0c2d6f9d6b9428e
+3db69850d2f11f4c3e8e06780d569ead
+3db6d01d0f3c363b28e2341a58f1702e
+3dbc9150c5cbe5ec7bdca628cbd18c46
+3dbdd3d58d9410bb77f35ee6be7575e7
+3dbf3d7f80061a1ef2fac86370ab0fef
+3dc4dec8e9c6df1737c2236577371252
+3dc9e08f639c371e5c25ce103a0e61fe
+3dca5f09c1d825773a553149a934f71c
+3dcfb3f1b02bf3cf2f471cc2a70c1560
+3dd29b520b16cd06596cc74b0ef3c0cf
+3dd4bb8c17802d55ebcfea276043745c
+3dd6f4f7c6c93648a44c6ac1812a9825
+3dd7764caa5e715e107eaabddd8a62e0
+3ddae4c3c89fdd11fbafe870141eafdb
+3ddfd5e29648883d0720724fde8b0d16
+3ddfe9d2b13df4a4804b5a3403ecc8d5
+3de4b8eef3cbd4b3236cf69bdb44c111
+3de8b31525449071be8655cadc6125d4
+3de9622e0e3bbf930bb79e2aab7ad97f
+3deb5b21adcd57751e488c23b5221d84
+3defd5d062422531fe2cdd5311216d06
+3dfab7c34e1dda742b6c5e6463e0bb26
+3dfbf8f4034bca36a9ab927eba5bc427
+3e017515f5f5d58ac0b45edbd9347d4b
+3e018700293ea123b1496a9a3c1147d7
+3e02b99e7f6a9f3be7e210167b2ac1b9
+3e0449d4ecba8f08c1181abe85ef31b3
+3e096a04d58f48729a36cf90164c60b9
+3e13b7e5515e5b7dac5a637a44e5f0f1
+3e144846ebcd905bc28a6f1df46c7d51
+3e16986abaf0ec6cbfe7276a82327668
+3e18f8652ac45200f7c869c204059b59
+3e1a588cb45560f3a50f687e4a92f108
+3e27e355d351ec66118919d99eb1a53a
+3e28147a8058ef2f38bf1666f10f257a
+3e29d72d6567075a39f3a3b9f34f9553
+3e2f456317d4631b1c1e21a9bb23cf7d
+3e2f4cb3d9fbff406ce1d6ce1bfb17c0
+3e34225eb8b013d6cebd9495e36a2d14
+3e379abf2fce66fe66197bd3de7d77e2
+3e385bb38db48ce205589e04b21c429a
+3e41b6ece4871dfb201db6b2c3a65e33
+3e421e5726eae7b4049513b424e441e6
+3e4d3f30d3a6f29f250f6037bc70632f
+3e4eac23c982d671f93462b410c95647
+3e50b9d5fc0e52c137401a01019d3e12
+3e5e0479fd72aca1d930adac5837bc4a
+3e64dbe157108ced1bda1319b5a315f6
+3e653a5f841bd5ca46b1f7ddd4ca7548
+3e655a6d9b700633807519e6ecd6cb8f
+3e684cabede2737007c155d766e22bf4
+3e6aee6859a250d4705547c321c80866
+3e6d23b31fb6909cee241d53100c972f
+3e745e585ee1ea123894c937cebe22eb
+3e76280199ba98025f30d8895086a577
+3e7920f3f1709f2376778819cab23cbd
+3e799ab7a7ca8b8ca14fe437855cf51d
+3e7ab4b868f29a789a409374c393508f
+3e7c4b89a4fb3fa64b21606944b47041
+3e7df75d96ee71dba93c6af4de97c968
+3e7f889572ac80bf61ed5bd0617ce1c3
+3e84a9b9acba27535142858f9586badc
+3e96bcbd502e6feb33b5d0f779a11971
+3e9871fbee9421ccf32e0c3a029d396a
+3e9d9eaad98cfb19e5d95dbf660fc441
+3ea54e3198b4509396ce594940685ff3
+3eaa3645b6e85aa0385afc516afc0987
+3eb58b28537f15ccfa7a4950869aa210
+3eb7cf74f371b87a5ba5f019c89e71ca
+3eb996c364e2f02fc3b02acccaf48654
+3ebf274d825d273cffac4d8e2470da10
+3ec531bfc9d4b1bc60bb1c1f2837ac0a
+3eceb053ac65aa92dbe68c13411bcc22
+3ecf94d7e1ab604309242e72bcc57b27
+3ed17d6257ef9181f7e6302f06ffb645
+3ed76ea8de26c93b5f636c12099e22c0
+3ed87da6d667aba306945c59e8b36279
+3ee97772c9a900f5633774f056d4364c
+3eea27c64fd62eb3bea5d503e91a3c37
+3eeb961240056b10d2612ec0cbee22d8
+3ef0805ed8036ec769f120986495bd7e
+3ef2f00e77116b7bd51157e9c9023ba9
+3ef545f0b0acd9af9f145deba954913d
+3efa421c937f5739322cb19edb0fda73
+3efe51b603500f536a85ad5a0a4d1951
+3efe5501bd862bacfe7dc80f27bf3fcf
+3f047884e4a6a6cfe58c6363418e572f
+3f057c09941b292478686da07d569311
+3f07bb2cbd2ac1978eface84584a9b08
+3f09958d77a71704b97936705f02e757
+3f09acf4569cd3696596894d58575836
+3f0e5bcd0c79102b3666ffe078adf354
+3f149f4ebd3e4ce4a369c717249d6458
+3f18e7f3527f455bab21626165f69fd3
+3f2116bb42b1d613dc3edb5e90ef9d4c
+3f2232694e4610ca0a8b3db345e4cbaf
+3f24964b12f0ec214acd3d7a38f02382
+3f26251b2b2f659e5fa72e5eaaa4952f
+3f2d26dcd741945bbc008815220dfe68
+3f3011a724b58b5dedb0a4ca3801e949
+3f32b1174f0674892b53adef3bcda92b
+3f3418d5d28d34c069b55b5107598d72
+3f37930433ad28e06ef247dc3219f30b
+3f3f339f433df6ec1668c1fddca92606
+3f41fcca900a60a1444346350e9cdfc2
+3f42e114e428c9865b3949e144ae73be
+3f48443a5643ba36048d2cf251b5d1b6
+3f4c941f4e3d478a7bbd8d7397a91608
+3f4d019d9b5336585ce811e6ee1add12
+3f57621afe706440c8ae50f9e7efd5f7
+3f652abe93ac9d0c39c917a7e695f3d2
+3f68e02435c98122b7dad0533203e39f
+3f6ab011d1c8fffacbdbf8d9fe960dc7
+3f6e961444df055ca0e12a9a76144422
+3f709488ced9f5cb8d8765018e16bc0d
+3f807d59c1e6c047b90e0c7e44386419
+3f847cc93e19943daf982798399627f9
+3f863b92d8d41f8a6522725591430102
+3f8ddbac8ae9bcc9e4a706d59f3e0202
+3fa43937ea480320d1afdf564b078611
+3fa58d5df808e8b7cf1990363e266eb4
+3fa67ea4fbc892211b440fb18d0afdf7
+3fbbb6da41a46b5279bfdba9ef42144a
+3fc2548b18d2034935eaa636c537ef03
+3fc5ba02b7422525e1b2f774fbb4bd61
+3fcd0fbcef3d823050c123d0922998f2
+3fd1f53f6aee7de8f2200aa4f678ca3b
+3fda81d0a78c843cf54e98c2a1f8973c
+3fdedde385e9c69df33e78804a2d30dd
+3fdf9e34514e3b06c376ffc42e538b07
+3fe262ed347ae3af35d5ba2e50ca53a2
+3fe394e541a5be321b35e58e2e51bf6b
+3fe9987a2fa1263bc1b0b05c90fb9fca
+3ff0a3f34b820babdf53b4aa60b3a629
+3ff2e061c416e4bb9e65e82e8da09417
+3ff41df90c3232a8c97a448c1466b3f6
+3ffa730af5596591f37832ce34026dda
+40049a2209a906045c20c2fb9a2b7b17
+400a150fb40aa2c61fcd71d5ef34f727
+400bb09d5a650bc0dc09c974786ecff2
+400ef8727a0fd5ffb7760c37526f7b05
+40107bf0a74657aeb7f7da5ab2c8f9fa
+40112f04b89b7db6a5fd8464aa02a271
+4016a4927786d6e458dd68d9db758b80
+4016c7d63f07b628bfab6fcbf2c9d62d
+401c834177a2b90b2163d23d9f31115a
+4020352328ed7dd4b80831a7030712d9
+4023222355cb776c9e97bbd39ffd856a
+402666c16a295046e9ccea00e1041503
+4027fe4ab7b6b10c8bcfc10b9413f387
+402b61835ba8ab20423e00d454cceee5
+402f5ebbfbf10caebb07b86baf175a03
+403459402a1d190b58cac86e5e8d7c1a
+403a8388cde8f500ed1675db384a1df0
+403cd689d2dd49ced15dc3dd39836af7
+403d2b88c1b56474a428515160cc14b5
+403ea2737ce9e2604c665ca790d0aef9
+4041f435e23c509b14a620e7a0eb19c3
+404262222de184d1732d10095b8d378d
+4044c1ea878d12f3d7fa41120b0143e1
+40471c80c974943d5bb721b07268c853
+404d7cf77828e7b9df64e96be8593bee
+404ef2b16b595f16580a82ff5e533167
+40521e8ff349ab9a1c3ad0038576af0a
+4052a6e79b808cdc9e577bba851c466f
+405cb2cf3da45ba09918d46e84d6b12c
+405dd1389fab2c2f861c23014c84d1b4
+406575bc513688b7c4277e3b95f86321
+40684b24132801c79271366a657ddf04
+40689df8257b52d5a9e4039a41f15705
+406cd414b08c8e179a683bc92fa516ca
+407a5e580a5270b3f8b29666491d1b08
+407ed137051fb99fdea5c62cf6438553
+407f696569038fa1a4e8fb9d8cc6d935
+4080dd3ff8719d3b6dc18e899cd59eb8
+4088edc46187219368a685c6e3cd3103
+408a376a00135bc714032ccb9506dc2e
+408aa361f273e371684583fbc9494ce2
+4099a29367a6ed598ebc9ff549c99747
+409a203522da70662452b38e9a19c15b
+409e9cdde3c5aecfd853cacbeb6b2249
+40a1062ae8723b9212095881b9abc378
+40a7217e89618b959c9ad5258739b458
+40afdbc2650c0bdcf9955294fab4a89a
+40b0cad4ed3bab8d9a2175284c3d6208
+40b1832c39f132efab1536d7e543b782
+40b2ab591303a7ffd01222aee78a4be1
+40b3713157a5325fcfa4ccad92a59d4c
+40b53df1bcd59c0c4247c7381c4b2f91
+40b60938c2cfd86c8d5dad302b4efa67
+40b9231ca23a9f5851eb61e87fec6aa7
+40ba4ef322db3a9fabe64202c3a612ff
+40bdf5fa9ffcd9e42bd52bc36834c277
+40c3a4d1ae728c127bfcb76e5d235ca9
+40c5d990883e9ebe837e29a72413e064
+40c668c81ce79695f241bf557b06912f
+40cafffa5d03e48a918cec4978b99010
+40ce9c9d7bdf182430aea2eae3e53b7e
+40d117d31d7826d7d50d7f321016e36f
+40d17f723372a84035ba7228ff601de0
+40d3fa3c4670b22e8bd4d98f8b2f9974
+40d45ee25ba2901c961646811f31715d
+40d67c30649267fd9ef8c47bf8ee7f2b
+40e5f45f7edb5581771005483ac467e5
+40e66bb3e127718c7d51ae6d5ca9f5bd
+40e701311855a783211afdc43cf00434
+40f6b0c39d914a070a1a5371858772a1
+40f74bdc8e06030196428582d7fbc778
+40fb71d7eb9b5b251b53b356eacc26e1
+40ff037779278496af784fa52307e412
+4104132642f5a8aaf4be3c7487958d9e
+41064f90416c4896489d911f3222f73f
+4108dda267c497d29de2f3a46c266e82
+410a51df65d48f2329322da2e215e1fe
+4112c57bf460e2410192fe358d81e67d
+4113c121e912619ecfeb5120abf9a56d
+411422c490eaccaca77f31e077b2e03d
+41153e362071df7a1dedeaf5f6c1751d
+411ac6a706fd5b3758aee8ffdc8a610c
+411c0537bfca3dc63968875c9f9f0e5b
+4121adf98a43d85c1ebcb34b1ab1d4b0
+41293f156bcc06f76b7e06d552eed963
+412bc7aa78f2e9fa5069a403001113cf
+412d1843edcc217b11612136a49595a8
+412ddb95abd6de2c999edee2ea3c62e6
+4135265654bfe0e1d94f3b8008e7412c
+4136f1ee7baadeca682db3981d1e529f
+4138e5f698cba3b40b096262d44ab8eb
+413c1ef13d90cd86ee4015cc98e17001
+413fbd39d55dcabe40edb99f19feba41
+4145215cb615d7726e6b4f563dda905f
+414546e0cbfb5880c0ce9526d6a62e2d
+41479e2f7e6d2d6c69ee26fee0c23a43
+4147e659817d3f406ab2630ecc8fef03
+414a7836435d3bf8918d5a129ea67f71
+414aa1d8a65e1d12abee64c87b970cb8
+4156d09deef99fa0ed5fabc9cd068461
+415abc09d3752cb3a0b16ec54057b45e
+415dd58d63055a6a1560c2658ee7e5d4
+4160d3f3c315293195f70b6267b296aa
+41610f4f4771abd13e46bd320fe0f105
+41619fc927ff28676cd0cdfe55764d71
+416b1f6aba90a3743228ff3638be5c6c
+416e0292bbbc90c700a17565ea8b9ec1
+416e88fed12bcc8ab2cc69065344ded2
+416f79be45b6a9a9c2aa8e01bb90d6b4
+4171721ddd34a8f24a12a6b69dde6792
+41752fcd2505495a50c7777fbb17b61e
+417f397943dc5e83789d43cc2b262f54
+41852d280315f97032346673499336f2
+418ca751e07a9efca344d18a054a831d
+4190bbcd76cf8dd7a95d861ef07ae75b
+419934bb4e79f9b99849ab01e7f5aae0
+419e340841220de6c315ba9eaa8a6c42
+41a3214001b39a1bdc7d462d83f6058d
+41a7125aed71705e1cbac88494345f40
+41abec7e3166688c2e83232e8df9d715
+41ae8ba02e2200a3552e2efd0d73a3af
+41af2403d1c1519f8113b2d03450d6ba
+41b05bede067e4d5a7e6168a08c22455
+41b32295baf7cedf392ec149774093c9
+41b37fec172d5394c70043b175f71ab5
+41bc5017d7ec0d2bd96094f8a049a6fe
+41c12a4f2f16e77c6efb81d24ae75ff1
+41d3a96d629de25911c8a91bffa84c11
+41e4f4cb364d2dc779fb919e9d0a3575
+41e72692c542c25526dbaad4d412e031
+41e88a1e59acaf4b04c7cbd15b24a7e2
+41e89b6c5b863a8ca2c24fc4de68c0a2
+41ef0545b9a3d8f2fccde2daa1f370bc
+41f77b6c44e30897c9c169d97b659018
+41fa09550baa6720b89ad14a373171d4
+420354c617ba6a59fc64c0b340bf337c
+42042ff4d73237771cf05a81b590721e
+4205e410995898e524e7b79bf1da4983
+420eedc9b7da410f66948e7c8bfaad6c
+4210caa5b5afe2783afce20300e546f6
+4213c392331495b0a6b65599f7897b9e
+42173bafa1c1377bf7dafe5c902c37eb
+421db0aa5e1ed771cea8ea66101aa6b3
+42204b0f6a5906bd37c26065d5311313
+4223691d860b3cd478e86bdd8e679012
+4224e1f7cdede3b4d0cc83cc63da4c3b
+422555ce8189220b02fe1bcf1719edd7
+422821d8d2ad6ffc1a387259ad3aacbe
+4228ad1f0d19d1ed2f5dd22604aec26f
+422a2d2be218fc7d709d7269a2f52183
+4230526e7a2d70a2c7b71a368e26ca44
+423357cb5ac46ef48ad40acb58054b88
+42343166d37606ba4c8eb661d956f666
+423550055157fe0e0670f571aba8a58b
+4235c6a979393790d60c7f59560f7404
+4235f476f12e3d354fcbaf9d22f464fa
+42367cd52e67ed3fad82d7d11f0e1892
+424491c0de9f5238ee82ea0eb4dec3a9
+42493f55fbcbb5e92af2bb793b6f21ea
+424a1f26ac3d67dbdc1a8932f548cd1f
+424f7d9588065a8a1babedc2efb5cf92
+42563c077fb9d138aa3f47b01cf0a2af
+42632201a82e54ff11ca39a76c38c12f
+42669b2312606c5c76849bcceff308ba
+426a4615ee936704115998f4716395c0
+42744f233da2e79f70f66abde1d75ddb
+42787b47e1f9cf39a870cea54b8dc6df
+4284f656142bcdb95a99895036937f0b
+4287946be484866c95a04a4a9aa5fbdb
+4287fabd5691d26db3e863018d71a262
+4294fa7f8a7ca533c1eb3214a65a3970
+429919e08486340ad479589ff3809ba0
+429cc9f59b6a236c9202c6a3ac5dcb01
+42a48b36c46501fbc5cfecadd624660b
+42a6f7e4a402d5fd7740698562772722
+42aece39ca522866d4a81679beb5cdaa
+42aeddbf53591e60c1a4ddc32d5b5de0
+42b214f4618fae53e60d1685e0d61851
+42b3a5bcdbd19c00940c84f40cfaebe1
+42b43d3ca62e7c442387df0035ba5416
+42b4e0c3233a4b96188647e424c58e4d
+42ba7292964e7e9684d329a8f3e18725
+42bfe5b3298c8de0283d22fdf4d8e0e2
+42c034a5032c8eb74c9c00705ade3082
+42c0db1ed3799083e07e66bab363c377
+42c248ea0e93256bfc35f6db1477b751
+42c2babca761ea216d00f876f29d95cd
+42c3421ad246b3b727ffbf8add512d7d
+42c3884440d001e8cdf35c726ab8e274
+42c913157a0a820d7cb43b6b51c017a1
+42cdf4919c8520189572a1f2939b890f
+42cf020d0c8a9909d9be6404bf789844
+42d15ac774ae01f4e69ea1c8f26d0133
+42e1c81e6b64c988131b0385cd688e88
+42e4e46f14c8f98b060efab7dc996708
+42e70e85460efb4b486ffd0569f67cbb
+42ee610ae52f808f329369cc2ae23c0d
+42f353a43086f44f5c82f7d9a9b17873
+430598d4df2e430ef8f2a135359035b0
+430713217a7c78d73f1c6eb89b09f556
+430c0d4d833c604e3f26618929a418c9
+430d4abfaaf329c5a25ed12461303793
+43131e3f9cd25cde859adfd48da33bf8
+431597cf12bffd6f3217f44a6fabf1fb
+43175f3fc31e544a55e77271c9ecf6a9
+431b3455620f6c2998301ded60676b2e
+431d16d731a60ed44ec100befd5704c3
+431e1c3c6a6fa7668db0dabeb52f567b
+431f2609672e1b81e7669b4e767f2f78
+4320ddf2c0b21dca6f398908172e3916
+432469a4bdd725527ac11acd6e7155bb
+4324cf094c8ff59203a11454d749a2c0
+43260bdfc2aec27c4fe0e73f32cdc2f4
+4328168529801aad486486af8567b2cc
+432cdd3f2fad54675d12a1718d748583
+432e8d874f26dc2457df00ffb6de61cd
+4337a84a2895f550ed573fe4a06c591c
+4338546ebb26db1b28161c313de2fa67
+434c84cb70f856023ac7fbcb248c481c
+434f70472646c4eac1fd1a01c593e30d
+43659f426e4e19c457a92bd2ed7f5c8c
+436d21ea6ac9a50f61e370c04cb3753f
+437295b965d9bd2e5b4a821275cf5925
+4372cf766a55674094405d72dff17c15
+437db62b1872fce224cffc5cc38f3e90
+43837c5f80a1f9056edf8b89e83f08fb
+4393429b94c68f3ce7127c909573ba63
+4393608ee40c8b7f5fd5b44a06c1f32c
+43963dcba33a3ce7e3f95d5df3d921ce
+43981ce78ccf46a0ccf9f8e826461a30
+439fef7ee5267773fb4a4a2c264e8c08
+43a221c59352e5c4ed9b84085250487e
+43a6fb07cc4e158ea48b3aa06abf5955
+43a70ad9194fc0425529e5abe9845e9c
+43b6c80c688abc615d091b03223517b6
+43b891c9854d1a09435dd49172497073
+43bbb50f3a3704870c05f608945fa4ea
+43bcd47204c77586a115001dd7e7c234
+43c19ca7c047a86e215f3d719e95952f
+43c3808a1c96f07c94daa14d5832f858
+43c3a898f8be2d3079916bdac660d5a7
+43c3ca858f9f35ea701c88ab1e03998b
+43c84636144af3849713105a0566f34c
+43c85ff3ab172f71e2f479bc91a6a64b
+43c8642468fa77f61233eebc4975086f
+43cd401442da64ab6ddb4f9def6e93de
+43d360ec4148728021a3d815f771c768
+43d3dfe688085fe054dbc128d83d378c
+43d9afcbaa31c297b7c5fb679ee1dfdc
+43dac02bcc00fe8ab16d8e1b5a8f846c
+43ddfe870f096c8f448a9b142e2f055c
+43e7361b61c2174d0365c5c544c1f78a
+43f18d061a871ed820c7e6aa0b122f4a
+43f26f2f2525c241cf3ab6c88c335919
+43f5d1d3068d8215b150e7933daf018e
+43fb628fff034e92aad9456ec92c8246
+4400134a41a304c28c191ed89a96dbd8
+440124d8176b7a7a88689f4b73b03ebf
+441204002d5b67030ab89cbdb2fbb898
+44135a0ee35d6bc613e4f5b293aa9ede
+44148a43ff011633754960b70f07d939
+4417553d47df2fb06e2cbf506418c0fa
+4420cc90691128a89414c8182fc7e049
+4420d501d0bd1c0bf0056cb2e8803c62
+4421d5a4e307ccf31ce7128b466d62f7
+4426d3f7143afcf41e15a7e1f2c69dff
+4428e36d05eee60c8c3e2f934b475544
+4432931465013262ce4b85e918d8197f
+4432baaaf21cab35f667862d57cc1f72
+4438214602af1aa9f5299f3ade2873a6
+44391bf5e4d8dbc287404dac68c600c6
+443a6b1e66608e76ee4fe65d4c260704
+444fe0202e5698467228fe332e11aed2
+4450e51a64adb6b6b20562744874a370
+4458e3c3b2d2f79752a011fabb8a53f3
+4459a2070d6980791debd3d79e5d9d54
+445d07ae80ebd3577262ca0135ef8dfe
+446fb11a864d5cde75d4c4ac058a46ab
+44707ecf8747217619c3eeab4efe1335
+447a56bc0a43fc1a75044e5f53b28472
+448a55731795e17b004e766e2f47a6e0
+448d6f0355a0b728eeaa26181f0cd753
+449388b9efaefa176a39329ff23fd19e
+449a50cb81f60ca19cd37a3cf6f40bb3
+449caffcf0331486c8df5d491593e0e8
+44a17c9ab9116efcff4d280bb803c78f
+44a31ba21d52b17655ab3949da7333e0
+44a533e90428d80eeb5e7b84bae5ed28
+44aea46d57e6bfcc4a05ef32b8926564
+44b2c51e2129795af200b76de1354d6b
+44b7a53ff264a6452af3ef5330323b23
+44bb167fa464c8cea4182ff829ad593d
+44bf83dbb839468a9e60655b2f6ee649
+44c24036368765e60c980e062dabe4fc
+44c496474333968989d2404add15ba8f
+44c84902f9739f55947bec0caf314e3d
+44ca1b7c43f5828c7c13c4b786ad4f2d
+44d0cd9ce7413eb3a1472e94014cbd82
+44d25c626e92c0ce998c134d30367ca9
+44d4232b99207587ecb426993407ed1f
+44d4f0f6808a22ad71275890fa0b4838
+44d65013c96e605af4f986f3ffab5d38
+44db457435cd0c8a95ec022bed430d0a
+44e044a5820c3d29ab5be5c13ecfe943
+44e5bf503c43d1c1b2c0f849e62ca521
+44eb4e53cded2adbf129967ddc569538
+44eb5476928c0aa716b0727375e98086
+44ec00115933efd9c66b18b57e486262
+44eec755ab157adbf5d8f2cd00091274
+44f335d4b815f06c86811be7ad241333
+44fbb4c014ec70a3f3f1b6fc03d10e22
+45017254704dd17b0c3fa6823f72a9d9
+4504033d4292fdb45a6f92daa9551276
+4504999d921c9308e1b7b3acc596acf0
+45088cbb325573a300eb89869c404488
+450d1f5343489b9dcbe9dbf6d45cea34
+4510601d673ff508535219115c57e071
+45179d9f9d2568d614aa317459db6dee
+4519dd06e5371f37409c093efaf1a28a
+451b135443d1db76f04108524006b6e1
+451e256740fa9aa45cd8264dae3045a5
+4522a2b70cd4a97474dbba2657f41bc5
+45243f170e9fbba7c8707baf4183b0c8
+453b4600c4ece261e0ef33ddadad8de0
+453c8f31907498b714ddd931691b1d77
+453ee68241357ecb070b5e57695a8e95
+453f6fa7ef70537d6ca9dfda03d1c99d
+4540fe8437406ab045e89ab45c9fb70f
+4546727252bf58f4ce4a368cc51875f5
+454be8cdc27968096eee0e7f90acffcc
+454ce30a8392666e072e9dda5ce7db17
+454e74777761111b10d605bf77d91d4c
+4557e835d62267fc8f037ae5079f4650
+4558d7f1fcdf10e800c58e0f973d0448
+4563ff689b0b986846c4fc79bbef7dea
+456b28e609d2f3f127887bc2e55e5692
+45713d40feebf7e487c5007ce529f156
+4573d4488727cccde5f9364a200f5817
+4577786aa9f49dc3933cbb10f8ecb229
+457b84157c97fc0ed12ab5c849c3f4ff
+458592896cd20368fc2d138999207788
+458c20a11fc24f19be2bf57c639e4678
+458c4be810ef38702f5183f54b7363ea
+458e759663382ec23676c5be0c39d1f6
+4594f29588689ed2a192adeed7eaf437
+45969b43dcbbdb0091b176ef28b13019
+459a577a90c09131ad3832bfca60dc1a
+459e5b8e44a4b842507e73687a230ed3
+45a4c388512a3a1efb0d7d122244a204
+45a61502936314dd2fefe089580279d6
+45a820cba54f5799902b025d69f1a04f
+45a8aa042d7599ab44166da01fcacc38
+45a969aba8d88304649acb52c61a94ab
+45b0f8119d312778ccfa889718ed4fca
+45b2ef614ca475ad65d251eaded321bd
+45b36f1c559e8ae7d7824e8708627ef0
+45bc5b0724254f977cda3dd4b5f55be6
+45c33bbeae7a685c908b6a6ff165a35a
+45c37542fc32a2f80a81e0ebd26025fe
+45c811c490d6f0e377246184a518169a
+45d867161252779f3edd5c09a164c84f
+45db2579c0495ef7b88862ba3e0ac03f
+45e0030b9949a556369a52d150d26f54
+45e0c5d3916ed493f7cc425acc005905
+45e5278727ceb349c9774a6570c078db
+45e7adc888b33cb12eca02322b5b67dd
+45ead397ce775f6b4bb90be1f663bf4b
+45ec0d06cabb5a7eb61a0fd853e612b0
+45ed26ff43beaed41607ecee5ed0a273
+45ef807d604cec1b99dc16f10abd6e3e
+45efbe27f7faeb6db67dadf1fdc69f55
+45f36bf149298c20f297977e8bf2587c
+460a6e2fccc860fbef2bdf4b6d411cb8
+460b23c558bd5de51a662bed74e06c2b
+46277dbd8beba3289a45fffcffecb810
+462c642ccd7e9022a8d75b6fa91b3383
+4635fb65617564312a317aaabda4b1c7
+463937feb81c81ee1c4b3677947fa280
+4645a4ea725050f4bb17440803c1c5eb
+4648988abbc3433a5d1390ad253b3b13
+464948eff059a094c27cf540e6afe526
+464bb77f33ff7e1a38e7ee1f0a8269c9
+464e69973404f7c123efe3956b46a8ac
+464fe9cee3b77c84519ac4b8c1650aab
+465637dfaab56b8c9c1028923b081f8e
+465d692274c2d0b908fcd64825d6b4d5
+465e9119778e651b72aba5eb7683e5fc
+466130509010a6bdd4b7c2e1d333027d
+4665219fd8480018bc3999cd4d6946e3
+4665ebf0d93852120766d57250fc032f
+466ca353c78a05da085d107914a6c37d
+466e26e2b6f4361cfbeb920dbf44b7c3
+466f71aa284268708f03e269585252cc
+467c09efaa7e0c09f5888a4f944815cb
+467d314043a983fe5111553a69348874
+467e284a00c60953e482908d3054af9b
+467fb49413b6af1b64c0ff26d99bd981
+4682dad548bedf7420033aa256601a5b
+468c6b41d5706d0934bb4af48225890d
+468e729086d947d3c81647c064720a09
+468f62df06db6d227a7928994a752444
+4690609a0c86d83ed90063b2cd387df5
+4691c8c972874acdf9c0128544180c18
+4692d1e0a516cd4732c8cbf5f4422eae
+4692eac475e44de63727430b6cdb37aa
+4698a90d8af43d76d0434f2fe7ad034f
+46a336c0cce8da8cd58a0f11772549fa
+46a80ced56261e250f7f57e385e378f9
+46ac82126c721feea1f313bcad970b7b
+46af1751d1172d31ab88e50f9657438b
+46afa208083d609c936398370208137f
+46b0a098d347ffeef33d5abae67a870b
+46b598e6c09e2e0bd13bf7e40b2a4b6e
+46bc59720fb5bb6a03687a56ad924424
+46bd4b2de72f6c396e914b56f81094a5
+46bec85a900423ab4bc30109cb9ae494
+46c070f3c65f16daf74d91f481c1d1b1
+46c3d5db7c22343bd1aa06a40431f455
+46ccf323a1914df94d5453d3222a305f
+46d36114e5d892d9269ab00f4f650f5a
+46dab7d2ce3dcae20935d972e18776d5
+46ec8ae78ea8ec9a87477a9a1509f356
+46ee0f72266b1c86eee0c7152a880a82
+46ef5559ae123a43b8d4048ada6f1d01
+46f1aa877b0f11e8c389b752c40a3d65
+46f1acad8cdbc7abb403df9dcf3f0799
+46f25128a4f7a4f2f7ab7a9221fd260b
+46fce2081322010b5a284b2b6b243ff3
+46feaf33f06e88f668efd1a178fd3090
+4704325bae6f7b896ceba4a8e9cdca5f
+470c6e1cc5737b60b0b62c3d7d181b93
+4710497acea44a379727775d4f076e0e
+471a0562ae4fda1174921273aec5fcf3
+472ca3d3b145e6ceb9934c0242f6e8ea
+472cf2000130f8a77e3b4de2265facdc
+472ea38a6ea8b937b2b957356faeb06f
+472eb1b0e2872a7fb640cf4458606413
+47342fcb9583ba20c9c7a57ad068cb84
+473c57f1cb2f656a414bdd356ef4e5af
+473dfe82d654758cfe657d9061bd6343
+47478b25e22d9d4fb061989d2ae0bb18
+474c6b76e438e14227be81d7fc68d0be
+475153a8e8bdb223f44e9645bc533243
+47547e4c1ec13728ab4a2ec4e73cf483
+4754de24180da3a6652260e9df8f8ee0
+4756a0945fe4252fbbf00d9cfac5886c
+475d921ff6eb9d0795a2a87ee06ee720
+475f3e17a11d8ca1b4212802eb665cb7
+475fcd63b1ef20c79b2cd5ded67b37fa
+4760e0349237ab1566b2ccaf1cb41cfa
+476546fe28f4c1768088e9d1e2ae0686
+476bc4309ca662ce565362f5af7b1c75
+476bfbbd630829bda9400c4f77db1434
+4770e684ecde467d17232341a0977bdc
+4771115cdd6a5cee3f4c97abe3818b5b
+4773c38157fe6efef0e6b137907bc754
+4775dc9069682fa94b47f7c46d1d28a4
+47778e2338a215d38c70d958b0fa71c2
+477e393aec99d4146bd0e5b4f8efd151
+477f1609629dd7944ed97671c27a1cbf
+4782796c8abde35b98544fda9b56f0df
+478564ed0e760d108096aa8b520518d6
+47867584869ff8b710bd87aaa26b792d
+478676863a3d40c3852d45ce1ffef4de
+478a666d64da8c5973cb2eb5609807f9
+478bac549ba7eb9874aa3af571f57501
+478c1199bed0cd1850ce66b77ce2190d
+478d4ccaa1d29478275582520869c8b9
+47922ab1cfc7c82ac8ee4829b2900f43
+4799b7d59476f9e734165ac75dd3f722
+479ac6a04e557f86bc9dd1abe80ed4ba
+479de4474e4d624851da8b53254a89ac
+479f9e2fc9bf6066197568eb55a2ecfa
+479fc6c8bf9812d21f82c5a13565d6fd
+47a270ae0149cf2bde4aac0d1bc51aeb
+47a2a6e283edda57ce0b735e7c2292d4
+47a3440afd24e081be3cdab031721602
+47a462ad799cc053fd2ea9f00fceb8f4
+47a505ae0d8f28b6174a1bf41064883e
+47ac7a74efeb6d524153043aacf8dec5
+47b27d94a7ac92c13140f46cb281788d
+47bbed66125f7455686793764e865098
+47c2dcaeffb59fb3bd4bdaab6825b94b
+47c98b3bb79ae5a29dfd21aa46ee60d3
+47cdd5ab1b8d917e0195e35b34a69013
+47d003461bef85719e96d30845629481
+47d0309897bd64e01401f5d5a2b6864e
+47d2e9a3e1b8f838dcb4aee6a6c5f626
+47d303740ffa97772fd3dd7488388f03
+47dd32c7279938be5fac0d92214e489a
+47e2ef088d205eb838520015cca93df9
+47e495841162601a1d8cb95277ef6fe9
+47e8a3fac046b30da05481070ce28cf4
+47e8ba98786c1cb27164fe254ef51803
+47eb7b7c0636e8112f70323b9677779d
+47f48ff32b1825dfd60bf920126725bf
+47f8902a7567b93debde9e3e7aca3f2c
+480955590593db12435e30ce06f1d1d0
+48112d8f97018750ee7690f7284e0942
+4811cb5912789234e8659100db5dc78b
+4811d5b51e257ccb20a25d820a3de346
+481556aeb98ac839867df43c1a109337
+4816162e154fe84fbe0c7975d053ce01
+481a7fcb6991089f6583100b25e284a4
+481b60ac03fa57481aaf8c6a38d33b88
+481f544cfe5c6bdf353d21c6cfb3eabc
+4822dc97af8018a119ec87e9b4b1b43a
+48263ec33fd3119fecc8efd597ab0ee4
+482653d71a12ed8b567f2cc6879f205d
+482e30c8e8bbbce5509c2900d8a79aef
+4836da8f3bde1333b5dafd2d1e42cbff
+4840e2034697e27608737cd457a4d7e8
+4841a9712636158aa9bbcddd46fa7138
+48420e143062aa8119ac0ab8ca1bd888
+4845562e7a500608274345fe2a5077aa
+4848ccc6bb90dd81a3dddd37dc59bd63
+4849cc05dcb11e3896e116a3454a732a
+484cb95c953786c0c8be29d75d4f43fc
+4854a9c7060e3c4ee1baee60ec5b14fb
+485a4461ea8387a8b137b8d922e14397
+485fd719beafb5c9d06de31318187794
+4863c43fd7ffd256768a158851592139
+486879f2e69dc0043cfda81c8b2b8732
+48687abe0fc4a992fbd4d9184ca1825d
+486c475cea18ec07536bdd901d95dc77
+4870278244277de1e836a3662df13bcc
+4870513c7cea90fb2651c2a8438a8949
+4872288cfcd68aee28448714782261a0
+48725dea6d1fb481a2c743be69354d53
+487812b8d6587351d270b776cb8db108
+4878478b9a9ffc6ca2d20a583aafd850
+487c8cb44f801c091c7863119e49b9ec
+4882de33a82735d293090bfd4a57e186
+488b079cb62599544cde58c756c385ba
+488c831ad36f602d46f7be305daebeb8
+488dbd41f8bcda89d739b2b507972559
+488ef3f348afa9c4c076bebad0a74118
+488f5a61bb38ce5d09bcf32a9651ea66
+4894623ae7d51c01aac4288a1a43d894
+4896b53834c3ef657d4fb2d0781f211e
+48975e07ad417010ff7eac269f010df7
+4898d5a2cb226176c86bcde91928b489
+489d8f72be3bc48935b79ca2497636ba
+48a14f2b4eaca93355233c58a171651f
+48a49febec488f4cdea8fe850be7d4d7
+48a8dca670bf9ffda2dad7abd29ca201
+48a985037b9870335d2e31d1b5649443
+48a9d18c867b0875a2bd4127a36863ba
+48aa4471c01eaca67c445f5147aa494d
+48acf031af35453e291dd1cc7d47a279
+48affa43b9801983c105cff313d88120
+48b299db5a7866ace5f450017fcd8d32
+48b9933597dc8a007c46027b7c998604
+48bc551873f8272167e299041a7b3460
+48bf1f9402f10ad1d15938703c7b4eea
+48c1b98a3705da20a92afba543830ea5
+48c1eeafbc9186744f04f3b063365643
+48c56637cb20a182e5b44bc5ec796fdb
+48c5ba7dcd419bbd383c8731bcada442
+48cf0e2fe014059761e74e40f8656427
+48d47ffd24e340cae4992a8a69178301
+48d51795e1b4c15c8885d0331abf5574
+48d54b3b1e40a36fb41cf9b3bca0d69d
+48d751b5b872ee5475f18242fce425cc
+48d7e1900d7d9117dec4b53d55d58522
+48df5a9d3a0ef55ea1d25708a3e49ba4
+48e01ca6e4bb8bc6990b2f6760be16ec
+48e0a23a0c744b40e81e696a9265f6d5
+48e3c5b42315c6c319a16b1d59da4d7a
+48e7657aa76ba5783c2ee3c962af9c5e
+48ea1d9e1d4ba19d4e497125450e3b75
+48f79d20501ecf15d66f72eb19dd74c4
+490540cdb17bd218f1843041937c2f2f
+4905ee878e5bbae74d889030b5d5f7f7
+49099ac26c9d878472bcb57b4a60c4c2
+491232654eedc7b686fc3f345d10f698
+49127df7cb1eb3158730dd188d212441
+49149ca72946aa4c22a4771e838c0e94
+4916bd09c617729be2d5cea105d8f8c1
+49195a2247d3703506a3fb0ba2bd102c
+4919ae4e4bb7e8cb016df12f0fdfa962
+491a9065ed4d9fb611e74ebfa2807add
+491d0c8f21895f18f7d48e994d44de36
+491da9bca9a335daa064752efe3b60a8
+491fc557813e8a24cb47a82c2a46e43a
+49209d486336f8410e8722033c311884
+4922da9e3aa779da68f9270028c8c0d7
+492545f991be1f828d490529ecb6ed29
+4925ded6b9e79623b2bddfa46737b387
+49291b3dcabfe241ce34a0a3138d1569
+492f0c7e79bc61457ee63a1c6fa5c978
+49391a06447fa9cb7a3fcc8b2a3eaf79
+493af307e02ef17bb93e11f7afa48b84
+493cfd0b503c6b8d0d33ca13d692c49c
+49418548c4f6bd001775567e47d499a7
+4944c83a2339eba84332388beff1887d
+4944d2cd597e6d62e460140cda6cd43e
+4946a4f79b9e94e57536ca819538a9e2
+494b14c347d6734163e9eb294de3b8fc
+494fcfc4c06c80d8f066fd09eb6fe855
+49505e7fce96815ab031494595af3afd
+4957e47e824073d06b6b56dc4c35f268
+495e733742a32bb34a87972af139f4f6
+49604d62d0156bb291560423c540b8aa
+4965a2feb86ffa7128d82aaf97646597
+496bc6e829b97499ff1d8b565503d12e
+4972f5ca1fb892fa146627b1d5241b40
+497b95732dc479d211d10b453622c6fc
+497dbbadf503c2754e3ca8f4ade772db
+497deb265ce2f0116bdc534b3f994cd3
+4980ada81a0cf0ec9e8f2dd1d28fa03e
+498178e06d720459eb9019a310cce979
+49843aefad8415f1b3526ca34e0c3f35
+49886634c03b26635fae0aa4bb1e3cf4
+498bdb63e91e12418ab9135f0ac62c55
+498d363012544b9065486670301a0b00
+499319e5d67dd5beba629fe3f11f61b7
+49955f39f093eba6ed7cc3e3f03ebef1
+49958031ef035df5534c5cefc0c38f4f
+499c539731245c647d2373688413eb15
+499f80023fbb0a18896021eee9cfbbc6
+499f853b101d65a4ee41d3834d4ce68e
+499fb15b4ac58730a2b0c765c81babd9
+49a4399c1dadf993d4dddc6139feeb78
+49a541ac92cb5b462e5d4a9c89412908
+49a98eab2b020eb2b046927bfa17d9f6
+49aa593e02b5137d2819a3e48d4641f5
+49ad0f4440d68108709dd61df4c62ca3
+49b4a5faa9c9bb7ce584e7dcd8ac7805
+49ba50d324201db70064dfd5137f31e0
+49be9642743db4da19d851417f2efb86
+49c28fb898f882ceee81c3228fe8a696
+49c66331bffea2895a5367e6b0943958
+49c8e270dc8c34a40ca90e3c8baccf65
+49cd71d611b4d53b58561763b37655d6
+49d85e3e4c53cdbd0205bc46cb672067
+49da2851f659811a955301f8aefad57a
+49df72e16e261cefe68ba1727abfb4d9
+49e5a26a35e6e95ea307837cdb437ae3
+49e7fd3e1380de337c368d1e7339db25
+49ea9d70d883d911e2b19fe1564a140c
+49eb0fd47a704c986110ccf87627b6cf
+49f432576ecc02420a0c5a16b2172ff2
+49f61a81e61ac77d88bb382551fa0b03
+49fb7045df30df154c196b145cc98ea5
+49fc628514f8671d4c27c15824f45c64
+49ffe61bf66ffb14c1b37d58ccfdf7c2
+4a017223ebd66f64c732102ef8f684e9
+4a09b406213a78bea0d12561365fea7f
+4a13632bc0a3fe7e31958fb92c7f4a7d
+4a13fd8d8d682e93d36daf353b3c7df0
+4a17dbb556bf915f4ffa92e9b05ade8c
+4a1a8fd7061e61804fab62152c008403
+4a29b5784bc42ee795103192192b4b34
+4a30b9cd042207ea8ce712eef99f797c
+4a30c6779106fba8efdccb55e2faa881
+4a35b4c71dba7173959bc30515c83124
+4a3785fb28585d41791f96f01b0d1351
+4a387cc18d53950f83f482d3ac16f953
+4a395a367cd706c50dbe0ce3dd3ea38b
+4a3ef8674fbfa68563912c7602ab2051
+4a3f66428e721cecda1a0107582bee8c
+4a44b69de263f6cb771220d6d083c38b
+4a4ad7e1b4b7c10bc73f94a3cd55f16c
+4a5033966446bbb981f371305fffa0bd
+4a53bbdcdbb7a5b26ba0f7d101ca2ffb
+4a563ca215c3cb47170067c594cab78f
+4a58bcc937633183364d2c24e94ab44c
+4a5aec3904db81195762af51cb911b8e
+4a5e6159dc5a47acbc20255d1aae15e2
+4a600315b683240a53334a80e4d690d7
+4a6378526a669e26d9a768f35b989c7f
+4a716a171897e6bab54ef6451c7f4807
+4a7650b634908b499cef3ab34cfce316
+4a7fe5b6f0930798dedb51b33bedbe40
+4a85b834208ecbd7fc4b12b33e9263cb
+4a863e490097f31aae317bf47c945051
+4a894d1bbe76e8f37223923497c1894b
+4a8ad6abb442811fa53536bdf3af5482
+4a91174f04bd407a69d79995b155e4a7
+4a91eeda9d3798360cee46df6a3902f2
+4a99aa7495b8c641ccc1513404c2819f
+4a9d77a4c039b1eb0359772c6da59022
+4a9d77f1c1227b7d231dd2cf3819ad38
+4a9ebaf200dd40a563b81309304e989f
+4aa25e517e6089ad16d71ad92397e7f3
+4aa807d19b7f27808133882214e60f1b
+4aa97158cfb78fc2284af165183f5419
+4ab029c9f83553544261ecd5bfe3b4aa
+4ab1e88412202da1a83dae7ceadedc5f
+4ac00a35ff695b61d8ac8dbbf96683eb
+4ac7df7a7d98713d28f96fd099fca00f
+4ac7fd05e733f1d52f02996221618dd9
+4aca6e6564460a0dd0b9cfcdaf1d6ccb
+4acad7eea8c131d8ad2f131145b0937d
+4acbe6b9418d95b83ca258d6ea3328fc
+4ad0b70a064a75c667cc121adcec5d22
+4ad188a64dc2ffb6d8ec8d4daabe27de
+4ad27ec4873d9075e4ed8877b1db7a2f
+4ad32f6ece5afe41ca579e36535363d7
+4ad3fac2e04814eef5bb1087c9e28bc3
+4ad8f41c8001987de29e6c1deff123d0
+4adda6f7ddab9ba85f1b1de9871fe32b
+4ae1087d6e5b6561a03e563003de250e
+4ae41175fd78d6bdd82ad36411f30e0b
+4ae4281e4f443e0851c17ffd0142f50c
+4aee79171c490a5496c7bc94197970fa
+4af2fd4c69ade8ed705bca3c0e05ba74
+4af8f581d23b9841b0b15bf604cfa63b
+4affe98213550e8194e1e477950fa053
+4b014acd2cbf3f95e0fc7a78952716a4
+4b02ff02759124f6f7af8747ea810a79
+4b0667268e517bd47ca7a8f68f835838
+4b0ccc004be9a9c0f16fac0db6c48406
+4b1045ed7a4d0b03d4b08596d66aa228
+4b1542804d209e432d3fe9dae7c21518
+4b1e1ee607b3da4749b93218a4c4763a
+4b20b031d4adbb9a9199339013fc5db7
+4b27f013d1404f1c11f0c1647bbb1979
+4b28a8dfa9d96dcab1954732fb8c0221
+4b2f6768236d5ef33d380f1fed3004d0
+4b3071dd07b8cb88487ad6cfe9ea74c3
+4b32d939a682e33b857773c6d06b98b2
+4b32ef66e53c1759fdc4413b1754bb2d
+4b33d60a89e739483ab9b99d086c0f14
+4b399e6d3f181e7bcd116629029fe3c5
+4b3e2437f831765dae329824332c6e28
+4b43e15fa04034188bcd27dbc04f82fd
+4b46c6023bf4d9f6df879844ddcaef2f
+4b4a72e09c14ea3572ddec5e3dd7ccd9
+4b4d94c2547f02b926a02ec922a11be5
+4b4f3e8665632035ad073babf06bbb7e
+4b53ed9095cae3c19a7381ee53ea987b
+4b581a0d05465ce655e7de1bda1bed61
+4b6381bf65927048a5c2d95d50d5bd29
+4b675472788a9dfd3540a2c6b75cc386
+4b6953fb0c78f696131e16573e296ef7
+4b6c048a27bff0b610e908b04a14fa9d
+4b6dd5c9b55d1a8b37fb29d6e9dd5dc9
+4b7011a27383015496e8a3cd758165df
+4b7752ece14a65b1b5d141a4953a6c4d
+4b79b76cef26dfaff1d70c9bb778b7a7
+4b7ae76c7cb07c1ac7694252c7cd02de
+4b7dff265f7dbdc932a4a1910d833100
+4b8044e4821b8d648fa5848db10e5fce
+4b8c91bae122759d6f145a2e229137c3
+4b905fcdd146c682adda12bffd7513d7
+4b9df12157560e4748f8d80f2b736594
+4baae66b5ca488eed09420f8bd16d820
+4bad638c6498bc534e682f4e8cd79cb9
+4bb4a5ee3651f1696199f780f08da015
+4bb640b05bd32055c7a271a73f49e94f
+4bb678e18dc42db76c35eb93ec891626
+4bb86787404ed8432d1f6e3545ded1f8
+4bbccc181acad93662a498e822525e2d
+4bbcf1a510ac3b79daadf1c9b37d212a
+4bbd13671934749cd6cfb0337d58a97e
+4bbd354d08f3c4a6629645b35b5890e6
+4bc31e899c68856f4533a9d5bebbd9eb
+4bc37b0d8eff20a510592f78bea31acf
+4bc947c01caa37502e8591cd79a1a8f4
+4bdb1c841ff8630b3ffeda18667fd2e4
+4bdc1d18b6a76d3bf9bdf1fd567dcaac
+4bde32a7274147ea1be8198cef914791
+4bded765bf27a0fcfb2a04ccfeb34ab1
+4be172115bb8de25493eb2e0637d7688
+4be1fa005bff29059408144789ef799b
+4bed8c90ebcdf9d5476109c83995ec70
+4bf5f6387e2cc978aee95d0dd73b690e
+4bf8fa28e7af0ccb594832fc66b87d69
+4bfca25b324a305d1cd808dc0227f21d
+4bfe32633a8139bcd01297aa39c24823
+4c04493836dabae3f1fa427fd9a70892
+4c044e443e9b4ee254d4835fda09c7cb
+4c05c2ed401c585928aa3bc342d0dcbe
+4c0886e7691557b9795bd3ebc3a9f3ef
+4c0abdd061d888edfe82d1cc6054843b
+4c0f4953406764981bb7d24091da6bad
+4c106a4e2ed6d37112fea67f44d158ca
+4c18087648f3d0c8966d0574ad5d155b
+4c1a9efdb63ed59cf91f40218ca36f58
+4c20ca877bf4a94e17e2e87e589eaa7f
+4c217642f2e3de6a84742593bef883a8
+4c23a2ecf8a3d2b96d582158d5b4a8aa
+4c29946fb44ae757b1ec84ee95b0a951
+4c29c60a9686df0bb85f210a2f48320d
+4c2a64ee2540abb4e643170c71f54e2c
+4c39a86f9b85b23eb25cd5fe91b3152f
+4c410632c7e920e5e6ad44cce47d9417
+4c425905363ce69009b4cc24bada7be6
+4c44d8580a59820320a2067ba95aef70
+4c498af961a3dcf727653c31e1525b13
+4c4a97fc9fc73a33cfcbfca1172d2afe
+4c4bce279c5e2f29e2fe8b5eeacbbb67
+4c4ef19e7e5985d773169ba2c86087e9
+4c5aebb33942297ac2340c5130830825
+4c63336cb578993664f137618ae1952f
+4c669cdea35028744118d2d2b1d64a4c
+4c7624709bb2ee45ea657939300bbd81
+4c7e3dd11be5c87f4440ad13869def81
+4c7f4b4674fb274112b0a963f7307510
+4c84a7230f98daf15a944289d8c46069
+4c94f30ec4fbef1d723d2801ba3ecce4
+4c95521415a300fd2cb76d4f1551291a
+4c9774498589382cc9bc06f603cf2642
+4c9ec0e1ac824c5b2a3bb85b71b19b6e
+4cac765feeab6f7a1cd0196fe4959d6c
+4cadb0cffe22a7d95bc62d3031c1b129
+4cb55ff2a5eafb0c4d18c04cd4ecbdbe
+4cb84ff6e627cc660af81c8df97f4667
+4cb9b767bf5891b291df15f4e37d0b7d
+4cbfab5befdb49e4f48b163a1038b5ee
+4cc0e6b83a24c2c12104275a14465035
+4cc17f0c154a9c3ba6ef124eebc2c976
+4cc2380358e6730e1a55aa725c34738e
+4cc9323a149b02a969a413a79d19ea0a
+4ccd64ec88004d2b371f09635ea6ffdb
+4cd505461e36e6d41552c6b91b1b88a5
+4cdadfda86e975c7aae03efbad6e408a
+4cdbee7783747e0cb1911fd769210d68
+4cdf51e4e711681dad9927d424c50fa6
+4ce04ea860942900936d01b74b528381
+4ce0f915f67dfa44cb14fadf037cab16
+4ceb0fbf94c6dc4058eeff3bbe2267f6
+4cee770b0ca3eb320581f60b2dc1205b
+4cf2dd8b082c2d3e0bd7ff04c86f92bf
+4cfa4d023575d40222e12bbaf3abc112
+4d06a7c9e617cc392966b48f91700d83
+4d11b2943a2577d708f7ee43904c3d1c
+4d15fab596524d8cddd9a00335624bdf
+4d1742f36ceee2da405971fecc05e82c
+4d1c338fd39c23fe7f4983361e9b47c2
+4d1d97f08c3ff33977a59c584e64ec5e
+4d1dee9bf9d8df55bbc2afeb2dc05e4b
+4d1f6d73cb5441ee3d44d1c50740dcb4
+4d1fb2f514f2bac7aad362cad207571a
+4d29bf077250e94019e58f8ab584fd55
+4d2cfb153906912edce336c94425214f
+4d311e73bd7a41b8f29998b41bfb108e
+4d37c0e6fbbb0d5b5acc1d8c5fae8341
+4d400997728d4ce237247560088ffbcc
+4d41d83a4a49ad0b203bb528ad1433ce
+4d428199c38f8767080cc2d15c225d55
+4d44430f0f23fe647f8e9e1cb0d8364b
+4d444e028a7e0efd54fcd2b5cc529360
+4d4a17b8824e2790ce08b1fc3650eb3e
+4d4d1b7115d9e6629595c45df1df79f1
+4d577e6f90cdff2a150ac137d7c17d26
+4d5f5934e399d84f6b046fa9041299dc
+4d5ff5a290f896a5d51309354fdda5ba
+4d6301f612b77ed571bf93a96f934862
+4d65593c16d62d3136c692c239af298e
+4d6aa5ef2a4a28fd99f7e6631a3507a9
+4d6fa4d56c5a6948aed1ba9569eba1f9
+4d70c658418a5c7f96d041499d4ab1c2
+4d72b8dcf98cb15e221142264b80afd1
+4d7b199468a09b7500cee06565e8c80f
+4d7bc51e581260be45e1a43cd7766a20
+4d85d451d8560b2df535b7371aa1d4a9
+4d8e13c4edaced86ad508787254f4ec0
+4d8e8c5d8b0dc5c64af4a008430e11e4
+4d8f973b37ce704f9d024d444ac27863
+4d8f98cc0b8722354d1c09c990a5da28
+4d97992dcf9d59dd9d7ff74c5e208951
+4d98b065f0bfb5159f8a10049f03974f
+4d9ad559deb8d6081d39b005c78514db
+4da03ec8d5c2d9b31768e05204d9a8df
+4da4f416ba59bb4550fcfb5d18cb72a8
+4da9ef00bd691d80b6e367b0f34a6778
+4daad7e0904f6e4512e05896e1cd4a72
+4dacaddedf6449aef26f4113361a83ac
+4dad054b8e6b68dcb1bc0e207d67f271
+4dadc169a307baf0b06dba86bd1f4a23
+4daf4d6b6438a7df06e8056d3f6d12af
+4db353a04507c760474e6f07ab23fce7
+4db58b03a10f3e9e7e2978c0ce21474a
+4db64909fca43b4ee3163732b67f24f7
+4dbb9828d7c80916d98e97e26794550d
+4dc099d7fd49ccc6ba633f816fd8c967
+4dc2d61cc71e3e10b7a1e176987f8415
+4dc31d98c88aa438dd024d7c42ef05c6
+4dcbc6cf3b452f5d3762728d0bd0cc0a
+4dd023c313784e4e66454390f97cbb68
+4dd23e0c16e46a71d004f5e82c4d54c4
+4dd5b9eabac961a5ef057a8d1fa53ef0
+4dd6a220ade0bc8f84f1b85addc6d4cb
+4dda279b3fc37feab4ff3b4dbad6b7d3
+4dde35dfc1873e7de15abe105ef87b27
+4ddf3f3402baad0d5f7b2e18f93b50c0
+4de376a3e134c4f5f9892f249483f3a6
+4de5b934538e37e030fe6b6d911de5c3
+4de76a814a2e22e86aded25e3c8f3e87
+4dea978d5c77e8fbf11691c2f7f68334
+4debadb886489c480c69b0396603dd40
+4debedea61f602c94397558440a507f9
+4ded27870ed39c0c414d5b10b08a6253
+4df5c15adba7f1854ae00a9dce4b21cc
+4df6f9d653efdc105756a1c11caed458
+4e03122f142f7d1301a38c8844b11331
+4e08c12df4962152457e12596d308229
+4e0921e958833846c7c5efb1dd37ce5a
+4e0ecdbe2600d9deedab998424da4c4b
+4e107553debb6a29a8744fed6bff6635
+4e195bc4ae3eee34aafd0dbfb223b4b7
+4e1de1b5713f317ee955f08c60d4830a
+4e25127e33820bc620354eddacb23a6f
+4e252431d2580af8c9fa544a8dbd62d1
+4e2aed86bd5bae7a1b8eda758d4241b7
+4e32a56959ac4c9aadf937b24c9b03d5
+4e353e56fe8c6638c26870656f977ce4
+4e36bcad6a6027f1e94cb5b4039ab42c
+4e3aa28cf1982ad04625629348c1a006
+4e3fd95cc87f022e8bc274f022428131
+4e40222f3c3a3d2bdec894ce4334629b
+4e41f02b3dbcd7cd245475dacb7c83e7
+4e4991bb09c2f268b08efc328a643b36
+4e4bd8fa220b49ffdf828c17d5fe4911
+4e63a617105b72b89b1724f91cc1c9e0
+4e65c054a86cb5aeb169e00133d30815
+4e694cb858a9f6c1103f3a75c540b62e
+4e6fa82f3ad6be8fa15dafa2888af819
+4e71120b241aa9281ef0043850768505
+4e716784684d95017a8e08af29be1eeb
+4e71b7ce38325275fec06b095f92d28a
+4e75667e5160ffb53b8053020fdab865
+4e787fa16b24875a0b787e8064754e83
+4e79ed0afaa087123528769ae605967d
+4e825e8d3a661f11bf358fffdfb245f0
+4e856269daf3a56d0efc0398224365e5
+4e8ec4741804311dcf4b94d5fb951aef
+4e8edfc70eaba0639d71aeaaef153812
+4e92b5d85215c6872288d1673861fc39
+4e980edd76bc258df020dc000273ba56
+4ea263c257e700be14083d9d64f4946b
+4ea4656c51a980a7965b78235e93101e
+4ea8595f077974cd2977f6eb28f3a953
+4eab5e77386911559358a84907817dd9
+4ead46df9735ffa822571aaf129a3390
+4eadcece053cdf6b8385465929e73a36
+4eae696a74121da6e08bdb703c608570
+4eaf2e17dc2ec5b0c80a6e1441ce4cee
+4eb115db5200d6557c457f9fed434013
+4eb15f1306ccd70eaae9da483cd18d2d
+4eb4cbd425bf334f3fff86ea7b2ec94e
+4eb5b2fedefcdf1d4cc7839e9986f1d1
+4ebc4a5bc3046744b142eac03640f9f7
+4ebf4e7f6784762d877d0df9c728c0b4
+4ec2f7bd978b83aa238f0a20fd426fc9
+4ecbd7871cd58ab2c21dacce00cf4232
+4ed261c8e056a3eeb289f2848f2ed86e
+4ed3021a2526445fa90a1b33f2bd9fc8
+4ed7760b17f6c3cd0fbdc59ebd91a0b4
+4ee487a19482d3125c49276f81631582
+4ee747d9eb14cfe8faa5e98f7823e203
+4ee7c01f36c96faa56fae0dc200b87e1
+4eec341dd60328d27fca83e063a5ffaf
+4ef3250947360cb44a7f6ef4450d674b
+4ef3a11f4a2e992b3953423455174e18
+4efc3e7f25b64eafe8668127c72dc7aa
+4efcfbc5416a38ff1343ea9ba2ffc2b4
+4f055409828fcffa8ac15af8f8c875ef
+4f146dad770585a75a97f899df835b91
+4f14a71b2c00beedc763404b65885d93
+4f16e1a12ce201eb4ce085c7fff3ddbf
+4f183d85494388583b174c4b54f1c79c
+4f1b099f6294307da01621e3d158e7f7
+4f210225a01d1272e5818c88c4af1ba0
+4f22ed862e51389b7918da45e843b27c
+4f27445536fedc698bb831ba9be40adc
+4f29313ad46c093707d356cb3f29d277
+4f2988d667e946c5addfd09b10f19c56
+4f29aa56383c8c27cd739b95f1f1faef
+4f2a522ae224d5f051d4201559dbbd11
+4f2be320180fc16dce9c683912c93f4b
+4f2cd2ac3a7556f0f6fcc47b284df2fb
+4f2cd915fbe71ef1962a94cc511efe8e
+4f31e303571ac9dbace3eaf33859af00
+4f344e3e23a272dadc1d0122c4648f52
+4f36914483f8c621989e9a6a47b93cf5
+4f38f7016487e20d187347bc86c9c4f5
+4f3aaf0562fcc24063e0d5396892d7e7
+4f47bfc48cd69b10c0b995dfe625eaad
+4f48dee1786480e1057de71647dbbde6
+4f48e60858dc789551af56083b5839e3
+4f4ed1d1fa97143b866ca18c249e0442
+4f52f3fdd7b1d0ebf8938517ff1adadb
+4f55a6a69f4fa2f58de643977e532e87
+4f5abe53c16b09896daab13ae221ebe0
+4f5aece007e1c4719f1a48e9cf26a166
+4f5bdf578e059ef6f30bdb772d022680
+4f5cafcf7ffb057b9382d4c8dd17d281
+4f5d0978dbaec80477f46405b260150f
+4f64d6a670aa60104481b55ee889c58b
+4f66fa624ea823eb116328b6816fd5e0
+4f6e5faf600cbc11c28f7c139d6943a4
+4f7476ec7fc7e81a18be156c556e7058
+4f76f76c7340b2437831bab6457a60ea
+4f7839e45a1f67150cf314e972e1d5c6
+4f7b868f7d6f5366037a65c15c3d6e4c
+4f7c418b0fa6eb8fbb05677125b64966
+4f7ee34e9deab9f670872bd088b0954a
+4f7ffbc0a42825a6db25f9adf1422401
+4f869fa2713344611338a84fc6d626b1
+4f888cfbacfd53389cf07fcf020b577b
+4f8f1bc2295f0d3339c974bebb403799
+4f9030ce41e05c95a588128d2118fe21
+4f91f47ad4280a66378d0922a7ba30c3
+4f9bf2aba5e6dfba7f9da70a44be9a53
+4fa098e48d8415a952ea73e8bdc06b9b
+4fa37c81cac27c9316d94e6206a030d5
+4fa9f3120333ebb5cfca1783851df323
+4fab7dfcd5b14066ded7c03293c1fa22
+4fb2ce6ff72241b33c7a25194c15985e
+4fb594abdb8c3a5d52e3ec7fb0e159b8
+4fb94b6b7266d19fc0e8a7923a136708
+4fc61108651c0fd466fccad318b08721
+4fd0479ef34aa0347b35b7e1fa3e28ee
+4fd050d75e8a909322918f111c491a3e
+4fd2dba6ffa7a9a22a84a331c3b046bc
+4fd355e1c8dfb45615b15f64beaa2871
+4fd4cb601693ee32a21f7a4ed5b4f4e1
+4fd58aa59ef497460af721698fd4143b
+4fdcc0c7b0a5d2529b4b0581903adb47
+4fe00689acdc3a03b950737789bf4a28
+4fe47a878c6ccb38367f1a3e0faeb1ef
+4fe4f23d343ff283cf8980a976ba9a02
+4fe947eec101fdefdae4019a0e0da729
+4fe9aad46f5d2e3208e3943bb27f6152
+4fed079bef01af38fd05c55e76a0984d
+4fedcd0d97b7e39eb42f4651e9ee2c43
+4ff4ef60fd607e78b22654e21fdf680d
+4ff6a4046994a6e48a7ca7114ec7653d
+4ff7194fa6d2800a77e15537dde4d8f8
+4ffaaf8a88427771155f1dd191cfe693
+50028bc9550b6846beeffd17ebcc3d2a
+50033fc4a214d5131764338b263721e4
+500720bab658f8a75adfbadbb4ac6f9a
+50103b6d4e10f18666a4924fff3e5e0f
+501bcf6680f8f3eac0e6c2b696b803c1
+501edf56c8a17f509d495558e66132d2
+502566308dd75fd8a78a755ebe9fc396
+502760a1d18c87651ca4f53f89e56bcb
+502f4c4191ecd2682c7e3920ec669651
+5030ff731e085d061692a451cec29941
+5034ccb4c08e19a43f4d617f058eafe0
+503f69e62fce3aca7aa4cf287d0a086b
+5048d8f3abd2d984195e86e44b82d6af
+504aa04bca7fe1364daab11d7a290b90
+5057a4f8330d1371260fb28630c34bea
+5058a98a6446693d64158ba82aef377d
+505b335cf161c2be74d2d9803f3816b2
+505bda1e15ab0ff92703f248c2860d8b
+505d0cc414ff9f4ce703fe95a303e209
+5061c1c0470532b6b69162bf4f0b034b
+506597dc9e330eaf61840e915e446d84
+5067c75fb9ffd5dba353493eea89d0ad
+506a0960b6ca3180e41c6a70fe2f74fb
+506d93d30f6358aee07a415ca6dc0c14
+506fcd465c6586439d5cde65f70af9e5
+5074661503f1ad716578b442f7c937b8
+5075c31ced93733f46878cf2725bf3cd
+507f5f74dfb8d3678d739ff3c5a04922
+5081222ecd02f76358c0a6b064ecd7ce
+5083a6894808501b742f1bef241c18f0
+5087b428ba745231068295e219f7e5a2
+5088cee84155ef6e081aea9cff4cf39c
+5088d369235e78f30ceb5a851058789e
+50943902f960ae3f1d8f220f692644b0
+50963b8af6c435561150deea2df0a65c
+50a5781812f9208f3ec6e68f378d0d0c
+50b07cc6b81aa969b6f354ed32926f27
+50b0d3d792379dfb2c22ebe6cd3ba2ee
+50b7d0acb6575322679f80f47a663e07
+50bd9efaa24805765e902c7e2999b4af
+50bf0b01ed8b442091a9e9ded8b99de1
+50bff042f7fffeeb5a236f3d331bac1e
+50c5635a5fe322fcc5d9d794863e07f9
+50d05716026c79d30a92e78ac35809de
+50d1de16973f3be118568479ba96b9cf
+50d5b2945cdd11e492547b9896926afa
+50d6c60e488dda4fca99c7693da0f4af
+50d99671b8d50d08c754a0e93bee1310
+50e7ed89c27732b8f45e462379ed6541
+50eb76dfe6246a704f846371d1926d6b
+50f0281a3312f994e45ed637a6e84321
+50f15ea61da0a225661b9d45c063bb60
+50fba0b0b9d7cb1a2e655fa16ce1a78e
+50fe8137b900e1335b2b1cf1de23f311
+50ffce71f24edcec31d22c88a1555a53
+51025779cd25b1d5af94ddab19c96570
+5103c86f37301240ee9e2895fe70cd86
+51042e0fb2cc1d12672b2e15064988a7
+51056dac3be674cc6fac108d41a64aad
+5108a46234af5dfe0bd85aa7fc623b50
+51094151484506345802a8aee722ea5c
+5111fe0515e3734d76f8b9400a329146
+51142f139161c15026799d0df5cc2fca
+51178f8ffe267a4985df5b403ce23830
+511dacc64b45b3c18d6d60326924c7bd
+5121fec93edd43c587943aae3d4ae7f2
+5122b0130e2a51d0ab38f262c442a0e4
+5126b518cd85559bc29dae91fe424339
+512715e138ce7064f554561a4aeee189
+512811b8d5fc13ae4ac1067136dc58b3
+512fb55153cd0647d415e1afe604a0ff
+51374c799ae60f8816f51e4a776fc05a
+513a0f26a252667f62e549ac035f5093
+513b1c35d914c99316b348199fb79ec1
+5144c447e129a36b7ae55417884091bc
+5146c455d7eb63ca21c75a130a223aa5
+5148c0ece381d5633eaef9fc2e36f183
+514fdbecea0f3f40544c0aeac44ce693
+51571da43e45ec568dc1791a7897519c
+5162d1510d40fa43643925c3c01397ee
+516631f2ef2bb8bd50dc3a86db14a198
+516743ccca71e93f76350517615c6066
+51684b7807ff0ab7ee9b1be2c986880e
+516ac427994880370839088bfb3d02c4
+516c089665e6f5f5cc2bc99c16089dbf
+516d19c362729c70b377f1e8698bde4f
+51768902ac94452774c6d03de394dab2
+51792c5275a43c870749b9294e8fedad
+51795e003c12aee8b7d69897e0c842e1
+517b4eb64c2725c037b13b096ee06ebc
+517c2d84efdb86583a810330ddc12297
+517e0f2b2b9af39804d9beac25f7ea4c
+517f89d7aef428be8915adb8625c91f7
+5181476986315b78a69542560e66d8b9
+5186f02aa7c5c9460ac6656565be4f69
+5188cee5f6224418f7499c7afd0990cc
+51912662690fed540cc6b01e5ee022e7
+5198e5b2f5b915f08e9d528c63afa149
+51a1e213a1f488022511ffb053263afa
+51a421a7200cc6be612dd530a03375f1
+51a46f9a4caf3e0048c5ea5e29a985d7
+51a6d41031db861288ff378ae0bebd6e
+51aaa1f686a164af20efa5a230b52c2b
+51b1ee1dd4ebe9c3d229cf8874ba553f
+51b37ddc1acde6bc6d7cc05597f58f14
+51b697f728e54a57ebf4beeb2f7f26f4
+51b7b75731d08a8e8803f5768e7b1d06
+51b96c618fca2bd539fc2e56ac46ea06
+51bc3fda6c506e444f8dad9b2677c14c
+51bd383240fcce841f61c926b89d48ee
+51bdd99c74a7dde069fe5d2bcdb8216e
+51c241d2b43722d95ccb945a3f2fe8d7
+51c2d0abd424f88f6a112d0ad1f8005e
+51c6355005ea0ac3baafee3517864c45
+51cb55861f0683b8eccf6df914499b38
+51cefe631e0cf345bed91de296e0907f
+51d9c95bfb0feecf6c811f94e1a337e3
+51e0fdd432828e255b47240529d88dcf
+51e15c63c35ae5673d7fbea36ad63382
+51e3be019889fb2d8fdd7fd5d69cf43c
+51ea80e2310e96729f81979a1104302d
+51ec8c9776461a7b508bd6df402103c7
+51f9388c1f661e9fb33cfe5492e99174
+51faca51e5954ca26d39c03c8b77d581
+51fb99cd928c6cdc55a78112d326fb4f
+51feb9aea43db7bcc0c5199d7b9a0f7f
+520171213451e1ebc54e03e077477377
+520278815ed992ab3265c48249df8d5f
+52030f2c1c050f59e1520802fbae01f5
+520962ba20c0c42c5fda41013418b8c2
+521229d81e0e43febd04c5481f6171ca
+52253b4f7559297827e115007615ef02
+522c8df73642669bdc40e75b52a5e0bb
+52305cc395699a26060f98dd65eee5ea
+52354c87d12e969b613386856880c13d
+5235c6c43617b8853d63b41f2e38d8f5
+52361b25793b0d3f0b16b5217f9cbc9b
+52390f12923c68231b6d9efe5a725d47
+524106c64938f8b43ea5edc81912effa
+5247ccb31145d2cdf6ffbcefae6b4d04
+5250a8559197aa274efe356bf5e8fa1c
+525580ffc74e679d5a89530f497af735
+52583beec4461481f7d507b246d290b3
+52623b84258d4dd55b9eae1f81c1dc04
+52623fe019424df170d19c54d78cfcf9
+5264ffd54b8c5dba90a0d55b32cc93dc
+526542b8f5ff7b958c0ba60df91767da
+526a4df188a0b75f070fa0f0d310a44c
+526c7a69b160b1b0f5591b270d4159a4
+527ab8144dc197e572b53a3598b6e62c
+527ba9cbbae5f4bee866ae74b170dc88
+5280ff760b526f11342020f4c0a51d89
+52813ed2bedcd1dce4159c7c3a38b4c4
+5289ffc88ff0442cff2dcfce0532f4ce
+528af8a5498b754a2c59e692e2085631
+528dcc70d5fc6d5476353f36fbd46365
+5292158bdf359b4a2292fd84e4017af5
+5294e4422b391ff4b9bb39147addfa60
+52986baba17dc73806912fb20055333c
+5298e1d28d03607ac5be5407d0961111
+529980ae025ae3c1872a5dd191174817
+529c79325149834aaece1ec426df1845
+529dcee6d2889fa0e578faf96301edd0
+529fddce0401d4505a483b4aebd5c5f1
+52a20121814b684f5dce8a31e0f485d0
+52a45cd0730585e90c54035a1ad7ecce
+52b11d3ce5cd5ab64703448f2530e202
+52b6610bd472bb80920c30667b478f5e
+52bb21e21d13f52b7d89b9927eacb152
+52c2e59c4e0d734f34efcd6da6e1919c
+52c61e25d8dc5f8e88b556a7f499dcb0
+52c79013406d0a93f30c90f6428079f4
+52d7f48b80c63212f4c6493922eb32d3
+52dbca5a4b893c9e09d7947af221c41c
+52dce9141629b846a65c960c71da4818
+52e53ab4e55dddbfbf77917738f4250b
+52e8736d940fcab3e3791f31286ea081
+52e8784d0c814a3fec7ee956e9f2f841
+52e8b464f24399ab2de55e57477a2ae2
+52ed12cf809934942959bd2fb4f89db3
+52ed9f33717f5dc78392dee4c7239c3a
+52efb8571eea24108caaf6e491949f60
+52f1e52c8cd9310b50edcd65d6c40ca2
+52f48947b31205a9851cd8b27aa1701d
+52f73fe26c0964bb5c7588b0c41d393e
+52facc9975c3d6f367d89d9c91ad79f6
+52fce820d29de4875e4b3738d56c2b1c
+530081e753caae0086ef39506367e8c4
+530a386faf89b113b9e04a97e1156c5b
+53131effa170643046ba202544d925e1
+531469f71846d228a70aa476f42b5d68
+531681623b8f8eba0c6adee9d55f10bf
+531bb4d4df969511530d22776af964a4
+531ecabc0fed5473645b051d767cdf6b
+531ecff8de31fbc423efc16cfc3e8cd1
+5321d2ce12f5cd2c2f2f5a60481e5706
+53223f9302cafffb98caa53765ed456d
+532662d31d64110b55c15621931c8453
+5326c29f98cb866578a7cb6e36c8c128
+53272f2b6dc727c3fbcd4938ec9768cf
+5329bcc1eeec8aac4badd596e03ccfb3
+53371b9cc4f2f6caa5d8137b239bae10
+53396ada794e714b5032c850c9eb6402
+533b392019de16bdbb6db8b6710ee621
+5341155db1201d5364b7f99517e8d49b
+534389a161ad2e6c3f7834b5963ca355
+5345d9b7daf3ac5dcb6e93ee104e80c6
+5346ce0c82e65625d16fa9b7dfa03dc5
+53496a760ae53cfbc03d8f595cc58221
+534d172c210a8d9673bda4b510be8402
+53547354630ab0c3835938e2b06c130d
+5358b771d7e52f1bae7884e84f795335
+5359d6bbf9b2a66226f5bc42a8c6963f
+535db42b77628804619c431f52e44797
+536374159acaf11a4a8803e7dad5c9b9
+538056f10f2ade7d38f70901772ea9fd
+53888ff8f214ff55b258b2c7e741f5c2
+5391641eeb9a825f9c6d3667432f5492
+539357ce31041319ef83705444e9670f
+53942efa2784c0acc659c339d0cb4f0f
+5397e49d5e8ec597f90cc9a3e7a64721
+539b94014beb121579f09dc027ecfa06
+53ae21343beb14657400eb23f048355b
+53ae223fe919df6639beeb47fc118520
+53ae7835c7e1d779d3337579db81a3c3
+53b03c928a44f3ddfaa5ee60cf8bd4d3
+53b0ff189e6789dbefed0dab7d2bc8bc
+53be2e73951710bc59200fc0bd2b2887
+53bf848eb80e550ca85245907373e1f5
+53c030c294d0b45c8860d39030ded8a3
+53c15dd64cbdda6c667b011cd27e1859
+53c26115abd47eadaa21bba11cc454ad
+53c667aa6f4ab98054edd68f4d0e1bdb
+53c8160f576f30b2bd9ce3f3ef981758
+53cce88f1a657d98182c71f316cec5f4
+53ce39009b21f8e9b70a8bb9da63b8bc
+53d2798824494ab1c454b26dd1b3fcda
+53d44e3031d74239d0d72d9da33965f8
+53d75ff4f8ea9d54ea6c483c657b2323
+53dce45cd7acaca425a2c45b8a499075
+53de747a1223818fdac428ae5462407f
+53dfca3aa478a61a4c126f0b742dc677
+53e1133d7e1c99e1ac2f746441173d96
+53e9608a90becc92bcba836d49e53d84
+53e97e41b7c84f64e47ff91e24242017
+53ebe73e2e3fdac0b29ef19f5a31d083
+53eeaa0485cfe23d9b7801fe3a1c4cab
+53f28ef075585357c240c462936c5ace
+53f57e34481b08f49e8b9baa23d7846e
+53f87b556046ebc22351544423cf1794
+5406106762cffc23d730a85af219f7f2
+540cffe58619a93f753bf65363808109
+5410a8fc06eee62d37b79109bb821a14
+54129ba4a991bab79463c211db2bcdcd
+5419d043b9b8dd8ce42b69f571579218
+541a57180662e79556d5c7fa4b53aa4f
+541a940fb6a2581093c026737dee418d
+541aa601c2532fc734e817c2ce74b518
+541e24028c54bc86ca92d7c6a825525c
+541ffd6007edee91f305b3b2b8791d4c
+5422a54ca63cebfc7f44303f5c1e2038
+54263f5df1b2735eca863be84f584a8a
+5426e73f66b0a97cd30f4187c6ae3040
+542b277ea9c67b8d9fb5ff7cae301c84
+542bcdf6c8644280b19d4fb5121c39d5
+542c8b6ad4a627c4d7efc6687ff8074d
+542dd384f16a5a416f9b38e2d262adce
+543abc852feed5f00c78bc06a95cf543
+543b73f97cc9a1ba299fae06761fec2d
+5441ce1bef9399fb06e1af2e6e98c8a4
+544874826a38150b18bf554403a03357
+544b49682cbef132bf0c7368e70290af
+544b5e3383aeef3ca37b66eb42feeaea
+544dab34a60f5278fda8c81711d9224f
+5452ec712198559fe9bde995b713e26e
+5454d4616b6404c3275aa070e833f006
+545917713376c36f2ace6ca0c63c1c5e
+5459a21046acfb11ee7fedcc780dd535
+545a35273134f06cd9b8dab5676053d2
+5461950392606b482e02f40bd486ede4
+54635bb47c051529ae7d152ded2aa0e4
+54748318f231637a08e74c3de5997427
+54778753bfb8c8f43b88e5ff59d6527b
+54794b125d1e856b3ecf7f99df279245
+547dde4f95392dcb0a5347fc98b994ab
+5484cf4c2514f58775a89cca1b99e8a6
+5486f169bdf6b77f46295b7ac6e42be5
+5488e18fe1949cbb013d3c3a34190bbf
+548ace16315667caaa8b73495a6aa539
+548cd3fe9714b9ddab7475bdccb0e41c
+548fc9fdb7afb49fa28a031242d61b9d
+5491264b7508d8f931ef84bee81f6548
+5495c4ed5e887af948dfc44a61684524
+54983de79329d8666abc9f07518e7e18
+54a1d05ace9e002edaf47242f22202a4
+54a6f4b8fbb1b11a388bda7650cdebcb
+54ad1a4603e8f64cc35b16989622f584
+54b2c22268938d7dfcf18c7b78bd41d1
+54b5369f5a77917add593a665b9d0eba
+54b58fd9b62c5dd4f51c1d363c4536b2
+54b8b3b60c48f22cdd1ad63e73cb2962
+54bf301c7115734cfa42bc4a95cdc286
+54c1ea0ae506468148a67d2ee08f54de
+54c3d8c0028b4b2e82c3bec29a99e9fb
+54e48986eb745b8e7235ecdf535a9b5e
+54ea295267ffb15eaa8807b9258c8bf4
+54ea403c70c882d2fe9725e2b12dd22e
+54ecbb32f20b78e0d78aa0dbf401b0ad
+54ef3ef3b3cf31e027e8cc2fbf597391
+54f4a0048f4faac762b3e4f2df6c113e
+54f728ae8857f175d0a1171e81c2ef30
+54f76c570b84421c277e8ced16936792
+54fb1e60887b49e4f195d114b937493c
+54fc71d6f67a95be39bf65ce7734110e
+5500de1b1be9e36958400e1cffeaa7c7
+550545d9662a70e6dfbf3d645c95d8e9
+550bb34fbf8ea5dfd4ebadf10fc1e666
+550fd76ed53908f848c4b05d657fa3bf
+551409600861579fed6b245cb2a2de26
+5522d25714d30cf0a0ea7bfd1b878dbb
+5531ff38e2c64a06a65fa6a0e21ecc8d
+5533562773a81187d18118c00b635d63
+55385bfbfd41d60a9449047b46de8619
+553b80297605bef9dc80803fbd76e6c2
+5552d5b1aa1508fa03f8d554f711b7f2
+555436354ff6de6b00771c4ad6b5063b
+55589833e4eb2fb7df0ec44a16f33125
+555fb32a1654992fada46e1dbc2bf76f
+55669e3db0aa117a6a5a5e7c525d45f5
+55684c8932717d7bfe6047bbd8b7ebf9
+556aa2299955ec406478a0813a388e88
+556b41826ff1d56e432c798ec8abd7d4
+556c82c10c337cf6f09432f177f64dba
+5573762077ad9f4e29acad78158f8671
+55738694323e7c3996af9ac33b70ab40
+557b3feec8233209520d2ead467ab938
+557fb020cede10823aa29b8c01b67f3c
+55818fd2d2a57f1009a145846d614927
+55867393b81d9ba98867defe458291c6
+558ac6c5a0de70867a690a722f25539a
+558e41e1d103d6edf46176efc62b6ae6
+5592b01631f75138c5f18880493d5334
+55962e1c8432d1fd57baf041a499a011
+5597f0f7c635a735c9d54d53a272ee4e
+559b839f172f1af414e3b629ce9750c2
+559ccb30f0d466b5d731462c73709f99
+55a3bbd2e86f2ceaab95290d5581fc6e
+55a96734f6489b41bd27fec987e01b96
+55af3a91152163bd2c574d99a17888e7
+55b084848e36158ce383628a3409674f
+55b53bf6afd2ca1660302916a321b8e4
+55b56c53cef94d700f3a25f98cc45188
+55b69ca2e426fac5ebec7fac289f9536
+55b865da7e1c71aee1049a85f8c97a38
+55ba2818fa030db9d87c999b33b1c687
+55bc46f70cb4aa303c12196b128285a4
+55bd1fcc901a27674a747c26a802e525
+55c10db1f5acf720604da2e75c108232
+55c2f5a0ad4b2e6614c9e76739905af5
+55c574551a4770a5f9cffb40b4990a19
+55c790ba962945bece51d98977d221a4
+55c7be0889b68056e11d91cdcf83198e
+55cf8fe27a84f8e1ec91804c1af9181f
+55d0dc4a97d7e21963ec6a2ed2bc0ab9
+55d27b05bab7756c24a65f833e450459
+55d5c5523f3e6c47f09744a030c6803f
+55d6fa5162c51d2884e9fbdfcf367c27
+55d8fb534717d087fdfea0fe4468d586
+55da13977b34500d72b81fb43de25dbe
+55dd33bc7bd3729f22af3fe72ef4074b
+55e9c3ba5578404f43d5030cc3d10601
+55ed517a2b39f80a7b1ab398e1a8537d
+55efa97004f99776f51d66fbecd72a2d
+55f29408aa3485c0700f160a9ae01b12
+55f53d5a8e1fb0292911a9ffeb56a02f
+55f9565ed0b894023ffd7df5199ba8d4
+5602234f75430c79153572b53a9a2ab6
+5606d8dbbbc84a30854f4d97dfac9a8a
+560da424a58920d930e96af8b2197b68
+56120b114dbb8a52a234e1040b0761eb
+56207c5c25b34ef673ff37c94d4fa3a1
+562ab80b49f2b179001a3cb6454e8aba
+562aee029fa2f61f2414632cc426e136
+562f38b0aa672a4d876fe333ae87db6b
+56387576fc46b7ed5be52259328a0100
+563c9c1d4711aeec931e78bebc4f594a
+563dbd7a848a333f34d759e65b8a9b6f
+563ffe952443b244bd4d3c15b623a777
+5641c6582d688cb3fc61ca4cd2c7f4af
+56448db25ac077d75c32908a30edb4db
+5646fb25fa4540f2579785856fb0c93f
+5647e8746de4d9141a0774664441d63d
+564df56f7b38b93010ac2b3709490724
+5650607c838795bad6575a7d0bd141c6
+56566ef04debca10d2d4e59cb4b73219
+56588093a6b112cffe162f155bf33be2
+5658a8971697f599c78941d430631960
+5659e33538795b8c827ebcfbf2407f17
+565a67126581cbc0448b4944af586336
+565d7515188ae19d70879bfef3dfdb0a
+565dfd0ed13388abe93befae65ae5e5e
+56674a7749fbef97ec6473ad88dc65ad
+566ad1bf7aa36cbf7b0aa50e2a28ca4c
+566b597124e68a1a9b6ae4ce7f646669
+566d8b8a093b3186432f3fed0fd39a8e
+5671a78d913d43e638f73d1ab9e44323
+5673c38c5861f2e85e142f79ea37ada5
+5673f20f1e6934c8cbf3d67b4eaf432f
+567679222c8cc4415fe91ad70327636a
+5678b37c7fdae89c37a664bf46d62c3f
+567d33ce9642537b151550829e4b1c42
+567fa1b91d9edcb8892e8fc3f4d4463d
+568375788543615da622637a2a1b1580
+56876a828d7258c7c47b466d7fbde78d
+5690be8fb3e96498afa993bdb2900a8d
+56926c181985900917e993e63df85f6c
+569e280f3a8550196cf8587211fb9b99
+56a1ff2719cb7b61b0ffd22defbf79a6
+56a31f0ed48ae4627a9b9f6a26ed30c1
+56a43d71ba7f4fd64d52d64b76cfbd04
+56a6179b84671d7722ea81d23f28837a
+56a61afb9855d17985006868df7b37e1
+56a7fb7d08b6b826c9353608e40ae245
+56a8814c63cca0af0d7128be50da5aa0
+56ad4eb8db1e1cb7d44b083ad6b8be27
+56b4b4327aa8f6922a0242dd04f54316
+56b4e99c0bc92262dfc8ed85ede44b48
+56cb8b2efbbf32cba853821668e0f18c
+56cbf072da6e9436470f727dddc9dcb0
+56e41ee19e424365761b8f73652f06ce
+56e48005d6fe7420dbb86d1bbf1f27be
+56e767e74ea699523fc603454ee5b299
+56ee1c1ba186799af88c962deda3af7a
+56ef97f21a5be75ca0aba5e2c578988b
+56f06800d1b61f8095853234deaec9b1
+56f7103325c186b673c92d5faa3549b1
+56f93a4bbadd417026707ea23d200188
+56f960a4b583df33ca37401c5dd5378a
+56fc17b46b5f8aafe03f7a7fc1bf1129
+56fece784d09a1dca5410e01ba18a71e
+56ffa3d20aa1abc277f07fed624d2f0b
+57050c5d926347b7fc7fcf9b897926f9
+570a810adf599ca3c6bf3aeb09379773
+570da4aa96052e51c7c30af5161d556f
+5713f94dc6201bca0f552bd2c167001b
+571eda32c9d1fdee1fab4661f5a3e7ba
+57251900fd828915743b5dff6b15bf59
+5727b5ebb1ed3e37d77f77093887d43e
+572e7a1715475001818ad896337de268
+573039a2f258862bf4492a8534f0baf8
+5731818e1a4fca49032b71645789794b
+57327fea8be7df7c7ab34d73064f5d67
+5732927789a8dba77159395b53144ba1
+573417150cf9d6387117521194c194ae
+573f37de7614d2496405c51348d6043e
+5742cf7e0599edf7c90acaa7dae547ff
+574c35a2a1a3cb942bb7dacaf9cc389c
+5751dec506f7c66df4bdee121b04f7f8
+575efbed7c91bf5048ff1e05297a1b8c
+5764320a12fdcebf5070c858ed822291
+576567127044204335d34838e93ad057
+576e042a3a4689b337174401b21d391e
+5771be44211ebccdfcfc6f7d475fca43
+577584137361687ce48ea7f4eca664f3
+577a40648ecef340878b5b00840de4f7
+5786261fc75f6679ae09692aef84e379
+5799f8d09a4c06240166b386e9d834f2
+579aa7b58f67e217d0a4a21ffee0e7f2
+579d136f566ec7a523272f34cad084c7
+57a2202dfc4b13c94be6759ff57ae010
+57a8c4e564ea9abea20ba83a3f3910d1
+57abd0a7fc79bf6d69dd50c8f492a4c7
+57b928766c3e8e94d42ef9990d41387d
+57c5c78b56f8021533757742ed7cec0c
+57cc8439bec971e72232cd9875b4a74d
+57cde13b7f7f2e61c5b8e971dde206a7
+57d108ce1a2a57f8ec9cae69b4c1613b
+57d4ef3c65ee7ebcb90bb92b82e05824
+57dc84a0ae91273a5858373d965034a8
+57dd0296d054fa368c4d9f9e6c0abee9
+57e1c1cd7604f52a373b6ff9a6b90fa3
+57e2ffea352002a0a725d61fe8dd269f
+57e7921420eb3b6bcf286d711e3c1f90
+57eb7bda8d41ff0e4dd3d5a889e2a8c3
+57f7f8bf5b6e316dcef12db0e89a69b4
+57ffed54104a49025a5119ae117a3880
+5801ddc339cbc4ce4efe632a0e3616a7
+58026195f60bd6b6dfc25a110e12c763
+580ec6d054d939fbff8d51146836a980
+581113f47be6f299836b74ff7e2103d9
+58136259ab38d07477bc5e275f3967c0
+5814395da4e6846eeb7242e92c81e2da
+5815490aa8a9c5229779965037b457d2
+581ccda86a8daa0b9d47d9a7e6f5e47b
+581dae8f64e1b92fdf00c172c5957058
+581e58f8e250946f7ff0078734b217f6
+581ef0b82a594ffe5f99c6c392b1c9ee
+582096af1b06a06ad8180450c640d582
+582855fe2526e5cac250c3ee1ee7c7f0
+582a6679c87b34f0698c96454fbc5132
+582ac2d3796356c8bad13fb6763d0d15
+5830760d5d69ba23232ec1f3f83842f9
+5830cdfa1ef57800a0b72a3ffb5befd3
+58357c8c5da853a168c845628b27bb79
+583628e868a6b38988eb68538eeb7c89
+5836b45ec0cf29436c447a5758ee5460
+583a153d4a18bc39639f371ab4b5adef
+583a95e38f86acd8d708cfd1bf9d819e
+583bc8a53b0e9f6ef06b4b3c23bd4fb3
+58409833f3e84b482990d6def01bbc6f
+58418935eae8c34a00c2474a5a45ccab
+58418ba11b58b4b13b0514f915c72acc
+58446803d8c0f9b1ee0e22bb13c2046c
+584643528e061e2994c8027daa5201ab
+585b3c14938928236602483cbc796a74
+585c8071bbb44b779904b5e125eac8fd
+585d254dc25ac76b8593e5c1d3c7054f
+585ffe37cfc32a2b8172f5fee8032c63
+5872074c337950d5f9ea9c33b924ac86
+58729458318001084ce059280c3bcef3
+58755e06097797b557589e714c0f8b4a
+5875907102ffda7de155fff22ec028da
+587c81868a4dabddc0c99a616cb68abe
+58844acf169cf903507b23d3e7071489
+5888c1bf19a1a31ad8a3c9c1aa31fffc
+588eebc25f88211e7e0fad2f666e132b
+5890850a4bbadaaaf40929ce1db50455
+5891f1365c41ddad50fef34c08b8b179
+5893f9925c79fa288b385529b19869cd
+5894f4249ac1f783c23e8126fbd297a2
+58952e27d74572dcdb276c2e0c2c8eb6
+5895aeb8d81c2c84044a40ee1cee60e8
+58990140537f6647fa694ded29ae721d
+5899467d9879223cef91e5111f54cf61
+589acde08b57ae755a478b2ae69f531e
+589d4a52a0de00aa0c0447c3087554b5
+58a062ca863cfc51c3f2811aacefd609
+58ab3d3b5b0bbe4ba552b130dcba28c3
+58abd6bebd1b6032db37ba1c9270d703
+58ac457cff0f82b7648691cbde55516b
+58b224d16aeb4d733e98e874ffcd9f53
+58b687566b4e1a1a68029a3b90aaf019
+58b700513e1454a9ee475c9b6b284236
+58ba26bfcd34c285d982c9dd34e0bf2a
+58c069752503a00305078bc462375ed1
+58c1987afc8d5961e309c41c4f698c71
+58c616da2b621d77f3c1819494db68a9
+58c6367fa0fa718e8bcd6b0a782642d3
+58c8cedaa2cf007121aff2bf2188c56e
+58cdd0e3d79fbee37e3d85cf3e8dddd3
+58d2eb47eb8c504ae72dd7896892b5a8
+58d351fea425c25f308876099ba1effd
+58d666f7a65ac4be7ace7b821db42fea
+58db735337e0db4849d12e3ca7a9ae27
+58df5c51fecacf8bf30a8ebb3566029f
+58e837f8133b426e98c449b441e521b0
+58ec280e1c3127def18a187193dcdaaf
+58ece7f05b7931e940d3cf1ab6fedb97
+58ed844ec72a88f7806d01b152dbff5b
+58f47cf46cca8969561ce56d2846865d
+58f84cc070e6048ca8f5fe8512b4e111
+58fc37a06de2e1b0bc824577d32cf632
+59021d2479732f3fac0d82fd89898b6b
+590fa5fbf3075d14ce887d06ed81d4d0
+59114923497f110e1fcd9924258a784b
+59160e3c5515f1079e6166f12c43b20c
+5916c830f23cecd581ba4d7848f6ec12
+591870a48d9eac24580251f5fb94dcd1
+5919a83ab512548be873610a315602cf
+592a22eca0a9cc480316f400416a95c2
+592b32df1cebaf19ff39354b3efec6ae
+592ef1260d3105f60ffc633121674668
+592f5b84311a04a426d065e84b6a0aa1
+59338f1eadd06a4b0a67fdc4dcb17f99
+59357288c79595d99df3914cdc29fb55
+593f091df1ebf00ca288f3f266daa901
+593f1e088e6f23ab521fba0ec78645a3
+5940deb51ffecdc2ee6a54333c23460a
+594814a1fede9fa722dd0674099d3997
+594d2fa080cb24927b49029d3cbe8dac
+594d58092ccd5579627365c88091b62f
+594d7d9cfe6d835ae3bd0c670474179b
+59530c908967f0e63b6a95875e31ec13
+5955476d34fd9fb030e473ac47884545
+59610c1cdbbea7577e512ada7c16c64d
+596280812c3c8765952a7ea5c17e5608
+5963f6cacade1e2192b3f8c0779006c4
+596431a846b9127f689d11910f0b0c51
+596a244d85b83386a2462fbcc7ede21b
+596dd3778f753bb0187b871beb6cecd5
+5973910eef764dd80d57ab4f11f0c838
+5975fee5c7b38c81efb5d7f1facb50c9
+59782cce3f505d86eaa15c38f3809960
+597bec145c3f043444f1814ce238a697
+5980f757fa9a4653ff51fc47ec35b1e7
+5999359214e8b53898baff4ac51cde55
+599b76545d5f77f36d377f029b94db2e
+599f3e2b59db1fa9be48eeac55f0a6a5
+59a34de450e4fd6808d6a50eadc9f526
+59a40940d13f68d4334b3bcb12c7fe86
+59a9029323a37b693ab115a4ae43b34e
+59b290b97a89ffd7f9762c81e5e45d08
+59b6817b5374bc71d557ed9808b68c26
+59be63cd107c4d6b4b06a8cc24a5b653
+59bec1a62110af95570302bea2b00232
+59c23767a7501b08a14c6a85c1cc2468
+59c2a5534791865d4820ba165727d666
+59c81aaa939266c819d7209800fb9a9e
+59d1ac4682a26e7d6a425ac1f04e142e
+59d7d1bb29fc47cbdd1f9de9e9146f59
+59da134c65330c21fe2af8166e10cff7
+59dca481f815627a9d8cf328aea426e3
+59dea5c357fd04d3505cb4f0e49565c4
+59e36c0a55c2fab89dbec1f96a4bbe9a
+59e378193307ab19604473b541b26cb9
+59e5e6aa2bfd7865a49ca63a9356b033
+59e9b7d39828f59a093dbc7efa2c10d9
+59f3c58fed0be9dc40d4bd04f496c13a
+59f802dc7f2482f332cefb26bcdc7d27
+59f83463dfa9d898a2c164c4e6963a4c
+59fa6cc6f9da4cf2d590526819bf3272
+59fdf2349a99b3173e3051834efc563f
+5a05a8b2125998c37e37ed287e818850
+5a060ca0dd29f238249440018262b251
+5a08f6e598cb95569d0d2090457a228c
+5a0a0197a972489d4f78c3b8ddcdb16a
+5a0e44cbf83cdd11fc9e99bebd06cb35
+5a0eb624f6a6ffb5d4ac571252a5277d
+5a13446555aa1ec79d0c3ae55df809bf
+5a18e5413e34fe2d3ce64839069bd124
+5a1c6e65ea51073499f99fae5dac6bc3
+5a1ca65541dc947e90c713967fcf6c3e
+5a20fb3c73b658607ff8a85fa745f7c5
+5a232c1669e47ebc33afb3d882427256
+5a25919331e4fd58faa97fb40d41f9d8
+5a29366a0a53142daf43b51ace1eda47
+5a2e2e581c5e66750eabd5d536797a38
+5a2ef4f0c4e138ab3039429354bd885a
+5a313e0547519e3cbe0d478b792debb3
+5a31af8d040f4e9b530fa5f77b1ba5ad
+5a3c51243391e693176cdfd5f6476e40
+5a457e7bbb8538c6618958e837ded122
+5a489e79701bb32096c77bf3c45fe212
+5a48bc8e49ef8671075ad0dfe944a255
+5a49f8fedf56d66e5814685802fcd11d
+5a4d645772f83d7f012751e37fb288cd
+5a4ebb5dd95ec4958d06d0309eb9bba7
+5a5727d8be17fd7c820ee29e6adb70f6
+5a5d06ebefd75523a43d9bcc616d2f9d
+5a5ebc0f5890e7a8b9bbd8e17e90dc14
+5a65357dd2143c7e7e510ea154e71a84
+5a669ce92d95b27fead0fe13733182ca
+5a678e0e9740afe99b4d17c435f2395f
+5a6e2832cf3add44b8ed8f23966087e6
+5a77a57f441fe13ad1eefab995a007e2
+5a7bd9b4e432fc72a24392c3940dfae2
+5a81b6ef8a91a52fd0f9fc717e87bb7a
+5a841949c35a6f134c5d7a53495abc07
+5a8e20c5bc40481b4a3322c07559d0ed
+5a92852e261d11caeafbeff9a563c016
+5a97765382ac7411b74752f935edd585
+5a98c3c93c2b2abd8ecdd3d91880ddfe
+5a98c5b1b5446cfc2d1f94a6b606e382
+5a9e8cdb98194f8aa10e91cac1a5f788
+5aa0d3499b17a8a40075b9b8e0c17724
+5aacfc76e53c0c7e753bce2b83f0ce8f
+5aad85cac3ba5339a30c90b8fc1b2cad
+5aaf764c9f31025ed1b9d69d7ce33706
+5ab12db9e525075796e2bafeabad6160
+5ab6ae5d4cc60686800d81d9c10218f4
+5ab7e5f3d4f5cb7bb9f11cee62273d6c
+5abe6b62722811278763ab520f7dd688
+5ac1d02eafb967a15ecd72ca20a690c7
+5ac4bba37463f79651a376e8696dd361
+5ac51f84df9f3ead73d7264a698f78cd
+5ac58e0630205485766dea32239484d6
+5ac783979abcdaa843f9f84f6ccbe476
+5acacab80bb2985cc9a0a75fc53217e8
+5ad857f6874cea90cc2e562fb01fd9c0
+5adab1ce05350e145619b457a1efe50d
+5ae896524de3dffc76dec23ce6e51f74
+5af135f48232eb30e2d50cce60c56961
+5af1e5bc2091b8d85f9a30285a03ce1a
+5af2c1d2e351a0bb7ebd756621316b05
+5afae651e26969e7bb0c9ea77daa6181
+5b037b1c4e0ad2952246c2294093575f
+5b04f8f1b26aafdc2428beb0561e1ebe
+5b05c6f97918ec772ab8def1e1af6464
+5b0d615f1c251d9de9dd29416ee7eeeb
+5b0f13ca002837e82581ef103f6d679e
+5b138cf0f415f34c367e6ccd0518098a
+5b19b83e3e5bdbdf18cd3dc565577c7a
+5b22fffeeb2c1d8505bd904608413ff0
+5b3449b95d3f74564097002636524067
+5b36f6c34d05f8904ed34b13820bc705
+5b39fdb609218d9e51135bc651fe49e6
+5b3fc65ec6dd8c3ef1c9c89715de1726
+5b453945de590ec89acc7639e651d59a
+5b4d0185f2e9b5d2b0c88c52bcf903e4
+5b4f1a919c6598b2b2c31ee662707bef
+5b51865b217c41b02f7c9dca964a782f
+5b5802117445cc3855ac25d5ab51e243
+5b69af95c853a4a6081669f4076bb281
+5b736a7e66f433d7406d113fa0ff3fca
+5b78581dca647062bd70c44ca6e4c879
+5b78764169bada39057cfc028477703d
+5b81a31e336ce7e952f7439df9a305e5
+5b8d6a787647bf68893f6120aa2a250c
+5b9444091a4dc7bd1c94ca593b268893
+5b956080605d9f9120e51408e067d766
+5b985c508f9d48c6cbc2b4108d7c8df1
+5b98caca62c76006d88ae6e98fb83d09
+5b9b9ff424c95259ca18a6378432ab41
+5b9d2e53113c837627ef1ec5c495d166
+5b9d72051ecd0084dac2ac9e0629e090
+5ba0b9c44fc91b68c0f087b0e1f7cfe8
+5ba4d7dfa091a88cff1c9bdceb8faeba
+5ba4d86c328682643ded571de8efc1bd
+5baa411612be294e38df2ae108ecca27
+5bb3372c2221710589fbfa71ccf4042e
+5bb3bddc2bf41c57995d3c93c3113e15
+5bb5670957b605823aa474707472a4a4
+5bb5fec7bf7b879f63f94deed4c77d2a
+5bbcf37929dca9fd8066b0d0bfc428f1
+5bbd8a4927e273f56cf1b5e5e3fc2002
+5bbe4613e54405c50ee77d03a1622e40
+5bc602a31825c0cca6f60a51987e1d5a
+5bc8001ba1751258ae1ac2ae5e381ff2
+5bc81824a65ed1ea9d1f4452cc3b97be
+5bcce7448e4d755bc0de8bf8cd17f76f
+5bcda44521ea68bc0dfefcb2639cfc64
+5bcf5c59fc1218fff63159a80058ebc6
+5bda53827ae0b5ad827a48595789d60b
+5be39f303bcf5b19babd063905cbc612
+5be782808a884f86563207b3318b34c2
+5be806e563a02ac26712fd4cc71c44e0
+5bea6b437eca6dfce4f92129ce1ce462
+5bee6c53ee159292ba415db8082b09de
+5befc47ee7c45446823a7b3bf7b18133
+5bf33bd5e5cc6e701b592eae65b5ce4f
+5bf432e5e36e1618a35357b8fd090ce3
+5bfb21a1fadc4164e75ba6b55c7c0e6f
+5bfc99ff3124c4bec3c55a6d4bc7c8cb
+5bfe7045ba325993590663f6ed166f22
+5bffefa1af06d89b52e6fe127c711600
+5c02e329b26758c011b839494c1abef6
+5c09133bf45c852a1aacca73b7329d05
+5c09a6f67271275ebe0df6d1962bbead
+5c0acb8a6cd92af6ed5b4d6d6e04f498
+5c0c50336ba6061140dd253b74e6dba1
+5c159b3401b013f2fc119f470e08ed2f
+5c1b61fc81a6c1d8b18090bada20af25
+5c1f81b333c99f52f8427d081d6b609e
+5c20bb6e12bf19b2adfad70a818c3ea4
+5c215c9bde42dcf129ccbad0493c5689
+5c23005a0db463b4ab216e1d18e36694
+5c2889683603c384791f62002191dcab
+5c337e0cd5661a410f87ab6c9bfc8f2e
+5c42b40b302dea1f942c455d3fdb3b84
+5c4bcece70de961bdd5df2353b2ab3ad
+5c4c234a35138f857c115da817c2d387
+5c4e21a37569a48d343e7f9363705b74
+5c4e2f0455f2d20334f5fdbd4a0b3305
+5c527140edea52a1b93a198186283d37
+5c57baadd4e4a25f0d2dca6a87c50972
+5c5e74bbfb0543aa3f6e58a312c4f0fb
+5c60b03f5ed9ab7f559e84226082ffcd
+5c63b144a03d8ee19c69b4f1febe3de4
+5c665ce26c7312ff5e0f36b244bf0b5a
+5c698fb72c910996006df40b508e7d29
+5c6e89d52e01ad93f7769d717f2cb818
+5c710387304d6f7dc5c0f295f9e345c3
+5c77cec33e2d1e5e2e9fb390210a4dbf
+5c8135aaeeaf406874e4cc6c5932cffc
+5c8290333ca51a5ec230bc5966d3e152
+5c84910c62ec024c62117bbf079d20e4
+5c85688e2e00aeb70038f24a3f142f61
+5c85d1b4d869a923a36deae91626580a
+5c8d83ffa50c5b31dea96b7f96f70162
+5c8d8c18ec8e1ee1c47a8aef673be05e
+5c922b29aac89168838d740d1fa30fcd
+5c964ab3727365e1a1803023753a4a75
+5c99402c57a1cd11e19cce64dc8d4951
+5c9e314a5c8f2c49af252827f15c6187
+5ca03248c8cac7b08f5fecccd5bd1aea
+5ca2892ea4ae82af8600b5b91add9a2c
+5ca39f3cd509d22ea05b1d4253799b4f
+5cb15b4a6a2718c90e8513bf4cb6023c
+5cb91518ad37652017437b5a689626ed
+5cc027608e203ef90d9d45e4596ac569
+5cc3bf3867aac4b8a105857c941d2d42
+5cc51ff3407569a994b7aa994feabc07
+5cc5f602175c872b771466d08e3dd5d9
+5ccb2944bc825c0fdc894d2aaf40288d
+5cd0317cecc259d939ab282e7d8043c7
+5cd15830de7d5c2ede110e7156de892c
+5cd2ba155200f3146c14f73f1461e904
+5cd2f9a6e3b4845c9ea315d0b9ac74db
+5cd405b3f7a12e211aab69977239cea9
+5cd6f2391ce1ea08c19fc0a85ce501f0
+5cd7a35b147b7144fdfcdddea734fe64
+5cd9d9b4413b3bff707040e719e76288
+5cdbbc4adf724f7e3025d594a44b2c86
+5ce00be4be11c656cab911a24a8d8115
+5ce421e7d7e76eafb8e322d3c5825834
+5ce5d006651944ce1a70e15375c6e287
+5ceaf7e749787cccb9c58857ce4f9927
+5ceb2bfd215c45d2f3d7ba4c85edcfb8
+5cf1af512ce8067bdf248dffb0d33048
+5cf2aac5f0403da4b19f5f9be9721a6e
+5cf2f99d6dc8f7ab6d08f0e62657cc82
+5cf348a63d8e712c4fb962cd05efa4af
+5cf4ccfad5bfe255f4af4735e3cbb452
+5cf5b32f9e11340e26c7a1b09aaad95f
+5cf84f6ba61afb3890dd79b521265d75
+5cfb5642b8b9e455f458dd6676fa2ae5
+5cfd807b2f5f8e45c80869e2ca85374b
+5d00d1faa8ee907bf533b40028910b88
+5d0ed9294da1be8be5c92f8f117fcad8
+5d0edafd728467f5da441f865fe031bd
+5d13af3bb3d10adb4b4bcd7e0ba2b076
+5d24c14930bc8bbb2970784225e84668
+5d26b88a6666d7eacb85ae3e391e4c09
+5d2976dc1cc5651d8b20e2e8c766d624
+5d2f90e199e040277d36586f44ab99df
+5d319e19fc5055853721e15e7c6f683b
+5d3385811c46b6d6cf9a4ccced1dccb5
+5d356a794029b611aa15afa259cf5337
+5d39aa75dd8aaea70bb59f7c506e5a53
+5d3e1caeff0e85b88a4bf99b8b75c180
+5d415c585a4ecd0e9a949b807eea4c54
+5d495a76bb15de81695df2e5aa578626
+5d496f15a3888a71a33f0624d3b13501
+5d4e7fe2d82198354e408704e7533d28
+5d52da859594f095d1a3b7e574d41e55
+5d635e7bba585a1444f433eb85bfdccc
+5d6673e25f6a64ffb8fc114149d6b05e
+5d6938e98e583a50a0e17fd384cc8808
+5d6a76cbc1968e24c7ab9074e3cef4d7
+5d6e43655861042d3aa4b3ebf9e8e3fd
+5d771bc95d58f22f2513a096b13820f3
+5d7d51883011cbfde65292b0730263aa
+5d868443185d876c2645352ea152c389
+5d8a7e3a69681d8c4ade22f284a01032
+5d91bc5a51a724c5e73936471dbb2bd6
+5d91c5fb1024d876d4f210f80c0b82b5
+5d9a991afa12f19c92acce953e40165e
+5d9d26b2c6cb7aa4fed549a470aad2c4
+5da244d6e8458aeb2ec548a8ed3edaf8
+5da45672ec37e58c10a4640c548c94d7
+5da7df36d30cbc85e84313ea1f1313db
+5da9d421c0b5ae5ed35deb3de10ed330
+5dae60e9e7188817574d3f8bea9d9daf
+5daec945e83f8d8aa62429eb09b7a9d6
+5db475c730f7987e1df3f116fb70b69e
+5db708ec090788200424571feee7a35e
+5dc2105207dce30b39f309480f197ab5
+5dd0cce4820023fe487f4129373f3740
+5de3abb093eaff5e6c807c85bac7d0c6
+5de8efee2d9636bf29aa630dbca9b9a2
+5df297c85d7753c4dcb2d0a408d55772
+5df2a7e0e1877f13f22ea2eec880f8ff
+5df8b59a7b2eb6e9f835b6bb454d30ec
+5dfd04639bf91236b4528a41ccf06d9b
+5dfd7efbdd345098be2571c60ea45166
+5dffe29e384117c689c2b89e62cb4923
+5e04696794cfe56a6fbc3e4e8834715b
+5e04ff74d2c60e13717e14dedfc4e3d6
+5e086af6bef5a437cf72ac423a4c457c
+5e0a7bae859838952c1878bb940b5cbf
+5e0c79a54cd258e9dde3d5663a3b0351
+5e0d70ebce5eacd2d19c75f428aa037e
+5e0d82135d89d5216e7bd13ebc763902
+5e11cb88810a2a595fb199fe51f99375
+5e184bf3a25e86e80d7f45b5e38f7aa5
+5e1a083b0db1d9fe0647aed0e7c85d42
+5e1ebd340c9698efcc8e5e0467a236d7
+5e206267810e3f0c952463fd42edc9ab
+5e252b1206716ea0fa0eca40da6fe507
+5e278216502d5828f323c7e3689e9980
+5e29c7f6e408858d46c9c01c8ea45e44
+5e2a3c9fd3cdef2a56f8a1075b4775ff
+5e31ab98969a09f5cf838b1c5553b2ba
+5e334cfc14800cbe82717cafb66c3227
+5e378551cb037ba53b6db6cc8925e8ce
+5e37c72552eb748993b1cef6c52272bd
+5e3cd6424220f8885447730b2e572531
+5e425bada922ce53ce3de8115010c9c4
+5e431674043943bc2dc5c91485864790
+5e4398445684ed9c83e45d7b9b0f6621
+5e48fd69c7d9b8ce0e4b9efacc691583
+5e519b71bac98302ebaf58884318ac10
+5e56b3e74d3fb72e6feca19ffbc800f0
+5e59022f6fe36b2b8f93b130cd033832
+5e6533d3e1b898332651b1c70c2c768c
+5e6cdba295e166163f38c200b37f4ea8
+5e71ce876029126b2fc06b9f132ef648
+5e74b92d4f91e9ed1b3fee592cb22635
+5e74f67fc8121653b6a6a801b86a49f8
+5e79a7eab3b2c2c654b0e420d1348991
+5e7ea177fcfbbfc4d4647f2ec854a4b2
+5e816789e6db69adab4e516321a718ed
+5e8353e468b45cf08971060d9660b373
+5e849e09221670f35f75c4d34cc145a9
+5e86070a356d2ed4a036a28bdca8e059
+5e86b84765be8eba76e7620b1e40e931
+5e8d7345618f025a78644962b0ec28bd
+5e945f28c037d057178e3c40b0b08fcd
+5e94b2d405497f422b7aef152e0d31ec
+5e976d98a1bf3bc86920c51e09b4a3de
+5e9872159973f42c3c6ca127f910483e
+5ea4aab0882ee4864ec36eb4e07f2606
+5ea53e34eb494c2fc9cebf3c73b2ee7b
+5ead7b232f74da6a12fd686a6c0c807a
+5eb0f8af6eee1b2a42cf4b75823c026c
+5eb2ff20daba1d57135c87f6afe7763c
+5eb873e4994dfd64f9b83abd363f3ee8
+5ebcd9e3a56283d09f42b6e416001644
+5ebf68e418f4ea2171110db0561128b5
+5ec843d67ba52f02d22281c4b7d7d311
+5ec844669aa160cedd91448ca546566c
+5ed03a5c35868ec35ef79c908b731f25
+5ed12f1ab1bce46d4b1fe2c5606df155
+5ed620a92aefc63fbe66a7e6b7900b46
+5ed8b85057924bd2a6385e3ceb8d37b8
+5edccf52eca8033b46808e7fb898b7d9
+5ee8fee9ecd07b3dc5de009b366bbdae
+5eec36b769b94d5695d50a6a4665b50c
+5ef5f0355f37507dd8a9f7edebf7f59f
+5ef97aa69c593aaa051c8418b7c54105
+5efb1e516bbfdb665868fc53a3af0de7
+5efb72b067d51bbbb6dc8daf3027271c
+5efc19eee8e088301efadac44b1c32d9
+5efed65a95588a524432f4e80f57ac09
+5eff5f75ba4bda60838d557516b99a12
+5f047f82d12f21ff793e7214932324a6
+5f0954d4e13c571366860df1fc43eea4
+5f0ca1dddcaad709e845cef806801504
+5f0d896da4e13bdf192bd237b7c9524f
+5f0d96744cf75c55fdafbe63c8e61289
+5f109d7f4a883f0b7a69486860f394a5
+5f140a216136ce86e7a559515be7c08d
+5f2684044900c9ce7f435b959b975612
+5f2b6110db206466481b700c0038aacb
+5f2e29815ff49057bbc0d4bb392efae3
+5f35a63c8f18d5f5b1061bbbef18485e
+5f3e12db7ba8654b840c8ec0295fece9
+5f3f0835e92f75bcd53218007f6c0db3
+5f4fc57ec40df0bdbd4aae8fad7195dd
+5f53892f891cb0f1c1d07fb49e2a94b2
+5f556a7336caeca71507f36504848672
+5f6d16e5030324c1da9ce229d13f5e5e
+5f737ebc74fa734d47f9e35ca6e5fc91
+5f737f8dad0e87cd6cb449ce8ce4c885
+5f7769f7e9ea853ed9f3b5dd4e174435
+5f79e08ee586574fd425cb9678c751f9
+5f7a2438c9467597971f9086e8cabc25
+5f7c66385d9513a6f17aa2c8b05771f5
+5f86313b2e10abc3980545b82c68bff8
+5f9037ddeb6b4fbef938218fcc2e86f9
+5f9577b1bd42108ea5bf03d0ab8d2457
+5f96c751eb0c2cbbc14dc879a5ebe620
+5f973891b458c733238ae45f6396ff30
+5f9e2b1870dc9d0704573955ccc740f8
+5fa0ab184558b0c6b7b700bc0bc012f0
+5fa35219c40fa4b9577b5624d02782e3
+5fa8e41a64c6c964017557bcb3debb39
+5fae322992aaddf35b0d26e21404c0f9
+5fafd3381f3642a4fd4d49b8803ae996
+5fb9a6bfc156bb4877990af0d18db54c
+5fc54f761f1d3e2b418955c88db80e24
+5fcce5c19d2e8d1ec5796a8a77f4e431
+5fd115ae834b2471aef74f2cca747601
+5fd280e5001e721864c0f567c6be6e78
+5fd7a10d8b3ad7b7be2122302e738aff
+5fddcd5880f49b725d1fee3eeb08c0f2
+5fea1a958fc0400dc7d4b297b345d93d
+5ff21c36a612a3f82c52d0c118ea63dd
+5ff31072b83e8eea6ffa8b5184583b92
+5ff94fb90ade2960aa8c30a9ff3ccccf
+5ffbdec60ed3fa3464b5ae154bc47b8e
+60001b1061e883c274f67980fb8a200f
+600a088394fa930a7cef8fdf6d1fd7fe
+600b45417865104be16c6b7bbeef67d7
+601011e3fd5b31fe099fa77f132b779c
+6017f623128671b434bfa2c84b37dce9
+6018c71d46c79829f48d460d025edb4b
+60251dfc78cc5b2eefcd9aeb4297d109
+603013fd36ce956035126258572045c5
+60354c6cca5927265d3ee190b929da77
+603e4c40af54b1173493a8412a6c14b0
+604158a692d4965e9453780b92dc79ab
+6047cfc909925ca2d88b999a306a8595
+604a914ad26cd42f77b6cb50578bf99c
+6052cfb72ba49e91debfb407ea53c6f3
+605bd214e925d5f6923e7efc38aa25f3
+606e49a610436fff15e413fcd7a373b9
+60778a63a17fb5285a435073e8df40d7
+6079222a92da45819815ba71afc945ec
+607a2d09d75b34b9278594c9649c4340
+6081bf5a7c2f3395ff20af01fe9cfb99
+60832b9cf45b9f60e543feaa0ee663e3
+6089c33226633c9358e8b142d5387d30
+6094bcaec290653a83a4d70befa28304
+609b1a670973242802c2a1fc2bf24410
+609d9bf9d3b8431b12e8d1b165c0b300
+609f2f18e81807e1f02289cda81a2865
+60a00838c1e28fb726c14c593314814a
+60a329b9eed91a5b8ac14d03c73ba674
+60a91542747760d4fa8ea28238d5e4d9
+60a9a978ee5d1be19b7ee106790ea09e
+60a9b787fb248ba1d0f3dcb573eec325
+60aa1a11cee4288f071092c43c8068a9
+60af626e389aa3fa7a2aad20ccfa918d
+60b09a21bb210391d7f56a9c1b95b370
+60b7620d9526d967b62dd166111c04da
+60bb8691c19ce98401997c0f8c55ca37
+60be80263062d433b9f2966b40e5d634
+60cd01d839f76493fe805cbd08fa8699
+60cd1a58db261d5747871a2401f47999
+60d0e93a1577100b4d18882fa296bcf0
+60d37aadca821331d76c7b5c2682b3c1
+60d60cbe705db060c63e344a63d65413
+60d89860effc0d9d584c21446480c6e3
+60d9012f8c219237973102647d1b59ad
+60dcee357d820a71732e3a04b21444ed
+60de2bf77d24d11468163772305339cb
+60ef3180b60f8f975807b506a061c6b9
+60f1019dba8940a284749845a10940c9
+60f385958e08bab5956b5b51debb66d9
+60f657a4feec53e1d1337fccfc098b4e
+60f817faa5394abc022223676cd69572
+60f953c8e503390fba20ca483bddedb4
+60fcc1e3ceccbf559d6e7ff3d71b502b
+60fd8ba3607117647d84be16b311feb3
+61087474c5bf1a7262c8e2ec0b6c83d2
+610e0cf098d04b52220d3da3a17b0ed9
+610eb86c6194e0ac01c085e7091108e9
+61112beac342957895d1d5468cc949bb
+611283d79722ab8d8c4b9d7854428a9f
+6115cdfb1f925d7e6d07e0d83654d55d
+611de7232fe220eea186a3480f0fd59c
+6126db0a3d41685083266d8b69b27abd
+6127353cdb7929b354f18b8ce75ce245
+61283f3d2c6425e80cb1abb8db76a8e5
+612e5a80989dc7560bd5faf9b2f4bfbd
+61325acf66e072e3b875c5120b7b82de
+613464c69c66ccbfdbaa7e4a21cb16df
+613f8c7caaa6c2e1834add719e00783e
+61458a00027930173690bd831144a152
+6152d2443ab4e00b716345dbcbceaf84
+61547ae4990d98384b2b0d12f51d31cf
+6163c1abbb26896255286b3d5c5d67c5
+6165b5e1edd56021aa8628aec9b58eb3
+61735550991373f20a9f6b431deb3437
+61736c9445d6b01f11ec3f84dfae96b0
+617a79c7ead107b0cd9a40966f47e1f7
+617be0fafb7abee054611aac170640e9
+617d610a3b923dab9c34c114af627941
+61853e0dea1db36b3306960e86d1bb63
+618a932b2c0baa9e9f4b3cab76dd26ee
+618e35685d2f791a00a50b1d5d6acd49
+618ff6e760b78ba6fa09f80a911daa09
+6193bbbc14a6d5a4a87c606394548f3d
+6195299d433d2d7b7e4526b2684f4253
+61964403a082163896cea6cb50ba0893
+6199d5f062545eb44ab81b0c07dc0e48
+619c4159d21f34bf69cf90648ce774ad
+61a406ee4b79b035ed3edeed7f533a07
+61a6437b2aadcc5256bdbad9626b9f1c
+61acb9bab5991a2656310bdd772adb2b
+61aead9e5d20a9d431a47df92da08194
+61b5bfda396b42fc77b2d055425bbb5b
+61baba42ccd9597e67c71978d9391eb0
+61bb023df8dd5087ccbb1ef332e74e6f
+61bc6b0ffd70577c89f13611aeb2dab5
+61be8a5a02582aa1df311bf2c18adcd0
+61c060a7b435f5b313b0bf151bf83fb4
+61cbf006ec5baa41eaf7a9e57e0065ea
+61cc9b5a249c582e4c3122fe7214ed2e
+61cea9a1bd4c0fc9895677e7d284341f
+61ced7a8164ea9cf16ff2b54deef3621
+61d2b7cbcd9b69a462c4352439adf03e
+61d2eb311736481473c88621c58f0d11
+61d58f031364bb4355a9e881b6381a54
+61d843e673ea5fc5c8e1878abf6e7d06
+61dc95c6833d4a86e0e6236c1434a460
+61dff17bf11ebc0087afea293609a10c
+61e39d2a5d28fc2bef003d26b73c6a1a
+61e53134b982c19f4758f4ea4a25d47d
+61ed3c07ce1dc11404b7ba241fe6f30a
+61f4681707f33c7c73c85a9e0fa9becf
+6200968dd17e6a6b43accb378b055e39
+620c34d90d8d46582e835f70df88db99
+6210c681468317d3d1fcddd098a0d812
+62117f7fc5a1f3350b2476de6c00cf39
+62127f24a9b9811e4c1e6c9d4afb8655
+621284b8191669248df7d9d52baf75b4
+6216f84f74e843dec67d3a210f49dbeb
+62182ea4c491117eebf10d4a2ba55b99
+621cd7041fa5a2eb476f60509c65ee36
+6224de483572f59a37d3d0ec48415f3d
+622ff1d78b93bef482622eaed87e08eb
+6230f1eaf664545480b0983372e283dd
+62312e4e7442e4f488595f718d231553
+62376c9ce959b42a512680a502045dcd
+623b903841786fd9fd992c2471ed04cb
+6241e5dab66360578daa3da18902fbf5
+624365af6f40e646762e81d1c6dddee1
+624483c3b39a66fe023c7d60993f7b00
+624d657aeee9474abe010245d332591a
+6256691603bea509a6274dd99f521197
+62572164e55569a3b207529c3aed1770
+625e2f820e367f84fe22d7f8e1a96482
+626023d31030d2d4ea784679977fd1e2
+626b2d894d0930e649815820c74f82e9
+626c3d600313bbdb4930e9f94da7965b
+626cf7a05c102bd0c8f75bd7a6e6778c
+626d5684b2cca5b86f58d33e3fdb9dd1
+6279c6a9560f0cf88154e021e59e9f57
+627efaa6496e8238e0714b3161d8b5d1
+6280a64c1fab3e69f0fe37a7aeefaa88
+628212f6800bc3c2d339babf38b06180
+628308e43ca25235cc8cdebd79a4e303
+62883d482f62f6c32e17ba2f8dd505e7
+628ee8cc820ab73df9635fc46dffc247
+6293b20adb50d18f51076c0408a5cb33
+629aefbd846e3d660e7185bb96811a01
+629d8c45a19df46617bea7f44eecc141
+62a7c4391e5da8c9589a90c63d439a6b
+62a81f5ea56f352248023f24ecd57efd
+62ae5276dccf8eb0a1427c1677b03e38
+62aef5f2fa7300fbc9c8fe3b222ad20e
+62af4d1c0d0742bf4ad1b4fe864329bd
+62affcb77b09b6abc8f5ac8e7fbb9ffb
+62b142ac3fda19210fc88d6c48da06ac
+62b9b63af083c574d566022687ca1269
+62ba1e72ec6c141fd1d714f47cb29466
+62c0561aabf989db4df6ed023011f312
+62c107f3d19915a3debfbb1d487ecd36
+62c74b97a44b3743b5724ea5c9b1065a
+62c8189c8668a8aa9b7147b381646ebb
+62cd12b33de6a095171fbdcf5be32521
+62cdcc4f89199bcb2e2f5e93e973c805
+62cf52ea6b0eae23bf26d29f555aeac7
+62d58c1235151c27ea499d9b731fd907
+62e197a86f8a7819a713537cc06b53ec
+62e7e9f53460133a170f3bbba0546ade
+62e8e879240e9f1766313df442a27630
+62f277f8dfe84d29041187edde69aba3
+62f689a29bef6fe8acbf07e163da1535
+62f7c267b7f0d5dee96813c1d46e3f84
+62fb76c9a9fe17060797805fd8a9c338
+62fcb9cae74bb89f0042ab75ca9d679c
+62febbb14e2589dab413c75dcc9887c4
+630072d5038b40c9aa87a5218762ab91
+63060325143d33d2d019e4491c92a7bb
+6308f83cb5204d41980c922f2b2f5469
+630da14030bf0434a480762155380900
+630e629ae99ca0f1099fb1cb379286df
+6311522bf92fb666757c17d4a039e9df
+6311e3f10285201f9208c1a5513829fe
+6317f48f55038bc84f32b0edabd37968
+631b84b88291584ebb7e718dafc5d103
+6320e43a8fe7cb90317c133f60816f1a
+6322eeddc6aca19abed546ac3d09de52
+63239c013345b6915c5493e0eca08ec0
+632a86f1b2c16fbb4365e0faf28b946e
+632c39c33e551f4d71f696b6739a7bbe
+632c3d480855e07615f7ba490a342765
+632de849b6243a525a2f04f48deba75d
+6334c5488f2d3a72ce85302522651af2
+6337ee76caea26f655374c1dbd68ccf7
+633a62b9d270ad92f1b2971a7206f2b4
+633daff3369f4a2a487d0d15f69483fb
+6341c3b0a9febc53de05e0bb420f62f3
+6342437eb7d26c249fea7122f5b4a76d
+6343c8e3cd472fa3f21a98b18e187f32
+6357523aee3af7bf91b6974a3357bc97
+635aa4d771991bf7b90229b08535126e
+635cae325d49679e13aa5d2d011c7e9a
+6363fef4a6e36ce75c9fb01d536348cd
+6367cfa7eacfd7134ec256eb07c23080
+63689c477f25ddda11ab457850c442d0
+636a19d912fa7361c99795baad27348e
+636a2b3f4e4486099921f4c5ed174b79
+636c80bed4a106dc80f82457a0cc98fc
+63754385ce7e4f4d52fb14f667bd0f3b
+6375b6d0a65f6ae2df972c3903184989
+6375d25cda81268ee43388878efed8ff
+6377e4a491e9ce749f71ef1f3445481e
+63783a4c27f3421b0f2b94a6105e3e7c
+6378560610c8784dbb3349c59e1db800
+637a41add14d531f1c550f15db3582e8
+637a997966f3884185e309cfdadaa6e3
+637e0a03c3445ae376d0963110f4b923
+6381e551008a356862cb8003e83794f6
+6384294a7883ac653dc374838294dfa6
+638d69b5bc6ee7b15421d1aa92e09894
+638f7381ddfd1280970f094c1aba5d51
+6397d6f3f0a07387459b51a9e03ccc7d
+639a42e8cf3d7661ca9c45813a8855dd
+639b4911ce792ea0ab9be6893845b762
+639e43cedb3b4b380df9c9181480e963
+639fff585af16beda029304a85fc0573
+63a7339478f9a70a1e316993f2ad1e05
+63a8610c1e3e636e27ae2ef350d151e2
+63aaee0edb53bc3842636dccf7516df2
+63ae0c76069444ca1d935444050eeb3f
+63b64d0ea30819264a09e4f1e32d015a
+63bad3e4b31d2d8201117e5c62f13b0a
+63bbba2a727a9ad8d463a8b8297b943d
+63c243ca80a9da8e748a0d2ea2c5a78b
+63c4d323a03bb8da99d0ff4cacda7d4c
+63c8aba952a723330eb1626674b8c3ed
+63ccebe769c893d6f94b9d435fc30b74
+63d5188e34cef30ee68389134c57142c
+63d899120e2a885670f7b02e2e7c05bf
+63e9743ee6f0c6211c4afa09ccce102e
+63ea0b07d1e18c6436d95a6309070263
+63f59c03c3a0450b68b900c088ab057a
+63fbed8f5e8dcb8b4e8c5ec3df67619f
+64011746a0129ade3ea7202eb3e30f5e
+640302787e5f55bf5e00e1a491319a42
+64037ee4b32a99d515afe31b8becc04e
+6405029263b8053f492569cc9bfe116d
+640b9e714377d1c520cfb7c6a647cc6a
+640c147ceb298a1529e0293742f250e9
+640ee5b4c56ebd3f4db4c40df0c484e4
+6410a6c5640559ac95ad09e6d8755289
+6413d57471d7c5208f1a4ba53c5e6026
+6418c5a8f34b53efebaf222c8615485d
+641d12d1fc3ec72139e421ea1990980d
+64200ec16f14c00bcc650f092f95a9be
+642098cc0c5516d8161a0b31f5b8809c
+64241e025668118df9e704a5dcb22c78
+6427b754d93204a45dbb95f0f0eea6df
+642b300b1aaa5dfdd7772a1cb78ea40f
+642cec039ce4eab4158b87580259ce5e
+643ac78e602c105e4ca453a08f36520b
+643c121a066e361facab00134653103b
+643cc924cf21e746c90aae3645402814
+643e2fe68ca020bc0fb7abe5781ea771
+643e3c4590206e455310c5688a1d245a
+643ffe77569245654f293fec82577f91
+64416e9b6a62fa2d58e04c81bf1e870f
+6441eaf5c46ecaccaef98350ee1e1bdb
+6446da7e20c8f6f1c828f7c0b0d6813e
+6448a059fc8117ad8d4eb9e79b533d00
+644a51d6c8bdee0ebea63e077741e2ac
+64519dfac522d08720cfbf666ad89285
+64595feb98c2e435db9c16086346cfee
+645e5b11649760de461706d41de72bf6
+646041ef861e4f0a13eede603f1b8777
+6468a1f13ef9809907e6225f92c70be2
+646a54e3771759e4dfca94912ab2dec8
+646ae148cab6b7f102e83d0bd8f6c122
+6471f318e18c9ccd918c0f0ce681f597
+64724d9404560ea4bf8b890e9370abab
+6475fd7b45f59633cda95867157ed9b6
+647d5119a5af51382060a0976fab45ed
+6482eb800b727e773f09ca5ae5da10e1
+64845e0dec8ab59e35fb42999ed7c561
+64917da89289b39342a033072cd0a84d
+64954ab5709e6874b016011fa17c98ea
+6495710e496dc63a70f4e08e2ca655cb
+6497e1d8ec365846fe4eb8f7e71df28e
+649f9623bffa2613ea9aedb5ce48fe30
+64a2087a31b712a293c721261990ce14
+64a506019b537078db5b918f007897e0
+64a5d6af398763080f539f344974a16c
+64aa24359a6df3a85be234baf7ce2ed5
+64adac12deee5e538014a84474ba4d0c
+64b02ab41c31c9c06da8049c59b749db
+64b04c1b70996491673626f918cdcb36
+64b6bc39324267518016803e1f1acbcc
+64b71215b860f52f08a6665d5e43842e
+64b7247ac0060206bf40ccf1f059d3a6
+64b8ffffafdeba587514135a9986f8c7
+64bfc2420d9eb925a4a6927391717fb9
+64c60e99ad9a082d267b1d08f21eb4f7
+64c695a9fff9aa8d101871b0256ec339
+64c8c4a69c1524644c8ad363fc6bcf81
+64cb2f254626ec3a9f1308ae98f17719
+64cedfb72a72da333856831ae6e410cd
+64d3506918f58db727ddea8c095c73cf
+64d744809ac084ebcbf60ce80608c090
+64e5354d2ddfc4479786cfeaf74f43d1
+64e66fadf0d2664b74b0d7e4cfddbe06
+64f0a393a9fac5e63791de4a445c5600
+64f5dc442dcd886a00a91cd464bcd637
+650186189ff0630080f0617fbf200736
+6501ff62498261f65d58891371c600b3
+6502781f3107899bc94d9bee82dc4423
+6508090cb65da9904a526be8095e2cf3
+65091b5b727a9e84fdde479fcc218e7d
+65148de6f02eb3534bab5342f39df111
+6515e764955423aa336d1edbd08d2d94
+65171339cd67df9ba4adda44d9ae123f
+651d0bc173084b33701060b4524e3e9b
+651ece6fc6a3f377b222ef1185c249ff
+65292764e98e4bef877f85a42c50e0c5
+652dd0b146156258bad5fa1e851e3101
+65381acd37fbd31ec7332d91c24b9e62
+6538af985e7b29789a76b974e0b904fc
+65448a50c4bd088e94e7b5e0b9f3fcd5
+654784806bc822ebf7b5eb176ea9f563
+65481673794949305aa723c0a4837301
+654a2ac52406b6130d1bf5fb0cc30568
+654abf702e82972ff28d21c261c577be
+654ef7f4b1152607473ae359e8a1b0a5
+654f88c5e09e407f55d7ca7ecfee55a1
+655174636296643d5c5aac71de1667e9
+6552620c4a43006d58462ce7ae797534
+655c50ddbf41474ea1d3e3063d1bdf1e
+655f5135f9fc413464730d6c0faee7d1
+656087bfbe999dad408e45ed938b9e1c
+6570a8836ca4117872776d60306d8083
+65749271465ec3d9187b35c2226c501b
+6574e47541b54e3ae908ef4613d5cd34
+65779bc8a253b01c0924f0640e8417e7
+657842e763bfd6802fce5fc855e93c58
+657a14b7aefb2b5b30f07f70d40afcfc
+657b38f223f56232e57d077572162c4d
+657c020512a17bd7c66561514cc98923
+657e7443d9bef6e3f931232e4ee5f2ee
+65832128ba4b6e8dd00240fc473c374d
+65897be195eb7fe9d6bf1eb3ede1bd0a
+658aab5f4949eb44ac349286008c45d7
+658f980406ec7d24655c2565dcf9b3da
+6590c446caad78c319f7860857215f0a
+65944c8d6430d1d882c179c7d3ec7eb4
+6598c6ea38d69d69b59fb28bdaf56784
+659aa65725a6054644cd1cfe49635d1a
+65a27cf32d4d8376066ac2fd30c821be
+65a3ea8801e6685c8d83595b5b4abddb
+65a63f96a392e262298a58e0bd315263
+65a6497ca1a3af9ec34b41288796b44f
+65a78d1161209e83a31633fb25b8bcaa
+65ac1aba745857cd13cd851e75d6d832
+65b549da8a525afa377e9d75a615ce61
+65be155febda2f0246bd163d170241a4
+65c5c5904e47ed634e3d5274a97f410a
+65cb32d5dcacd10b9ccab96a8cdd4c01
+65d235134d6ab01abc8286b435613b51
+65d61f095d9522d154aeb3ee58a5fb0d
+65d77eb10a5368e84eb9e300bd0ba843
+65dd66f4afe435c940ce2ce11b0c59e1
+65e56391b362fd9914585deed803b029
+65e63e67a1df9aa8b2f4a12a5b727c94
+65e6fc8f5bd331e1f10b292b29ab2f9c
+65ee7faad42c3b99dd28fc1433808a62
+65f5cc2cc26c93d8b7b98126581ac63d
+65fc2744667ce55e4cd2a598c49f8181
+6606bf470076976cb33bba5b3e16854d
+66102b17649b814b7a6f8668de47ffda
+6615e4048a20987e8351fd9f375eb8ea
+6616979d4f673416a4196715ac336f78
+6618ab0fc78c493a5161ff17a08f7f67
+661d74fe6b394ac715d0f7c916bdd069
+661daa090402d07ef0c27ea0aa710b32
+661e656b3a05989d24f514f56e7eb3d4
+6621bd9fa03d5e7ba5a44c21ffd46965
+6621bde5a4c69f191540f6469a18d5af
+66221f0d1587da95c68ce1911c4a7511
+6623fd69a4220452cb3ec9a19ec46d7e
+6628677c36afbcaf9a151ce65b773372
+6628ff366ac65a911a211111322cc332
+66295f145de1fbb9aef51d08d7d807fd
+664250d0eba03e4291724c7024fc3974
+6642ce928f659905dba610344311b257
+6647f15889ef674bcd8240214f323d6a
+6651499eb91df180e3f81c193f4f0f72
+6651c2d83bbd62e01585ecaff6a7511b
+665440eef371f38ad0b4dec0a77c8546
+66544337f7e14bce761c51c1dd654e49
+66558804cc47b20f5575f3d47b2349f0
+6655dbfad7b6e0b607b1a27cefb72f67
+66610b3822cd4e1e7d793037fc08f1f7
+6665ea8acf16cc1f7af1c1be4667625e
+666650c70f8025972353ecd8348f0510
+666fe6a63446193d91a1e9220137b0b8
+667142b1005eb904761a9a9f38ead918
+667b33aedf4bb7d98759f2a8a864177d
+667c2fad5fafd6b869fde57165e8f816
+6680a7593b53deab949fc893b1081242
+66823cdcae40219c88f5a1da74f0fb46
+66827bbae3057e9e4a1cafca897b5364
+668957e4628c38c2c61014aeca4f283b
+668a0378dec91781a927d68fb642a4be
+66983299fb4e30741f875351dbc3e2a4
+66a06b9e1417e26c77f3916146f72faf
+66a2377f2b67f9134b206c2c4ef4ccfa
+66a447d3b4ee3605caab8e7271f453ab
+66a7f4575d76fb99e0e765e56315e1c2
+66a803a42cb11aeee596c6475373d1af
+66a80c6f6117f7636424d09da6a6c0cf
+66a910dd8a4a36c4a97e6a0952143098
+66ab9ca37b0221e38233fa0b0983c01b
+66b0282b24dc1c1aaf42d06d6014708f
+66b3ae0f238d0da08e796e87200c41a3
+66b726a84de8ea226de6c8efdb52b967
+66b9c0026c80ecc96b1b31cbe52af4bd
+66bd56d93072633cd7b68a8a0f765fdd
+66c13eb7b7b68b0596ed40e2766bd36c
+66c1b45c49b29e376690c943b6ef7c22
+66c1c196e8353adbf5474376ee2130a6
+66c217c4ae6a533c0427b3cd6886a87a
+66c7e02eabe081eb4bf81026d18f4767
+66cbe9bceca821e05e6b8e50a4405955
+66d4225411ab0f4e325148fabb229bf7
+66d8ed87e387591cbcc571299cd26cce
+66d930fa9fc856d0e6b3b2a030b71d48
+66e03bee0dbdaabc5aa949c097e03964
+66e64fca3c833fd875292761071ddb63
+66ecfd0e211ebac37bb6dc72a3432436
+66ee4cacee05f8209e0671ebc25cad23
+66f38d4f9afcdb6d0040023f10ab6dd7
+66f4f540f91b4eceb0c4a5cd9900549a
+66f7f59fbbb600cd0b5e272a73e53317
+66f98d0535d8333746560b3a3fcce87f
+670333bb5ee5c2bbacf90836f011837c
+67039ccdf1254d644be3fa40c1082f0d
+670c68f729abefb7b244aafc145105bf
+67104b26cf89421a35703fb278633a01
+67123c6781e4d7ebd5f113d9a3de322d
+6717437d4ab2293aa17da8e477657e9c
+671c5ac82cd2d0a754ff5124569585e0
+67206e8721ba079f7b837f3f0c5decc1
+6722f4c1dd14f921d3b4a0972fb905bd
+67239caad1421bb920a3efa8cc127452
+6728effbbc4fcc38e53f74de04783c64
+6729fcaa7eca21499cd139222b604adb
+6733bbf3de196badd04f02ec6d40c903
+6736a0e0d573b134fef71b0b905f3103
+6736e11218cccfe695052643de9393c2
+6737fda83313b1dadde371a784f1396c
+673ac3d7a6a04cad22a9f9902d4702be
+673ad86455d01e530a4a1b496f7d9bf5
+673d168b1117792c5246ad22ddad4c0a
+673e04a20ae1bbcbd78cba2604f1f7be
+6741bb1e273fcec12e68f885e27e06ce
+6745eeb71a3f6fe8b6e72f609373b085
+674ebbce748857abbf63ac872dfdd7e9
+674fab4a97ba9378bbf8cfa0bccdd671
+6750e5bb0b956a66acc418d6db008a41
+675242d6afce9b05aef088ad7223e01e
+6753222290891910d418786d4b16c5c8
+67535b9207e1c7766b433ed1f5d6cf91
+6755e02daef4f809b4294a467dcc672d
+675857eda73192a383a78498cc90eb69
+675b651eec66f13b07233fcaa7791cc8
+67622d98662b8d345f7ca28692347eb0
+676295c7a01aba2471b24367a090d2bc
+67635d3371acc553dd680b0eab2073a0
+6763befde2da8f68490395bb6dbdeff2
+676428476d154a5c905dd1aba4802161
+6766e7fa68bf27e8a9c81fc97572d1e9
+6767dca03fa4646e454c3a4297c4d9e8
+6769f7415d5c4e48ac985ffc84c004a1
+6770ee75a86fc2c79ebb17e50a5bfba7
+67762b9253400e636420a4e374cdc085
+677685c0eb658fdb48074b4e89bfad71
+6777d90ec99189d73be8b817a3ca7803
+677862527de3ee3a29cb3894534dacc8
+678875b4b8b7b95d9c72bba8c59a618e
+678c407b9d99c9fd9728a45a7d0005b4
+678d4c6c3eb5a96a6e4966852ebdee0e
+679b390e4e14fe8f487b074a0eb75abd
+679ca442fbd0591b564d8a7f011ab155
+67a4c834e4e95e0cea47ea22fec05411
+67a4ca56fba357552b1a6b9313962f60
+67abd1ea9889e6682c8a9c988d98668f
+67af80d4e70d5937ec8d5bdd16fd5dcd
+67b1a62ed76c0d17dca93945bf076448
+67b23e4164b79e09a0e2c447d388a93d
+67b5e28f9412a56ab54b1fff5ccb1152
+67bab17e574e41d28d1d6067c62d1433
+67be6cc7342657bedc6638fa7ced0a53
+67c061d3c354c25bb8a70231f69a759d
+67c1948d16477990be951efb035fda10
+67c30ec41f4679a777800b9cef483730
+67c4d1a0e9c35225306be1f023622a49
+67c75023a5a77931e77acae9cfdf6ba3
+67ced3a23e5d29840b9ab2b8619eb28e
+67cf8c10276033a74409efb370f7f2cf
+67d12342898acef37ba4cd93b0cdb916
+67dcc517493769425cd48c318d2a6254
+67e53e28d32b59db4e65841a6c1c8b5e
+67e78af6d77df71324c23a91ff8b8d63
+67f6087cff96a87878539b9ec59fd6b3
+68037d43360c8747a40c06b6f9d0e0f8
+680ba98799de1d607fd97c478f39702a
+680c4d5cda8f8bf3f17358da5cbf507d
+680d15d51e6bd11657e631ddd2c2b8b8
+680e6003d92fa1e3a7705b952fb13ee5
+68115de82d1d22e679eb0f8da2a49dc8
+6811e14f1eabe5dbfe4e3f1389ff6cd0
+68123af1d52e59984a710c426476de29
+681470a5fd6072b2a727bd2bb63e11dd
+6815850f90d972d2f8c4fbb5aab0462a
+68188aa6c41075ba57a179be9c185e32
+681954c88b7cb251c0211411dbca2433
+68221fda70d6b7aabbf7daa57bffad88
+6822aac5584c5b5d08f049c78ba48984
+6825fd67beba95fb0135e0dbda446db7
+6827c4a54cf3b935732608f6d8beed48
+6827c55cb45aca75fdbceea955bb4c05
+682ae9883275c2d210957b23225f288e
+682bee4522c2812638f3a3020327c8ff
+682cc9943564daaa499721eba56fb927
+68304d6d538534b19fe12c4d3797f16a
+6830eb4f0c6c62a6b49541fab61b5186
+68336c99960f5568d66b67c30e5977a2
+683760aa6da31fce27d08932ea495065
+683ad86039882a1c6cbab46c75eb23e3
+683c24b13d409c97d720770dc6804c0f
+683e42abaea3dbf877902ec5342be316
+68465db51faa6a5ba825a9e4374ea592
+6848c38185ebe64d3a3aa9cd8460954d
+684b77bbecf9f676ba9cabb9b713a84a
+68550206633b243596c5b268f26ede54
+6859dfb5fb3e8cd2041d45057cb5be3e
+685c89226dfa5da6fde3deb35f5da7cf
+685f39c68eb697cc32d6d2eacd4fccd8
+686a2bd968179d80f23a801317351bd4
+686f23f3d65ff35f1565e5b48328d08f
+6871e01df041d90482badba866d80719
+687318df41118869610915998f406b53
+68732620cd99e9c676fb61b2831c0714
+687d3ee55fffb00fab1a838907e43f96
+687e724fa5bf2b175f949d08f019dd08
+68820d37ebd80607113d45684fdcc486
+68864aa8bfb2188fa80ffe4e4c830893
+6886bfb0e1ebc4ad28b6d37fd546e633
+688ab0d391a92dea10b000416519d113
+688f96710800b3b505b6bd1c01ce41db
+68951c9b67598a73d694b8e3bd326c42
+6897816192adb30bdede2c30e6bcb91a
+68991ef8ea0923f129b482c37b0ca6da
+68aeced531a9cadb91ac7eeeef23986a
+68b1f564ad2a298c3510e036d334ddb9
+68b268fecf172bd3ebf20b7d711ad139
+68b8fb238e9f1649e96992ec8259ddca
+68c0445e38b9628ed04118a9f3e21303
+68c21a1eff9f299d176668096c7d2e89
+68c487dfd58bfbe4f380fc124bc45260
+68c5c59c0fd073e080080081f410c38e
+68cba8759e21db92facbfdc870e1fa42
+68cf757e04c3461a28b08d999056210f
+68d3bf12946cb08fd334c0312032f1ea
+68d4ac182666ba14318bfdfdf552ff1d
+68d4c9c5066af6c484caba5a3ae1b3f5
+68d60ad7ec6faa9cb314653408e5346c
+68d930294b08178f3d143fc17ff11394
+68defe203dafd0f67f5187a21e2233c5
+68df25a5c43783f49dc9172cde84aeb7
+68e6652e6c0a21e7632bb35657fecaa5
+68ead957ef0484b3c0ab5689625771e7
+68ee9359339c7bc8a311c77124799edc
+68f17cc3682a2b804b024f3a23f33ff8
+68f3b2a2d4bfa2eb156784fc5eeb1b55
+68fb55b385f62a5a3c27b7900662d4d8
+68fe427ea829f62f5a358cd92488daa5
+68fe4d1106e5d5d6a3ba33caad44f9ad
+6900b45aaa7a7df78be624ce551720d9
+6904b4d84dd1ddc289f1a60a07aba1d3
+6905fbf78a94098850aadf62f8da4398
+6908dd6ffa078529400c2714c64848e7
+690a2cff94fab90ed552fc27cda5c666
+690bcfd5b6d53511afa04a895a0166ca
+690d7c3d12c53e2d56fe7efb583587a9
+690f8ec01bfb7c8af689aa887378155e
+69147ee89844d876da980b9f7f081817
+691bc1e42ac28c31249ced51e2944136
+69286ef418965129a4858a28b52b25e4
+692ac69cad3fddaa91b20730dd055c1f
+692f54ab6969c06688848561cb79bb08
+6931cb0e9cc8b4bae90d47d12f2e4de4
+6935860c0290a88b6e0cf70860d008f1
+6938aa9c590059d106570e5cc3727735
+693a739d09aa7e9984e3ad496e3780ac
+693f158d557aa8ae0858f3053858e389
+6942fc929759931bf2b68351da45e5b5
+6948c7aecf5dc72cc9831672caafb6b5
+695056b81b578931910021c9c5955059
+6951805bd57588459905ced7e95dd08f
+6964f7fff1346e82facc15026de838d7
+69656e40287954c5ae46c5003647e288
+696c1a59db8138a585ac243730dbd6ee
+696fa2e1dbd1dc374d05dc303b3752fc
+69702bb28e88cece3216789d93043aa7
+69725e935a3e7c9ab3370862b33f6730
+697285f312b25d17f05ad8d30ea8f0bc
+69766959fafb8982d39b13c86700db49
+697d2f8a8b1922e000b640b2904f55d4
+697f097cf674811634fc4bb6c8457041
+698010a64d2b2661c0219701fe1ce0ef
+69873fdd95f4a1b8ae2685ad6a29a452
+698a24d85e8c5f5e9333bc66733a354d
+698ac6dc688f69b35d86d2c39cb49cd5
+698eca99112291fb1862d679c55f20b8
+698f2a13c20d442547e53c91234dec38
+69928dbbd60a2d60abb39690a10630b7
+6993e328808ecf97a504eb9d436fd995
+699b18914659ab9a64bba894924e70fb
+69a036c59fa2101b725fbd5cd65fe5a5
+69a128fc71256fc48f9168d9fe221b32
+69a6c422cdaf3bde60b11fc815152476
+69ad9db8a9f8c9a4d76524a1eb7af79e
+69b0452c395d47615225978a4d070cbf
+69bd3e0d4d5eeb7da26b99b75543302f
+69d04b33baaa2aee2ca0c8d04c254e8c
+69d06c604319a2687048ba28a2b0d278
+69d5cfddb5d3f746b8c31b4cee024782
+69dc2ea9ef60bef771756260283c253b
+69e5e61f392b138b8a60a0e01ff1111f
+69e95d6d4f14b098368ea7f635010783
+69f3c7eb415e82bf4679270d8c46ee6d
+69f613dc54f072ca5e96ed8555c3d74e
+69fbe8dc05378835e36e6da3e40ebef4
+69fdd5c56f8b49bc6398023b27e0bdbe
+69fe34952ea1abe70f1bb2efb3fb0f56
+69ff093de53d2c42ff6b723704e1934e
+6a004bf7221ec4dcb00f2d07ed0696a1
+6a01cd6902194eba8e1ce2264e00fd7a
+6a01e0a85f4d8d7e45f3a6ed24614e4e
+6a04c13cb8328d89893304012831022c
+6a0817a09796b2d4752eee6b6d1ab314
+6a0d47fe329905c5f35d2c6d27a9786d
+6a11110dc559e8a23bd5d5d2a486fec2
+6a134c24e4035acac5b4107aad732421
+6a1ad3b97e47312103b1ce20f5faa81e
+6a1c3056256eb2acd1055a87b4377370
+6a1c43cdc0a2af5f28240a743282d8fe
+6a28482a2110fb969a037a145f9addd2
+6a2ce2a6b507054ea368049c48172d33
+6a31e57059f3842dec60d7ea4a501f31
+6a3b46777a1a769a2c279f4ddb62f4a1
+6a3c5a60e510272e29d5b94cb4f35f8a
+6a3ec7fbb8aef2081c806813775ab399
+6a3faff798f73e6a618f7d78fe98ca6b
+6a3fec32cdc00dd48d7e493183ed2519
+6a4a9b5ba1867f7c9f9536e54a1fa22e
+6a4c63d9a7f5285c2c10ea3c1a63c2aa
+6a53bcb2b6079abb1b8412e195bc05a6
+6a5700d2ff0556ea908e60e44ea3c4a6
+6a5e0ab1eef1d623a15b0b235175d0a2
+6a616e4fbc57b64d8538611a0bf0a423
+6a624ccd9e0ae51727292052f2414861
+6a670c9ed095706958eba3a2f43291ce
+6a6fd9fd12392f7b9b20ffcc99bf7408
+6a71918f3a5fd75f7fc8a82deb9a15c7
+6a7c627c9eae25af1f2f8dd16252f5bb
+6a9001fb65f21b07d0360397b502929b
+6a9185b9e61fcee664fd58ed10fb7596
+6a91be077377eae1087e595cd2d5f0b4
+6a96f2c2c1b4e3122d66523af44e924a
+6a99638a4c7393f37002260fe3b7e81a
+6a9e86b37e57229afd3fd1fa6cc6e681
+6aa01be2b0246b53a64e83bf121730eb
+6aa1d7988ed1133c8a41369ceb1d9a22
+6aa8f591b861550931eee1d93790261e
+6aa9387158cc8567d4a792eb528aeba4
+6aa9506b49d02823f402f1e9e9a7fda7
+6aaa03b03d99227ac43f3bc9a2df472f
+6aaa9ad9267c3f7e5a5641f8e747a2b7
+6aabd57b48f9def28cc68f6e5a8dad5e
+6aac9356dad3d212289b463f7d9e7717
+6ab339d6971964fa44759636aaac6b97
+6ab3f0c6f0f9464061b9bab55efa325b
+6ab585343b116a6513debe6ffb8f8853
+6abe2a657c2043da4bcd4582268bb08f
+6abf1592618ee73bd78b34a3c29e1832
+6acbd0d5e3c9a399edce1c382fc9e329
+6ad7dcbcca1e5a9b5ac4476edcdc359b
+6adc0f24d020c5307fee7e0d2fb93954
+6adc7942dfbefa422356c00d11277e3f
+6ae0c7a85c648b90dc2eec269818d6fa
+6ae525df67765596679b3c04f294d797
+6ae5b77982b4708996b0433d53072fb0
+6ae6f4f0f166bfa2fa4360c6331ebd4e
+6ae8990a3783dd6e91007cb5ed232858
+6ae92f58541067a276380c23c76eb466
+6aecdf3f8b468f195592e07cef30631e
+6af006119a36aca9eeaf04abeec9ee7b
+6af406f684ff90ac88f7197960db5680
+6af5b105930dd8e238da8ace6690f077
+6af68094045ce396e402752b857b685b
+6af7facf91d029ab0a07408e420e142c
+6af8586727426aaa8f58bde386155dc1
+6af875167a459f14aec22d4bb6cd734c
+6af9b168fabe5dfefcf00f1f7d8d1547
+6afe51d6feafab74e3630042333a7a3c
+6aff5d17c71c958fa5047799faca018d
+6b00521285dfeb11d4392c7d9389987b
+6b02c9c4ecbdf3d3c8752fa690854b3b
+6b02feef4c83945a1d7f3d5f37d091ac
+6b038439ca169313085bb4d0aec781f9
+6b118016b4aeaf714f6f731ef39e3369
+6b14315add69df67574a85e12400de74
+6b1587ea065b5e7505214e830e3a00c9
+6b1620b3990203d01aecf82dcbf7024a
+6b18a2881d187fa58c1fb920f6480d5d
+6b22d627ba38c711133d7143081e7edd
+6b23106ba977040914bac9dc7a1a97c3
+6b2842e1b33250b65bfa28b7974539ba
+6b2a8957de5cdfe667ae14b022eeb944
+6b2bfe71ff803e815d5081f18310c14d
+6b2eb6a0bed750bf4f8874c8abafe12f
+6b2ed598dbaba0ebab411a8232fd3bdf
+6b33588375d2ec88864a33b48508c16e
+6b36217faf4250cff6df8b4596b65ead
+6b4650d2d1f361c3f76afa8c178c7b68
+6b4a50c0b809116c3a74ef18c2778b73
+6b4c11a7cebbe7a9000ac9a5979ffcc7
+6b5175bb3d322cc5795c221bea2376dc
+6b5551a66a6e0383d6d97b76ce41b541
+6b565bcd388ed4a2708093b8e2c549c3
+6b566fbdd43e02e337afe7f9baf3a909
+6b58bf4ce290a7554d65749eb4ed5336
+6b69dfddbb682a7ecb334bc7e8a10627
+6b6eb66fdbef789077b5db61e69e36eb
+6b710cf511ff60f25b73dcd712560a42
+6b7b95e05a7a2922bd7f22d40f52f23c
+6b7d3121899a03d377248372d60b5f14
+6b7f0dedb7c66a6382dc0b6f659e2d45
+6b839cf0130457a4dac7cc24e5039ff5
+6b83aa0cd4b31f90dd35bf34480d86bd
+6b88e542f3c1766a949ddee931d27b98
+6b8bb991367aa1cd2612fd2d2585ed92
+6b8d84441411270998276c8387b12a11
+6b8f0fe402c4999cf22daf7979bdfecf
+6b9111b9f49826fbd0873edaf96bca5e
+6b96b6107bae6a0d85b0831267e6d1c9
+6b9c3b0b55f757cdf3a70d35892b7794
+6b9e58a9c80a2261bb86d15178241c01
+6b9f32749b8c7645d1d86a9451dd0fda
+6ba5c657530a89d4730975ebfdf7f210
+6bab3b3458f8f4b8b722e8ff99c58dcf
+6bab66265075dc238d94860b4e2856be
+6bacafb91aaf70308040d26c551db87d
+6bb724a1b9902ab2880f949695d12960
+6bb78e2eb7376ad77719384cb4a57bfb
+6bbee55928265a95f9815df4032356d9
+6bc39c69b59a715fbff2bcf232de12a5
+6bc72ab7986b472bf24ddac073d65307
+6bc8fd58be131bd5c864ec9db93515ad
+6bc9206f1876db2d3f9afb5f850b3448
+6bcc546507c3dcf0e5c4df35a667a2ff
+6bcc9b2c3d4d41230e6457f170e02e37
+6bd12a8d09fac9dc538bfa7b715c16fb
+6bd82b4ebbc5ff9367177d38b9d81972
+6bded9e0a8a10bb4d9adcd3156e7de78
+6be04bbb4a2d74af862ce3388acfe46a
+6bed57435a8dd22f6138ef7698916457
+6bedabb65155e967422118a4392e199b
+6bf348fe9ae9dea0ba33447b5db6bf77
+6bf569aeb41c6d4dbf447630eee52365
+6bf5fb954245cb50a89aff4e92e05260
+6bf9e713fb27e24dbe124e0d8503458b
+6bfc027794c9f72f417e10e4713a5aa3
+6c0c00d17f1c1ed64dc7a6154ebc0753
+6c1142b431d305efcc6ea8da1308d164
+6c130ba270cb3d4c182f2986b9cf3ab1
+6c1e0a5c6969c08abb78d4d078ad445c
+6c247ea7a91efe16861b2a1453e8c4f4
+6c29e4042f33e4b08044caa12bc46227
+6c2ca7eec1e8ff465658007941dc7934
+6c2f19d0cf81ef6f3be35242c22d4150
+6c30b7fb38d14c60d9e8daac0c1a634e
+6c329cd42cbaf846302d357f7e9ee3a6
+6c35e56c2c66853aa779076425ad0621
+6c3bbc425ed964d050ba119b9d7ad610
+6c450b8fa8aec8552ca8f62bf1d43fe7
+6c46faaf620e1e5d283a6a24fa60b57e
+6c4bd0d7c65373ef125fbfaf0b82b7b3
+6c50d5933cfb83cf35bcabc95e705b59
+6c512bf20ca7641fc6a7a57d237423da
+6c562d0dc6d4248bfc846f87bd091431
+6c57e383e232f4a1f1719ece7f6529f8
+6c5a8a47b8b2e8e73958c2b4c08315d3
+6c5b354dc3c389ac586de8fcb94a2392
+6c648a98859c609df0f23f084088c7f4
+6c72b919ddac76d122d82f4adc61ab99
+6c74635e39e85c31d020a8ff2c501321
+6c778c08bb6e9a8191ad49fc15356ef7
+6c780b642ef67aadfa48e7e96dcf6f8b
+6c7abbe3bc3df8e1abe314c00c794604
+6c7b17954c905905c6d0affbd67d816e
+6c802883140ba8fad2bf94699fd0eae2
+6c84e10e5666a5e8c214d057146de9a4
+6c88a8fc3d4ef620d1075eabcfea627f
+6c8f752ac32515b8975b15c00e68b6ce
+6c9946aa3f2b24650f11dd3f82892f4e
+6c9e4e04c844505f9e5820833f4f4e24
+6ca00fd41ad9615351a47b9161a19dbe
+6ca464a6216630d5a6407e0e49ade257
+6ca93f483bf4315df9018ec3c32de5ff
+6cac4eb8f7869475eeaa8dfefea77ed7
+6cafe3983e3e1037069eba41268abc11
+6cb597f9ca168c1f5e4291b5b01410a4
+6cb747f4d2307aa2377f28cff4b3c68f
+6cb7d44fe9855f0aecd06e1c0f5f1f3a
+6cbc209a23896b517138943fd042c4a7
+6cc05ffc2b875424cda9ee3c48b83d71
+6cc4dc4233ff9305a95d50666084873e
+6cc83694280591837e342ebf5bf45377
+6cca843e133cd053b79229bd595d23fa
+6ccc8278dbdb9452430ffd780766d206
+6cd80634d97a8cb858e7d1f50de36e0f
+6cd9af9edf8b35680aae92272a0834b4
+6cdc299fbc1aaccad3edb54644177ef6
+6cdd89b1c944245d773ac91b91699068
+6cde49ab3d7fba7526b9c00c3bb172de
+6cdeb7b1c2a168a0972f102f721abf83
+6ce6aab593e2b0b7b8cc24f0eed75b1b
+6cf4f9383d193e09d37bf58e9c887ec5
+6cfe982d9fbe14c0ea70404e73e99374
+6d0491852b1348c99c8aeed7f737e88a
+6d0a13400f91bceffcb4ee0ce5d5b358
+6d152f9185aaa7267188145d02ea46d9
+6d167077ea49de07212e30651b6ea3d6
+6d168fe0b4b172203271b3e2cb82ea6c
+6d1c8c7df4e04ee1f5726775b81bd34d
+6d1dbb6712d7ef76a659d56d43d3711f
+6d1e016f565af8f69bb8a09bd563d84e
+6d22d052e545afa8b6aac07d626ea4f8
+6d261755821e376a860d8f7df02ef1b7
+6d2c5045fd20f1689cc912dda8680046
+6d2d6f4309c21b5a5ca1be8971b0ba98
+6d2e403c78034ba6444718626b063ed0
+6d307b61c62020dd5a9f57679fa074f8
+6d315b858c9b1b383103d18e656f4775
+6d35814974dbd37c8ac2df2079d34fec
+6d368a4e7bb6a96fa1704be2f0f7e74d
+6d37f4a2fa064b8fd0828856a06c8797
+6d3eb1d55fa3def926c79afc34e221e7
+6d3f9369dbcbbe6f833f09ec9df95831
+6d3fcba3bbfc06bb0caca79bdcd63c73
+6d434f160bd06a5adff0fb974bb91534
+6d440cf78846bbbd678f8654f383b137
+6d47bd32e6c42a59de10f887f4e154d7
+6d4fb46c50d81d28fe6ad005d3ed7af9
+6d5e78acc718252d273e4c5b717bde82
+6d5eec73b6541206f43a71196674c79c
+6d612f119504b16d83896a1911b5a538
+6d629ef4936a7f7239b7cf89f9415e72
+6d6613ce9108869127ef59a32f74ab80
+6d661a4cee4ff82a6cf95606b74288c8
+6d6654a1152d1bf09445a5abf17bf026
+6d669dd8d31ee153ba3e03ac5f0b4115
+6d6ab4cdd7e2858320840cd3234fbe43
+6d6b4099c9de2a4e02cad5d4fd2c795f
+6d6c2beb8d4a417caa44ea9bce10f9d2
+6d738938b3d69bb46af66d6971072004
+6d767c03520ff13b11b5d18eef837bd0
+6d7858d395da46cd32c425f4737a4615
+6d79c2c16c5562f8164be3aa4563bc71
+6d86a8492f95c3b491d4aba349601a64
+6d87237d40fb24e33bb059fa0e4ca984
+6d885a37c66a40487c1d0d9163a54e27
+6d8b1cf6206710541fe1b3bdeb5724e7
+6d8ed7e54963f4c8d0c73e648076ed69
+6d919199271a1b3283f4d19c0b1239d4
+6d92d48f4c3a62dfdf3b7f770d08da0a
+6d93c745a45ef2d05a8dadfa6b0d0a61
+6d955034e0a39e50e549b1ed16f87152
+6d977162409b60814c07f59a78eab717
+6d97c408c49b83ecb1619d4146cb8d56
+6d9a496417581ced03617d2bc2b22197
+6d9bce27a89e366cb504eaf2215514a9
+6d9edf21820ceebdefe2ac5c1afb5233
+6d9f1cc7c9822f773eb6968346bcb24c
+6da2c728b45c2e8650f8754cdc3499ab
+6da3866489cc43f6048408ad5fc45fae
+6da412357d5b6360e3104fcfda03a2c8
+6da8e3b8f74cb4180fb87811f8903c86
+6dac1f5b5d65b886bcd400f91f8caad2
+6daddbabb49772432c908cbc897f1078
+6db13cb0d2d2b07ecc1519519b4b56c9
+6db8fe2a31703c954afeb5c84d0e958a
+6dbcb3185eed907b36e1a89b090e49e3
+6dbda9432f7504b50fb2088ee034d968
+6dc37f14b78fcd2a0c6a51797482317f
+6dc70e0b7a629636bdb288332ef62117
+6dcb042af1f3becafaae52f4c7eab416
+6dcc7c538b4f6bed902082e02f83d282
+6dcff581d897f573f66ef72a25d80efc
+6dd0e6f00174e5c0cb64964d5e14da4c
+6dd70436550e94e9c287c42f87e34507
+6dda7dce8f347158f375e75710a04c24
+6dddd7221d781f752cc696168bcacac4
+6dde6109edb758e38b94fd87e786dd5e
+6de2235cc21ad5bfff811d7cc33bdfdb
+6deb5c904df3c8182c801a8b43470eb9
+6df6ab5d1082483040cad79cbce540d1
+6dfa337a70fa817ace4f1b4de1bd0d51
+6dfb78366fb41381d05cba9fa8b814aa
+6e017069363d2fb85a8f1d5459e7f612
+6e04bc04f598cc7147513148e4e5f5a2
+6e0acbfe8c30838e138b3563ebe9f36d
+6e112018c1d629b8b29ba40bbbbc12f1
+6e152a80dba19ab8e8eac0ffcd422054
+6e15ddaa0764200dc5a609fe115c5c2e
+6e188b06f5dea26cefb8b3536bd70541
+6e1935be1a50248a64d285bc7f7b96f5
+6e1f7953208ae370dfad1818458f0482
+6e1fc31b8c1d428da3fdea1ecaa81134
+6e2dfefcadf5183f280107ee3e180651
+6e32bfa906ead2c666d9574e603452af
+6e3b826641d16549056267586e95657f
+6e3b8f73145e6a36a50f28addbf5cd41
+6e3c8c3a8650fef386956190cab4648c
+6e3f1e1d9376ad011eef2477e9e0a3ca
+6e43a0f950cf67d1f17b339cd13234e1
+6e444d73ec1489bf3fac4a0d9fb41680
+6e459e7e011eb33d5b828d47241d9bce
+6e4e29e23d1e07dd3095f03ff5f624ee
+6e4ecb20b3e8429af2e45e0ed044369e
+6e4ff8a672dcb6e7a85c93880c802f83
+6e54cf7b22f7f730caadf8a53a236db1
+6e55d5826ca850f2a97570674dc12d97
+6e5bbd88c608789b7802dbd3073ef03e
+6e5eb13d4564bf66e3325356f307e7de
+6e65f4022c5cee3d5e420348979e77f7
+6e67538f4108989e0f53b6b08371a40e
+6e69f97a1a4127232fc4c9fde20eb0ee
+6e6aa60e8912ec05d69013e9ee4f4ea5
+6e6e60018417ff14809bf5a6a78a50fa
+6e6eedb54c4330103ca60845c3014fc0
+6e7292cefda5953128390c28d9c208cc
+6e73a963a7e609fe0fde50aac48687ce
+6e749a4466dbe912ec56b4b854fb553e
+6e75b52b74a192f7a188d6bf07e882f9
+6e7e573c74884bd5550e1bde013a1d25
+6e80b985b3f6cda4c0aa0ea210b803b5
+6e8494853e1905e124b18a6f9b0bf9fe
+6e8e290c2b39b1b38cf0f185c6b42e84
+6e90561884e603577f5f4211ae10d06d
+6e90e334d900d53f29fef6b92bcb259a
+6e9add5c20e92429f89c9b57e42d166b
+6e9da68ce4f84d91201963d86993627b
+6ea151f9a8df0d85be5cd7bfe4e25c06
+6ea43e5f895c5e100bb95b06fa394ec7
+6ea440957a656b9fc55a5aff56cd990c
+6ea8438a823fde80de9373315ca22162
+6eafcd7214bf791967695f62192b5627
+6eb45226caf1b5a299f02afbe069e5a6
+6ebef3aede68bd9a94fd474e9aa040b7
+6ec173af5aeec1eb93efc7b93a6cbe1c
+6ec5512689d76ad807bad4cdd38fa81e
+6ec625a47c1dbd17532b802bfbc5995b
+6ed18b4a3b4aa81c0d3cce77880e7eaf
+6ed743bb8135e90cc3b7157a4bdb17f8
+6ed904a7c60d842f061bb76be0a4c456
+6ed96d4bf928fad2b6811ad330879d2a
+6edb2c310e7f6d6bf6b6f57e77b9ad9f
+6ee75b548682b182bbc2ab228b1c014c
+6eec03906aae0ae3ea0b05a981255ee0
+6eedf6293039c8ae5b99f8b439ec5505
+6eee166520141c7450e37ce57ab195c8
+6eee52c3a9bcc4d54289ed2748217e51
+6eef7eea5fda71323bf52c4412cd9057
+6ef3bde666d6be0be7638ebb7cbff32b
+6ef4f6c00588421adcc1cc19c729cda0
+6f0401f3869bb53614a463d9202469e9
+6f065f30eb83550bf82220159df49182
+6f06a25f3fa8a1317c55e99f261bc066
+6f06ef9b48be8f296ece2c52f888db7a
+6f071efe4f46a82c2906ce072c353b44
+6f092c305b789be568f99247ce7f3abb
+6f0e899137ba559c704b63ca531eb135
+6f0f6d2cf70b877c22496e40c28c64b4
+6f21c085649b355098e2991b77dc67e4
+6f2e25d6e5ab5b38d8a021d53d8915ef
+6f2ec87be1ad2e852203a487bdb826f5
+6f4399b6db0255f742584b4558cd0376
+6f4425a3d6ef85a7115b6ad306b54974
+6f44397602357803081113dc23e2aa9f
+6f4651b15377f0ee5fef353294885192
+6f4cb867fd2cc360cb99952d4fb9ae62
+6f4ee309a8a9282bd90d450bce333830
+6f5122a5a62bc65a118e41ce61c180f9
+6f5563e6812df346685fdc1a89531e94
+6f57d5eac5473a1d1d2dcbdd183902a2
+6f603b51e015a20bf00a1e1f08d2bae3
+6f63211e3a26b73d2359d27f46ff58ca
+6f6cb491bccc7343dc60ff8513669d31
+6f756ebd34fe55cd37b13e9b6284c8a3
+6f78cb5dcd627f774c2e12416722bcfb
+6f81d178c3a2d2f87c3fb422927eaae3
+6f842c1c2ef7ceed2e6e16153953668f
+6f85f3a21a714084e72086781c6ac9af
+6f870212a109d84d3f147ede87d605c8
+6f8cce7239362d713fdf8dd058a712cb
+6f8d2eafce61d71b48b3c089ff427f68
+6f94b258c2fb21ca0264ddb22950b851
+6f966f906e8a6468e3b92ecd9293899c
+6f99e60f921c65d210c5a2e1624f115b
+6f9ca348533c0d41018464bd931012e2
+6f9d0a26895959564b0dd1af367d99b8
+6f9dcbcbf17073b32ae49b72be6d17c1
+6fa088b9a43c3dc6a92f2d9420804fa9
+6fa230407961f58ef1ebd2db0b12d8a4
+6fa29360930fc5141e8a3108114194b9
+6fa59bf9b2efb9348021f7d3ca8079a3
+6fa8ba9c0ff1d474bf71ef56431c775a
+6fb22a4c0a694c56453796cdfb1ddaf6
+6fb3d250e36030bc0fbfb21e9f6ea7fe
+6fb4c17a8aa687b22518fac921d4739a
+6fb860a65a8d47ce0d0e3f9f8c0a2b84
+6fc80f4eef6574e0234dabf6e24a3df8
+6fc92a6bea027576445ef81938e8dec4
+6fcb46d78059390d0ebf2ac8d5f8020c
+6fd0bd65726a4ce6eddbb6bcd6ee72e9
+6fdb74b4c058c2e22c1ed00f4bc63719
+6ff465194b7325025cc57ba339367f0e
+6ff65014ec474d7fcb88d8a95b73579f
+6ffa4bc9e779feed145f71b857e647b4
+6fff70d00ded4353883a3ac908ccf1f8
+6fff774cbc0b3619b6c00fc019ae52ec
+700104cd34cac9ff93aed895691fe2d9
+700a09d3ec6468b6b00153d2b078829e
+700f0050b3ba8bc916f20221bd6642db
+70101a433f152122a6322395da9c40d2
+70134adb3fcbf1638886bbb9d8435e36
+7017f130e76951bc939aa63d4a4bf0fe
+7018cd92fe2494f35e94cec0cc272a86
+701b474ef1dfc6e5b60fd416f19a1b42
+701ef72e0160ca9a4efe923447e5c69a
+7027fb97a1a17c9e996b8884f0b2a93d
+702884f588846cea9e4e44f350ead097
+702b74f6c72031f985c046cb92966869
+7031b15143f93d0f82e83b20ae0b8d70
+7033899b62be00587c5032aba905f1f5
+703a4123dacd9178153479c4adc99bae
+703d92b7508e3bc61b58bfffdb226a89
+703dee7855fd99d4bbf48c928b0e0ed7
+7045396789edb33a54b64de2c7b2a520
+704614cafa5bb263657e2150b0733f5d
+70497cb1aaf0d0913ae972e0d591e178
+705941aec673e91431d272f6608a84e4
+706761f1b4de9859a9a04767a33fa14f
+706c597ee2103585d6326b79fbc4d3bc
+706d66e8bc0d850b8df04efe5c9eb707
+7072ad2830b796baf734acd1db243f6f
+7075d15e64218993fa763c24148a5474
+70783d44ba7f85c37a0aa6c3f720dccb
+707a107311012ad210f099515a24dbc3
+707c86bf03a5f916c2fe9f0effd658f5
+707d4a2cda3ad1f1f2427dde457644a7
+707dfdce14c970d578ade8e2cdca2216
+708211472d4555f7284efca3c027ccb4
+7084a82f0b0ac0a31aa3d568f858e2fa
+70870454dd9134026e2de497f447a91a
+708d97e840f30190e2aed2c3ebd2cfdc
+7090e60276b744c64306c460c7dc53b5
+7099a174f4724670025ea3337a3e2b16
+709bf18787b6f19cab6135d3e83997a4
+709daf1e0ae96bf32a89e13a4382ea6b
+709dbb611ae20561a24dc22d7cd2ea70
+709fba765d524b7335f085fc92156cdd
+70a1fb59a0abdc6c950e60014036d42e
+70a4333014bc14e74ca36830e4d2d765
+70a65f091aad0edce23f887a0dd377a8
+70acb7f045cb0e67265e14ce399e4bb4
+70ad12ce765bcffa07ff30222e30b3dd
+70af9d0a4e8f9afdc4e85f561cb2fb8b
+70b1976f379e70ac287ef33f4183b184
+70b3c3d69433a9c3a2aa8d2b7f1701dc
+70b6ec7c726c17c15f83c4869befecbb
+70b965b9e0cebf519e3c1b665543860d
+70c62a999d32b5e132e9eace511d9a76
+70c77f897378a1c0d6dca67cadcb6b45
+70c79aed0c1c1f079d1418d58e472713
+70cd3c7784ed6c63084740451b3354f0
+70cda0ec549992322848971095f99a52
+70cdfdce881159f4bc101602fde9b547
+70d7488bc68328e8796f2aa0cd3afa1e
+70d86b98a08dc492123f75237729e568
+70dab77265f0c683fee45ee22bcff9a5
+70de180a45f8a93c47ab4ae59b15cb3a
+70df189c6d2d1d56a2e510b940e341a0
+70dff34eb2cacda75b2433731c93d47f
+70e1c291b400d0458cf5794374b261b8
+70e7de2331a28b10f294a60caa4c3448
+70e9f7964791be94566a9c3873140909
+70eabd87a9324f25862866fe063d0a58
+70f0213947aaaff89cdf8baf32b6bd40
+70f8c381b84ec5c22c5eb18ef2e7562d
+70fada3eb4a9ce17e4e16fe77cbea696
+70fb5bd77b55f223cd4ae7ac95b06b44
+70fde0b461fd979e928d17e413e46e1b
+71011fde4873d26f8828e6d4681f38e5
+7105cda114f00ca57742b006c28ed2c0
+71080e5f0a951dd28a2fa3f48eb7429c
+71091a4bfe23b654e868c140eabcfd9a
+710fe70a66f5c8b993f1906dac3e5aab
+711ba8136fd9cd7d2e1c47d49475db49
+711d4cc2d6f51b1c02cd22df9bdbde17
+711d6b567730f09a576c2ff9901f82f8
+71253352244cd5442ec9bb74bddfca24
+712f8bb544fd6478a6d2b8bbefad14c2
+7133fb60d4ea3f71cc9a9e13caf4bccb
+713961ed7b62099ee54e5d8fd3639bf6
+71425886d5fe44e61c7a3a078b796bab
+714896c3fd4610aa99f49d8909fa7dcb
+7151aa2035d8dffe075f41f144d0db00
+71531e5b26e95a152dfbfb8acfe5aa24
+71587fb4377b11fae5bf26043b65c178
+715f0b0c40b783538e9dc79ff8062cff
+716a8d2addac40455e1fee9110a15a36
+716d60bcd312df8c0e9819b9876154a1
+716ff5d0608d5e6f65a72f26515c18d7
+7177325a8f63b23e86b2f0226f3eb8ab
+7179179034cfd1988e314d5a18e45b8e
+717a19b8a75eb4d5a0ef50fbe0055d3f
+717cd0f4480db332f70a9a2d150f2382
+718d4b398d3f787cec72d2287b77c805
+7193b4c990cd0a595faec7180a77d49f
+719d4ddd91774551d4041d0a348b9551
+719df82bbaa486042444c3da55215c56
+719e9624e234e991b0f4ca6def98e5e5
+71aaf3d79f18467200768b036a5d6bb5
+71ab2b9c0cf76f65eba699af50456e3b
+71adc40ab17e42461f21ffc544485bb1
+71ade3c1ea4a0604ef336e1180e83f72
+71b9623f374d47250ad4ea41b70d82ae
+71bbcc4abcc6ae1d93a7a833bebc471c
+71c9eec3ed19a02514f42e160b09e5c5
+71d3c503bea1c983fd385e0f66288632
+71d4b403336be34c06a7dcbf0ad4c580
+71d58617236c9cead9905416227ce12b
+71d892aeaad97670bd09961035b0b97e
+71df931fc9b74f86c5da8f6be022ef1a
+71e436fe5bfe338b68bc3ad35107d73f
+71e5636bd122d8835258e7c0857deab1
+71e8c2a9a897f4684e5a5a30f44be029
+71e9810c8b14412404958ed5babbaf8d
+71e9ba7cbeadf07bf553dc70c04a8a51
+71f08e2f92fd2817c7e73b255a5d8b32
+71f37d66cbb17372adedfb172ff8f7da
+71f4978cc2e697678c4d8465b65842e7
+720a2e634987bcf3a0fff3ae5ddc7e9e
+720a47281c1bf4c490ff1f1ff0cf75a8
+720bcd05d6528836d40940740add618b
+720c0139e74dc2de830a2dc0535adfd5
+720e0d545038ea2829901b4d44505b74
+721c3d75bc6e7eda26203d8698a5e6ed
+721cd49d150b2510cdfa896b870bb39b
+721f7f57c3ba681df8b9bbf92a77e4e9
+72238ccd26fa8c3e8673f87ed9a52527
+7234e0826737360e571b032dbcfdee30
+723c9815e63441d80bcc9a41753d379f
+723e1b8d54931ca4dafcf440a1250d92
+723fdbe07081e9f27207108622c7906a
+7247d62f558f9ddb3f4dd65849d583d6
+724e625a7c2ec23928355d600f65e567
+72549e78cc3272047ec138a497f295b5
+72550b8c08f9841c683d50722f7de465
+725ebb1e0073aacc8351ff8759cce795
+726663fe880d0c649db0e57e6142d3c0
+7267d918a8fbfc981f4dc359ee9143d8
+72687ee1eaef2cf32f965b297eecd226
+726d3be11975be12af84e29ee168fc69
+7273b6a93c656916fbf2c81c15316084
+72753393f7bc51584a59857c05a22771
+727988817c973c69480535dc896e6599
+727a22289c6c0dbc42947dc8bdae5046
+728eaafa6eda22eb3360889855c4c136
+728ee387f5fa4337b3870d5e59c514ae
+72917eaff4f85bce53e62199d0155bbc
+7291db7d186189d1ef6fedd9fdc38d9a
+7291e550cf2a6d8a4e63554507b25ef2
+729aeff05953e73a0aa8e55f43b1b714
+729f0cf1b2b7a57330993a3b0e6f7d95
+72a7f5c6b9ff389e88c7bdae906ad969
+72acffff520a019e47f64df0fbb44de5
+72ba0619f2bc73258b1c2d99423f1630
+72bc732a19dd7a6077b124d88d0e24fa
+72bcc9e8b74f401570deb71f438a9867
+72c7565d910534bb3357206a9282eefb
+72d731ed6fb3747db31bdf0291ffc307
+72e14f9dd3fcaf2f11e00c91e755b55b
+72e3188db1f00747889f249daa890495
+72e5027c97c04838dfd4b590e2ec0eac
+72e5360a8ec09995ca3c852e28d20eb6
+72ebf0f6c9b3d20bbac909d6a51e5cca
+72fc9cfd4ad6445aee55c0cbd61380cc
+72ffd3c559582fc06763c6164aaddcb3
+730a3abbeefb2422ad12e2370280d79b
+730bd7a8d996e33e6182d4ef655e46f4
+7312950e52097750e7b672af8fb29823
+73166173b2a6eb77597e311e02ff7e2a
+731f50f4b8e7adcf70bdebdc6be79dc0
+732cce3aa1cde7a210a8db3a35657ed6
+73382b58a1cef711286464c280c3dd3c
+7342e00ce8e78658bf0675bea21e717e
+734a9ce4726176d9a3d228792af2a144
+734dde6fd79bd8a577962e4f620d1e48
+73539c08377068322e1ddb8dcae92f59
+735edf9a87c1c16d4ffe820c61886f37
+73603bab56b0188946e193934bc48e3b
+736188a49cc96c1967af1598a6c694fa
+7361e372ca679264d0d7c637b678f551
+73621e462ddb3deb864a17f8f192185d
+73745a54d4a424feb797fab8de627851
+7383e0e501b513991ce21d82e37c008c
+73850859bfd341e80c5182a26b1ee6be
+7385421fbf5cb41ecbd8cd5e87a80e19
+7386dcfe66f95b17e33e091f8265998d
+7388608d57046211cf195292ed886716
+738b34cfffaaec1fbf09ca82e9f56eb1
+738c5bf18fe78f85bf1ff57d26dcb81d
+738dfbf4885cadc6715b59568a70dcb6
+73923e67457ed90a3c87529b606c1e1e
+739712434749a89ccf38f2d4feecfc6d
+739a0f912f3db4e7b9cca41a6c5298f5
+739b5a34d5848250c2642fd2a070c817
+739fe886398d3993d9362a07fdaad8f5
+73a0a6b5d8f24467a34ef321e8cfba7f
+73a631da7eda99fb1f5fb9aadd700bb2
+73ac5b9f81ad6ea075429b33fb3a8c11
+73ae54bd8a8a735f97907e8ad1e20abf
+73b4568b316fc30424f09d31e145199b
+73b633b067dd8af0b7cbbfefac274d4b
+73c11316cbc71f67980a577b288451ce
+73c854693644cd41694b5d55f8e5e052
+73cd3a1131a26315d5185ced1fd1567e
+73cdef0968d451a70fc7358c2aa7bf40
+73d2685938ed445fbfee036d73f35c35
+73d76b1354a940341aeb0d36bdb107a7
+73dc5f64d89b534ebdbb00b54212144d
+73dd8f76e0fe8196e258fdaf0aa37ab2
+73df0da3461d00492b51687c0d382b8a
+73e54edf0c9de4fcdb396393e793af8c
+73e74501142e471610fd11d626945683
+73e8de6af8c58de2499f69d8a52879eb
+73ee76325e7e66dbc823d4320684d0e7
+73f6c0e2c3eae7d75d8f838f6428b938
+73fc418ebac9d06d37998d3ca11bd9ff
+73fc523908a5f110b34ca01a29c45cfe
+73ffe2144e5025b16ff89f1ac6e3d8fd
+74025f5321d3f2b8860ada325a2bb6b7
+7410734c59b85af83d0261ea6f97d11c
+7411b8c5c4e77467eca34627196495b4
+7415b71dabd10766174f8848032804fa
+7417c3943b3836aec1ba0d06c4af2d53
+74205614b5645047b0df2d48d3b6e463
+74219b088ec2890a32eefd5175b1ae0a
+742afbec7a00203f8683e5b15d6dfb25
+742b4e6179edab9735f96a7ef3f58358
+742d3ba3050255ff05b60c905b83bcfd
+743f80356df5abb9e73f386609d67fdb
+7441fed93afaec98d1a9c494d4904ec6
+74434e11445fed550864b86b1f25dac6
+74460f924a476dc8c371d10b0ef30e89
+744a03ebc53d45a54485e54ed3ede695
+744a927c5f628c3f4bd0e4c8f0a00f1c
+744ba0c389c58dc64a3f83ef913b32d4
+7451b0324ae648f9cf76d0bad16ac2a9
+7452ad5e492daf35280ac2f4008de755
+7459e9572301cad87e35ebceea164d46
+745c51669ae05fa2e285e1f81d992523
+74627957863e5f1755b4b288f7923786
+7467ada3459d126e92f9e2d57e1075e5
+746f3572965fac5dc67507565a63e1f7
+746f58a34a5c43bb28b5673dbc5f9419
+74703c91ce77bc2531e4793268157d92
+7471bbda83b7a0b53efc392765b19b39
+7477dd1f4b47cffd13052c320576a5e0
+74782ecbb7dfacc4dd88533a6be54c56
+747baff0e4d6145cfc40696b2e5646bf
+747f4b9c5fd7d13f1b2611732745a589
+74850be58d8daafda6ee8fdf3d864734
+748735160895517fa79a565f4395ff21
+748d8de4a8048782dcf9f2cc8f7eb817
+7496569e48dc3e578c1c9e165d6a129e
+749bb3b1088d4f5b40865b3e7e5188a6
+74a1f583ed3eae0053cc0f9a056f24c8
+74a2a464ec4e50c2d24f79ce5486753b
+74a2e6c29f5c426b2852bf4f5ab5876c
+74a3257bf080920e94614f2bddf6dc9e
+74a51e2c08838bf786ff0a4a1b631a5f
+74a6149cfe776c091b7e6480f687b550
+74b216776a185b60b889c138bfcdd8ce
+74b5b1cfcdef5ad6f9bb7f2552456711
+74b677479b37e69e511ce9fa5f892f83
+74c087ac78640c919782870ae10c4184
+74cd904c8b8c0546af153bb38ab7a202
+74d085fac55dfec3c672e7c75fcdec96
+74d1fa0e17c04403e12fabc2cb81a110
+74d208edec901939d09101893c6998fc
+74d581b823fd7e50898b56b4c72ab6c3
+74d5f64471119715be7f4e8615c3c539
+74d6d7ddb327b2a408b81ea439850cf9
+74dc835bd5ab91ad948280e85a4c3a38
+74deede98ca8606028e83a4242074170
+74e118f76612c372d01b6e2c3dcec774
+74e41883e1504136a4ba0f2e4158b60f
+74efcb4a0d188cf1d16d9b68d5c4a1d4
+74f02efaae8165e225653248a6372120
+74f18374515f699559876f76aaf0f0d4
+74f7f489c15494eddf736e8b360c1d76
+7500a349828fa323e5341dde55f93502
+750188bb2b5acb75aec33dcd44da2e03
+7501f7bee27212c6cdc20eacdae53612
+7503ac5557dc643516810bfb87232741
+7509fff59eb32f2ff770cc95ff605052
+750a70e327e769a9dc31686a9073cb6b
+750f729c8e2a6a6473ae91237f29af25
+751164187f6f0d850f4e5af4168fdb5d
+7514345ff4f7746f7fef1c158c58a888
+75151762895f5afbb598f406d5de78ea
+751685187432e011a09f02935aa2dcdb
+751f1dec5d5d1b4dec62c972b9b37960
+751f31d4152c3472d275cc5acaa24bbc
+75223b15ec0bbde4410ecad4e756f196
+7525d4deb95ad651fc5231e338ff8a59
+752ec87ab4834fe2d143d254e1a8cbb1
+7530d73a182730e5ab7ea73fccbf9f26
+75328cd04be0b83f68c1609d134206a8
+75392f32d496cc16903b15950212b912
+7539dd3fd7f256fd9d902fd12d29913c
+75427a8de2c3207430ccb38a9aa732a3
+754314fc5fb0e5226b86188383184d2e
+7553a7fa946c52504ff53dcb85de8d62
+755404bef5ae044ffd724b84d6a35ef9
+75552b80ab6ca4b8427270114b721d66
+75595cc283bcc77f7f6a4b9b4bac191c
+755b9ff3a8b5b791e83541482a825c9c
+755c69222245b9b6f158f514a93a34f7
+7560100107cd39300a037634781e6230
+75610fa24304cd15d1f11dfe7d2dc2d3
+756141351c01ca7c52c800073f7c691a
+75657eae2727d6cf4dc34497389d74d4
+756ad34f64ca92ae1186bc2dd37f1634
+756aead7f75f041c2efb75a52aa6907e
+756cd6ff439c490a164db526800cf6ec
+756f1eabe53576d749b5203f60369c9d
+75735d793341ee9ac6cb0619f0fd8083
+7575433f7f06e586dccefbcefde64b94
+7576275bd45b56feed62e708b71d38f2
+7577696a33a283979a5d4d5b79f30393
+757a65d5efaaa42468f02df51fdfc9a6
+757ae7ca6517416abd02b50c0bd12383
+75862842c95b9f534bb90b45d71ed95a
+75965f3c30417806d1def37cfb0cb3f4
+759e78513fefb6a335b942fa63ff25eb
+75a7eb541a3cb23e38663cae0e5f5916
+75aac3311bd5fdc8666024407f3403d7
+75ac19cf952334ea660defcae70b28f5
+75b42d7a2e160cb2c92b1054eb8f3986
+75b519fc70615de7a1e3a634af6bd6bb
+75b62fdfee732cca7de5d308eeafacd4
+75b6e3c51ea9f7b87096a380d16f65e3
+75b7f9dde35ab9c5328980525e6eb354
+75b9430e87638fe94802cfc968b50278
+75bb54960ba8ad5e6ee95f23e7d3bb9b
+75c06283aeb4b0894bea403cb66772c5
+75c82f3a9f2c3cda3bb20fc059752d57
+75ca77c3ea1207b79b8d678fc73bb1c7
+75cb7d1b8c9a2742595c4b96edcbbe0d
+75cb8a2378fda15577cc41d2ccf03660
+75cd4f2c648c08ee65fbe9fbba414694
+75ce461672f7ffc5c366b545712c4eab
+75ce772c3619d44df32f1862f9e1b2cd
+75d5f129a46378a474958e14e07c4dfb
+75d7ec5211a938f0a072f6ce0b4684b6
+75d847c133501f39e4a4b6599b590e30
+75e1faff31da4b1d1d07341548f784d6
+75e5746071a09c9c2156fdfb5626b6ec
+75ea88592f7586bbbc2778164e6c6d98
+75eae2100b15db737369ed4168cecdfd
+75ec5bcc7162dcbc9b6f9dc04dbe494a
+75ef31d6d1e7e57f05969dd1a5dc07b3
+75f26909afc1c485a59a6b7464ffad0e
+75f4c55f0aa2297ab2ae95d6050acc47
+75f5d8f6e18651a54575d7acc19ba59c
+75f6e7e0f401d311e9b21cdd86a1560d
+75f84f68e5f5daf2c952c84412a4596d
+75f8fd9ee97eee2c358f2a3c9ed6cf39
+75fa872f3e9531ef2d0b8d765974b89d
+7601153a4b73cdd4aee9991a32644967
+7607394e421df6e48bebfb127c072ac6
+7607bc98b05f11ed9d8a7f7b46c828dd
+76080fd492ab84d5d1d6ce0ee076411f
+7608cb5a72f95d9cc5f3cee825988ed8
+760e8b6af37e2a93c62ace3d144797ec
+76198249db02529642f053524b4e6470
+761a27089e07282c78943359968eeabe
+761d18b7e1e16db1aefb118966b28951
+761fac49aa7858693deeac7b58cb7578
+762b055b60a9af079841ea320c28a4b7
+762c800b0beedc00ad25bb08ae2d727d
+7632f441f07e4ff0ba342138423fdec3
+76365401ec0a14cfe5d430167e60f793
+763696ee1beaa32cd87d51434cbedb14
+76451c5e0156643431767fe64113f16b
+764b91ad44767e67b5582c6cebcdd84d
+764bbee44c40b4b377c245e40846ec1b
+765368d75d8e89a971e52949193507af
+7659e478620fa04a6d8a55559d2899c0
+7659f5dd277732db2226727790c1de92
+765dbce76e64f5e24eea46fc89a9e380
+765f2d1848f5f9d3c8ed63f4618ca0a6
+76607325dbc512bae1ec9ff6e926b517
+76638822d2c4b1d5860e1081467ef8dc
+766505e7b08068bc5f62317b74c8bd41
+7666c7c68e986e2ecaa651b215337530
+766b2e79a4adf32cc3c5d6fda95ce9c3
+7671a3ab2381a854543f8225ecfb3773
+7672e8d96e407cc7064185d7c66fc0a6
+7676751b74ad398b2b8c4ac5d10fc9ad
+76815e64e14401342b1e852d09a4cb1b
+768328413075628d8b02697e91f2c294
+7685b53ff4cbd20d3933f055c6db2421
+76867e1cfd205c4aa7e3a33ddfc9aabe
+7686f61dcd48553bdc5299e229ffc273
+768899e1c070ba8564cc45d595acf897
+768abe1f43fe2c55f67f1142d2e36783
+768dc8863736bb679c9a35700fe3014b
+7695790b05e4250102672cf12803c31f
+769793cf60614ed827114eae8486978b
+769907303ba1e0fe4c635df229b7268a
+76a0a218486a26d486aca6e5e450b081
+76a2cf20ae22842cac67c2b84f692380
+76aaea8d7257ee97823d35e0d0ade2d0
+76ad9bef19347cec3ef2dc3e110e5143
+76b4cf96cf96ede42de839f3791488b2
+76b89820dc7952e7f29e56ac01f48eed
+76bad596317167e08fc2e1048dea78f5
+76be668f145191fdfa9e06916a5a1340
+76c1b9a08eb7545c7c5bb20629405872
+76c279f72b97be96109acea635db0a27
+76c77734877a35e8a186f93bfd8fb588
+76ca5fc8aa44ed7290e8b3a6f498615b
+76cdacaae25015bbe5abc9cbd43a088c
+76ce2909146e98dda90a78bc299e70b1
+76ce99f35e6b96149facb47f02d07230
+76d3203cc086e312a2b0af637832b4ab
+76e2da5790b186836eb26cc8ce76ce44
+76e92715bc6377df686d4160cd7e0e45
+76ea08f5afc8a51e5a9ac665fb94e912
+76ee85a4a08602abebeea642b9f8d08f
+76f1a068de3cbd433dae7146775b6986
+76f1c5f700dd6f573b5757481381e50f
+76f509576ff1f823ad0262b6234c8d86
+76f5a4673b7d0198a5b7964112fb4ad5
+76fdbda2bfb22b239079a8b346a9746a
+76ff567188b793f04622bd10346765c3
+77090bd1f6939aeff93b51f948445d63
+770b7a9e7eb98ae9c543b142cfd8c689
+770f76b682f2e28e36324c643fac7037
+7710054c0952c134de1c5cfc466f8040
+7712a75943c6cd478273cba5b1af6c9e
+7715422594b72166ec93a5220053c941
+7719ba479ae6ee85588e649cf11b4738
+771ef84bcbbefb37d2675e2b2551a827
+77230595b9d4b322d0c4ad42ada9cbe9
+7728bd7f4f530b9ee80049302e983dbd
+7728f5d0cfe258d130c460a5f637a021
+7729720472d5d2bd0928fbac485f3787
+772977d188e0f158d4532028d9d2ec08
+772cc1843ac897dfd5900c4861c13515
+772f34a6c476709667030675dad36612
+772f66885643a7c740fbad203f8472fd
+773250f1bfc439fddb9ee4a8f00cc44a
+77346d7992de3a34ba4f0f6b78bef2e2
+773a8196a84a459f7238d8e0fe91f6ea
+773c1924ba24ca13864d0387bf4bc81a
+773f433d7ce0dcde71061392d0f7eac5
+774d9c42c7593d2775cbc4c77036a712
+774f2ded0e126546c215258d2d49c2dc
+7753fe5b0a70dbd0b36e95e90faebcfe
+77577297a448b6c0f451b258fb2642f3
+775841e8fcacbecee5ea87141d64daeb
+776239277baa147728b48174c301bd19
+7762eb1aac8775def76b5e14c3eaebcc
+77642c224df5fc38ce02c37af4fe413c
+7765c8af5bf85464d549262086b135cd
+7768a32007279e2387090c589197c0be
+776d36fdfa6f43d3b00d79d319d4f3b6
+7770fe7ef3cc7f2661943e1c1bcf30dc
+7772104b6a48d0a964eab08c35b1e4a4
+7772db192e29a0f36e81a7ee1f866fa4
+7777ff7c9ece94b24c685daf078827fb
+777a362eea1fdfb519f75526bdf30286
+7782e41382ad913d475485f23556b6df
+77892af404fef357f6d8878e347d4727
+778a4206c65c60b96c7bf4558f41e3bc
+7791b73de84dd70b0d820a27c1ace434
+7793c6dd52b1cd8a5a6c2caa2bcc242a
+779732893f44dccc0b0b690a16b4b89b
+779ee912ca8d4c148174ee9c990d58e9
+77a36ff89f19e5c117e17e8952d536a5
+77a575ba2c666c3e3382d701d4a8b033
+77a9e1612cd750b41fa61757d4d68877
+77b4659485c8c0bf671b14ad659909e7
+77bc4b354310e95366576cc317775f79
+77bd0cfa9deecfb90dcbbb3b9b76434a
+77c52f354b2202a8f94c5b2a03788801
+77d03d27b69f931b23a49d2f4ac51aec
+77d22316bf50a026848dfc7d7d9c9bbb
+77dda55b96f8ededd8363bba6dd75378
+77df6e8280eb6ae7212cbe3ddab11087
+77e822b8495472ccc3d78a6aa96db11a
+77e9bfc85482f66956f87c5009ab1c0b
+77ec2a2613a13b96aad36469eada6642
+77ecf85647962b102490822ee8d4e069
+77f29fa42e74b78abffb0585ced007fb
+77f85ef553a64b9eefb399de742de714
+77fb4e91f977a000b417cc5a44f99680
+77fee2fd1a000fa4186186bf989e026e
+7805986fab6c7f4137febd105079e668
+7808662175d47e184382a36a20269865
+780d1eb461415bbf30520179f459a2e4
+780e625e9dbdc2dc0cdd92a93670cc67
+7810ca4e37ef6b311117343ddde8c163
+7810d9ca371ef3dab39e36a825cb087b
+78113b832e0b372a15c9cda136011556
+78113f4e4b795c037102f1522249fb9b
+7813454108d5aa6af2a0a3683cee9058
+78137ddcac84db12d14a7acbdd0ad3d5
+7814202bbbb01913511f9a8343d6b797
+78168e511585355cafc891fa8cf63738
+781b0fe1fe8a7825cdfcbe6fb3c6371e
+781d65e987001643304f7e684f883b79
+781db07c11c6461fb96e8cf9fdff003f
+7823351a0010db45d357816d90b3e096
+7827c944f5c2b2ea78149b1c1941c28a
+782ae383ededb9be3da7c5027292f39d
+782bf247450224831354cf237c56c61e
+782cd8c2754b778ace4856cd85e3f711
+783354fbdd57ffb2a746ab263778b1e6
+78366cad4903755ddce9548e7243ace6
+783e54245957f75c9b3a906d111950c1
+784294e2aa48b75df41908bffc5dfff8
+7847759b14cfc2545e295ad5b4fd248e
+7847d20db0bceb94f93f0e87ba96fdc4
+784ed53fa39f1b8b234fbd5f125af206
+78517d982ee511293bb9e5593f7da641
+7854aff7d68d4a5aac52edbd496cfd8f
+7855ababe4ee7a53f8806835236a7b77
+78566f74caad6f98d450b6e7134b9e7a
+7859b7f818365615e243c6140c7020e7
+78628a802d679ba83de1fba9016deb52
+786410752874111937bfc838dc79ce9e
+7866670e83fb8dff959db686c1ad127c
+78677bef2c8c515af57c83a4840e1993
+786fa7d62c99d8e098f43eae734048cd
+7870b85ce4f5c3028cac7e5aea212e70
+78713fe73f292c6a56ff755594e68516
+7874c378896ff89ae7508d2b47639e81
+7874f4600e41e35a134170e32a6c7739
+787fd0c866c6f705ad3ee228cbeb7997
+7881f34a7f3ec531d7fdb3b998839879
+7884c67ab833bfa586f5a1be415f2d38
+7888d24a01d7d3aec25900775004ca0b
+7889b11d99a431bdb7ce64a5571e07dc
+788a0deb0264da6ea8e8c76585cc18d1
+788a131a4603338ffd99640c8ed08919
+788acc82e61837142ea96a0fe18b861f
+78938a2f96ad676ae0b8539335e12c7c
+7896578870ae75025390f0c11511d78f
+789a15f55d5c2b97b1ba91bfd896ec29
+789cf5f6ed9908f013f7a730982db529
+789fce2588a968105e9e7b13b2d8e70e
+78a523969ce53bd58e6e82a0541a1b1b
+78a659b6df16e3658dcead92475f5120
+78ae96c8a50b35fcf1eeaf8ad9de25c7
+78bb8f5c0262014ee68d4761af92c448
+78bdbae06fa818f7929bfa307a12bcc5
+78bf751ff25197cd1ee8c29fba66b6e0
+78c7c5866e70153cca255926573694c0
+78c924ca1e152fdd88ebc772a84e4524
+78cb7c7c31c9635a89b0bd13feb257d7
+78ce5ad040e06c58cde4de011db7552a
+78d737c8ec70ded91442770af85cece7
+78dde448e653e6302809233ec0cb19d6
+78dfbfced373358f7b4f536a85a13d04
+78e216828ce758827b96facc5b9c2419
+78e2a6946a6551139a899f159d027d24
+78e30137b2326e5f91150384337ff244
+78e455e450bd2861258e7d597e444fa5
+78ea05a60010cc035af37a280637f134
+78ee1bf4e43182c7b49ef0d2b329e63d
+78f648b3f2da9663ea99318220b92631
+78f6d313b33098cb63adbffd942c2692
+78f749dbb9635b61d95fd77c654f09e1
+7906495a15f82da59bfa79e1536e4457
+790b72790dd5d508a42139b622912edc
+790d9acf7ad26d4720ff09e27f8c050b
+790f205af1a17dc5b7cdc3ecb997409e
+7913cd130e18265104f42fe4d4b9c0ba
+792048a14a704532bca033f1ffd0cbb0
+792640c94dc6d8a13b70af1b22a3e88c
+7928632cabeba99de781f50ae909e9c8
+7928f8035489a286786827acabb980d6
+792a38c756291254c57999b6e7e285a9
+7930623f70c285c27eced62cf1ece718
+793f8ce4fd2fbbeb14f60f3e27ccb17a
+7942d3107945a73c384dad5bea211654
+794c202c3b281be505998080bc7192ab
+79514b72447649610db18e0aaeb53dbe
+7951f18200e920faed16bcfa80e62c0e
+796491e43f8aff2fff0790dce552875b
+79736d63ea9e5bc07e4fcfcc54346fc4
+797987c43e057f0aa673cff76d3b1d4a
+7981087e629805464fc0040cb9a48c60
+79876b43065b114161a2a2a00b24ffca
+798e5ae737c3e8b7840f5a0775d06277
+79933e97a568811149beda08c6992bab
+79941d9494aabc0cac79f17c27936fec
+79982b35df1834ba003d463b4de60a69
+7998832a321e24c9be118c333f4113a7
+799940916e52418aeb24d8d6c871da7f
+799cef9d3674a62fcee7801442a90893
+79a1be17f5491ea6e95f6a3c16bee3c4
+79b1b21f0950102da33d2591b0f5ca6e
+79b36e4458c293a6bf93e7f6e8c59184
+79be487850dd3847acd02b450b2c5fdc
+79c656c62c3385e06cd08e8208837da9
+79c8815548e52747dc904dd6b7bdb336
+79c9ba5237eec035f48f5a3241bf6aa8
+79cc18458d3e03f58bbaaf921946a168
+79cc5850b3f222816c9a760fc7aa7948
+79cfdb9408fea80210183c8d4d97504a
+79d3e41597266298071355e8949c46ee
+79d5a4fc7abed3fbbd89f316ef4a7029
+79d64eaab1f5ee502b4bb0305100cacc
+79da5c23e6a98075706e5d784e521348
+79df27edc8416cea8329d062cb7be46d
+79df41dfcd5464bf21f0adadf059b9a1
+79eb9a4e97f876457942b1dd86a5bca9
+79eba5fa7ded7286acfdd19bcae2643f
+79ee162c6849fe512833ae10893aec5f
+79ee9d39961a70f6bd8637487797b468
+79f475feb2a98845cba93a7cad958823
+79f8a41600be68ffe89fa83b5075e602
+79fbbc22e665960207b2ca0464bdcb2f
+79fe92f82a8f71a6ad7aa60badf7ad42
+79ffad0d06b1044587297400b2d6fbd1
+7a004af390ce64b63708dfa65afe6e20
+7a00d83d51c910292d3b97c753534b7e
+7a047d89c889154be8dbfc794f163f12
+7a094a17dc374d06f822e101511af3b1
+7a0edaaf0d3a3074e3113632181b5114
+7a0f89ce9fb46fb3d71828ba15ed1e51
+7a1ce4ea19139a68f6831c9141e2c63d
+7a22805d92d5cc7c02e85998d25ff458
+7a22c867fd1d5412f69f5f98e64c1936
+7a22ea9304af8065d8c8d588872eaba9
+7a2e49987bc4eacb879b692f70bbe5d3
+7a2fa297f6f748face2ceac77814c5b0
+7a31ec289b5e6a3e15b7cccbb7d44f2c
+7a33dc7bab012c5b51aeab2fddbd7d5a
+7a34964d0397ee6bc784a736f1a2c569
+7a358636acb27d54e77d7e2c6176abaf
+7a36d81e2c8f7b96de864a8bec1ae8f9
+7a3e1c0807ea6c4399ef18430caf66d2
+7a3e7e046ecefe38c9e68cbc64b4e833
+7a42ef1eb19b571a47dc201ca8519317
+7a434519c78d96a1f82ae6cd1f824b19
+7a45f297a70a14d867c3d9eb310c4b41
+7a467a76cf1ff1297115becaeeb1c589
+7a48cf7cac8790f6d4866ae9cec8eaa6
+7a4971a3005a07d4ada01c42ab52ce81
+7a4cae07f21b78111e1aab10261d1270
+7a5420b968b6006b403975182cb4b613
+7a562dd0744ec0f5e6dc3e08122806c7
+7a5aaba6700299419624e03a312ca813
+7a5f40eabb6cc045075a1701d81bbbba
+7a5ffdfafc23325153586b30be1238d7
+7a720f93c16eedfc776315ddf5b825a7
+7a7776ef60b6cf8eafafe0b61e1ee2c3
+7a7f10b9628d5270c8dbaf14dc46314b
+7a882b4e95e352dca2e595b76bdd2d60
+7a8a71ba52af2199bf56a16cbb0dc4e8
+7a914c3e8acdc4fb1fca289d9d831ed8
+7a9164a21bba9867f4fec396eaa8f481
+7a9dd700b977733d196204430e748c4e
+7aa45d9d7c325b8dc1545762eadc1074
+7aa491075903c739890aa9a3f35b4901
+7aabad2470955c493f8e23931412ce81
+7aae04b7f01a0dcfde37aae4eba08a31
+7aaf11e04738066d17fbc953ee472b84
+7abca3f5e7219532aca6ba989662c01c
+7ac071d80c5d733e5fd02215777ca92b
+7ac3e6a92877ad59d5baf53de27a1ace
+7acd8ac45a6fd2ac766612491f2c9d07
+7ad0d488837f31bd9df4b22d41a1629e
+7ad339a8f1d527be4bc8c34ad6e2a389
+7ad46ebe0aef62c6650def37251394b5
+7ad6868e5ecae31c98dc9bddd59de89b
+7add00236520952f7368d976ebb08473
+7add70a1a7abce9d26bddd418791ddef
+7ae36a06049dd5a66d96e41cd71e5b86
+7ae8a46c1ae80a7e94030af67706341b
+7ae9cc169e9ba57b02bcfa11c2ca7b57
+7aeb5450540e0fd9490d550e59ca26ab
+7aed2b672938c1a4308b235be468da6d
+7aeebb22769a63dea80368fb1563c123
+7af3cb643b72f45870ac32a53bd3450f
+7af7dcb71f74734be0ea396a2c436000
+7afd50ba03ba3ff3e574554e15f25874
+7affaead8e98a6da6d0c85c5142692fb
+7b0140b840ba6107bf1ffb314c9eae61
+7b03b578854f6bedf80864c770ae5f06
+7b05f3e6e9785cbe01c653dfb28e21e8
+7b062da8fd54c145d543f36056b0095c
+7b097737166e18fa941adaace7f3d86f
+7b0c892896c3f7354788408e8a2a455d
+7b0d03a6242fa31a6af47ce5f76362ee
+7b14594e92d6f013629df4c096758185
+7b153ea5acf5ecf4e892e03f0c23b0b5
+7b154424762ba40d24eeaffa7cb39f81
+7b163de3a892186a19f346a203d97569
+7b17fa6f55b82524ce5e3d9574e136ce
+7b1d31c05ba22180574612fa4a241d6d
+7b213c4efc03f0375381c8e16ee8f49a
+7b225685ee3edf502656f09256a2c6c2
+7b22a4ec1466e66e4d89fa49b80f9960
+7b309fad7ccd301c55f2ef4914d435c4
+7b32f2d8035c00b6edcced99a87ef1f3
+7b3588f0d983994e25688b44b795bf26
+7b3b672ab3ea1b3e46e23e45e31a9401
+7b429043f2415a0b44731f5641504b4d
+7b442b1334b98ea3e5f41168b4ff558c
+7b63cc9360fd77b94c7e47e7ba1ae88f
+7b6702b1b3faf33021cf7f360092d358
+7b6967cfc8216033b768fef9e11dea1c
+7b69c2b1c13b5307ae124b9a0feda140
+7b71cbbd72e74c19ac03c781392e3f08
+7b7595e06263fd7f46c2351c3b9f0dd2
+7b784bbd7df9f42031f2576a0d7c6bdc
+7b786a6e77c7813cae6cca340e1c6f59
+7b7b33ca337880f158970b09f5fb529e
+7b7cd584f4c78636466e82bc1341a1ff
+7b7d6e7858c8cabb93c911a5d5558c31
+7b7f950034c825544a6df2cd3c0f64ff
+7b8947446b06facfb6a4c342815edd31
+7b8a920f0fb3536848eb0359f5b7fb05
+7b8b41fa08b9c075f750031379e4bcdd
+7b8c76094226a32f3d8d77bb4c763f96
+7b8d2b1ae1174b437d498096c0c9c007
+7b8d74175d798b077a60e7bffcb88335
+7b90d21217515a211b91a6f8a6a419a8
+7b935b99ab33ad2c7347b49aff084148
+7b93dc69018654419ae0b8f593251ff4
+7b94039042e12a295b6385093319830b
+7b96005cbfdd5aebce53bd016d04f563
+7b96b7f1c5649bf66bf90255e5bebbfa
+7b993ea3c8a58348daeb125b2e050a32
+7b99b23d805faa9ef0d9fefa6c279ee8
+7b9c965f70072c2e34f4cd10fa8cc870
+7ba28a6465643b9262e97cbe00a1f1c5
+7ba55fa28b4b8ad8effe6f640b1a5f78
+7ba5b52ef1a4554d4f7b281ccab01cc3
+7ba871d7091d7a7d4afde47fe1cbb3d1
+7baa1392e03609803bbea9e5659d1f16
+7baa6d9dc521db9b5631b265ea6b7046
+7baacd9e63c7b6c9247ed4ae3dd0e5bc
+7bace45e0b7d8b7baad0a8d65ac3bb72
+7bb1284c19daad261e9c65858fa57810
+7bb48ed058aad494a48f5e8ec836bab9
+7bb52906af96bbabc0abc3fdaed78281
+7bb9831a4363fe3f86ee54b070857038
+7bbc27e30756e1599cbfddbcc7529e9a
+7bbead025c086daa9e99b9a0277516e9
+7bc3bae0f8766c419f48a57ce4cd087b
+7bc4316933330f0864d8591a16b426ab
+7bc50f19180ed63af24c5835ef97af01
+7bc74d8c9c00e0d578b991f703b8cc26
+7bd164bef2c79504eac96acc9e79f100
+7bd21b193bf2efa103aef06120ebabf2
+7bd620bb7d55c0d7503d17844ea24ecd
+7bd8c8013161148d3fac2304d5acc660
+7bda13c1e80520b45b3aa45c5fbfd562
+7bdc2d2e5935fc3c90568c47ddfb6824
+7bde0510f4cf964a46997604b10ad60f
+7be29eb361348f8e9fe2d74ca12b1a87
+7be8e454ec97b36718ab542215e56560
+7bebc5928a98f5b4d65bb78f20336093
+7bed540e84c95adda43e0a2a999ef46d
+7bf0151cdd7cab73fa668de92ec9e9c5
+7bf491f9e1c779cd94f0e329b784047b
+7bfa4fcb9600173768b0558e0fa37825
+7bfe072992413425f3bffe156cf3423e
+7c022193abca976b2efaddc5f52c6730
+7c0243001f7afe60d7fdbe20d70813a6
+7c04e0f26314183e3797cd340ec00fea
+7c0c63d1f3147ad29ac5384f54acd4ab
+7c0c84f0a78b11c35a29ec24a5b8e877
+7c117e7f592496e55accdc82ba5bba4e
+7c14e0cb788b270338323a30b8356299
+7c181f12c53899fe7fc01101929cbc66
+7c1b3182277453cbee001f1c62e9c475
+7c1df6bd892f2e205f5285467644bb9b
+7c1f465cd71296d72ddb6379922dfb64
+7c2004ecac7364540b93297722915718
+7c275ba67b5da741b5174736e3f0c812
+7c2c3d69010499ab56b2af4564737440
+7c2ed7dd1602af288bc67ce15cf27687
+7c2f8ed908a6a029dc87030b3fbbf270
+7c2fb12104a46ffcf6ef034bf3a17567
+7c32801eb8bd44d694ef1df6181cc0c2
+7c3f198d2628523c4d9313f7889341e8
+7c4550174044ee51063c779668e4ff7a
+7c485d2b5ba5db2e2b9d8af4b0910d15
+7c487ea71379518516c5a6e0aaf693e3
+7c4fad76b8919f008d6d2958e902781f
+7c51f03d368f03528840e3eded391048
+7c5f38aa81dfbbdfb9b037631db5fb58
+7c6432951d9287ffaa6cb0be0a896cb7
+7c662c0887ef4a241004c5c5cd5fecaf
+7c6855910d0f8e7f702b9ee862ee36a0
+7c69a0aac1e6b3ca1b834aa323221321
+7c6d7d06beda6cc724601292905177dc
+7c6f5d58c555fcf8c5a5ccf484e5497b
+7c70fd545bb67a5a41b11a50ef1406a4
+7c7b38b7f361ef51683a941e3ac74d2d
+7c7c3c60f3c1897bc794b203e6c07935
+7c7e705871d824b9855e11608dcb430c
+7c7fcef28987003728041a594e1ece1c
+7c8735257fc745058fc5527cf32c3734
+7c8bdc0736d73c195d8cb5fa6ab514f6
+7c90210ca8a0ff3098dadf031e979865
+7c906485a529a0f09081550278ce4b09
+7ca496c4a3bc4f12e5470628cadac3cd
+7ca6b0af93757a202cf7a2d00dc8569f
+7ca929c64c3bba0a7e75a019a151056a
+7cab085bace31474a39a2050a4e74309
+7cabfe9028a3c22ba123d0e785ee66f9
+7cb2eec90180c45b6fb351dfbc83647a
+7cba4bc14fefcc7ecef1e49cc69d514d
+7cc6e45c91c27141c61ce99fb9a78327
+7cc904d7dba52b02251ea35404e422bf
+7cd643c3967a3a7e98df49e35a5cba12
+7cd936ad1fd9758d8e9e060d38b20486
+7cdb12d87a4c237284f8a8fb58b7a0cb
+7ce25770cf735f7df22447f021765bbe
+7ce35ed02846eccce84427996ea78a9d
+7ce8dcf826f5226505119a99cbe40616
+7cf7cdc7d475b804dd4d9ec3e6725323
+7cf8a7d3b55e1814ccbe24caa9b8bb29
+7cfeed2cb2775b9bed1a5568eed0b118
+7d01a58f3e80e40c30736fe48387c8a3
+7d02c477af05f252d667532b40b448ee
+7d03828c0d62e45765bb8a31a73f2c24
+7d0b1693f64e67feb23bb494d36dc91f
+7d0f866069d416aee5cdc8cb948436dc
+7d1076a092137583f59cc7c8af528b7c
+7d107741f7b851a38c5defea6cdafbb2
+7d120fea98407d573233ce1a897cf456
+7d14312f7f6fef0e7ee131168c63652d
+7d1610b11e414874301bd5b2f31a3b6a
+7d19552148675211ce53569ad1db97fe
+7d1d22cdb2bb4323f2b5d9c462147d03
+7d29482c7ba014c985a70498f1a2b236
+7d36f243fd925f95ded82fc12d877fef
+7d38ac588599055780a33fb5d624a864
+7d40d0e5023392d60e051d745c0b1fb3
+7d45e5c223e76a51194a7d509352764b
+7d46a579dc564565cf031f3d792be1dd
+7d475835844d9c196f778cb2a99d51fd
+7d492c1ee6600969ef90d3323897b760
+7d4937b51be4a4412801459175721969
+7d4a580a98be59ef0369a08d7fa5ee77
+7d4a8ba92e4a0d41f4824baa65cbece4
+7d4d462e4c77ebb093492a531be792ea
+7d4f9e48630e6370f9c984bc8e1ee055
+7d509f22df2fc9e864de96cd9fbec691
+7d58fa1363834697f3260d264dce4185
+7d5ecb638014e7eec7a053be0d20912e
+7d6bf54ce106e7fd6737606210da25f6
+7d6f32e195b1015951c57b84733b2230
+7d7338fb01cfe9cc26bc3bde96addca3
+7d74cb5610f31679c9d58dbe85dc22bc
+7d76fd8281ccf27b44da699652cb3976
+7d83380272eaefdfcd487c9db960f8b3
+7d835682beb54f37a9bdd4d563bbf365
+7d84a7615e85847c0e313a63800c8944
+7d89494402442054b5b805d4af80404d
+7d8acd079b6578e89e051eafe016f7a8
+7d8b671f459920e4f62e0d64f975770a
+7d8d1326178c56db49a1062a75dd4d2b
+7d8e12aea17e381ad66c8e5e8cb8bf18
+7d9448d6b37a5308e41f2f186ebb4d6c
+7d9f1d858e07414447b68acb966606ed
+7da786c3809c8e8bb93f5eef1ab5e926
+7da7da1b05bf5e9045c62823f764f392
+7daf8798b34733b16500d25ac861edbd
+7db3e5bcb7fe1bb380af497ed4156b3a
+7db57c0acda7f2e2f19d9c07f7157c87
+7db69cfc1e33de922c049ec66685daca
+7dbbc89fa915b6e207cbf4136ea36387
+7dbc01a15448ba5ef4a418eb635b5a3f
+7dc1d442105839a87202877702db7bc3
+7dcbd89cd4413e4619f7969536acc025
+7dd00f5b0572b7021269e12d9ca6b55a
+7dd0b30e4a9e6ef27312c5778ef42383
+7dd177f363113bff54be0aa88054e1f7
+7dd5bd725d7c33445a142874f690f3b2
+7dd95389de2ed2bc53978911e6dad680
+7ddb0bb6187e0e3ceadeef1b51191cbe
+7ddd00725d2934615a81a0c1db5932f6
+7ddd8ed803905d958d2bcc6c4533a6d5
+7de419fdb5788d46df45ff679979e1eb
+7de5d596b690088bd5f3eb7bfb43ccc4
+7de5d5b8d23becbb3933201ae48dda56
+7de7725ce59a63ffb39e4e0179810a97
+7de7f2d1e482e4c36b984729e87f973b
+7deb748571fe156aefb17646efb436af
+7dec67cff5b030849ea2944d36913cbe
+7dee78437b42c41f0497b0cd34b373c1
+7df5de076acbd57eb34245f12847695e
+7df8445189cb8e6833140cc492154d47
+7df92f6ae4d6e1800afd78df5bf0a8f1
+7dfb4e598c9648702924e32daacc58ab
+7dfce21465098790210d8eb02328d85b
+7e066c263bd9654784f3dab76a02d26b
+7e08fb4c8c5eaae327c2a4b2cf9f97ea
+7e0c5eee845b76b7f2cde01a564af3f9
+7e0d647ad1ab16691ebf163b7d2e65ac
+7e0d8c532fe0c5eaf5580dd3335b9697
+7e0e20d17d88d03abea88c55fc5ab7be
+7e0f6bcc73b55b8431ac605b63bea9b9
+7e16cfc654decbcae9c3ff5c4cffa554
+7e17587b374f13391b8c8ce502235147
+7e17d9bb1e6057ee99ebad2938f8b7ed
+7e19022bcfee9a05a81dbdbe90dc99a2
+7e1a58ecf42d045db8218643f00cabfe
+7e1ca8eca3d90342e91d0a5ef40cb50b
+7e30a4226f50fff94d46493f9badc7a5
+7e36a094e7f0bee3757ebf262ce09b8e
+7e4231aab014b61847ff6d7146bffb38
+7e4984ba28efe20e81841ac8e8d7e4c8
+7e4c76b54145ca17eec7b345ac072140
+7e5119c004ea79acf99a7bd3fe36ef23
+7e51d111c63b0cf63ef22958a21226ad
+7e530645c6d5791549ae4a2938bf97ec
+7e54973abefb8f48aae539e1a317f0bf
+7e55bcca01bf50b97eb2c0294b4ff641
+7e57a2c83c76aac808eb9ca926fc9e46
+7e5984f8662299731197dadf924eafab
+7e5a1866a33214f73c5ff61c3f176fa3
+7e633f8aaa2d1c505c1863bea4904f24
+7e63666438a42705caac112ab974d625
+7e63ba578bad3b6cded92973f95620d4
+7e6957b58e5cbc1c53e823d96c183ed4
+7e6a17005e88e809ee09593170007b48
+7e6b29fb7684214958d389db42bb1ec7
+7e6e5f3435a1faf7aa83b55ea1454ebc
+7e6f19357865aced17373cdc81fcb2a6
+7e713862c587a85cb7eb472070f3e7b2
+7e72fd328d79c8415ce5c6615aa72dbb
+7e74c34e28e020e8f5a12e30512f16b6
+7e7d25932c21ba85c1b4bf80c7b942f6
+7e928236febe9f2f8af192c042d941df
+7e92f2e356f0152f1d87a569244d2e1a
+7e935a13dcd27febbc1671c139c7e2ed
+7e94440e7fe7cd5c17c766c2baeaa1dc
+7e98a1d4d7dc18c49b553afee2e9b8ff
+7e9dcd2e118a6533690f2c85192c1956
+7ea58e0a8a9ffded9f864a3ad6c46abb
+7ea75b25b2132606a2ebec7b64aaa17d
+7ea99eaa46e88de99dcb2dfc4242a210
+7eb46cdc187f6a894046bbc7eb316462
+7eb7166e568408f75d4ebd999881ecbb
+7ec07e5e47a8b524ec939103e2f8d1b1
+7ec61b4e98da0531914ba09104ae9ee9
+7ecc0aa0f5b2980243c96a284abe608d
+7ecd5068d551eea9a6f18a3f978400f7
+7ecf740ae7e653b368aec4af0ffe6bfe
+7ed06acbfd5018d1b53159ac69244b42
+7ed6093f020145d0224f995b998b534c
+7ed60c743a0cb2664a610b344b60031b
+7ede16faa0ec228c4dd2cd4f7a91a534
+7ee1bc37b3ee673cffd539f7afc3f8c8
+7ee51476a27e4491521cf4fb94ac7122
+7ee69227e0c0a00b613a59a2b28fc6a0
+7eea5f5746027fc9f41b889f3592ca75
+7ef2eead9669efb6360552398388b4ce
+7ef6341d0b6b2d1cac54904cc9118bc6
+7ef642387f4068dd741deca3237c6571
+7ef6f91fb5e5d264f163bd9daf8abad2
+7f02745e23cab68d541999a3569799b9
+7f0448e52822992594828759cbedb60a
+7f092bb782e336d2c68efa46d2804945
+7f09f4e093868971632310b904dce074
+7f1414d4369c88ff5fda12fcd1e49407
+7f1ca31f953e32b78fa4517fad7ff8d7
+7f211e173f10bd3a017352864f4c6080
+7f23b1ccbb4c84588eb439c3599dbbe2
+7f28b1b162157fe95bc0cf3a97641e62
+7f30d0748669ac4f2519e2c5220f5290
+7f351f31e1d2e197e33da2e6c25ce361
+7f364aa7a3d5b8967b377d4fc6a348c9
+7f372ddff28b7d71200607fa68dfef59
+7f3e3408910e2e8666e6a368c8e61612
+7f3e97eb822588347157fff87f00f941
+7f4075bf17edae852fe2c35d98a2135e
+7f41de15f5a9153edcaed9ffb346452d
+7f44ae4ba139baba725b61418b3858e3
+7f48e85c62dc2744c653b56a54f98ec7
+7f4bd6994308225799bc200d3f2baf37
+7f4bd76498bf16b80849760d4076e483
+7f4e8b760e5929cb0d277115160d07c3
+7f5523ce9fbb0e2ea819bd46d58d6ea5
+7f58855b59ce48464c7ffd1eeebcedca
+7f5a034e1cb0ee910f62e7ca0ac153df
+7f5a857341cc8eaa2a9e6ec4d6f03dfb
+7f5bdb3d3f923be256f39e78760c08af
+7f5e48107e91613a352f561a0a03849d
+7f60b43c85d3c449273b7545a82271a4
+7f640068a36ad5bc4db04c58f2131af8
+7f665b1a03c5c19bf6d014d2c39d236b
+7f6e6235f201aeefa74120b549f680f8
+7f76602c63250d2d670db96f35601b27
+7f78b7608a63fa3bb824bd318c5b288b
+7f7ad84aa903b91bbbe7bc70821f9035
+7f7b6bb7cf38344704855f1d71cb751c
+7f7be49eaf54c2458f271f605e847661
+7f7ed6beac93e5b1cabda04ff2154fa5
+7f83dd79375b017c760b34362d71e5fd
+7f852535345209ec5a5d81a1253273e4
+7f877c0c6bcc45f6bd2762f06d19343e
+7f8d7877f9d34c4b446a0b76989f6d7d
+7f8eafc748f99eeab5b19b9b347ca9dd
+7f923f6ff6949fe8f1b909fe62803c49
+7f93942ca8967ce33f89e6270e9d3080
+7f96d5b954677b2c1c0168b3f59cf110
+7f97e5a4d01c68202602a43d27d6d231
+7f9d82f64bc8d7c4ebe7e3f0a5a542e9
+7fa5c478e15c506de7ce2d34411af327
+7fa95bb2cb629ec59f3b52f1b8adaffa
+7fa9d731110fb509a959ae3fc92e6f12
+7face646dd54b325fac515858c9e3bf7
+7faef7dcbc425722e0f121f819d9953f
+7fb3241dc87157df3a313394c4ccb2a4
+7fb3ddda51df501540fca7ac22ba4181
+7fb93ba66476316381e6cefe88d1d9e6
+7fbcda6087bd27d9f955e413c0d63b55
+7fbcf0b82dfcd72b59b8b761978d9b23
+7fbefbd4adc895023215b48dfc4b93e3
+7fc095dc97070eb4b1a5284792f1f777
+7fc0ffd9006e94acbcd5d7a9aeca74de
+7fc260034ff7bddf7f08dc94a8d7f419
+7fc9396f2d064966dc8f67f141341a6b
+7fc9434ed5160a58217e363d755f619f
+7fca16c35c5011400f9de0385eed69da
+7fcc8134508c34e9f478363ccbd66bfe
+7fcc9c2e49e4acdd4685696948b51d81
+7fd72ba23910cd9790dbdfd7bf36d862
+7fda81a0c965e32e85dab6496ce8e142
+7fdc7bfb397683bd83f6d98a2075e98d
+7fddd775eedeefc9fccd444dc813ae6e
+7fde123967cf7e708bd2cb2a4e5cbbb7
+7fe3468481fb452b1ee8c7fe043cefaa
+7fec93f76087d8084f0ad7901d775f78
+7ffb376290cc2fadad89a98d2cfde256
+8004cbff78666b12090efda2ea5b05ac
+80081e99722f26e6e0e33c04bfc92262
+80095ba0f6a1dd0f4227060965a39153
+80107750e9cfaeadff8092e4841a0ee8
+8015f00d13087b42c97d8501c216fbfd
+801716a4dea2ed1b10d5877c717fa898
+801887f7c94da94cd497e5504a61c5c9
+801e758608bdc95206156fb1b9f998a6
+80220c2dd88ae3237c984ec1a26ae3fa
+80260467f4f593b0d30693d1d8a80fb0
+80262e78750d20341279525ed06741ca
+8029888f7c47c2558a304b04b6b9fc52
+802a5dccd22f62dff1e3b61174da6131
+802e2ec4fc39b27914331d0301965968
+803d5dec8058efd9f0434ba3e2bb3fe6
+803e7ae7c3c1e87701de86745fc18962
+804bdf481c08258d248f4696124d8d1c
+80518c73fa54eb1288c5dc540ee7fd6b
+805cd1fa1e874e070bb73e6613e49c79
+8061a1df47839e059e4a581ed747d779
+806332b347a672b9e22e024b92354acb
+806436a4b2126229c389c47b8fe3b191
+8067da87e7e8bccd6038a23e8a387a93
+8069a735e38d17765d96dbbb8202611f
+806dd637ea1bb04ac603d8501ecddc8e
+806ebc06fa9439342007ad05d27cd77c
+8070196f6b7a0ec7e3f1b144769cb3e4
+80706506c99fb6f35bac702f4fafd647
+8071198535d5ec51bf774f8b1abab311
+80712ca660047c5b536a40f181c425f2
+807268c6ed79f1c9f9d51b12a4920397
+8076a0e0b45f14c834ce1edf48bef455
+80776f78fb791c4d23946a2ff446e163
+807c477fa0f4a350d52d91a90a3764dc
+807c92cff297ae3254d38f9de3048393
+807c9b36ea559795914fc229b6f43665
+807cdec4dd3bc9ed334df05d567bb7f3
+8081c4bc8b5aefb7ac624a814ec1aac5
+8083af61c3c03e4dbc649df83fd2b4b1
+80856fcf38fcd8efe7492b57069881a8
+808578b3fa6fab92a3925e150afd06be
+8085b9bdb7d66d56fa2861d2ee4bb623
+808ab962aedf140d6c1a6292613394bc
+808b840029cd9c979ad4b22011f48375
+808bbecd06941a8f8ebd5a5b54169a2c
+808bc9b9aa5c539e483b53a1c7bb02f1
+808c7854a38e49a78aeefa0205f6a012
+808e2ba8dfff961d0c435e36eb2c52c1
+80948f42637c7908564f0a209a5f4a2b
+80952bfe4b9341f7ea6b543d820cc322
+809a1da3afb36cadabf978296798c9fb
+809f21a913774f48bad8f4ddf613c3c8
+80a8118a1989392238ce9d9558e9eda6
+80b035e7165e45e5855f5ddb64e34b98
+80b204e11b6faf07f542a410021946ba
+80b2ecb2a5ed4246232f6c20aec6401d
+80b5164257e7af08cd638d5adbe42d54
+80b86b2438d41327435761a559a979af
+80b9e7eda0ac67eb5618520fb2d14d8c
+80be88445069c1db1091d5be944a6fb0
+80befb4e9bf34560b46d753ee6115acb
+80c3d596ff5c17de3ecfc1d61805339b
+80c751f7484ae209e9032d404c1b1e68
+80ca083b96b41ed5219e552239be1158
+80ca3694a9cd1260245bbae93d8d22fb
+80d35979301e9e3014d57c99796fa481
+80d5f0bbf371af1bd3f3fb4b8072acb8
+80e071040b122f6a28016ee67bb2f088
+80e4cbde28611ff4696222b0fb7b198b
+80e6375d85c678bbcbae72bf1f2a5a4b
+80e6515e8ff466adb395fc5b81e6d668
+80ea3c1127d8832c1293fc56bfce0ebf
+80eba0ad3cafa219344b9e60d11d7335
+80ebeb440addfe5ecfcb69951e577abe
+80ecee6f8d1cc5808ab37dbcedfc3b82
+80ee11fc8f4201a5f985d7f007131b38
+80f5a5df22356d1faca100de372c4638
+80f7b5a4f34bfec121cef5972775ab8f
+80f7db73a2142d3ce3928b0c3efedd6d
+80f8c0f70c00570b9412189438dc9a30
+80fa242542da2426ede716f2344326c5
+81002f4bc1db9b19e45044eab0fc8ca4
+8100c9a74700239cc09a87bd0628e129
+810586e3b0dcf0146c3a86c4f2d859db
+8108b360c510c7bd0e39c754211d1077
+8109c9fbc1a8b898d832b79d01669a87
+810e45d633f021cff90a0cb58ab2cd1b
+8114676130953f8f4d9c9f32a90c2911
+8115032be418f62bbaff2bb5ae66eb09
+8115157d5770a9c80621206b19fb56c6
+8117dd281cf613d3941ef27da5b6cde7
+811967b67f2048ba02b1fb069c72e3e7
+8125330d73deb1a51cb820c64f6ee52b
+812a39007380376a4badb7cc8137ebf3
+812ccfa6ec942fc28cd083a78a2ebdc5
+8134b06e914354ad8af23a4902a1ebd9
+8134e52adfd2b79407fc9901ef83ac34
+81364ea67ee13b2eddacf5447a0626c4
+8136d2afee51d6e7dbba6b99e6101db1
+813d30c1454f6794be4b6bafc518bb26
+813d52d31ba09073e5ec18076ca9511f
+814082e3494d833081c69529f206b7a3
+814a8120a219ff6729ee4a248a5e195d
+814b604dc153bfede548725b246368bd
+814f4c5e5d8be73616c76e8361335952
+815789d99a81344467d6d83587f555a8
+8159d54e44ba8888287b35191ab74774
+815a7e4a0f46ec1ffd8f87db757bfe81
+815da90d1d45dbfde242e857f9335cc7
+815fd0f5890b565f77a435545228011c
+8163fbea55d60facdfbb724b520c7d72
+816883dd09c7ce3a6b66bbdd0f1a06f1
+8169576874c298cd355e115e39004243
+81699359642b77e2d30bb5a56118da55
+8169f80244df9e0bdf4affb8976760d1
+816d5242066702d5ade063ff244a994e
+8179f29d1ae06906ed15389424f26703
+817fd5bf020baf7a24f2d794b893f1f5
+8180b14e8f3349de73d84369eb4a205b
+8188ea14a7c2435a4669e7798ea10712
+818f0d073d87adf2362224676071066a
+81902ae22e8ade62b011a493b910719d
+8193df93a298556262d45c6b50524ef4
+8196cfe5b0c065b6abc0067ace6187b5
+8196f664df340f45e0b7e9a22c0a318b
+8197bc93084e778a1933ca6904b252ee
+819a281fa37afc6e7fefc02d88411e5e
+819bb4b421fa00354541461f1a535c49
+819e53ced881582795d79cc9d357e8ae
+81a0d603cba3be4b4fee2368f968c7a6
+81a135c8d2d1f26e9788752f0d38aa6e
+81a416cdc8c39d3c201b2d9276976d46
+81a83750c765da4341d071098edd72df
+81af23316fe71904a19be7044826eb9a
+81b0eaeb439539b7ebc0bb21a056035d
+81b28dbaf46f619e71d445684ea163d5
+81b58a0d64d1f784936a5ecd41d60f24
+81bec247515eb0820569ddda3cb7edd6
+81c26e8fb560ffb12fc50345573a60e3
+81c2870086830ddd4abb499ac0a6fd00
+81c617b9c1906597820122e9356ec825
+81c8266ad029d460dedcb2e32fd61028
+81cec86c6edaf5993f1823473dd044d5
+81d3aedacf97aee71d1b40c0c3a7621b
+81d4f9d8076717216bfaeeea3e7fff7d
+81d70569ed6b35bf1fcb65e6ba2a7903
+81d816ec0e15fcc0502aacbd16c1da67
+81d8b0702d68a26f29034f56162d2afe
+81e8fc97078d940854911f301a46519c
+81e9ea953d78d0e60b58781a2a0bc742
+81eb0609b2386fca330d3f1e95bcc0a9
+81ef9106ae14f48c29d9d3a3ce10bdd4
+81f6f56c4cd0ba64d85a7d1b565d789d
+81f723c483a15b361efd3a58006c47f7
+81f79b5d02885cfbb0fab1c7ea9cf20d
+81fad47c334aeac99fd501edc6052634
+82015a6dee9167d4b6a4ea632fb71885
+820a1b42bbca1a1c3dfe79e23e219b37
+820e91577fb856c3daf785f7cbf30d6d
+820f356da3b8480a5c56f52f8389ee1c
+8213cb107716013405dc8449f6bd9700
+82144ad445cbb2de6a03fb642f180afd
+821609f309343282fafe9a64a1b43733
+82183884d167b3badcd6433d9d8911ec
+821dd2e4467cc269e089421fa7e5178b
+8223021ef8c2b88a53ccdb3b0715ae78
+82241bdcd43b7ff8766b8103bf839f1a
+822982886bd316fb09aeb8c4010e5595
+822faf9392fd41cb0fa791ea951ed04c
+8235ccd805e9bdfbcdec7c07659f7ccc
+82382979242e5ae7552ae2896a4b741f
+823eef9ed3138bf4758e0cac90587874
+8240973dba3a3527a130e85a7b3e6c11
+824500f3f21f7475077d6b89f2902e01
+82596d97a6205f8f07e1574243e35678
+825ab3fa130effdf149255c13b70d928
+8260462976391b769d990108aa24d5e4
+8260bf2fd7da5816e2252599ac2275db
+826a3b959f7661e0ed12c51ef626504d
+826f5296e27d787f4a92a102cd044d9f
+8274ef499cc6940bd85feae096049945
+82768ba559840008989191ee84a674e5
+827de5f08e8f6b93a396aa883e137460
+827f3aa024cf44471a16f97ca795fa65
+828b55d3591b5d342f3ca92b21d6e9ee
+8291f98902958a95f7f0b0575578a10d
+8293b497f376f115de7b05609c3fb4f9
+829532698561f1a4ba52b7f4b076230e
+829aacd812ef6b9fd4e64bfb3a3a587f
+82a46da7fca6cac23afbf49696b49500
+82a968aa47d9744ae3ebef61b55908d6
+82accfc009d48cb484e8f5c20fddf62b
+82b1db8f2444031d9f49d9fa34a8aca5
+82b3b6325b217c939b7d0654655349fb
+82b65fba279c6e1c10502d5579fd6eda
+82b714bc6299fc30fc4459f8b192ec10
+82b78e0ff8f4f1d43f140e121df5de8d
+82bd8e563033b6e3b7a07f497a061277
+82bf9dc62ed88c3aa4d06af9865ed4c0
+82c02395eaea970f78891979390c157f
+82c6f2117fa87bd19ce87d6bfab7c8e3
+82ca74509d25520df3b613709a5c4a07
+82cf3fda6b9ad2cb92fd95901cc54db9
+82d227076ae755438e2fd8d6b057cf6d
+82d3bc5da153b1f124c126bbd5deb113
+82d5abdbf8e204219a87e645168bfafa
+82d87c1410121ce5699b321e5b1d52df
+82db3af749a27f51a89e73182b08628c
+82dc2e54f2917715e11fad672e1333d3
+82dff4d298cff11bf60a5141c4823a26
+82e6bab1ee4bb8618760158ddc75d21d
+82ebf7786291974323bc383ae33c1417
+82fd78511c9e8a28ce7e2be2242d468a
+82feca66471232b224790fd6fbaa904a
+82fef80d609399159d6a9b0a5e1908be
+830458baec3c72845bc10e6574c99448
+83088737aaebc275c8e83f30d9580e31
+830947418b71054df4c0cb19b70bf909
+8309cc973fdb6ac83ea6fc4375f1680f
+830baa817a8bf5fd177df62165fac5e1
+830cee72b12c5c97ee090f94911abf3f
+830e2e695dbf4319c95c542a362f4bd9
+830e68f7d77fd5089d2522051ab2812f
+8313a1430fa7b292e2c454acc0011331
+8319f4d68d7ef8162914afdc83c773fc
+831db29a71f891987a4e181ec647e19f
+831db8368e8fe4d103355a178e47a03e
+83248b9adab21b1cbf7d2d56e0fee145
+8325c932bed746f9f9350fe5e3dd8585
+8328796fac20e8d372b62e183afaf1d5
+83339e6e18225b8ae0fc486ed9092753
+8334163e7f7d2e5c1daeb7efe56381c3
+833a4f00b5b7899de5cd04546515a2ef
+833c5593cefc856726603bdcd3d8faa3
+83412498f484678211519538d2bc13c8
+834162c3378bb09a3f8bd2c55283e6eb
+8343a2f2dff7355c258732cb7d1c63e2
+834a51b85fb7f2bb75a15fc44074c300
+8352210a4b07c4c585460cc19c1fe049
+835259d2c13bfe0e1c27b4a1f044fdde
+835aaed3c22d08a196d3599545fb8586
+835caa24c207708dec43c98cf9d56bf7
+835d104ff8c4256f551094be8e26e7cc
+836786ac31e394ae0deb26c50819c97b
+8371b99770e36222752e1e9b0f4c7e4a
+83742476ff35fe1c3b7f157bcb93767a
+8374c1a08fcf3776f2677e11f02fcece
+837533363d12628c9da230574a3fb9ed
+83777024c32952d97e66179926171ab5
+8377fd7056a587f4a518034fcbc8950c
+837810020643531478d4b91a6acdf459
+837a3db3977e6c6d37d1df240e30c6fc
+837b5942f81fc2ee464b92aad1af6cea
+83821ce457ade29f4452a7c59685cec0
+83833c66cfca9a1d2a4f40f03548324d
+8384d5169312d50e505c2efbdca14355
+838575406fee6f53c8bc87ffa6e5bc54
+83866ce35382c3be6c66e050d7620704
+8387e4b44996bb7841edb697237957de
+838d4da0dbc50fe734f8224cb13b1e0b
+8391df712c2007955fbf651f6f291a7c
+83985c3e5f2746d13bdd6f54d2acad89
+839a5ec02beee88df484ca34135c55a1
+839b3b9f5e4bbdd550e8ce6d882fed9a
+839c8ea920f43d942d1dbd85bc30a84b
+83a1d9130b611c5d8a35782c3f1c42da
+83a78fa5104aafbc822fcad682687066
+83a9a7676ad711b8229dbf65bfdb7420
+83aa35f49c15a68ab4b58d3e85766f48
+83abce2f06a43ad521cd372197c22e96
+83b0a45ca34b495dd155c85ac07ba7c7
+83b9fffc8c19b0c259331a09680ef062
+83bb940190f161eb67407eb18a13d91d
+83be20637e553038ca1af7c84b856632
+83bf3c6783db25dfeb78af9730097917
+83c1aa3be4c0e268291f7b204cfad920
+83ce4329272e62f55640af0b413cca4a
+83ce98517a9c88922d1ce5d12e250fef
+83cefedc340bb78e43350544940ecca0
+83cf0aa31d59c1d227eefb7213d1d8b8
+83d0abfdd5a06e83ebf150e1b011c422
+83dbfed22a3e8ce86dbb2100254d37e7
+83de20d2ff8acfc8cb42a3243f4bb26c
+83e73db25d59ba44293b15b5d275c65f
+83e835d5d8abc7ce422f4976182c0853
+83f6537afcd024ec6fdd5eae4571f5d8
+8401dea3d964afc1a1bea6a228757356
+840a409a046b6f8c9018564f309715f4
+840b16010485e56e07c9567eaf709579
+841331a3ca7c71849e729294e37ce621
+841359a891bff28306d0454c9a3b82d7
+841383bb17acb88d7777fafbbabf7948
+84139ff3a182c17a9c6c668e12a1fa1c
+8417ddc784be7461e5373d6a8a3fdd85
+841e012379a1cad8530f4d72937baee5
+841f40c5a2bc72808f1f583d804cc6fd
+8424d98a44dbea364f7fbf259e3f8e07
+842555e6a7e7d92db0dc25d6f9a1c2d7
+842c2d9955795953faa9de49ae14985c
+842f5acc10a66317338eb2cac947d888
+842fe4d90ab902e0bd4db7282ecfd153
+8436310c286c32f5c98b86ebe0460a6f
+8436733d1bf8e9437ba7da6967d118c5
+843a20fae4bee6cffdab7db5372abb75
+8440864a49c574bd75b0acf123c07b98
+8440bed23fccd506e6220e7395190df3
+8442c508f0280f36e67f7eabdea016f0
+84446027eb293d9877c38f86dd09be51
+8448689cc009bc14dcbd3290be2b7d87
+84524673de6cb7015ac353c0522a0ec2
+845bc71fc764fec4d3f9dbc48695c897
+846a4abb1dc93f26dc8078ef37ab8271
+846c034de0d88610196d0d7358c724d5
+8470b1419213b93a86dd1bbfd805f879
+8470f35b3da24283e6c0e06cbb8014ae
+8473a57055bff9858d01aabb05693f3a
+8474699d240e5791781bc858346927c0
+847a469e6530baa12aef2d1409e83440
+847c9e63db01375a5013c79d384cbad5
+8483dea2af2a8ce01bbfe2060b0591b8
+8485ecce3c5ab8f2f97caa78cdab36d5
+84888d512efb1ba0cea0764498941e48
+848b8e875737296ecc0a541ca2ddf3f4
+848d79aea0b62c632e660a59da4986fa
+848ff93398fb8ec0de7cefe9a4678961
+8492cf112d95a54ac9c75b9b286a9a31
+849d8c1056c8abd8d382c6ef92f14ff2
+849eecf21071f319d439a666ee10232f
+849f8d2eec6c85360f8142ff4262ac7c
+84a46f8b2baafcda5e3462eb2c0107b2
+84a4cb671c4dc215e9e855aafa956cda
+84a70b559ff9c3476396a1fb835bec44
+84a78b96b3550d10a9cbd045f1ae99d3
+84a955b89dc1a3e1bfd83050887a4efe
+84afaf1772d99efd889ccd47c918e7d8
+84b2a437a1f17aeb21956ec5f43c1af2
+84b77b1c2db89ac7ed1e5a3cffe52dcf
+84c229260f2470ebdaebc1c11eb72b44
+84c735d55de9382e392270e2cb41948a
+84c9c3db92022d1ac8483e18cd1e94c9
+84cabde851a86191b2e11a45980f8401
+84cacb5c3bc09490b736c7b83e89a827
+84ccd983efda686e8ecda4dc86179ca9
+84d0d246820cab7c35aa74b52aff270e
+84db2fbecfbc11473efc61e173f2473c
+84dd0e5c64f27ee83fdd36200b2458ba
+84e0977853336adc9ea69da5fa7446f1
+84e1251954012c37a7d9d1390b894ab5
+84e7554ef8a152b1f670221a879d08b7
+84ed2966f4571d53eeeb8a089eaf1e9c
+84ff0e9c4788b3a2fdbd1f6734d4643e
+85003294d819aae09247b4f0591597b0
+85036aaffb850d7251171d6dad6ddc6a
+850813262cc2af06a7c86413f48e436a
+850a91bf2cc79a99948148e47f3d9889
+850ae021176258cc0b42be3d53ec4942
+850f2629c7716fe4d294b2d4f831a1df
+850f7582ecbf7fecc56aebea5aff9eb9
+8510ebb5d162766d83a1bd1d2443910d
+8511f02cf4aac0e8ee5a7da1602879f3
+85135c3decb9d925621de5d89e2999bd
+851452cfab69a1b973f2d38babdac5c7
+8517f4fcae991fd54fed35a04eddcf01
+852166b9d3af257b33ac8bc86aaea9ee
+852a20d7ce2964c422f78d80bffe40f0
+852ce2f8dfe5c60918d328a92b8c260b
+8537ee3eb9850fba65a6b1ab6a2c0fe2
+853c5bd80ad863f52000c263e25d3286
+853fe955fcb0e5a4a6d8fcceecfb784b
+8549e4fd9125e1b91254f4ad8dc5e618
+854ab2e7e5739f7e719d506504c87a41
+854db32ba58bf53a67bfac3755f379b5
+854f42986f552edad42c25520d3ee8cd
+85551f09c7da5506ea42b8203952d6f4
+85569ac20d46a4abecf5a20578c52cbb
+85654a9d0a969b187d3c7492c1a679fb
+8565a58ba3540373e507132443e06496
+85683bbb130d27f52d70820cbe962206
+856e3798b8c00e865fd153317bb863c8
+8577ff1b1a6e7dd27b80f3d4e87d073e
+857afad9fe0f5afc937171d4ff656843
+857c2a95b0da3d7dd1b1ac6bf359687b
+857dbbc127f009ef525e90df073ba296
+857e6821d7c79c38fb72e65dfa763d71
+85857645bd72c23db465d4cb5b63a4f8
+858a4eb6106da7fe4f235697a06ee7e4
+858e4ddd566270e581cbafaa1bd1e3e1
+858fd33959f4976224a6790126139d9f
+8590949f1aed4384a8952e0f90a997dc
+859235ac9aaaf82bf72f3a1f56c523e5
+8595968995563145ce91310cae9b7c64
+8595b19046d6ddf114f746504ee16a7a
+859cba2c4c07bfab9d196d1bc1daa319
+859f6fa1e02a0bdc44da2ef614b38257
+859fbdb8d3427e3f191cc5e29c5b14da
+85a17070331706428703fb1d02261396
+85a4e3e6abd7259b71cdb2dfd2ca011d
+85a839757cf77b0cdf3910b4a91a6d41
+85a8ff1b6df262b72ac9d814fd67fef5
+85b0d0b83cab7bad61cf0fb191c755e0
+85bad1e432da7438b5e5c13da4809c21
+85d05b647ab3be9b221d24de81960970
+85d295b532a904280ace1f0c945b6869
+85e1bc302f308448012ef72975af8e3f
+85e1c6c50de43efdefdc48a76e772e14
+85e289aa04ce07feddc6c37b7867b93d
+85e597ea6017b70d49db82d48f2ff15a
+85ef0c6dccbeb0f45e6aa4750e857cc4
+85efbdd16d11180ba3ccb78328a4dcd8
+85f24fabab2464452e6de5a6bc153f16
+85f2a1f53c4824388ce5cf47a45d5834
+85f3357e0d8635b94f20143fd5e8ffe5
+85f35f42391d4b2c4e9a02a580bcfb0d
+85f39c76ce7ce0ababfadc984e22e9e7
+85fc208acf7016f851f6a9eec21e9ad3
+85fd87d05d56e553a2d55689f03a0fb9
+85feb6b1a990f51aeaef7adaef6aff44
+860012cee25223c013f31616d04f1271
+860328ef8b19a83f32afea63852eb064
+860c574d09b4682a82396541be0c6160
+860ccd24e795aca9724758b32b36002c
+860fa354049d314f93bbe907f4f2718d
+8613f7b7b2943ce57131db2c8be26076
+861553e01c6de0d93eb0f10afaccd1ae
+8617094f5d6917f6d654b4951db76c1b
+86171fdc5709a49d63c721a56660ed48
+861d27610749ccd7fec1e38a7ca53a3e
+8620fddfaec3de219885aa2b403dc9dc
+862dbd3f1ee5e10b440e5056f2da01a7
+862ef1007018cbc0320ea5a87305ef26
+86365b8b8c34a7b2474affa9af469840
+86393795d9f5e201f94815cb88ef2fca
+8639aeb10bfe3f8a2b25ce48217a5179
+8639d79fb13e25db04b9ddf976a166ba
+863deef02938a0efc86650ca8a0e8246
+863f48d8dc0044080a424292f54d518b
+864232c07cc73c68289c2fd2f58f1caf
+8643f4d05d024c9cf3eda4e135c0e9ce
+864df149d409eb6c9837686ac58ed9e7
+8653c88ac7fafc4a98d53342b447b436
+865485c3753da5f5e343d2c434ae327c
+865a8e63843778222352940f78118d79
+865dc03a1bfe5b970057975f213be93f
+865f8afcbd6ddd7cfe39bb2679ca610d
+866033e74fa5e001e1dc2e19ea447d24
+8662a2eae9f9aedcb135120867a701e6
+866670e09867d7a36009f4c383574ae1
+86677ead60d5e7e0ca8eb0be2238f501
+866b1f697b1de6febac3760a93b55538
+866bf0601da06e7790217f985de865e7
+8671e9d3e06f946090bc32babdffd81b
+867241d55995f5f8f6e9ba7c30746a8a
+867245882a7bc622cddb829476b353eb
+867383b50e58d6e374568df31beffe57
+86749d8b570d6837a96872deac180ca0
+8677114fcc8cc04ae79df70e009a2d0c
+8677ae53cf03c0dc854d0af7df2392bd
+867d15963600afff2a935ad6ff131ed1
+867f335ca7342ccbefa6565c90b56439
+86807cd7c7ce3eda2396ae92198f94b0
+868d09837d71504a52d9ea99064ad80e
+868d4bfc5164ae0be6e6976c704925d9
+868dc8d84cf6d6d82b4181a909ea1102
+86953c6267858d3058be32ce12c1fbec
+86984658a05591915fae12cd5c9c9674
+869be2446ef4dfaf057564ccde28ea27
+869d1155363bc57711da4a35d322fd1f
+869d6f2b56ca354774092889d93dde7a
+86b418f5ffcd92e89c186e5643b6518f
+86b731d0f4a55150239cb917c8b0f5b9
+86b9eb76fca4780377a1a72c9eacd9aa
+86c073e06fd70e5792cb6262cdced9e6
+86c1b70d5e06c03d0d9447dabcf799cf
+86d0b897973e32ba209359aaf6103596
+86d2678e5bbc4e8b57a2bdcc65074627
+86d3e3af293567b2fee44f2d56d2349e
+86d43b5e496a4acfea9b1f6826e1ebe6
+86d719c682846e957b817ca3a8ac3e9f
+86d8f20d84fa36f8dd842f3dce27323a
+86dec86c4c9d67b2ffca5940d8f77c10
+86e4fbba2abf29d2503efe40cf738b5b
+86ed2c95f3c7beb69ee33950862f904f
+86ef728fae4763b0de753e67dacc3ae3
+86f1df1f382669385a6bb1647d707150
+86f6b3754b1dcfe5f0935a15a315b43c
+86fe3cec391eb550a6a28c0f025f00ee
+8703bf7f81b6c7f6f40a090d461a1ca0
+8707aedf7d7e91c95b95f1f3a1ed761a
+87081376c2ed97f20bfb324d5443b31e
+8719fea29173fa9710aa2d2f97169380
+871a249088426849bd13bde2e9824f5d
+871db9609a95315aa842431a546d7a7e
+871ed5187108c3586e5e84fe5df5f73b
+871fa027399e020db43350431a5cc9b8
+8720a773c5243e9a2c862d4973d77864
+8722c9c453411e7fa6f369b8c96b6865
+8722d6cf8883f8449fdc94216ba965aa
+872bb22090c3592985e5390275c3c570
+872ca7b22f07e423f425707a2e295b57
+87399db91ab92178ff7ef28092b17e7b
+8739eb9e4deaf9facc531e4aebd5e13a
+873a10366fd532b4576681ddd8b03080
+873bb7f85eb40852ce1ddbde4554ff16
+873ca002042a50f1c110fd06e1ae575e
+873e5724ee139da6fe844a82288a1e75
+8742fcbbe611aff3bd1b2386d8f45e7b
+874400b6023e564d8d7c74e6d6077313
+87456cd4c2279e2a3ed734df87b6d8d3
+8745904f602bc1034904265bf237327d
+874609de4ee126c73c404fc7cb733407
+8754b87e9ddfa4935a35706c475bacbe
+8757f421ecfe5d2e4b517ff5f394846c
+8763b63b9672568591ee11c751803c91
+8764b38186519e6dbb5cdb029b1c5ce6
+876a8958123b315712d6d4b51e9b63cb
+877274a90d34b70274cec078def7908f
+877400486e4e75d2e901dc5b6528fdec
+877579c6d30d077edfbd2bd05cb98b27
+877b08e05a647c0cb3201721ba4d7218
+8780b29f10787a6452de38e603eaafa4
+8784a110d3a5ea6077ae6298989be163
+878af3581d8402fba256703138d2734b
+878b308c6e4de749b59e77708e04722c
+878c448a5ab2000ad4169c968c6eeebf
+87906097355d402b3c223a6b6fe38913
+87917525f38c049eb4ae3fdcee2d0cf8
+87934d725d0fe38cf68052c06b352b96
+879e0f4da97ac5cd06a4ae516a894349
+87a08dce9020879b1e2b48f3420ff5b7
+87a2cec408affdc8fb82fc1c51ef45f4
+87a6afdb2516e73875c60ffeb40687e3
+87a90dbb648045b7ae2b12cbbc38da0b
+87aa0bb3be7c303a63f5e8fc2c4757f4
+87aa4448b19da378ebd376274f7f4402
+87ac57e011995724e4298a3b96791a0e
+87afa5c51f8905bc1ac4f9b6475eab3a
+87b2069791cd8ce4b5db983a3e067e65
+87b5e8a7e600ef06909863aad5818546
+87bb3434379553a8f15e1f59cafdc374
+87bc2f2b8059523e3dbee9b30829fe54
+87be89aa0dc7f8ab2bf0d13e1aafbed2
+87c688fff57fc5a6978366f8065b4fa6
+87ccac55f0af63fa8557bda648292ee8
+87cee6e60c2aff13ed21ac185ef4a929
+87d0afcaf2ff06435dabe43cfb1d0926
+87d8db9c12bd47776bf14872f0f73c7e
+87dbb5c1d8aac961a8e05628ebefcb1a
+87dfbf758dfb9a6191f8e46978492663
+87e1ae3d3d2ecd20c87bad3a5900c234
+87e25d632e125b1bd084709de744048f
+87e57d8502932eb0d6c9434f0110d94e
+87e62ae02742b3991775ba5723e0fdbd
+87e8625178bb4d6c32750fd8127b2c8f
+87ebd6582ced5b5667e5c19eec52fcbe
+87ed03ecb9b8a4f4d7b1d92e3cb85f3d
+87f9e0c79bc510e7b44e1533fb49b66e
+880689d1de1b2458952c4af24f37655b
+8806c7cf328fc572e38963be74e3ea20
+880c0fd0e344c7b51231be27ebed29de
+880d3e089035fca27de7934badfe4aaa
+880e9628ad07d377e052c665c7c56b4c
+880eba2f30fdbf0f5277f62709f23a51
+8810da0891d2d64aca5aeda872ce2f4b
+88155dd939bc65bbbc4c826116c0539b
+881b9669e81d55d4c8b5f4fdba6185e2
+881c413f5c7a65a5e9f74f95689bf628
+881c4dacb44c30345a714aa46f601601
+882003470f1f893b68b9a18680d8bb36
+8825dc29df68b90b40aa584fbe337bc7
+8828f062c209668797fb239ff8b47181
+882b952f24a599c88fe98dd2dd1a5970
+8830405557f66111b9e3d036e5ea6554
+88305642014bd46b3986470da85d30d4
+88319fa9c655d926bf7540eb60d95c32
+8836ef1ab01d31c060738f8b7813ceca
+883e65b36803f022b7bb1c7e70258422
+88413cd3501820cd2ffcd69bd9c14f49
+8844911451d1383740575df568233046
+88465fb210dfb8d19370095b3d49238f
+8847164d9f8ddbec0c8067f053755f81
+8849b592b8caede34a8b47670d196ce5
+884a95ede3816f1ef2e0cbf0441ab1a8
+8850b15cd17bd5cd5c3389718b590dd6
+88522bfe68c7ca3ed0048d0a18583123
+8852866a81f77d712e51cdeeb90ac031
+88542ae381e359609d2dd90f22c9832f
+885a80dab4aa63f56f5033de1b571b0b
+885d59cb3c92473a257401cd918dd9bb
+885eff2ba360ad1d76047af739cb13a6
+886127b7185e6719dc2cd63663b9ecc0
+886b0fbadbe374623d2e8f156ddc15c9
+886f47ca01dd1ae949c33dc0db68b37b
+886fb225990449a210978779eea8190d
+886fc587f5e5821396e3561e13c16844
+888122b7b2a72e83079262ba1e598ac2
+8882ccbb4a67e789ace53c8d3c754d26
+8882ea5ec89b7ed77ffdda851f7cbaa7
+8883409243c91b0b3ef658f4245171ef
+88878e5063c6c5f63c7a644a6e2f69dd
+888a47422e5b5a079b4e3e50e0fe087c
+88905520e38773b045554f74b0d3a3b5
+889439543eba60d1594f6c8ed345aef6
+889638aed40f14568f27d3b04ddf1fb5
+8898f01342a2eb77ce4851d3b16ce826
+889e5828a79b3ae48c06e9321d561c1a
+88a445d0b67b1814e2756a877dc4aaed
+88a5e62391feb4349b07e9592f8887be
+88af338a341853c75b40eb48e76f68fd
+88b2b892f9333e9a58074ff67980e23c
+88b36856a0fa650a28deea1e39f71a7f
+88b414cf95de36f624a006b0c3c503ef
+88b46cf24a7a3cd1cdbcb1029cb0c6e3
+88be702d4ffa3b0b0226d3017cfb6878
+88c7a699c9a540f6bc330398d63ce71b
+88ccf23d5ae87bb032e25020d53a498f
+88d15e718cba0db954beb39da982c69e
+88d7f32bf195921f2310e2984c2fbce3
+88d97e5a067fc25d6d113a484a6c9e22
+88ed9a579cd578af80172c15f10049da
+88eea9cf9f747f822042b4fbb928bb69
+88f38f4992960ea60b45f7b5011b54e7
+88f66e5d4b4ee8f2ada07af1aef35acb
+88f813613b42a3e201d1e6fabc651291
+88f91c874803ea18e1922d3373116b8c
+88f92b9510709860762a15d59c09d4bb
+88f953bf631ea9cd19a42da0083b051a
+88fac3d30efd15cf0b2621cc58535c1f
+88fc057e114065963ba75bb32b17cce8
+88fc9cf7bc9aec29bac7c598648ce150
+88ff118709d6803b96be6e7222cd4c54
+89044f83c6d544bf31013c77654fb2e8
+8904cddde57510aa1b92596144e98c74
+890c6ae3ef77b86025db713c45d3cb71
+890c88426449f903733265a9e61daa4b
+890ce5fe70b83634786d9bd2c35eb61e
+890d9b962e9efcf8c395a2b53665bc1a
+891270bdd8276530f2e78b7d964a1c39
+89143aabe005f474278ff736e93ea5e3
+891ea05219b8c55e7f32d0c73b28ddf6
+89224a4758e97ecd119431fc78cf3c4a
+89273a96cc0cdb10cc11e2b384f1a643
+892b53dccd474dddcc0f56e73f3b9bb4
+892f8f2dd1b23cb695d739b7e03b2bf9
+89309ec0f1e6531ff832f4358fcec008
+89324fdaa12bf9d76c4e7915fe39fd95
+8934bf0f1e082182c8db75a93532eb3e
+89358b8dfe4731902845de1433dd4800
+89359ac56f0892cfe9315ca09209b620
+8940230479a1a957693ea8980e46fae4
+89456516276cba595d7d43bc1bd1ad8f
+8945e049337dfb4f375de77c84ca9095
+89466348a1d8d9db84d08faadf1d3320
+8946eef725eea6e9dbb251835e7902d6
+89475674c1cec8d84362745dd6254f35
+89479a895d07e9fc801b45f934b95ecd
+894ce878f4a36c41880b92d5d7e2bca0
+895231c8282f4177f379bebca640265c
+8954d96c9da27703f0b4e437b3f0017d
+895649db5a2ecdea98d975f1d8d7c9e8
+89564d089bff68aeb6643777f3defe54
+8958e3398038129d27fb1e30a42973b0
+895c31f6beb3a14c6fe8e141706ae7d0
+895e567aecb864208ceca2c9fdec6b33
+8961a4f41f7b9f825b0ad9feaeb22ee4
+8964d9f18fd72bde93424a8dc4eba4ec
+89664040418e4791d3cb7984d4524228
+89667ce2925f44e9e116ca2705d40f91
+896b1f65beb853744594b1754c04b419
+896e122abc6550636354b3fe5f5a069b
+896e98ab0059d6cd5f1351498cd0dde9
+897b435a43d828ec7e4d2855d3f1fca6
+897c8acf24d0cf87cdb1af2afb5a3072
+897e551d31ab084265116c4c1e53b9ea
+8982066f06b0e98140dd23b39afbb653
+898d2967cb03dacf73293fca321aa7af
+898df540b6b561b19606b16f469907f4
+89929cf65e777782b053e3343d320b56
+899332ccfe991e9b5a4521b87d37a956
+899966f86867596b36562d55b02fb546
+89a0fdb1f8b3a5002be819a6f6acbed2
+89a603e8f4328f2f18a4c815fef49e0e
+89b13d57a72a4bd93bcb01873dfd263e
+89b6b0884065833b2957063c8d337e2b
+89b82a5ebc5d44de2bfab8782a1fc24d
+89bf345bd8a04f1a10a39c6c09cadc15
+89c27806c7a69b987fb5d469658de7f3
+89c5660783399e48904fdc87ca9ac1ae
+89c5f0b4d7587e038f6fd0b32b9a2482
+89c7357968c87c9f8683f4b3d3a1bae1
+89c8451b2ae033220320a503027eeff1
+89ca6ba30351ea74142d454dbd1119ca
+89cb127aa990f4b83341e02c92a5ebbe
+89cde0c21322884208d02a71f17333f0
+89d199215282a06ced19c0d51ffbf618
+89d2fab57ca476915e32f1a1f3810884
+89d625eee9acb4bce54980d3a7a04f70
+89da1dfc3a5b8e447b9b940f2f5a9365
+89dd2d2351ff7bbdd92226d121d8c7ca
+89dda8eef07ffd29633e7c043a4178f5
+89e27446774c4de4ee7dc50a5e842e57
+89e4cfafae505da5bfc19b2f8a0d64ba
+89edff3df70ede6919d3c3db4e523249
+89ef08d3897e162bd2e6d7168205f855
+89f1983dfaba17a2fad564a2773e3cb1
+89f4e90b4ee68090a881f04a32d4b6c1
+89f5f14ca5e52214bd284d7957c865ab
+8a0160cd26cc5c398447b1448a096040
+8a018d2297a672db2173d8e648d01f18
+8a057ca2ce5b2075e936c111f4dc9638
+8a0bdfc6406a1a3a84d62da2a7e962f1
+8a0c21a83267c397265405f1aff167fe
+8a11991893a319fe2c40ee7a18954e36
+8a1848d4a03a7df5b64ccfee0152caa9
+8a19ec97333f9f8319c46b03b1ea6f63
+8a1b558453e693b13600bf46ed99dace
+8a1c026bc010a5be14e36fb336a540d8
+8a1c052c85a3de37888ce81c5c7daf18
+8a1c179dd32739f31e09c12d751ac000
+8a261e11651154bc60630d6728fa22c3
+8a289fa00cfa390dfc2f7be2a93af629
+8a2a10b40d8715fe2a88044e9f84ca11
+8a2b8bb91236a9c6d9b444acd8af649b
+8a2f2cea60501d3777c5c616edaf5943
+8a2f3191c88bcdf6962d5be181b9d998
+8a2fe6dd445ea0c6a521a6bfd9302063
+8a39ee05b5e1429d2b8049244d49e11b
+8a3d8d8ae68efc5cad12cd28ee4c5af9
+8a3e37c501a7268cf6b2c5f61e815492
+8a41bf38597848cef0154ae05f301de7
+8a4a231f34cc02e85d1d3d786a54b678
+8a4e21498812aaad165f5dfd4e2a78be
+8a535becd8c9efcdea2220fecf376cb0
+8a56d47c2c3a9b2d470f0499baa369c2
+8a572ebabc35b161a197ea078f2adf8a
+8a5afb5d559120d90e5b84ae10dfd102
+8a61aa2925c1e0dbbc2f951d564640c7
+8a61ae4abfadac1f030aa9f768ae7893
+8a64f0584ecad5b98daba1fe66145838
+8a667d6e92d9542e4cb69cc5b5bbc9e7
+8a68dc19a258d9714415312236d81363
+8a6b253e25bb0697a4935e303075345e
+8a6b853f26451c455129673bc4e0911a
+8a73be28b66ad0937433b2ae2e846c51
+8a7440aaac7d5976b54ca776d4245c36
+8a7818951a457abeae076af4ef178709
+8a799cbd54e4c0ce81d39f31033250d6
+8a7af7b1770df68688a073da8ec7aa82
+8a863f3054d3c35d5d6dc46967b39b0d
+8a892dafc2d26fd64235093d1b228355
+8a92555883a159951da4e1e519cff22e
+8a96bb224d324fd3d99eab204be84ddf
+8a97a656afecfc0a13065edb24e1e178
+8a9d4dfa11755a8e5775393f74ad78a7
+8a9ebb91e0897950256e0fa54bc1245a
+8aa2d1d4fd9126a3e1e8cd7453e9dea3
+8aa5c7b106a968d7463865d3b9b2c336
+8aa5f6eb3dc49d9cbd966c46a0b4a54a
+8aa6f836006b2744a7f2e506457498ed
+8aa969d5028fae05cc68849138ec845f
+8aaa9ebbedf6815884b43207fa279714
+8aabd687628beaabba70bd115ae379bb
+8aafd1d3e0b0a36d574752afce381d9a
+8ab14aa3f05065085e60cca474e424e6
+8ab5cf434446562ce8b783735a2c8b1b
+8ab7f61287f73359f9d1fb226ed13d51
+8abdffc2469c1b5df727e361321c3145
+8abf67f14e1ba0656ed6737f12c03c2e
+8ac10af6dd050ee6b0322d86633f72e4
+8ac4a65b87e45dbf63144fc62a315f0d
+8ac9579360b645adcb00c7a92809ccd9
+8ac9ae4dc71aeeb9d1f6bd4b543862a2
+8acc5ec7378bfd8cb109e7b1b41de5c8
+8ad4564a1daee2641a4d66a04a604023
+8ad97d7ba8379b12b745435b849d33e5
+8addb6d816c5fbab2645af75ef42c604
+8ae3b15149b91893ea9ea170bafff816
+8ae4b36cf19b30b3029241fe90915155
+8ae57edcad71123777922674ec57ab45
+8ae698bc0e6e27fdc35ca6304c7add83
+8ae99a8183fbc23029476eb77dfde306
+8af235481608387e49ead9488dd3a695
+8af4dfed145ef20d7ea6f0a9056fdd8f
+8afa4e0b2ea23e1956a15be594204125
+8afb37aab6ea2d898619093707c2c912
+8afd19891a1aa4e10d0873548a9943c5
+8b0ea861ec8aa0443dd3f5ca40753c5f
+8b233f4a0d77ab5dc6da0d38a543c02b
+8b33703b9b4144cf814f3ece4de14f47
+8b3b7052cd530dee03f1133c5871453f
+8b418059afa7b2bdcc560aef94d7b199
+8b41f556daf93d810c884da29b0c0afc
+8b433f760acaa0fb04611f4c812c2953
+8b4629ba761940bfa1a9cb17e415a03c
+8b47f79ffd278517558cc4c037559193
+8b483b47c9d08cec5c1ce1413fe58f2b
+8b4a0533b673290fbb8d7b0b0fd8c85d
+8b4cce257592e3642c7b870aa73193ee
+8b52c4d0ec972b72048acafee5994c3e
+8b59a6638e98e9c3b638f7483941212c
+8b5c870399c4d683819f675382d2296f
+8b5caac1fc15573ea354aa5db05e09db
+8b5d1a997da00ca395d7be81ba021886
+8b5ed3dc115ccb7dab209e24edb9ae3c
+8b635abfb386c6efd9053fda9037525c
+8b64ab727581942bb7caa209bb2c5e0d
+8b6969880d14eb3bcbb266fa88ddd26f
+8b7027b606d7f140b427dbbd38f7a2a9
+8b7bbb4414afe313c84749bec55017ee
+8b7cc8af0cd71319bec1e25eee1ce114
+8b801427d14ef0b1e7c289eeee4e0567
+8b862dc598c7b3f4f1cc110760189cb6
+8b8a865a84ab9507fd7c25c0fe87324b
+8b8ab4aae35f4da2bbd84ef616509efc
+8b8b6b0632068c153f6d490865d90dfe
+8b8be6c831b165d0affa0783c36b06b3
+8b94ecb0a9965908f785dd968f489a82
+8b95a036ac6577336f69b0a1c4f33ef8
+8bac64659fa918bf14edb0007f501229
+8baee2f82e6e7e1c2debee214f3a27f2
+8bb27be0429be86bfa61a98dedac95c1
+8bb5244f5f0f8e7300bc2cdff21b0664
+8bb5b588da6c0dba3cecd7f1db492f1c
+8bb63cb33e7c47ed9f2b4cee709c1b2f
+8bb965578277c895a15b6be96886fcbe
+8bbc024a1b59881eb0850ba6ac54aed6
+8bbcadfb79be01a36badb667e5795df7
+8bc6dff2ee45be0ee9d25a3608d37f9f
+8bc873396bffc3119cdd5c3118adbbb7
+8bd2f4bc3cdfae147288fc62fd79e326
+8bd4546cb622ffa2d0ee2c89944817c0
+8bdaded1230876f788aa9634f1538830
+8bdc23cfa1e06cb6a625bf8a12f53931
+8be606a833649276df5c7db55a8ab36e
+8be964e0aba26d47f049ccbf6c3d3cc2
+8beb6e2c52cd946a3fa7512d7251b3da
+8bf05546826a31bfc5b787afb714e926
+8c0358c59498b15e3d55f13b086c8ca5
+8c0696053a40112ff61b20be5efcd8d6
+8c07011c105c96187a9fb9005fd2e96b
+8c0b933e62ee0781777697c17c0d2053
+8c12bd5b3bb8708f27aaab1a006b15c7
+8c1824946aac4a85c3d5a37a6d26953b
+8c259fa36c9f53ed55ae82a65a66ac11
+8c2d4ec65e5c72a25aae2f00d5e46b84
+8c2dfa7d855f61618ebc4e3e4d61333b
+8c2ea48a155bdea73fb2e4c40c7a6d94
+8c355b187218d356c1abdca8c5f422c4
+8c3b9059cbf9644ad8a72f360146300e
+8c3fdd225f12364e94b0ddd245f24dcf
+8c4c7c3de220c8a771d0421d583202a1
+8c52e3950463af2503f20e1d72532d6d
+8c5943d95dc7fbcf7ed7ebb403e447f7
+8c64515a8f2e858653d974e4b351ceb0
+8c6477653bd0adb513d3c345671c80ea
+8c67ee9cdd5596da5789230f8d197c25
+8c6abde21bd20320c36a80c31149a0e8
+8c6ee2d3940dfbc38cd191a8884dcefc
+8c7780ff7e5aad97f93433cf96044059
+8c78cf2ef0e6932049ec9a7845692f9a
+8c78d889fe496cf530e926bf382ea990
+8c7aea267c12643cd7d3ac9d09fa70c6
+8c7b147d5836d6594350effe134245a3
+8c7c34995c4df6ce090d449b8d883a31
+8c7f91edc63e23016d8074b0fa6dd16f
+8c813755ad8612a0343d88d26a2e7528
+8c82a84fe17e48dc021fae28b6c21cb9
+8c87ff6eb943e4723a9e80a28e40d3f0
+8c911b33fd0e7353c50a243d217a4220
+8c93954715236f748a3c4046f5d683c4
+8c97e81724ce91debce866cbdf92edd4
+8c9c2744a3617c558af1db20df4d59d7
+8cab6ecdbd3b5843b112b0837b991a57
+8cae910c1c5fb2a8ddaf2e4da0c10f08
+8cb33f31192cdd3fde9aa9a6a97bb930
+8cb3ada88b206df46a101e3d00e53c54
+8cb64fc7168f637bc313c3bef3fa96e6
+8cbdfc05d921f2b83fee125df0630cb1
+8cbe028ff23442522e32bb4c0258e81e
+8cbe1dcfb1778f1cd9af93249fdd734b
+8cc488605e7dd17b98941416d78ebb51
+8cc880a20c3f1c1f0d87cda10cf1199d
+8cce4b6e0b7ef9cdedac88bc8f15c70e
+8cd4a926ff21a7b845c5a46070d343b2
+8cdb78ba7c1578e72c7b7d9391df4813
+8cdceb79e12e7d27071aaaa989889996
+8ce0a1ee58ba3be9d93a9b49035e74e4
+8ce1a0d2100d307812bf390e53c372de
+8cebc5e028aac0e507e133da8ae83e1f
+8ced2a25962ed0e842df4c55873f35ae
+8cf021cfe7ceca3f054004eb273f43eb
+8cf0fadf140baf991d11eebe78a55ae6
+8cf219c6c2c5e261c9b2a06512cbd3f7
+8cf62f9876be9bd1151f1863b3a57758
+8cfb4996fef7b5e081589b3f19f649ea
+8d041caf4dad972a1e3bff481e65e968
+8d044bea68932472655a623ea1a606c5
+8d0609533abb0b68fac7f4bffaefadc1
+8d09dce4cd60516615ffe7233e368164
+8d0f2dc4e6cd3e2031d7bcdf312acfc6
+8d1fcfaac59395212f322bd790b1f429
+8d208ea370b796a08515b90e1d8333ba
+8d242eaee558592397ba1d577723b1a3
+8d2b1304e979aee0a292faf9a8579ee0
+8d3153a1663840647ed50e821353cd53
+8d33ba755807d49ed0bae51a91ba3fbc
+8d3583e948e2ac463725f1287264124e
+8d3609c232f621da53a7121d5ceae424
+8d37e821d2df6589b88ad04fc53bf6ea
+8d3dcdfc13295d7c8d24f029979f324b
+8d475b590ab51ac4f1fb7a7cb5475c8e
+8d4bad225c98b4d19f03c23f13b40aa9
+8d4f211261111265d91291558146b1b3
+8d4fe5f378fabad0dc458cf844d18178
+8d567c13a05e682ce74e7162f6e16e2c
+8d5cd25d6dd0c81ef89f8c590371eeb3
+8d5d62e989a955bc510b5a844f6f7230
+8d5dba1ac484bee0012e72ecee7ceb99
+8d5ed8b719e33d95ecbe15a299268c7d
+8d61ee1350f473cd2c6232c5caf619da
+8d6b516f104588e8898fc40964b33501
+8d6b7100b78085754dfc0d330b235993
+8d72f5883c7d47f6947f7fc6c37b9ba4
+8d78e52033da94cc0b335aa01186a4b7
+8d7c4ad83d47386360971a7bed959cb1
+8d7d837da0dd5bca172a6e04946ce705
+8d890151cecafc2cad37181dea22212f
+8d89d9a7a69435b7e07b1d7af3619e79
+8d8a1cd6a65a737a40e60a77bbe00737
+8d8a48446aa1d74296aa07fbb0c7fd4b
+8d99109cc6f0940841b8850331b716af
+8da870b1d850914c18847e2a57e5b519
+8daf49dcd2be2f1798d036757d256bd2
+8db54250cb1bc9f024435889ab582a71
+8db55b67c0fc79a82bd3e53756559c89
+8db7b76f63867a6469ae791b9f366788
+8dbec8287723f891c3d548faf7c75a6e
+8dc1388173deb626ac65e89f8a18e0fb
+8dc2bd536230dbe0d6352d1c0e2c0958
+8dc3fa60a38cbcfebacc41885a78865a
+8dc8eaa2a95bd562d6ae0d4c45c46b40
+8dccecb83c2b354c8ad75abd36393aa9
+8dcec7f91d7414d40472b759f9f65407
+8ddfdea4545adbc47aab93f1cd1036ed
+8de19b2071b4336f1c89ffb1a610e518
+8de7dcd887225964a1c1057835fe60f8
+8de9a01ba286e96406b2385e2d362054
+8de9c7d78652148a3bd1dfc6361ed8c3
+8df7835d9ab748416280451f45511fd8
+8dfe24c1886ea3fd985b4a9dc347acb7
+8e00b586267fb4b08e0b1a22c0927c4c
+8e04f31b976565043ce74361b597fc03
+8e07e76e7c0be71dcb6e08e8efe6c0d5
+8e0e8629e4b85cdea88da485c0862a38
+8e11a831d45a83104f5898a6bbee1c12
+8e15e9c7bf8eeb71bafe349ab94b3e6a
+8e1c596c88f9df3de33487bda03bd8f9
+8e1da1a3bb697d92433bff919456594c
+8e1e32ed1b27e05e2cc2e1f83b5bfabd
+8e1fc6743830e75039556aa30b81e849
+8e24443c0cc133e136d87a96199f8e31
+8e2d3e579ddc4f0fd7c5e06a44544388
+8e2df3499fa05ce52753650d2c04ad95
+8e2e90222bd202b54a04d18c74e5be46
+8e30d103bbd45f2cb961d86803d3d718
+8e31d6658ed3f4330f545236a8e32734
+8e3aa245acabaf2fe0591d17b3b78fc8
+8e3fdb154f099c8e5b20e0117ed65346
+8e42379da0124be2742febd38433a3ed
+8e473263a9b74e8c84da86443454ad14
+8e4915f7ef5666a4e42cb34c9738da60
+8e515015595f4fda24f541bfd93fd816
+8e52e5329ef1a0359fa2869a0b41287a
+8e5b7689804defb624a05ed62312f2a6
+8e5c2212da7960a0280a7f7f89f16c03
+8e5ca0ae8b56a36b977e8d41abc99579
+8e6028f72569eff2dbb92fd62a10c4eb
+8e6824c01d170a0f6896d5fe1302a9cd
+8e6bf7e819cc60f6b55231d2ebdf7d41
+8e8a05f05f7df9df6bf4114cfcc86eac
+8e8ae389ee65ad3e610be0b1c764064c
+8e8c831a4deaae5323e6a708d212547b
+8e8e888bcfce691e554157a14b69e95e
+8e9ad87b3e5bddb20d03283c47b7f016
+8e9dd5d70386370b323b2f3a7ae77f08
+8ea30cbfbb0982cc1c4826eb9897c541
+8ea3d7813e21c03aa861c07178835c55
+8ea6336aee6dbd25e0eec48c86b1bbd0
+8ea70044976072f057a4f9366ab37f2e
+8eadca9660c0d8c28494dc138bb19177
+8eaf6852c44fbee4d5686bc31bdf03ef
+8eb466259e0bb022df0cdfc93a62d72f
+8eba7cf00ddd5d7f4fe521815c257320
+8ec32ea72a76b68fc7db9800a8f17e30
+8ec3f7806715044e5e07b288721bc2a1
+8ecaaadafaa4f8f0f173cbde92a27b6a
+8ecf56191c0548bbd5c0956efe526081
+8ed072d33439a06fc8feb53c21069595
+8ed7582a71f5e017c6f8a52ade39db8b
+8ed8d462a869723ba32c791fee77f612
+8eda4f1ff0eade046cff1e8721972888
+8eda5af05f34693b5421d4d9871f82b9
+8edbaf58eb37b0cc81e96d6b9d1face8
+8ede2e92a7aebb469b7df500713c7d6a
+8edeb6d11f406d5c2fa9d7b65ad1e4de
+8eeb0a6b0f0ab01eb3ca0f4cd1fa7303
+8eeb5a76d7234fd543e48010a5415742
+8eef3bf21fee20be02163b7cf7041b41
+8ef567913c918591a18f8a9758225197
+8efdbed41be5e34b564d7f7330f94e06
+8f010afc6ce27f997c483303134d780b
+8f06e96bea3cef4a8ed5efc694594681
+8f0a569574a64a6430c21825ee741ad8
+8f12acfc09e45c48cc9c7945e32a10bf
+8f1cc6206fbb4b6bb2c840e9cd36101f
+8f21ccf33342422550123741773742c7
+8f253aa1e98173c7cf091b1bcfdbe5ee
+8f290db0f882b04989047693b33b09e0
+8f3783b747e1399a858a18541226461a
+8f37b3171da9e29842183ff086e92c2a
+8f3ec3587752efd663fa30d9643cde71
+8f4913596356008a2aef90dbed8a9cab
+8f49c7ab0568c1c323378803a250886c
+8f50ee4c49d2114500edc6df0971ae79
+8f67b3bd87f65965872240fa684c617d
+8f67d1694bb89280d1302f17dc6b9e4c
+8f6845b06b76d37e4e0859e06e780e29
+8f6c2cb48bce2a0cfa9dd85530282c59
+8f6e36bc22ffee9347d27b63e25f972a
+8f7069841ac5af6fe46f82370da2b067
+8f7247346dbbe5c64f2b89ba4f01ea6a
+8f770d14fda54dd5bb0b88151b976017
+8f798ac2cb1fb634ce9724fe0d6b83b4
+8f7d721d7bf0e3d170cb7ba8604b774f
+8f83a53d333ffbd203ce134a32c63aa3
+8f8a2706708d7b978434bd1b6bae54ec
+8f8ae3ffa10b3de30741ab8209b8a70d
+8f8cda8e252dc360c6f3eb24fab8f72e
+8f8f7e89e1a28ba32d4a109088248a15
+8f9c9e2b3f1e4481a96bfb74e894bc61
+8f9d70fcdfd052d2d87ea88471f1b6aa
+8fa47d1a79072e7cbd40fe1f37a0cf49
+8fa66e66b9e5742fd2a85ee9609fcc44
+8fa79e45c02e289d3cb907ddabb363f1
+8fb56baf14013de5c230e1fd9e8e9711
+8fc076e4425f30c9b0736208fe8b0dc7
+8fc74ca90d5d6c1808534f1aa0e84f26
+8fc8decbacf64638d74c8f74a5d2bfb9
+8fdc0a1080b363a8799335215075f63e
+8fe19a77439aa79d03f6df0137b7c981
+8fe6f718cc488237f9634b8f759e3a3e
+8fe7ebccf14502220be74377b692bdd2
+8ff43001bfa0790801809f3380cbc6b2
+8ff95c0fecb08048a4f11903d821486b
+900274df47d4e505dca881754bb5d5aa
+9006e07b4d3ad1e97f3f5eb1fd48851a
+900e3b13fafa6500b12b88703a1f0c23
+90139936cbd39d94b6b2416d9611f4c1
+9016a2c72b46351ab733a9084ac92a30
+901777048ec9464dae0f41bfc195faf2
+90189010c22cb6b6983f7c8c09a90d2f
+901e7d74b4fe4c3721da4b15c7d8de01
+901fa22595e69be00eba9386b92458f1
+9020b39d7fac5f3ac7e10f6f7adb3943
+90219ac9602e20227f734c9f78311023
+90245a412ce28da43027c69af071d46d
+902ef7ed3653fe5d590dc52082e42c23
+9034f3515ddb27dc0b7910614160ef1c
+9037e21a3750fab30a3654e17a1e84de
+9038f9d79febdbe36d0b532cd79d9427
+9039ddabf57192b719688867654fea29
+903a2079b53668b0c5ddb66c7a8e5114
+903c7804573c821da6cf7ca754d2062e
+9042e1a2c33d1e6b569e9e4bd63501f3
+904445861e305194ab7ec532bb5a5da7
+9044547c93bcf483b3913d148650ae5a
+9047da6f77f7c3004a4b2f96aa1b0b43
+9049757eb53543d528be2f5b26da9c18
+904f826fb200ca0a540764a0ec741dde
+905b9c778b2f955fa49a85eb8adf830c
+905fff028c0a0bc315adf0e783da871a
+9062113f2a3d990b82121f48e8c9a391
+906330a303b977258a40032bbfa38a4e
+9063f2370c14c154cfca9234885c4a69
+907d228225cc0ffcf57942b0e308ca2d
+908064bb3af304fc9ab0a38d8f2a6adf
+90849b8af9972c20995ec8264845052e
+9088f9cb7c1abd4e3dcbe3e97f780e61
+908d52a24a4c95926eafea098d6fb559
+90969bdbad7d09361557a70462b5b2f2
+90978aa5b82e0b4bce17109e614dbc41
+9097effde3a1e4f835eb4fc75aad65e5
+909af4d61c5d988d14b3b96727f72d51
+909bd2c856915de4c1432f06095ad347
+90a43d930b93befe7693d0b3fece10aa
+90a952ca43e04f7e289ddc5b62115961
+90abd81df9f214bf39d6ad08b26e31f6
+90b3505c4a0b05e9c0886a28e1705d99
+90b4e40d14fe3f7ae4113389b4418481
+90b55a37fb405e9d27fed9e286bed9c2
+90b58ff83ab87efee0cb73f62ad75314
+90c487fef2c007e430ffd88099321613
+90c93067c632d8e8735a332bce14702b
+90cc0edfbc8632ab7af06128f160c33c
+90cd00a685fc54fc3acdd4c244c53d4e
+90ce407d28d5705a3eb35974eb542e24
+90cfaab6b35ea4c580fa0f4fcd9ac221
+90d85be82eb4abff58b5b9aace28daaa
+90d89de66e0cf44ec34c61db369b45b7
+90da5195818a765b3bc2d3c483938a92
+90dc05b61e06ca2f4a447a85b780a3b7
+90e3a63f91eb2a29d89410b2aafeb1a8
+90ea59e39966e49d6f7839f34b6dd660
+90f1874de26e9ae12f99679837d372ec
+90f216f22ce6c731dd9143ffbfd483fd
+90f2441259d0881a58f7bf896f086913
+90f896c32f442a2061e65c4ae9213055
+90fbdded3176e4471bd124a7928404bc
+910874ca9b6b932bcbadee4c91a1ec55
+9108dc0c2da0eeca73cba59e0566f418
+9112d1d05f05d75fb23d799b36316f71
+9113ef531293a7ba9110d6d9a2e2509d
+9115c46bb98720c98d1f8c8f853415b3
+9132b0fd15ef5233a651f928984f1697
+9136d8b1e9e69cdba1aab4ad818a5fe6
+91380d74e1da667902c77284a62dec28
+913a89fbec23003dad940caf52c349c9
+913d5903a9fae8fb1b56e68c700611f5
+913e1d76d0ab3514b70d70e801cc4388
+914bb39e9abf7d4d9da9669a47272c63
+9159d7e66dde47386f0a0b6e1357ee50
+915d6c3439a0ba60800c6dae4797113d
+9168f5252be58d05fb0529dc58cbbb27
+916e9a0e9aab0ad38c240c7a75418373
+917115dc40cc3dacb72abf2952129861
+9175f323276fb6275836aeca4b228777
+9183c2674f84d031640fcda087b89dc6
+918876434598e5002f2b051d5a5e63a2
+918a81f2455b4e5028d40337faca74dd
+918bc9c5ca408c47df5b2f83343d678e
+91946678096d57036b68ae27060642af
+91961398c6a1e1da8d27e1a272727e88
+919afd9ef35155bd013ca0c0b214747a
+91a2e44a8f6db641ae68e5a4d00e8259
+91a557310da4289e0027bfec1aaa427e
+91a79288dc973ee6f803779b721bbea0
+91ac4f6677563da4acbf06935637ddf1
+91b4fe75569911ae520237bbeb2eabfd
+91b82fa02109806f5181ded7d41b3b6c
+91c81b996dd382db282c1fa59e9751e7
+91c95ff4b8d5f9e711a71e3cd597b467
+91c9ca3fd9f7f7d618d65ac2cab0d96d
+91ca4d098420743a07d6579fa3daaab3
+91d3b782599a36bfea56c85f419ecb5b
+91d552ec6072c9e548543478664d8dda
+91dcc2ee059fc3bfc6144de24ba6768a
+91dda87e90ecaba02eca3b50fc42126d
+91e80267b96e6bfd55eeb63fb1e68adf
+91ed606b64e88e41a15fdf317f7d6187
+91f08ad481ab7aa7c85ab7fef7ad6a6b
+91f19c02e08cfad6e36810e60d132367
+91f3bc7d3701e633ed343cca7ee423a6
+91f496a941a7c76f307da0ee26856317
+91f7d79c85db6be2d66ab595d0a1a845
+91f9f2eda30cf79a3e677d9a1215f5cd
+91fa5634c761846a94136ed32860845d
+91fc26b3bbdf5b9579a37513003a22fd
+91ff0ec55f1100fd30638fd71b93552a
+9203d41c547f00c21e950c68161ef9a8
+9204a4beba7878ba0f4d0d74c82547a1
+9209ad7f53c39aba9cfed276bc480b27
+920ec0d328ed3874cf6f61b882cafbaa
+921a12e1bf6579399ea1feeb5966d5b8
+921ab370a90b2357605bae838ae54919
+921b95f850196df41b240905618df4c7
+922feed288785b0fefa3405f67f4a613
+9234d943e1d83c0bd638696f62a3de64
+9236bbba829b2d069f7148fef9abc264
+923737699969e6f9435b15d5ee4a95dc
+92388653361918222a0aa0da1c33fc08
+9240768733b4fdb8833fba4a5724dec3
+92440c86f9ac2a081800e3babe1e163b
+92448866a30a0ceec7c1efd907d326fb
+92462c46c5e3c3d8ef2a033c6a657527
+9248f5db79e8a67c90ca0ad4ade91799
+924bdabc2ebf48a51ec8a89224e90fa7
+924c7c31e33b0bae559ec5091d991a67
+92558c8a8d6beed0cc835a688e092e4d
+92566a71f5f86ff71bf15ec6ced0ccc6
+925e25f2ad8a3f866fcbe2b9d5ec4972
+925f714b59f80360c2216e351bbe201c
+9265756b9834fd66baa76c5f52610795
+926bc9540f5de489278f2122b376842c
+926df9edd92cbfff4acef79ce71ee222
+926ec7c3df28d92daccf5fa47d72bcac
+926fd7fdef736921e92e12f1097bd575
+927354d63e839b46893ec8d4c74a6055
+927408ccada9cfb21488048a991adb97
+92764213fb123a13c3a1d663ec8e94b5
+927646f37d12520a2740974ad5398667
+9279771e81aa5e215f35c796075fe99a
+928040daaa04d8777a57c5f81da3cbed
+9283f06c7596a332cc416af5f35c1143
+9286a5479b522aea3dffb20f55302ed1
+928ddd4e23c1772507b34ecc88408909
+92947fa4e9e659472208100887d86427
+92969c92ec38bdedd7dd31c85826d268
+92990a24ffae160918b1b42415f9f663
+929a8573f9f69c172fb3cbdb1931e5b6
+929eb260791837b6c64c12a7eed6abd1
+929efcb17b11cbd0599b347b5c8fd4a7
+92a052b67e5e0cd940829bd551b098f8
+92a856ba24eece8d37ff32eb62b31be0
+92b64b9960af990a09b348cf1890f9d5
+92b70ad63af2151b6476b4379f9775df
+92bd5eba2fc0cc9731d8377204034d74
+92bed906e3519b0ab29e537c18592479
+92bee067a187e9003a169f0ca6768e89
+92c20a2dc5fa66339f34d28a84c80fab
+92ca5c3f1e95634ba0e7d057d8177a11
+92cc1c96d19bacaac8754d7027190536
+92cc81ea83bb641c6039e44359bb46a8
+92ce22d54dacbfbf249df37d3ada007f
+92d33bf6d5b58d26adaa100f4a6fa0a1
+92d85c8419e344a168760550bb84ffdf
+92d9fce4b13ce861dd7ad4706ec6b878
+92db1ce2b982fe1bd04894fa92923ae3
+92e0b55c049f423c27a2d2a6c3dc3006
+92e0db778c9ba7d5f7355e62949b0859
+92e8348bf1955ab2affbe4873cd19723
+930e1a7bdb1b4f181311db2ffea21ae3
+931240d009b84da67fa1ee7e551dc271
+9315f0648f29ddbaba4f62aa793448b9
+931ba5a196963513516e390ddff68166
+931c0a852a0d953a89fa76cb69a9e65f
+93241cbb9a583e8e8c8de670e0c257ea
+93266bd72751b4f542217c8453f00f5a
+932cf249f0b7fc30b4e6e293f3ad4331
+932facc0273581febe60292ea07e6cba
+9334f8031d855343b2fec476c0ed7c45
+933906559f6c2d466ba71f53eac099a7
+9345ea7ed31bb22b264ecae9a0ba5c0d
+934776c941c5dbe294eb21e5746951e1
+93571f86d3eb0a5d1ec22fb525c645d7
+935db2eb3b1d51a040fadcc788702fa2
+9360e210e7f6d8c4ffc3e4e8008f3dbf
+9360f5a2f60feeb4edef047a8753c599
+9361567ba48d4da25caf0859efddf48d
+9363f3a7a91d8fd21918125343afc332
+936e013b89a70078a80ddafbff8ec029
+936f1baa4e4fc9bca398fc6fc5b91490
+9371974c333accb09fa466017847e201
+93727b5271da8e236eb12e947faa8298
+937357301142d440caca96bb9d700c3a
+9377b4da61033a14ef84576279bb783f
+9377c2f69e9b6f793e03499392301fa4
+937bd72f99ecf41bd8725369db7ea36b
+93893788bb959d3a959f60440a8b5930
+938f6fac0e28907620e588e59639dbf1
+9390e0932c478f9aee409fdd48b91159
+939b484223fd16d444a3279b41292e90
+939c550b4ce776fb50e4a5ca69c6cb97
+939f2e9d93cf60c9055e7d295baaed06
+93a0c9093f097fc53c4179dd01cde90a
+93a8860bad9f442ebe8f32db068db6f1
+93af1ea89ba5530ffe57c399a54fb811
+93b0f66d51601ff783dd2639b9471e39
+93b266a74e0f7c876d7a674236721550
+93b2bcd61c9a9dad1aad0b358045c2b5
+93bd9d6ed3eb8ec532c03a30df3a5ded
+93be78ede6240b20d81b76148d12e1a5
+93c6a43bf364d236e9c5c6064db21b2c
+93cd15ea1cdbcd1e19137a6c14926368
+93d12dc41f485434426942eec7699c8a
+93d24049cde6d1c822e6ea823ecc7722
+93d5c2f73f3f09d2386966c547d9b702
+93d83aace85e574c50acd45f4b8d53bb
+93df4c0ff19d24f641053df578c5e553
+93df8e02f13353740eea882e0cfcfd84
+93e216d9c5b5aba1ae1fbefc98e77227
+93e443c47a5ac9fdb8923ff179343b29
+93e48654708960e25631eb3fed231d04
+93e9b75ad50462a4dce621271b03e5d6
+93f1d6f9bee5e8462fafd7b6a29ee149
+93f37167eefb615c9774a3d2e68e1e6b
+93f4517704a286507646bb6dd5043b2b
+93f475927714445f199a200e34458c3f
+93f4ccf92f05aa86365bbf33e4c6740b
+93f59f428114c83012185a1eee9be70a
+93f722ba149f123f93fa7b632b04a20e
+93fb852a3ec8ecc1ebd5ea0c7692b225
+9400618bc3df790bdb4fa11c6e570634
+94014a826be5662f5bd094fddab035e1
+9403f378ec5aea1cbf44b4e4bbad8c4a
+94075987f8db123edc17bdba27904805
+940adeb70caebcbe5c73bd0450ca86a1
+941dffad0588c4b292fe17b3c3fb7893
+942ab2b6e6016ecc1ae90215304d09a2
+942d688ab776281cbe1d9ee4ff77699f
+942e0057fc84d2ce34873407596fcc24
+942e459ae28f91e703979d1e11e1022c
+942ff960cd5950d8a297ac8abe3e9811
+94313e083cef8a45eb1570fe01f4197d
+9434e6f004570c73c1bd34e89d71e6d5
+9437d0b3eec4d150d987c8810c37b079
+94393b12529aa3921d8d466c2ea8b062
+943d82c81185239d756d98bc64d33ab0
+943dde72b5afbebef1494c23fcb54a01
+944060319841cdc449f93e376a160ac3
+9442a8bb48d6b66cee00bf7a5b1a1599
+9443487f24f43a532685f480d9f5924b
+944f9b0a14f27e361aa1189467400d4f
+944fe398346134774ae7095d8237e3a6
+94563fa32bbf3e04677c78f71682f8d7
+945676fa148668daf4d7676a4c0b7eea
+945a1b974238b4b2710c53344e80a399
+945c7cf7ed6b9972f011634d5b05da65
+946105a2b83870897915c7e5bb2420c7
+94680ee1adec2063dedda657d098db94
+946c54f7cef97b0f61600571e5c2383f
+94746e5097cbbe340de7d48e146fcb7b
+94773eb1ff628584ddc241aa195106ce
+9477bdc5e8f6ac36adba056e1ae2affd
+9479f97165cf87eee2a6ec50a24a0200
+947ff2ab617be63c08f00c77eef488a6
+948b71a10028f539e29d60dd45d37d43
+9495a944f7cce258c09b1ceba9d980aa
+9495ea31975a5a8f03ce9cae1866b8a5
+949da6c4c6c896c3a7692c2afed505f4
+949e1cab9625367b8c1a30b5995defbc
+949e5ba84dc8acf97ee6271b630ca5d7
+94ab78f0626943fb47b0701a1282f29f
+94b264ffe8a9048706b167217228f190
+94b2a347e17a744824fa165f60d19ba0
+94b4b7a2f25852649b73e34322259679
+94b6ead678277ed64e6bb3f67cbba2ab
+94be132f4484f24dbef167a3a206ef0e
+94bea96f3fd5235307b6cb9c8e57552b
+94c100c6b5866e42de374a3285491d3b
+94c24fa17e31040cfd7f7acd42d35f2e
+94c7603257d0c004c559f1dd5f336ef8
+94c7ad7262786fa4904a8b068d556a68
+94d4441fd25a121df00be95260bf3c37
+94d45147b6c2e18842a2410af79adec1
+94d505de8bd81108ec78d5f1776dac6f
+94d6b63d66d5426e8a53807975f5dfeb
+94d8c5a640f61bf427c756f70bf3dc3f
+94daf44ffbb463be0cd16e683a0a1da5
+94dddd80a3ea688cb44d7dcc42c40dde
+94de6dd8c01c3e04f4bad25a160aaa48
+94df8d9e8d5c48662c6fccf72c2db5ce
+94e22cd21ee6e3d00de342b9fe3d3664
+94ea25775c150372ffeeee8e123f3514
+94f1ec892671db18137d6b88ad065ee8
+94f56a15311c43d3bb2a739bf1c23858
+94f958ea32b643757e13d64f0ec91c55
+94fbff43b3223c2f3d73190c36046017
+94fe53bf2313ad8130d05e1a727a7ee6
+94ff062d68daa28832d806520bb1f44d
+9503f6dadba2b360d8f8d57d4c5caad8
+95048037ed45508470ecf6eab23c5f6b
+950e52a5cb3812ffbc5a0ec7b355d52d
+951706454f0e65d189865e67c5a77b06
+951b2962dc4d8fe0f7cf7de103252ded
+9524b7040c121289f60b645fe5287dfa
+9536db8e9964e1c4b876f593b1e3e7c2
+9536e36db830f81e1ea782693685c00f
+9537eec49f9db9908f2352a0c6311982
+953b831b06422abe96859fbf55c6c3a1
+954184e454a2a6f00af4942be79f1e37
+9547b712351a4f7b3434e3d10f85478b
+95517bac9905743f6562841a7cbe610f
+9557a40d962f06ddbfccb4e716670534
+955d4fdffee53e7481f22234bbcf612b
+955e447882e800e34a1fe42d9fa05669
+955ee7b607d73a439f85b2e8570b941a
+95618abee5ed2560a176b86c831e288e
+956204b9d79593ac96a6ea0537d66b43
+956530908dc64e040710c4b15ab479d2
+9568a882d43cbf6087a821e97e512f0a
+9568f556a1a5cd388c019a37891c6215
+956c205bd60918942974ce977b628bea
+956cc29895abe7b4cbd652e4ef9dee2a
+95709416186b211e59643efe69678a55
+95726306daad5705522ff2da89a2d5d6
+9572d6d669df79bf32aff0af4bca8f6d
+957c4a5ca50886aab9f63fd2a89d7a78
+957f4d2e2ea05f6fd6752087e38d6e63
+957fb41fd83989fc566367acb3c6aff1
+9584d88b6a7351c209474463b22c67fd
+95931f7707ba535ee7ad380c74913fb2
+959a1acbde11ed25a3b5009e3c4c2e43
+959deb22525f32f72882ef46d48f1364
+95a14fbe774ed423e7ced17638412500
+95a8c0afe13c4a5ff30cd9392eabe391
+95a8c4e1ae8115cfd5498c24661ea57a
+95b3e927e8dbf51cad1a046227b2aab6
+95b8f1409bca511c85df2e1955401c0c
+95be85b4bf59b5d2be452e9a0e45ce33
+95bf4621fd05089c985f25a839cc2d4f
+95c01fa830be857714193e4423de3abd
+95c251b92a448929550c9ce60ffd10ca
+95c43ad21780e807708797819403508b
+95c9e9a582fedbcc84c66d7a5a31c493
+95d134677d40874142d2d4e8f9607933
+95d2a6eed9de3c2b3c21818873b336cb
+95d9b3a85c62990da03e29da0f49cef3
+95e274201101f216e8bd92e62c77f6c1
+95e349af5f1d42016da0eaabc869f2a1
+95e93253a619126cced4cbab5a69e33f
+95e9efcba1cf43a5a8e904189f1ca84e
+95eb06bc411c527628cef89509d068d7
+95f5b72a4331a3051fac26c90d96f320
+95f9eeb1d2a52337167593a25983bb15
+95ff6ca0dfed9b7fdce343f18a7c7c51
+96081fc904881e9197c5fbefbed14d6b
+960b423bc75c01b30d877e98afa68486
+960cfcf08e3f707e06dc7c728dc44ace
+96114a6cc60f368aa63d79b41e6cb6b0
+9612a55bb3d270edb3ff48779f1b8dbe
+9615303944217bdff0df04413d485d8d
+961b60acd2709d82ab7d52915ba23659
+961edaae432aee6cf9e3c4a0f80304f6
+96250b25580981468376f5c7fe043dc1
+962edd266a94eb557041fbf8c5d30c0d
+9631f57788b210d50bd95278112e8fe6
+963359d4ba6890bc075dd8b5709dbe42
+963866bc2d0dfba40d0c5f8de251307b
+96399e179c628c4a0b871707ad42b550
+9639cadc98735d4484ebc753e1c70052
+963a32fbb93d2ce5da6eeaf87f17c881
+9640daa6500892fbd666dbb246317b39
+964329b46929f3630fa52d2a17faeabc
+965c9e390f0f05648efb5cb1d0549b1e
+965db93df2f1a54f9692fe1fb16ac075
+965e22b99ee6610e70d17fbd3192be9d
+9668abae0d9ea7282c7c41cae35cf973
+966bc855fb3c62d10dfae9134be8cf90
+96707493f2379580a8dce9b55fa92ab2
+96721de9a2ec101cbf50a79c1ef1dfc8
+9673aa33d6d2ca4e67ad77304ceeda4f
+9677b6ad6164e0a3a6dcad90ab9c7c94
+967832406b104cea8fdda445950980a0
+967b01a89ccaf0e7170c35d24980e158
+967e69b63c886d7634754cef39911513
+967f9bba25b593cc14faef9ec3ee083a
+96824a20e5fe8727d18b3df47fcf6e6e
+9687d8567e6c5e90f2395f001f9378ba
+968923ac315a91ff8bef4af03e87b2ba
+968b3f04bb9658d8e2ab1455d5440aa3
+968bcb427771195307da425582bfad0f
+96933e2083f795d973fbbc521445b003
+96947baffc7b44c437dc8ad1b204f239
+969614fac19521f70d44fecd4422f513
+969ff44228aca4326420b47ed1cd7b1b
+96a03a6fe10840ecc6765ab3f62bb825
+96a47ca681fe7f7748dfde0468496638
+96ab52aeae0cb530858a185c33c894fc
+96ad4c0f8e15adfc5615c9ac9fa418f1
+96b166310f26ad17a6aa7c106711feb1
+96b438bc0cf9c192b59da764d47658e9
+96bc7f1618f99e33f564dad29e2059c6
+96c1fdbbaddabcfcd7164faa9e096317
+96c5aca4068eb1ee05b0972f2476077d
+96d4ab835c5008dfd25d81ca2e6a02d2
+96d93bd9cbd444bf29847621aeba1fcf
+96d99ab9da6523be7c95a53b4f08a0ab
+96da0dfcf8a263fb94e0131a8278e16e
+96e900c5dd0de55d2bfc0b4c2522fa87
+96e9a3ba79c5d299629fd3464583e469
+96ea784158e4b550e51df6cb67b4d122
+96f584471dc2afc6c224096004d450a3
+971002557b2e18bd5c676dc01f8be2c7
+971041d9543367a1566950cd77d751e4
+971632af16ce958ae5c40226ce32e13d
+9719b6ce55b96a171d5f404c685bb70b
+971ae29d26ab6e6578f3358fdd99710b
+971d7326c79bd9292ee5f2861a9cf563
+9722c1c650f00c1abccac843e79b2f5b
+972475297dc24672d79d8814004cf9e3
+9727a7980ed239400129bef3a7b3f5dd
+9728bb415830634b9260fe1415c155ef
+972a63e62aca267cf49ec23e79e3c24e
+972c487a3f86e3084c77a8368b1cd842
+972daa442f9a0f386a39d1a22b490bcb
+972e575fa1b1b3b298f2807a657623f7
+973364adc55a1fbd6e08c10d4cb6e0a2
+973526a994fd3d8fbe1236849190e2e2
+973793441794e526e52258f53e467391
+973c4623a6e7da340eb60e5e320c97c5
+974489a7d051f11d102d94bf28b5419b
+974703fd7e0188c2cf42471c48a6c9a5
+9747b9b930e6563d6b070f720f6c48c0
+9749b4337dce690b897ad0b9a9e55a24
+974be5a085d18b5041456e4690ec3ae2
+9754524b5b3d8b43f833e896f579cb05
+9757670b24c3362103d8599467063074
+97597992f1517fac2a77f45e544e2748
+97598b07bde384ebd26e0c4162dc3079
+9766724a97167f4dbf39c6eb7ee3a5e2
+976de60823b0520daafe310d58079b8a
+97727acfe5566b28d759df940c8e5dc9
+9776357db5da42b16790d3f6acf04ed5
+977e1826b8e83dc15133d6957e51ac16
+9797abbbca5933094a0ebae59cc10f84
+97983cd65948f23f18fc7e2a04e466f7
+97a57d307b3dab3ff796277d89861d26
+97a5da8c04f3c82cdaeed5b57a4f483a
+97a5f2a87a2c8b0ad921e76997e5bc42
+97b1e4f8518fcbc1dcb257a1c317d6b2
+97b37f899ed7c1d3a4692f7b18543bfe
+97b80dfaa68568947dd40af8758e110f
+97ba3fb75b7f2f2a9cdc62e3841844dd
+97bc5f1a024dd9c014a36c5dc7277c41
+97be6be77626d8804c6535d02eb6b8da
+97c91858cecc18abab0e6cdd3a033a9f
+97ce03e2f456caf925024d1580a9562a
+97d4ff708a7016f0925f1e8702655a3b
+97db192a74c0d7713ffebd54a18ea4b0
+97db2f18d5ad1bd289d2339e6351c461
+97e5829301aa1c03739f8b576a39b09e
+97e8143397fffb51f5bb125828299e3c
+97f0afa684c351072d65474f39ac6979
+97f2870bc46a8d069ad0051f8370ee00
+97f321151d42bb74cde79f96d9861511
+97f3bb9c0880a5a4eb6cc531a636af7b
+97f5af9844f3bbf3b0f82c992a24c731
+97f640db7545e75336ca64b62c6f84f6
+9801051dfa98d753d3c59f4d9b93096a
+9801c89fcd7ede5d2e7561297b93afd7
+9804e0e3ebccd359ef91a18588182b3a
+9806b54e46b6b4291ea779bdec6d4e7e
+980c90ce1f01ff630410891f76bc68e0
+9810e8045e6a434dbf8add75e4959655
+981233072ae033a20cf158a288b858ce
+9816d5795f2123dc3400e522101a92e5
+9817274209c845b43537c8a3fc6f05f2
+981cb56ec7b7c2eafc9a4d61806d53c1
+981cdfbf0e06b39c06d517d3609f0622
+981e2924dcbbcf253b5afa514cd2ddb1
+9820b03da3e468e7e8749b7297854404
+98237bd28eac299607d400889f968e03
+9825b208635bd6387435d44af37977e2
+9825c8c811ff05d452e8176cb32fe578
+982a203b018574d758327b50a7c43fd8
+9830a39accad3c14e4d744e2ed35b9fe
+9836a2d691b71c2e2951fc75b1412fc0
+98389f8ca3d64c754ae66ce151bf5876
+984c342094eea7e1c8752637f39e4b28
+9850413262acc40d3e791b140258e55c
+985409c48103d5367714b9cbda8b9ac9
+9854e539d203750a1f54ebf2201e780c
+985a4cbeb150f9eb183c82e5b3219407
+985d2f619ae51b57162f30dc2118dfd8
+985ed3bd4c034ef0bbd42b5e36b30a51
+986af39f8bed3841397297561d5adaa3
+986cca8ab073fdfdaed55a013fd3d5e9
+987287a38325b00adde3116feb171455
+987393e0844691924f19354b41be030c
+98745fe9f4c53a2081be4fdfd317f150
+987d907787dcaad18d8c892f475c8021
+98825389e5572fc3ed5ccd9a826daca8
+9887c8112ac7108fe005d6d3459bea34
+988add19399a04fd2ed6c81cd0d91218
+988ce9b9eb36a9e7713b58ef98db37bf
+988ed8a8315531f344f7314f39b2c61b
+989279c6bba50c54a82d2419bf2ae212
+989677edfc59ba2af1cddc3b978e55ca
+98971604de800d47789f28c4c287f8e4
+989b4ffad1c1d992b16ce0573dd3adc3
+989d439f68307a8cc484dce3b20dcc73
+989ecc240324f997d5e1789747af9053
+98a9c5415c2f0f13cab2da05415e27f2
+98ae9241c41f7e4307c01c7e22c9dae1
+98b25f00b25fe75a9871840fd36a17f4
+98b4e6bd0bee591b85fcd083e4800311
+98b6eb643a27267ae252bfd1f003165d
+98b7ec1e19905d36d391b04c8c67a445
+98bb467efa0b403ed1b71887f74c945f
+98bbfbb71c020a8a9b3fc77fcce32b89
+98c59b5a2be1f6586e85cc73abd19b07
+98cb8907d50bb97c4955a4fc3b1828de
+98cd2d4a4c28d79d9f26d0c2bd84fb76
+98cfa274959e8e6cae7ee76ad5f9683a
+98d06d2f5c94ccfc9e0b513643e52000
+98d10f2f909e637648fcc9a402d3bf0e
+98d11258621a8311e5b8bf4282e6def3
+98d9819375d562e5aff3a28310ce203b
+98dc646fe50fa365d904b3da5ce9632e
+98e3130acca7b432d2f1dbbd6633dc61
+98ec6875c25d881ed5bbb6a879376da7
+98f43aaaffcbaf05d85bf8ed19f9316a
+98f7d2ddde6853188ce00e023b1d1359
+98f8449e4b0f2a0d416cced5745a97a9
+98fe0cb4b061c20d3d12956c2fe65512
+98fe618a3ef98d07d2bff144d1c53cfb
+9903b0218c7fb88cc023f6a066cfc1d5
+99199d68e7acf2945a2a23ee94a14dbf
+9928457b727a892ff83cbc826b966e00
+992a2acc09d0f0202b25370b8f135d48
+992a94b402335f58b9efe1c9c90a4f5e
+992df7fad95f19cfdbd0881ed1296a64
+9933e8ada843fe5cb2660a60a9ed07b9
+9935d15d6347b509020fc164337a5c5a
+993835a5762033e25c6d31d27d35af77
+99397a093960f75cada201535d8afbc7
+993b875b84f7b0afb2857acf87bb483e
+993ddcd4044305bed0cbec5983897f23
+993f03a49f08e60fb3c6eca9017cda8c
+9943f41a2ce7663d3554803aad3f3722
+99451dc3573da46aa887dc090b9822de
+994add08ab5945115bc225f2efd20cd6
+994ca46f045d336a9f0f83e65b432cf9
+994cc0029255e3f3c1790954c510c4b0
+9952a011ec7c057d42d4363088cfe566
+99537572a95b5c80d7e91a4efd17dd3f
+995c2ae2796dc65ba029f5f2d32dafa7
+9960fefdff8a58cc8160fb215f644bec
+99614e108361e58e7190464275c0bd5c
+99673019b873ffd447623d24638c7ea8
+996bc0c25724c0416e1f56d4e18d502f
+996c4ebfd1cb46ee46b204970c4a1a96
+997207b98a8db8be45fc9ecedec0344a
+99728f565f0314165a3972524647bce4
+997a1d008d5ef574bedfe17178fb6091
+997cc2a04813d5e7d7b2ce197a1f7b70
+997df4a45bff429898bf8a6c2825447b
+9980ab57721b93ad056468e060a07f4c
+9980bf2d6748b4f336a43d2430608d67
+9982948bf46b7bd3a2c433958ebacfa8
+998594acd134e1b53838898830d04ece
+999013cd579d6615d8392ea87db9e137
+9991d033cccfd2f89ff36d7a43112722
+99952e744dd77a7fd2e60b986294cf66
+999f586fd524f6933dd7239dc27ab264
+99a24f766ef5c61b6f6b3b3b9a12e1e6
+99a45058d5e66262ca657291c27e8c65
+99a4b2843242522a4f060f2c408b5e56
+99a654a338efbd6fc6a0e2d7e5f1e632
+99a6c68f58daca9a3bd411349cca4e05
+99a9b282ae165463844b3109dac3c1f2
+99b01b7026a89999d010d56ca65ba83e
+99b78b73e260b25e311cb1610fcb7149
+99bedf22341c75c5eca47d8d6d9b0d14
+99c65ce8b44c1de97d503bcb6bc96d40
+99c86f3bc07c963165ff5b76e4e40e42
+99d0afbb440990d4db1fbdfdd17c2bbe
+99d1501077821b4c90d628ef705b949b
+99d6019e97feabcdb49f68d3ed019364
+99d72f0eb6b8202ca80e29ad6ffe6868
+99d97926de2f6bafde9f598148a6ba5e
+99db12b4ddcbe31519eff9ab61a3ff3c
+99dc43a6beac54b53f1b79db129f14c5
+99e1b97286f64c7fd80615281774a51e
+99ed4d1e4de7783ddabcd227316308ca
+99f0f82ba5febb60af18640c9538842d
+99f69dbd30e3d9651f21f243a998bebc
+99f868eb6c6190075cbc47dda930ba74
+99f9a19a39d7619c591e309ad111ae08
+99fa550b6486ca2baf3d873480e91891
+99fc1f1099bafd39c166e48877826037
+99fef578fdbe2e00a934c0f33ee5b42e
+9a0042adb3bef413cbc61f510430d27f
+9a078806d0fad797d6dcf0603cbd4707
+9a084299cf87ae5d7388985b3bdd6231
+9a0b8cd434bcd328fc660906b6376796
+9a106f7a3e72a0ef1c29532e61922c48
+9a10b0390daff5ed34eb7c14d4b45e3b
+9a124b15d886618bef3eec663593a862
+9a1706060c181cd8a421d50141286fd4
+9a1d2ec74cbbbecab4f6c78a0a3ce330
+9a20995a6f80d37366770ba8ceeb72aa
+9a20cb9d7b4134e82cf09a6856c6671d
+9a21fefcaac971d304d9f89d0071524f
+9a250214586226a36b5839c6db140714
+9a28e554d42bb75ccc5a13df1c3a0dad
+9a2a9ff771e845bfb2377b07e6351ee6
+9a2c1b80e41024ba2251f780ba56615e
+9a2fae2409aa6c8227603cc8537da731
+9a2fb00189e84eab5b319134f18f61b0
+9a323025e305d89eb7f7c7f692bcc15d
+9a3299f800b6ebc2460e74a5ba53e491
+9a387d8cb566ee78a96193ef1a192850
+9a3e17fe7d84ca993efb7893f24262e5
+9a3f65d7fb3314f21a85806930c814d5
+9a4f5ae8d315e68a537b6f8d658c8990
+9a5eaa85758ac00242d40f98b791400b
+9a604110af202551f31755b60fbca1ff
+9a6322a2079fbeb6b5bb82a2c9c640cb
+9a640b5756abb61711666db8355cade7
+9a64f0135177a7b77398221842c7ffa8
+9a6a95701ad00fe7445025544ddd543e
+9a6ca5902386c4361a343c4c2f0a30a8
+9a6eb714517ab8e1044124bee52d4573
+9a71a1c8b2f2968785fc15b60a745594
+9a76ad439561cf5b22472bb3309ff997
+9a770b51ec2966537cb3d80614f0fbf3
+9a80162cddb4d24fb51a9ba812809954
+9a823353c6290390b281aa9fbdacbe30
+9a8738f9d45279fc6c0f06e15dd31116
+9a879591420bd3775dea24c85a2ac67f
+9a8add0b5945dcece09fec916eee6a91
+9a950a641179ff88fff59a4b6d91f048
+9a9598aae42ae47b7653d48d6f316de8
+9a970b363e806d01cb4a34c1ab72701c
+9a9c8ece56d0965a11cec168d13a103e
+9aa8295fdc5855782213084cf32f3599
+9aae1a6eeb6a87b24370bb0ab0540e31
+9abc0348f19e2a7332a452761bbda42c
+9abee557204f22f21125694601e862ed
+9ac1a3463a77339b55bc797c7f676b5e
+9ac395253c70f82527c8efb1ab5f5163
+9ac714e51c0004f08ccbc666a3b8aecf
+9ac856a06353f0661698308dd6a49a17
+9acae185c15b3a8e1378f6d407e45a67
+9acc1408eaf275d80955fb6d7c86475d
+9ace31b0e6600645fc369a137c512c16
+9ad24d9a4b634b1434d1a392a03fc670
+9ad2fde5ea54120a6f2f4fb5d2459725
+9ad3ea7addc4eb56904e9c75efc0c0b1
+9ad70cc24ee79a4f1d4890e061e8112a
+9addbe765b40096f3b88b72e81613cd1
+9adf444d801740d252a3e1ef34a03d0d
+9adfdf1a4cc22a5556d243b90d7c9d41
+9ae9591be382cfc45425b95e71309701
+9aea1f1cac816ab7ffe0baf7a0cc2e12
+9aecc3a565142f991dc8162bcfd7903d
+9af463c3fe323a8171a042fe761d1601
+9afebdd6ddad9e336c20ce436f2dbeeb
+9b050b29a51c821352328ad36b75f675
+9b0680dd24542c87887b28f6c69556f8
+9b06ac784f943e6965fbb312dd5c685b
+9b0bd19a572d45da058f1ab843d1e54f
+9b0e335d4f2a92586d3ac00841d542db
+9b0fa5111a4133758d56a2e0b60c66ff
+9b109829547e4352b3c882709640d896
+9b17a662f37c65be1dc0a2601be67d0a
+9b193accad73f3a8c533af098efb8669
+9b19956efa8bff480a5ec4228fc36999
+9b1ae07290d1cdf2a87e0505e142f141
+9b1d1f271805ec311012c57a612919ce
+9b26586e62fc26a34d78da1f4f29541e
+9b26fdef8e7e71e50d25975546d454bc
+9b27cfa7cd034ca572953d8d0536f8ce
+9b28c190fb8af1835f12e78527c7d7a9
+9b29631b084552d7bb89f9123426499d
+9b29999db3bf9e6520389bcc4e9641c5
+9b32bfda75a54110ec51526f63710b32
+9b32ed36c490edcbe22a895bb36f290e
+9b3bf44c4611ab5f04126c9d12918f0b
+9b3f2a04a21480621a49ab5ea27ee1d2
+9b4303b36a2a88f4a95ab0acf4c11026
+9b470327cb323229d8ec942b4cae9bb6
+9b581050e2ecf0b9f9d4c378a4b25463
+9b59989bbdc05148caa21fbd71495b52
+9b5a1dae532ef9bf9dd37ba88d3c8710
+9b5e31bddfaebbb14c5f91c0327b9e8d
+9b5eea025b77256b7e2daabd2b697a3f
+9b60df072c6f3027ee44a2100cb74996
+9b6ee4a9dbb1184c00391993aab48080
+9b6fe181665f80ccf11f4821917f059f
+9b72375f708b0a62cad42a72e4afffd9
+9b7486947ad365fdadcc773a75720c26
+9b796ae99a780f985c10c01d3642fe51
+9b7a257a3ffb10c392954df98e94f2bc
+9b7b944fc950bca5edce4c8229038ef3
+9b7d88067c11d22ccd9f86be4577c7f9
+9b7e3e613cd060cce8432971440c1a65
+9b83337aa3412932c25f40176b5ee1c4
+9b84ec457ce27bb961429561751b920b
+9b8554f6ae48eed99f4edf4d66524dfc
+9b85fc71907a78d1e28cc7d9c6195185
+9b8e5013ed0a3eb00ee34ffbc9d89395
+9b91c09cafe91467493be288ade8ed9a
+9b942445a0bd32aeeb86bd922cc0636d
+9b949bdf85ac91b8dc4eadec68fb7309
+9b9638e3cd3451ccdfdb8f069461df58
+9b97f24c07674e8f710a9446ec90cd3d
+9b9c096618636608ffbb0d893d7af5e3
+9b9f3d4702a291ff307d992164fec34c
+9ba35ea204c86c15353707ca77563e34
+9bab936a606915a42c18ae6b0b37a4b0
+9babdcca2fb7e9411de167206fa248c1
+9bb56af2d871fc38e91ca63daa6921c7
+9bb73ca867c339e40f589956a994bdac
+9bb76a2fd2a6133b9e671d30aa7f0b30
+9bc60241f70be80046243f842702c282
+9bcaf7a94a9db8a662eb4fd3c3d50e49
+9bda9800e66f4e3a9cb66973c769371e
+9bde563a514d60ea5ece05d7d628f770
+9be314a6af4bf72219be4aae60d3e692
+9be907d1333c349d642cc853ad841d71
+9bf1515d303c782486ca14ebc2eadfe9
+9bf77f6db4eb511cd2b8a1cb3af44708
+9bfc78c4290b03e71411ca6fd4b95479
+9bfe28fad5880b23d388dc9de3587263
+9c034476a870cb2b09618988d07c39e3
+9c03dfc873c31ae3108b94359e7278e0
+9c0e2403902ae525cfcf16c71b831a46
+9c0e56e5aa2d0613d3f636c9339cfd20
+9c14ab70d8ea2613b8a8db6b6d8ca889
+9c188c0402bf930dcc47982dac988900
+9c1a485bb8da2ed34e39457932623599
+9c1aa2667ddc3a4fc7865f65c4ae2a06
+9c23f54ea5b67ca34dcd3ed826835fa8
+9c2ac8d127341092c560f544d44870f2
+9c2b821b4b016fd977980d108636e9eb
+9c2bde90d2462140588a7bef6168861c
+9c2c5340021622a5d1f6b340ba259ff4
+9c36de49a8b789926d0a78db397c187a
+9c3c18e67a28c5a69f0f7c1b8bba2701
+9c3ee59feeac225fc1a6efcdf758102d
+9c3f8ba083740f9bf7badcd9967ff895
+9c40ce86b63f479ddd9b0f2968a142a0
+9c4514477b208a2202dfcbeb69d11c2c
+9c4706ce76b191d72a2eb421cd625e17
+9c497aecb96abc2a5cb5c360c7f8d156
+9c4e31171417d805ae339d110d65087b
+9c521b366b4068f855b2b59401857f4d
+9c68bfcd79c2031f41fcdb917c40f6c7
+9c6aacd3a127d07011c3c5b9663b4e6a
+9c6cd7cc2a9b0137f8f64fff58c97fa9
+9c70b3a149b3b009a890594933a6af18
+9c724db389ee002d4e04e7ae635474c8
+9c79f23c8d4fe9f146ab04285631d4e0
+9c79f44ffc67c70461f8c81c24e2e8db
+9c84ba7c5065f0ada916a58ae7714294
+9c881e9dcbefca8e2e896663a9f25d66
+9c8c3f99502a60fb004285c651b33046
+9c911d1e91da1dd09d0db7b49b8d6652
+9c921edcf290a38a9d0ac33445a46668
+9c9705a69dd2da51358aa9cd4f829231
+9ca8f5fb7bee23ee68d0b9b6d7ef81d4
+9caa2a0c02261e9b83b3b74ded77eb72
+9cabe7db618be23abb862faa2ae0043b
+9cb03083a669f675791a810c8efe2e5b
+9cb0625e1a0388287855c979cc3ccc1d
+9cb193e8fcb1a338b6a8353fd27c293b
+9cb8c52450f2908c8b5cda073bbd3caa
+9cc003b458a8b8e2818934a349fef105
+9cc2f1b9b7fcd1e26b6696859f972a78
+9cc513dcd5f39a4ac3d1b879ec4324fc
+9ccc10d9c9c4d05d31254480607d8f1e
+9cce3e1cb9b6d2d82d2054588c93d0e1
+9cd11c7473772e875dee00b6abc87e1b
+9cd55fd5c46a85f03293f972441d4742
+9cdaa71d475b8655e9304567c45fd2d8
+9cdb9987fa143095c4524eeeb12aefa8
+9cdd4f5dcc015846ab92db8e15373835
+9cdedd882054902d8e4b207080d26e3e
+9ce08e10c5cfe510e10979363b8f17a1
+9ce4443ce1a81cda5f8331d30226fd67
+9ce45c528bf0632bbec6265e7b8aa451
+9ce49f66d05a3698e9672ab71d93c022
+9ce5469ba4072b357e54e6f1987fb419
+9ce567387041f9761ad8de80bc0837fd
+9ced740d11d5d82573d11d779007cca3
+9cee4b8e63688c4e31a4014519887836
+9cefe9e9c3d0733821a75e70213942ec
+9cf34101a4f4ae8fcdf3b4958da6b76d
+9cf429e423d51ceab0b58d7ee16493f6
+9cfba90f300f65c50719f82578666bb2
+9d078ab0bee2e88cd5f7b2fff2784327
+9d0e284ce1c45a54b87eb0ed1ac45ee9
+9d0eaca84ba9a3576218da3e9f807a66
+9d124808d20e471bc123acf80ee75083
+9d13669a54a0641de3d5b7f46d6d10c3
+9d194d10cad48bd920fcee7703e07199
+9d1a3d8295436a7a9112023f6b789bfc
+9d1bac4e0bcbec10b9aafffdf7950e14
+9d204ae75b9614f0495be3932008ea95
+9d2eb770acc5c5faa3eee32e7c8da74a
+9d30ef61feebf6415e212501fd3b8051
+9d33a1b68323b68f950cb2b28223728d
+9d3614641bb94ebedc3ca44ff1708a6f
+9d3cfba4cfb1d0b1111620251f9d68e2
+9d45379eae41dfe215655a11a9860d24
+9d46e18e290d2a369f02de6bbf7658dd
+9d4a639cce13727cb814cbdd32805e0f
+9d4b0a3b18f24b672e6542049bf540fc
+9d4b6bbcd89cf1e406e1541c79ec1995
+9d4db32ab640fed0e13590b68e302204
+9d54d328a0b8ef4c18d65199f28381ab
+9d587a46370e6f4535e4328810b7f230
+9d593ffd35e14f83ca530c6ae366b618
+9d5be346a55ad49c86c87b306fb9dc92
+9d5eee56eea325dbba8ead8b3f34ec1c
+9d6600f52a7853f0c3ec34d6da5b4488
+9d68fc2311aa5231e1612b6d800795a0
+9d736e55c3cb558298331f65a04ca0e6
+9d76092362328ed92ee148ff35fa84da
+9d86e9caa97d78704d3d5d742dac748b
+9d89256bf4ddcac11d3f9fb9a8589a55
+9d8f554ba1751eb6f1d06e8e48d829ce
+9d946c9515cf0e4bd5af8ac30f7398be
+9d990be0a303be11b290bc443da71f4e
+9d9b38697f104cebf0913dc7aef7e4ec
+9d9dfced9f1a192e7a7fbfa5d3ba9ebd
+9da17bddc551171017b0f73f861ca136
+9da2547cb56337f074341fe1aeaa4a57
+9da30e68151a854380c520733b56b899
+9da47f91c2e3ca422a0879fda9cfbc4a
+9da4ecae0d130472c782bfae637e1d6c
+9da53f9523776c17a513b3b57c8e96ce
+9da82c2fdccb96ab140f80fea2a52c63
+9daafdd849dcd80e1c4dff3b18d72bbc
+9dacbb6867fa634529ba85b2368c9cbe
+9db607c1f5da6d9afb22b3dfce7eeea4
+9db61601f0efb4673c5be1b595441e25
+9dbcba23b3bfc76648d971b518507129
+9dc58faba8c34f18d3ce6e132814813b
+9dce91a19248d03352bf24ad50ae5bb3
+9dd00a094f41d53f106d9029b6059ba7
+9dd194dd70c454e38a2df74ab5b6a7f1
+9dd33bef2522dc45c17b1e314c23cd5c
+9dd3ff08414810f3989ccc381eedb4f2
+9dd4361a8e88929f992a0469f95b2594
+9dd8e5f3418bfe4257d7918b7c6975b4
+9ddef2982eb977497b7138ddf2b71ed2
+9de970cff69eed314339b65abf1d54f7
+9deb970d7b9121a768ed2c839d6d7957
+9dec6bc06c4e5abc29ab9b0499cf99c7
+9df17204912109ac06201eafb3f0a28b
+9df54218d3a9a22211314189d687dab3
+9df7a5e1a54055104f962f191ff470bd
+9df807fa61108e1e67b58e75f94419f7
+9dff57c66e1ad292389fedde3dd370a9
+9e013bb197bf50edb23b990950d7f442
+9e03879007d8053efca40934b49b5df4
+9e0ee38ea6f5674e37b42cf41548f81f
+9e109f9cbef1843c36b9dbe13d9016b2
+9e17dc72b2ededdb48b35abbc994e8c4
+9e1bfdc09b7ad24645a3793cdb0bf403
+9e1d97729a7385cb8676662e2141ba06
+9e2449ebd974f5596b00877e08f5cc43
+9e2606e32f73457d0d9a9b2ed5bb6f14
+9e2eda1c4f83929dab40f1a2939bbfa0
+9e31feda2692a26b35e84aaa3f5f2dc9
+9e36abe16a5a3cd99e656fe029ad38ee
+9e39bd820d236220ee60588ac21a7789
+9e3aad39819f760fa31779908c25d11e
+9e3c21f1f8986e8a2a025b797dd19a85
+9e43f9d05fb38177e10edb82c1bcad5b
+9e456866be7057383d137ec8d330a5c9
+9e48ee528f13794a4a9943d21f9b19f6
+9e49906ad75b1116e35e724fb8f73d74
+9e4dc2e769a7a61dc0e58c63e5272383
+9e513e4ace5f991460fa2b6046bbb688
+9e5515ccfe7d2507f6d70e88cf7417eb
+9e5712c955d89028108e52e13f5488db
+9e5fbe79cdebbf6325bea2f1a675dc2d
+9e60970247999d68e9a14d96d13ff2a1
+9e623424c081318359f63cc0eb3a8f71
+9e6f0cdd81a8005f60952c4d19089c57
+9e71b16c0d9caa9ea68e1b484220ee43
+9e78a0a3e4688b35241c2a8aed02657e
+9e8105e0a0aa5db3060f49843cf5486c
+9e8bc9f9733dff0c40731b53ab857325
+9e8d46cc05bbcf298ea169f4f8db485f
+9e938d92fd659cae26f2e70de70034f9
+9e945aedf671ec4ae1f1f82f2f19ed72
+9e980600faee964f0b809daeddb852ef
+9e99070827b1225519f3b9c4364e1857
+9e9ddd94861db26d2c5a64a2db96aad5
+9e9e1c249dd975a2c7b53fa451012924
+9e9e9b0cef9abef0f980fb2818306b9f
+9ea192468455dbc845e090ed3255bca8
+9ea4626a8010f6b583910cfc91b5a521
+9ea4a36bed9e163408222506300fb235
+9ea7829a5d5461f15b081eac68be11aa
+9ea93fd4b49046d774b32ee3a4e3dca9
+9eaaacf8776f959225944ed2777cf5a6
+9eaab062b5bb7d0df37c95221d6d8ce1
+9eac6f0bd10fe3d00ee70196bc8fb226
+9ead152a369d3e55e2f4d0a5cd22987a
+9eade967a04df82a064cd81a0795f1ac
+9eb2f0af835e54478407de50af1c5539
+9eb7acb72abe07970be8d104bdbff67d
+9eb9e791e0bd2efc7db603cda00d0ee0
+9ec1a7390e2abd75f4f103443f1c5f70
+9ec2c99c3239edd406401fd4183cd092
+9ec7e5483b022460569ed90706810f1d
+9ecded1a6ab550cde56df31d3764b40e
+9ed0692ef2393843f82b5c65fddf219e
+9ed6f754c45b90168d6345ea46410ae0
+9edb5becccf3197b52fcebbb0f94c787
+9ee6c0a166e295cce272eac2814fe4db
+9ee9a19588d35df27803e334dae77fc6
+9eee94438cf6a63ddd668c1651b23e94
+9ef3bc88f5fcb2ecf97f10cd2891b16b
+9ef4a9740938ea6973c84fef9680623d
+9ef756856aa4546570278f3bf775131e
+9f0007955e55a3c5e5d29bce15fbf1fa
+9f000b4173f83ec586f9aaa27fb689a7
+9f00d5ca8707ac39840aedad433acb30
+9f0b58de44b98ad75c0e6128a34d637e
+9f10fdd0ba7e9aac0c1233da980a98c7
+9f1741e24207ec85de99653036a9ce2e
+9f1beac7b6a2d9550cde5fc502f1050a
+9f1cc2b639554923084000cbeba67bc4
+9f1fbab35a344bd13e40b4267f935932
+9f200e257386f251c6a3bc58760c313b
+9f2c6b0d979daf69249139022c950a18
+9f2de3ec64dcda7364a5f67f79265a9f
+9f33ce84878221ace4c3e9cafc52fb68
+9f3504bb84b9fa5a36ae57934f2a3d3b
+9f359ddd800dc12d8fba1907aad2bcbf
+9f36886024be001896a00613c7c2f29a
+9f37932dfa37bfebb9fcaba9f99cb6c3
+9f39064c48dc286f614b2f7a5aa18536
+9f3b651213d6b9c0454807955dba40ff
+9f3c3d05d119f58e6921f097790852fd
+9f3f3f2efcdbd4bd71c58505803d7820
+9f413eac9608562ca5bf5599882f1abd
+9f4611657c4bbb7011eb63e7b76d426f
+9f488d2da29772f33246a3421c022a25
+9f49e9b5366b093494f2527d87b17da6
+9f4bb452c51568c326191e5eb0002d1a
+9f5804ba72dd3283889c7e0abac99b65
+9f5aa0b7618b5a20ea6666e47ac2d2d6
+9f5e6f5635c1d914ab65ec80dc136d58
+9f61a79bf942114640ffc67a925632d2
+9f6223459c7a03f268fa274f25e96ca2
+9f62bc6f1eae1ae3830ef830c995632b
+9f64bc1417b406aa7243ac89bae076d9
+9f6bea8ad9df9edf0145c77cf498142d
+9f703ad58fca11d8fe8a2415ccaee667
+9f715a1e507020983822b2e3dc21470b
+9f72f4558689fe93a5ec7986cf218f9e
+9f7cfac24ecca104f50303a0a80d25fb
+9f82341fb405a64c44ce46c4fa058ecd
+9f844fdad91d93a3fecabf616026b43b
+9f85175ce91cc463aaabc94a98b8045f
+9f891a768537ffe38023c48591684ff4
+9f891c569980f3a1ef2e162899273441
+9f8dcd074e90eddbbb61587e50005d83
+9f921c723edd536888f7a88c15836029
+9f952171b872ed2863f41427097dab9b
+9f953152db90da6cc41bb63e99aa3f6b
+9f966959344257f6499be26d10568d2e
+9f97d1b7b4256d0f1e399a7d25ac96a4
+9f97f83a929156a29d8dda7dff115541
+9f9ba3c30dc9317968e8ec8f29de488f
+9fa61a6b88f795b6f38bf453c533cae6
+9fac9dd7abea22815be7b6c71ff18f4f
+9fb630c9599530f5c27796fc588b0c6f
+9fbea916f47ca09326b00089b93dbfe4
+9fc19357977760581706234cc2c95d39
+9fcb4d2d86754c670ab97c73251fda03
+9fd3e4e469e7ca11b840bfa5cccfe15f
+9fd529ea0cf805e659059e1087535b95
+9fd557f9ca0c2e0c2d13b2639da8b34d
+9fdba845d5fc699d9e254a86e0d6bd08
+9fdbbc5721448d6912f168bdc396093e
+9fdc99297647c4e4caa87dddf9e82520
+9fddf6eecdc21984bc9cfb8d668eaf91
+9fdf1d41c0ab7d7819412f130430a882
+9fe0af324b2d2c1177a8e369a9f5cff5
+9fe2f4a9ef6e476ec69202dcd6a262e8
+9fe3a8aabb8e8b8664c7012d4ae97d88
+9fe70d38beeea21444ff97568dd1219e
+9ffcf18b72ae0de030d2c0ca0762cbb5
+a001aa53c992b8e1b44507178759bd8b
+a005d053267da46d0fbdf438ba2c50bc
+a00bd37972e644cb0f4930710c866691
+a00be5fdf5f1f983fdc2d2cf162e07c0
+a01013f15ee11b694450fb842590b930
+a01512a3055e7c01a51d4a6575e4c31b
+a0184f28213ff985ad7ceb0a55f67731
+a019661d3d4a64752d5fa024055b5a85
+a01b069bb752667d25574145e5fe2d1f
+a01cd71c166efebcc3fe549a26fd95ff
+a01eb567c6f10c7b300116baef769c95
+a02188409b5c324afc48250850db8bf1
+a021ae7c424b5a560d9da319c348ec4e
+a021ea56fce6aba6652c78c2a1f591dd
+a0225b4cbb6c419467e5a16e894e95fc
+a022874d1adf199f1caa1266cd9e8db0
+a0269216f1dfa879d11c92253dcd29ab
+a02a8dae004c46cc48d1357f5fee97e9
+a033c3f2565a7b5364d347c61ecb3388
+a034022394e00c130275f9eca73f86e4
+a03625c2def37c3c32fc7f4a23bfa79f
+a0399b4d8eaf3dcccc996c5c6004e62c
+a043756f898f1a40c15ed290eaa8a49f
+a045996ff25d6569362ef78e09e503cb
+a04a230e6289cac0fa9d66e22a80d5f3
+a04add201290aa5254a9219446bbb199
+a04bc8fab8e7951f6d3d98e527e3eb17
+a04e5434222f6b38984e2e2bd0344f8c
+a04feb568ccaf0dd30e49f25f89f16a1
+a05223d38ea206b5c86afa29be3243e5
+a054f6fe22cde40f4579b91c42607bb4
+a055349829c710c19585f23b850c46c0
+a057a4150f4dbdd91ccdb1c7a9e04ef8
+a05a303d9506f7adab59e6a8ed4991cf
+a05d34d6e0898e79bd3c282ab96a9309
+a05eaf38ada092177c2d6fdc001f6cd1
+a06ce69e36e8003de48dce635ff83935
+a06e6a008270f161a5787bd834cd074f
+a07c32d30456c644b471a81e864d05c1
+a07ea6c526940edfdd2a1c4157202ca3
+a0811b7e346af11a89e5688bd2b2f171
+a081f2fe0fd13dbe1f1c54787d503a93
+a08cc02354932fe88bf9c7a2c7a09e3b
+a094e410ec544f3e8e63ad7e2ec63901
+a09936d7c6fa4590cf2d7f4032023ce1
+a0adfed21f13453d2ea745d05993129a
+a0af6594eb34895029b017f1f973fdfc
+a0afc172c8c8af7fa3bfc0c4e93d2c35
+a0bf49b1014c4e41749ee6da9cc8f58b
+a0c45450b4bfc86da85a166c969e573b
+a0c5546ac5d1dc26284db9bbe62d286e
+a0c5aedbd48af78ac8d4cd50165f2930
+a0cb44fc35a9f0a5a8cd284cb88e29e0
+a0cb46fa016ff078256314ab5d1330ce
+a0d28d63266f4e88457545ee4e61be8f
+a0d3d4e1a1da0a93f39458becd8ef866
+a0d5d7485a6dabc9eac9e87b624c1bd9
+a0d609a8d35d9f3cdf7dfa6d097d9bd0
+a0d69d7b821efbba78ad204dc0887f47
+a0d728f55c8b9dadeea9d3d92938b24e
+a0d90c3ff08f0eb2ac242d8f257bf737
+a0decee73201290649f2a1ecf40f2696
+a0e0d745fb6047130de8deba180fb973
+a0e5d3291be6a0aecc64fa4d1740b9fa
+a0e710f5491a92cd9bdfac9e43799419
+a0ecf529454cda3171558468fee38f6f
+a0f3c060dae277b1e570931664bdbbfe
+a0f6d2af7082969e6552dbb65ef01acf
+a0f7f49f290e5f26872de6e971fdfe30
+a0ff1321c7c05de00879b7273e2e3ca8
+a1033a801e0f1ae4a7c3330a3fe4c8bf
+a10e41c9868ca874a31149dcbc66c9b9
+a111037291d55295435d9b569bef5988
+a11232ac6b0d850d9adf9553c4a4f37b
+a114c588fdd324b11a9b0cad73555f89
+a11558bd2148bbd471c6e43439483634
+a115f40a474c1bfbb5f9fe2366ac8e43
+a11e8309532499083a5443329a07d077
+a11f13cb0db16cd1ba1fe2208655b366
+a1232c2d54d60ba9ec2d768888b643ef
+a1271ac307f21e56a1aa1012dced68cc
+a1272007555bf5800838148e14c61987
+a12c81719b73dda0e5589bca45104a00
+a1386aadd6cf93519ebbf5957ff8d3a4
+a13922e37585a74a9174e8fda7fe796b
+a14251e1c1f33ef5489815856f592a7b
+a14e787b9d4caca45ac2ade19bbbc259
+a15397fc4949ffb6dc4781a74390976e
+a155422c453b0c4c371635ae33314481
+a1627bc656d53060e43503cf993d1808
+a16675e63d2e05b28bb88efd03cbfcfa
+a1689a2b819145db32d384c5f479cb03
+a16a6eda353a4e9f4053059fba8b24f3
+a16e2baa1e3c8c1cce0c932e9433e237
+a16f21ad17310b4b704ba9b51f422f06
+a16f3f16d0838c1ebd000eab07874f71
+a1772f4941ae5efb2cc25d43602f47be
+a177c332f5962047a15db842e8a26686
+a17da48a8abc29a54fcff471d6516fda
+a17ef5c7cf925ee6aa20b6c800f8705a
+a1857d0dadff90f4827a5395f5f8fae7
+a18b7b88db6b03ec2bb70f9d556ea3cc
+a18fc245b7269ea294a057dbf8abc8ee
+a19152a15598aad20488194474efae80
+a19b48a13812ab37bfcca445932f7c31
+a19c7b96ba4ea8eb13a7c41d3b7c14a5
+a19ed9e47f0bfd73c9ace792bd072852
+a1a3d35a6db788de0a19e05d0e8164cd
+a1a3d77cc43dd59743b8b511ecfcc5bc
+a1addcd7be30562f5c9050e63d787f63
+a1ae194ae74f175fd247617c7f8b89a7
+a1ae85f7cc770e0686b386c3a9d0c6db
+a1bf596cb6d003e3850be6d9efbdbda9
+a1c2561423aff7c1cd62f0bf2c0a8803
+a1c4c89851502becffd8b733f4f05f73
+a1c4e3588503477f6a712fe7bda7f849
+a1ccaef6851d55dac70ec6d373d3b5b0
+a1cda912ca1f9ca3a0d8bebdcffccc15
+a1d05df16f3434664311a483bafd5f25
+a1d2442c8ae623793e6658b8be0c9099
+a1d82a1500f8e5cb19feba2cebf35473
+a1d94e1fbe2a6bcea015c76564b10083
+a1e4b54303f52ab0e514447b418b27d7
+a1e8e5c5c175e3bff2dc157c88e9015b
+a1eb345e271b8da84a3d8c0af5077121
+a1f021a18f99a8ae182d5737653e59c1
+a1fefb1b98da3cff0b91394e515f6e3f
+a1ffdb301be7d4de21cbea53a4860a3c
+a201a390e40cb5e2db7dec9c22be376a
+a20b2186a2630b21be787ca1114dfef4
+a20c20ada4018428e9d8efe99963b0cf
+a21460302525000a1c9269abba4bf069
+a217a99922c988fde0abf094857a75d5
+a21a80f546079a9f57e921916be9e822
+a21e92cdcf6b57f2789ccd9e53ce038b
+a2237cb6f02e1b463f051e1a01af58be
+a224dc8fecd6d88c8e3d69a639b6d631
+a228db604527bea8d3b1ba6a8f101f7a
+a23a77e52e46b661603be079bd95ded3
+a23a9b4e9fbcc6cf725607a137eea17a
+a23fc9b47b0a18683f64c771a68e97d8
+a2458b7bff22a1a44a958f9e47772643
+a2466102fa7e26373c86dc6f67b95d56
+a2476549ba30b0fe4d84c360f8589f81
+a247d86a850ad860c666c5931653174e
+a2505f34dc1a893d79c863e37c81cf73
+a2511b84b2475410148f5b7b3e33bbbf
+a254bd2d62bd6958aad41a65ab339fa4
+a2556a587ff3b4d25a03886b87068516
+a25eb2a27f0834e100731e864264c95b
+a26077209086bd4ca3cecac772e7e9a8
+a263285ec661e3b4646ea00f219d9abb
+a26443dd10a593b2daafc3434b21f0c2
+a2650e05fa51fcd062b884a8746b5c1f
+a26c1490097730b94d0f4779854e5a95
+a26d24189a0212b3e9374f06400036d9
+a26eecdfa63527c3370ed96300dfc6bb
+a2735e239dc7bc7701a06bc91ec5b9a6
+a273aa9348b34e813ff403769040e515
+a27754fc5ac425838294cde5d6d364ca
+a2797ee7dcdff93e84c63f458a4be892
+a27d6a92723ef338be6e49779bd8c1ba
+a27f3bc78e1414b2b8ac13b04c62563b
+a27fa2770a8203be5849d3dd118bf5bb
+a2839e76b82faa7a3ca76c52040d3897
+a28602005f68660f2d13c621fd82bc5f
+a28c86f57a9df3c49b9a026de8c4694e
+a28e0b00285e12f1c01427fc1e34e3d7
+a29939b3ccf1d42781f652b8d9d41462
+a29db8790bfdd428f91d4a77f534ae52
+a29eb4501a17bdc156dbc525a9bfc13b
+a2a3cbdc810ecbc4404bb424d7dce422
+a2a429a38286b06742433903cdaf2bc5
+a2aac11b515b35dc50b8c9321580ce0f
+a2aeadc4c2496f6dbfe369cc5e75877f
+a2b5e5e3b99e918e5ed3b105b7eec3b9
+a2b91cfe8ac27f7bb6a631a3dd8c1b75
+a2b9fab6b4918b55c9a27cc4b9f36a8e
+a2bcea4932dbaa1bcab273d9bd8f2d8b
+a2bdd931e57708d5e7a048cec0c2aa1e
+a2c57bfdbc31410130920dc3f03284bd
+a2c9d97b621982bd0c889c8cdaa1c9a8
+a2cb37454cbabe283b060c260a16ef45
+a2ceae794cce3b67b4ff177c9d720cc0
+a2cf3bd2583aa9f3946f9606cf775af6
+a2d54e3fab0111b1ebd3ee3baf63ea95
+a2da59a042c28e21fe258df0e8dd471f
+a2dbc9cfb16e247906b2b097ae077ff4
+a2e2c1f31c91e6ac0eb791bb2f98ae3e
+a2e3e0d40b7d023a2ddd02534cc2dc61
+a2e5452725aa6e541d089bff0ef3a6a5
+a2ebbf0dc39eb59a4c9565df4b0d634d
+a2eea4a114bdd300b25ed3b7789df603
+a2ef972a833c7d3b21778c510fd14f45
+a2efc5bbda097bf697788c791df4ee5e
+a2f27ee9765a0c4e74d48dbe862ec81d
+a2f41b6b1a00f4d20866b74121e35552
+a2fca6c69294e7d7a3c84d4f80732d2e
+a3010ddfcfc489f548bbd7d7b2ec0d1e
+a3023a0fc7e6b17108840a4c9e1a6212
+a3028ba297f7cdf3ffe4efe56d666e3e
+a30522a6cb75765eb8eaf8124c418873
+a3076051255c9bff513d6db11c0e022c
+a311d7c2751094acd3fbedfb710ff99b
+a313c216fdfabe7d12ecb63644ff9576
+a31975017b4e2893e860736269151bc9
+a31e7c460ef44f0df12942171807fb3a
+a32877428a645ac790fa33ea24b12f2a
+a32878ae4d95c9a84c7ffe5f67dbaf0e
+a328f49725869fbd3e21f48ca9feb856
+a329ae26787e417768b9bade028ef239
+a333f72fd313ebe2855812fa2e36c5ae
+a337e5bdc1701c80b780062edcf18950
+a33c3fccc23510eeabc7503f1a3d8d30
+a33e52e5c5e1376369598c748ebb84a7
+a3486e3f4013a38d66cc9cb59672c0b6
+a3489db9cab7af00370767201a5f08c3
+a34b2472dadba391bc4879bfb596879f
+a3530b03fdc5b47955683bf9aa42b8c0
+a358dba532c4da0477c25f8444033d4d
+a3594a37cdd82d5f77c90e3071f6a475
+a35c15b07442c0393cee4a095842b9d8
+a36587b9d2ee5ac4f92fe39c26e6f75b
+a36ab9ae8f1901c82c597a90b84ee5cb
+a36b1275817af346d05ad3b0597bb964
+a36f112afaab87ee1bb28000c931113f
+a37482e503177b7a35ad575650d2f10a
+a3762cd7d5934a6f64415a90982be20b
+a37a784d2914eff602935ac0af278254
+a37dba8255da222dd4a2ac391da05495
+a37dbe82ffb8a8368e8aea409e6cce05
+a37edb9e1a4580b7b89d4262879e3065
+a380ac9c8583f48d77d5a690baad941f
+a381a0ccd5f95d58407c3ebdbd104ed9
+a389c17a58bb58b775fe153d4a589698
+a38c1b466f6c496e98343398d461b307
+a38e76d20d5e2ab5233cb1db7fad3847
+a38e9979814fc15fcfe707249925f203
+a394a2ccf170940dedaaa6fc1ba883ff
+a39fbf2bbc24e255ae157fec09c5a670
+a3a30104b76ba3a368525fdcc417fde0
+a3a56045629d866ef413cc12dd6eb5f9
+a3ab5a0b368b618b7a5c02ab276eab74
+a3b1e135f2b0865e2f6af6f9b2f91179
+a3b1f9ca8e61a751f2e458dde51c0de2
+a3c04b6f6195774140e757ae30d8a14b
+a3c41a40f82f32c5029bff42519e3d20
+a3c605865069949892a251b555131208
+a3c656adfd2544447d72890e9297fb3c
+a3cf9509f96879fc569ed21d73192360
+a3cfb706a4fe5642e1a3f760d4f29e77
+a3d00ec39b07064ab79f8299020d55b1
+a3d7b80491160294e495b767326f0c62
+a3d849627945901657c2029140450671
+a3e4daab6dbf18cc2d34de4311374d6f
+a3ea1f6ea91944dc5346d870016ea5b4
+a3ee94296aa34e69159b631fbbd3b7b8
+a3efb3ca3076e00ea4aa819a8e037ddf
+a3f04bee88df151c12c5b5dd627d752b
+a3f74fe7fe757467099d28748127c316
+a3f991d1fe812d9c077ce9acb2ad5df4
+a3fbbf92f1d1c2afadc66c3a949749ce
+a4004ae44d809b75163846255eb00fcb
+a405898093f1d9fa2c0ee0a01d768d94
+a407766bd82262267d666ebb2bcada85
+a407e3734437da008c56bc3d2cdea39f
+a40b37654839ae60aaae19c835e37aca
+a414508b05bdf630f0c93d78b51c0f5d
+a4170c1d89958d1c4dedd06ffac13573
+a41a1e8ead0191c73f00f839ef5894a8
+a4279a13a95e53879c65350d814ae0e4
+a42eab6930105da2e2807fe6f309b735
+a4302cc20eb59ac7caa2680f17d49ff1
+a430a1139530da49869220489a46f417
+a431a78b6eadf7ddca4e01544730e225
+a432dcff9af843b79132e80164852eb5
+a4350dbfe734a3b518df966b58914a2a
+a4353e9e55d717be29b9fc4b84e4f056
+a43966508c5c1285f89ec358d3b464dd
+a43bda7aee775b69b20ef5715007c78e
+a43c5de704b36e3b1e015a30b79a7dd7
+a43d81089ed3fe696688943bc0ad5d5a
+a43ecc1af75a31bccb870d31c0a9dd92
+a43eef9ee0e5bf1be241eedac39a5d9d
+a443879d36ac5ae023c485a084474557
+a444f4ab2204174dd687786541e3d780
+a4475a40e0bd7a180a62a35bc0f35b60
+a4481c3693d7e883ececa9c8390ab93b
+a4485441ab0b726d5a44c3ee7564b62a
+a44989b64b1450f255feebd379170ef0
+a44bf1e0aa4cf8acd949f5cfb8876a9d
+a45231051a73088c6e13ac18de75d0ee
+a455e330c26f5228c8cb94d7786b5fb2
+a4562d21cdeeae66edc965e7351525e4
+a4568cd497ef1ecc377c97a058a7c0a9
+a457503495682802834fe512a0f4615e
+a45c53a161bac48701b41af31aa0bc24
+a46698737cb4f089f78cb1a184773db8
+a46cb6c157cdf72cd384e5fe5cda5955
+a46d885929e971a7ce338df14ded0370
+a4705a855ed70f80823d30a66c544db0
+a4884dbf970f050baf117463dae3b799
+a48c2bc6799061dca6366b08fa965378
+a491935d5162ccfb22a63a75d5091075
+a492e8293a8983f0ff2f77834c2e713b
+a4958a034c4301a72739901b75766e74
+a49c02ce65fca446ce693c3d319b4f89
+a4a7b4e118c8f7040990c416c103be99
+a4abbfb8f16fcbd250c960ab9f657990
+a4b072d4978526c6e499d5f879705c64
+a4b2747d9b2857894034a3589aad575c
+a4b4fb8b0df5270e3cb907506f5219c9
+a4b71511a4c6ccda4f38e8f79f5d7342
+a4bbd1e3df889c556d02c229dfe9adef
+a4bcb34168a6426fde09fa3250c175ad
+a4bde247d19fbfab6f7b8071bd5815c9
+a4bedecd7acf6542974d0bb28b5967ed
+a4c39421c09a940f3ae9a9e83757e80c
+a4c4e60b53400717935bedf84ebd1f0d
+a4c57b8fe56ea7d87a2c27fc775ad52d
+a4c7a254426cb4487140ae17688930a3
+a4cb690cf10466f72098da9253c38495
+a4cdad07bb2d1969dc272338886148fe
+a4d26986beafe5d532dac54210501ced
+a4d2aeb539941ac2915ac7e2a1320d66
+a4d359adb840ad96398e7f39e823390f
+a4d73c41e13b0e61a232e31472d5f323
+a4de465248f984b1090b3e33e9260af1
+a4e22156336d9cbf25f440c994dce108
+a4e4503ac3cd7f5beaea4b281cc0c915
+a4e51d673fc3b10cbd0d29de73f46845
+a4ec383f72f56a5c152ac1d8e0993b68
+a4ef9cda7b55f7f220ffa4d7baa4606c
+a4f0a52ec9396ff20c6f67d5c3d3fe0d
+a4fb781aa749b6ca97ea02ca48a18283
+a4fef1f69a166e5d2e4deac9dd095f2d
+a4ffa5f0c7fffd24dc93017cdd9e7881
+a5041b1dce7b80ba16db3ff6c69754c5
+a504e96cb1ce1dd7d339e25c3d448e3a
+a50dcf5dbcc38258afcf7a19cad7ee1c
+a510bd092b2b0ea44c19c6092c55aa6b
+a51145d7b6fbcc43597785ccaa857598
+a518589bd1e8e71275d94f55c2335d4f
+a5192af6a0f00b168c6fbd640fa56e1f
+a51ca8c17f9487941945c9ff5fd4f61b
+a5216ce4c388a51c5ff5307861a189ad
+a52b48bf6084404cb834c3637c94ebd2
+a52ba0b7cb3567710a359bd1d313c1b1
+a52c4edc59cb9cb1d1d9bc1e7896efda
+a52f0aececbbeab17f505865b6d93c31
+a5328ca5eb5c464e8d9b94eb9747bef8
+a5383bb03c3461e6c973a9f60ade7116
+a54a8c8d8b2dd73b3faba5feaf4fd231
+a54f7deede6b53dedb7aef92f3e6c61d
+a551a96dcde83b4f80e6b6f1bdc08def
+a552555201886152855e204c5a4976c9
+a552e110caa5c5e8d196404ec08ca009
+a553024a3acf3da321acd23dc43f2c8e
+a5570a7e22addb8fc7b22662e67429a9
+a557361366d6c9b892812fe2c52e15d1
+a56963ed55086bd1c0e732c92623b557
+a571f2054b89e696e8d4e0da113a5f49
+a572695dab2c78225f4ac1b89a354d5c
+a5727929bfa0f2e6130ba6d1258405c5
+a57a0a351cdc9edf7efcbbc6d5198e5c
+a57c4431b4ca3bd3c8a63b8691bd6f9f
+a57d5414d6b45ce43c5f05de4a71f475
+a586ec469376dda5dcd4a4c1fe49e986
+a58f21d260a8a1b769479d2295600f61
+a59264fbb291ee4a7e6c14f7d9a04323
+a5945d4129214ff646db70d16d2ca368
+a5982b0dfe905bac0ec720cafdbd0ada
+a59e15dbd9e53b143423adc9906d127e
+a59f592df7482d92d9e8eb6a8680c887
+a5a552726577a65460e6ad950dcbfffd
+a5a689cb9d9012ec9da00f00a73f3115
+a5b0e845614d983a32dcfe5725eccbf9
+a5b1d1a32b2b41f184d9f8b57b456864
+a5b899c7020c66000f2aba2740a0b498
+a5ba1a2b2f6db09cba767ea1dfb2fd39
+a5bad6d753e622c1c112491bafdb26f4
+a5c0a24ec152c7b56e4e7ed0167dd224
+a5c65adee364c70ec860a9e058e4cdda
+a5c782086ad9692ce91c714af0951c0b
+a5cb98d92f6866b36e1ee8147df77f44
+a5cd8d4546ddb90bd8533631d8cc14da
+a5d1ef06e4dcd20237535ad389aaa232
+a5d602636c189438f48426d3b3db611b
+a5d751b835b2d241fde300fe690b463a
+a5d7de5dbd907476dcf3cbaad0638afe
+a5db22b1f6f1b0d031e2fb789226b5f0
+a5ea1a4e59d06e745772bddb404dca33
+a5eb6cd40310afdd796bfc8fb35c88a4
+a5ee1aeebceead355bbd62bbdee3f315
+a5ef453ae71997794093b8d9f62f7659
+a5f25d3e6cfaba8ad613303167cce778
+a5f2c990fedaa2747ce413422a8c59cf
+a5f48e4e0c0ef83220e634a711e1c7de
+a5f4ed3e58ad3b20ccb2dc01c99261cd
+a5fa999ac25f428335a669fe964379c4
+a5fbd4c850aee312360f2e892c3ab2ac
+a5fcfcc4071ba89ffd5c76fdf9964e93
+a5fe6cb745f5ce7f04dbcb1362d4d53e
+a6093f7d08b8c5b16c87271ec838afa0
+a60ced6916af7ae2b72f2e9788fc923e
+a60df7d7961650e90708574d75faaec4
+a612da4b93d33a4c1b4ce66afc454567
+a61b15e25f09460d642f41946129d0a4
+a61d2317ee4393fe16d84d7971f17e1d
+a61efa33aaf660873c26fb9438085fee
+a6299c78ffabe98f27498cc5398b2aec
+a62aa2d0aea162f405bfba7be7b6ac2c
+a62f20995b167153a2c61978ea5908aa
+a62f734ee98798e151ce468974207494
+a630d8aca0b846a64e0bb8a60da58df5
+a630e033562b2502f1f098ee858d0e73
+a63197b3c93ac9bb909cd308b3667a4a
+a631c1419e03f84d3d1ffc0ad3751163
+a6343515721108ddaab24ca02301cdfc
+a63597eed3eecb29917e646a8ace39c9
+a638889b0b8fc569b269903602095681
+a639cea41157ccc829d39be4cbfd51a1
+a64a29a174f936e7fd844376cd250197
+a64ed71f5d5d354be359cdc5f9172a35
+a6500ed28412329959407ab1fd189813
+a650f1be5235bba5e3cae340675fdd3f
+a652a870100eccc9d146aca3a2ef102d
+a656d451d4cbdd0389b431ff218406c4
+a65928090bb4434056617f44ff06bad3
+a65da9a19a867caa1c73368f36156cdb
+a65fb7a591532dc232d88fc8312ccff9
+a661ec652c02185bd50427539d92240d
+a6716da12d51b21652a316ec928404ce
+a672d722bf7d518ffc50ee4d5ad25681
+a6764b06e239d78f07c7fafcf0efac80
+a677d649f2aa9cc88b523b0393b1b29a
+a677da74bdb2887d910822fa6c56c874
+a67a972dc93f5d4360f27fc1b506da39
+a6862548fb3ee37a1eb192a00194c9c9
+a68df55bd0d5074934cec61e87d2aad9
+a68ed4b191e408e735fc65a2b6643a25
+a6932311c0212dadcb7b07aaa6d8ecd9
+a69de5c4a69414e9330d64e68fab7ec4
+a69ef562048276825c7df9d8f9ace3dd
+a69f7d65f6b6d1d4d212bcae15a679e9
+a6a59b14c4a0c0fa5c1b6208fcf2fac7
+a6a7f11a21fee5c42a9d408f90e124cc
+a6a9fcc573e6997bf21ccdcdb0f7ccdc
+a6b0cc8b01689f46130885eaf4d32f8c
+a6c7d0dbe9d7445a9ce19fde1c0a88e5
+a6caf8c60bda1a560e15bfa202702cd3
+a6d44604e86a19997282f5deacb3dd3f
+a6d5d2ca27de290fb13dd7d9ca10336c
+a6dbb0ca18539cd62021c1bbf9d134a7
+a6e6ea68bfd7ec7b987e13d7deb319b6
+a6eb384cb16aab840f8088b680c9468f
+a6edef1451200b6f23c90981dc6bb4de
+a6f56366959bf744901a75a3a575d9ae
+a6f69075484db186e7016ef55f9dbf22
+a6f995548851270343cde1ca53274771
+a6fd8d71223b138992c77e5e27be0ce7
+a6fe8e7c1f9f222a0480d5463d4ca5b2
+a6ff33b7de7cd82872befc1e9e54ef6d
+a704494f9faa2492f3366d989de41356
+a70be5b9486560802e177e75e3d7430f
+a70c1a4633c7d8d0092a3e4a2d31b5f9
+a70e9347c23fc3fe455ddfec4f0400e4
+a70fedf2ccd98d08230ac656de2078e5
+a71833011f93ee64074a33daf77f5538
+a719f420334dfd10890f0a75febffac9
+a721b5980fa2324587f512ed8e191653
+a722c6a97b555feba7921af166d40092
+a723d2e50507011effe89804bb02dfe7
+a7273b61d2d749d7078254a4abafbdf1
+a72760025cb756d8b3e9045dc03f03c9
+a727bbda31d7543dfef7eced5f46b76f
+a72a27b2bb8e2b4ff8c6263428a99e71
+a72aa5b78adc0fbd042c98602520a44b
+a72be6c9dd856f5ae05b927574179ac6
+a730061b86f005f7db82e3843a68c3fb
+a7317dedc7fd56b1b3db8ac32213ffd9
+a7355e48d98f1c7f6e05457c549a4c92
+a73bd906b3c5af56f46634a74b92661b
+a73c0821f6b0520de88512d77de003b9
+a7403c9bf31a8bf1f759f8c1b42c1657
+a74699e359f0d765168219a4d6239089
+a7469eeba20a1a2a83e29e85aec024f2
+a746ed980abcc7fd6ef265c4169d170b
+a7481c5afb0977324a19bc4ba123b78c
+a7483cbf7898a617a17cb1d997790680
+a74948873ca768881142d0bf8dc63982
+a74b1d299d1cc3be949104790d574522
+a75429a6aaeada52258abfebc90c62bf
+a7577cfae04ce78e64f3d6152da0d5e8
+a75c7555ea80465f5e13ccb686244f7b
+a75d2dd49dfabd6695483c96dac5c5a9
+a760a8860e41c4412c643c3e6c0be7dd
+a761ee2504cc0fdbcd90ea44121bb78b
+a767943204382fecc21056b63fe4fdb7
+a76f6802f3e4bbf5ae9d8fb6d3c39c7a
+a77af7c1e34d19ec3e9c080bba9dfb05
+a77f7d310fa589bf4e192fa26832e87c
+a789fdce26fd4f54234c8bbc93965c91
+a78a8f882fbbcca29addf5e5c1b21894
+a78c3df1755a65913e38773546b78537
+a78e408b05897458924dd1efbd01d0d8
+a7967cbe792110c24efc624f15a55c5d
+a79b6c73aaeb39826652e9a045452f49
+a7a35a3e5464c0ae3c535988f6630f16
+a7a3bbb1de7838e037ecf5109c05d669
+a7a71bfaa697ff117f264b4fdabbbe9a
+a7a8757ce72ab5b2e5fdab1345c8b4ae
+a7bc0595121b98ba6ee6e5abc4cc49d0
+a7bc439551bd2c0f4c5c9eb6c08b71ef
+a7bd4df68516424b6a6014a344628073
+a7be60ab3118667a751f6afa33087089
+a7be645680cfc6a6a78705489a12b697
+a7c5e8e68159f5e81d3854740def76a6
+a7c6992870485b434a142cef86f292ae
+a7c7f697212f846e21d8191784959217
+a7ca170e39ab05879f4ac8a449b69d7e
+a7dbf18feb9278bb41e7941ec16f8477
+a7dc404a9c96b0b73202212487003d5a
+a7e097eced828dc7b20946c7d21cce19
+a7e0aaa8995edc2d1a26c96479232536
+a7e47d018632e3033a3797c33bd3b10a
+a7e5216dac1a21eb81e7161bb1026ae9
+a7ea09dd1056187b020f522425202386
+a7ee06eeeb4edeac3a9f3c524cb9de8a
+a7fc44809aa8ad09b694d53b44a63c07
+a7fc889585628e86efa7e59d1ba41f0d
+a7fe63c4efc8c37fdc03fdb4e8060ded
+a801bde7b013adb54761ea8de91233b3
+a8127c4bb076387ee98257022f66c503
+a8157a7d0e3e260cb64a51cff47b5509
+a8158076acb1d0b0a296afca4c71acfa
+a8170abb2bae8b1288566b5fe754d3c8
+a81912d089d896cd4ee0675404b8cec7
+a81e625437e235d331ea942018ea0158
+a8203abf1e658a7ea30939118a5e0b40
+a8327e2938384254c5c52c9356319981
+a8338de28b7b0d08032fc6f46c423592
+a833b0e488c0df72e86a053416a5b5ec
+a837f4d396fe60211c6d0c9199d80931
+a83dcc35a788be3ed4ae0d33452162de
+a8418f118b480f3b4b1e4505429241eb
+a842297060bb977826d5d6458731eb83
+a8443ff9f2cc0af5c4ec1057035fa898
+a8476727289c3c96ebff9158d9f479e1
+a8497a29a88c27227f251de5a28b0afb
+a84aff98c850bc5ebd0555a9333ada5f
+a84d4b4f38fb136dd5cd8d0457c30232
+a858c1c6ee95e5954a6d9f596b5f168c
+a85e6e8f71660db42bb262c828d7072d
+a85f6a6b4e355b614fe72a6ef12a6fbc
+a8676ff679be268e84be90d9c3050fc7
+a86a4f49bef16f0e7d7004d0be4b3be2
+a86dc14b0553ff9303d948480370ec92
+a875d696d204220d40e159c97ad910ba
+a876f5234fe9808cecafefb0970c9661
+a877d6b474ee2f29819d5de32ff18a41
+a878e474925867e4119a21c6208e89c9
+a8871d48e1614532ba760477754392f4
+a889b2202a150e483e8a5dc1a313fe72
+a89fe66cbcf3435b64986c910af8dca1
+a8a3910c01303a363d7c750fa6455c6c
+a8a4896255ec1b9f571e5f04775080ff
+a8a63cb7a783a611e0eb21040fba040a
+a8aa4d7001ccf054fabc302e84efd32c
+a8aa6134938033a6abcbdcceb5530c83
+a8ac68600442239508f3564365eca842
+a8b3795ff4b72d0957678f4e5ca8de04
+a8ba540d9bd8109f0364d28533121079
+a8bff426604bef7d9b92ee2d6ef75675
+a8c19d3771220b69b4caf6e890dcd220
+a8c23e02cd31e0fb5b5ccb497931342a
+a8c59828cb92a9ec28fadff351b79f88
+a8c899b7aecb9ac02006f2270e1dff9e
+a8c9436e53fb1b8d07780f5049bb19e5
+a8c96be24390dc7884ae9f609efa496d
+a8ce9b503c4acc2b8987420941cc6979
+a8cf4f37d8ae1dc0f4f1479411db4c1d
+a8d28d6aa5a06ef2948812791fa3e1a9
+a8d465d6b03ed27165ca787351372c08
+a8d4ad54a4be944799ed44369357c632
+a8e03ca309a0c6679d8479e63ed002a6
+a8e6291e981f4ae2c4ae13cf21ba10d3
+a8ec39e07edf3439c208d912294797eb
+a8ec9963d57f2342a547aff422b4eee8
+a8ed3b9e2f10c8e21a659a2c212e8e03
+a8ee86824ec476fac701bac1b42a0fe0
+a8ef515aae9770df131e07eda4207f37
+a8f3099b51b7a7e0eabc4b1b46a7c69a
+a8f35bcd4d447d2761349856fc68b586
+a8f60f9f3db81585f291d9fa66fe1fa7
+a8f9acb5930ed8c99a3e3abd2651ee6a
+a8fd96e132a38af7969cf9319c4976c8
+a916496ba165f42c9f4cf9c2d34a6cfd
+a91c983170f6ba2b792070a2dc8472db
+a92601ea1172620dead87f68ccc31c8e
+a927c28dfcb8e126166e007660567493
+a92c86b2f88bf3e5bc78966ccca5124d
+a92f0e3889be852de30921305280ddce
+a93521740c5d6f1b8a73212bbe466892
+a9363d386dc3f1757d21fd1df75021ef
+a93964e91f6f0d45d7ac80af6c9e4c28
+a93d35765116314f01efc647bde9293b
+a940f4dc341a9b03ec8c2267fa59d68e
+a9431a62b7ebb044b489e3a6b1d0691c
+a94397f704e7d51ae3e62517f2f4d440
+a9512d54de43b1e1157a5de637db8e33
+a9516e7961aef0ab8e55f4fc1b2ea685
+a9547293af267606043dc73619a1d8bb
+a95aa1fc4fb9bf3f3cabeadfe16c8cca
+a95b9e4510e0084d70ed7b6afc496df8
+a9630ddd54b7ad136cd38b580b8a79a9
+a967f57688e95f0802189163f26d765d
+a968d706553240206c40567b517769f3
+a96903f1a3e3acf51924d1bf7709a6c6
+a96a1da56fbbbb3900737d6c8607eae8
+a96a256d2ae45b5ce69f5645d4344c24
+a96a4df93fe35f2601dd094ac2bac991
+a96b0d7225ea68bc857580cab75bf633
+a9703ded37523cb21a65e33901b6e6d6
+a974ef3756d642393d3694751842b936
+a9789d1c54f68cc87a734fa67e97ad23
+a97bfcb7debcf883acb3557079856942
+a980a3cf3d5c2815c799b487c336fa03
+a98383799472c1839c390291bd1ed0f9
+a9848d5aeee01bcbaf133012cdef83dd
+a9873fbc8581d66af8e8b15d07da1e57
+a98a7fe4f15d47c25db9cf857e266f86
+a99ab318f0efeb20c129c4f9b6368277
+a99f517d1cd2a623f096c0b34c1a5a13
+a99f6fb54acc674007f35f87040b852d
+a9a04fa1b877de43d03175e2347f3919
+a9a1091cbc057d2e578a86135221aca4
+a9ad2b4357b50f807aa85f970aa4c84a
+a9b43bdd60f328389455011d0f622982
+a9b47c9ef0e45f721ae405c9c88e42a0
+a9bb5fb6bfe48c23992ae091377fda23
+a9be1a5f5bd1897e65d513a39f1a0fff
+a9bf3b9ac11fc355efa9da970186f927
+a9c0a6bc0e3dbc9363cdc323b5d4e71b
+a9c15ca6aca2c390ab605509a6f32b24
+a9ced68ed00774ab8fb76fca3c8bb21a
+a9d57efd56642588b2681f19791391a0
+a9d77fdff295f39067bc806f3f440ec9
+a9da20325b224d7f1086969946ca6d29
+a9da21fa6b50de0c5cdd9a0c55f82537
+a9db85d9aba2591780d3809f8eadbec3
+a9e0d106b3bb15e48f5fafbf6cc262a6
+a9e13452ea060e3e1558f36931ffd4ca
+a9e2477224a79e6ffe3b4abb9a8eee3c
+a9e9439d16ec1408318610c28c80241c
+a9f69078785078fdcd7401b4d78335b2
+a9fc20eca9bfbed94b966f4d5b443760
+a9fe223319a97d9b04baeb1db2554deb
+aa07898bd32308b5eeecc1bb0c27571c
+aa13891d30cbdd13032e0dd9f4e495ea
+aa16444c37aba77d3f90b3d854a7dd37
+aa1a37dd2258643005515da16cf9ad8b
+aa1f717e77b530e8ba25b207d49e1b7b
+aa1fcc09399e5732fdafb11de3479676
+aa369f1823db1bc950463fbbe78aa4d6
+aa3f851f2ebe97c73274c2cca2f0c1dc
+aa42a33b3d4a98540b89387486ca3314
+aa4d1521b76ceaf6b62c505ebc2aa276
+aa4eb21df5b479c2414d8e36c3e73c5a
+aa51228a4a7efe52d0342783d81ded36
+aa5559c57eb83aa8afc7d751872d7e73
+aa576dc5b8175d47178909226f6c9ab3
+aa5abd33a2c35ea275715775044bbd94
+aa5d77c54b85b330ed95db2c41afd394
+aa698335b07402d9e912f52c5dcf17f0
+aa6ae4b33f89a70d8fceea52bfaeb94f
+aa6b2d060a58fe75f2be369081b7c060
+aa6e999fee6a2bad549133bd824f891d
+aa6eb13fadeb54560db07371f1478016
+aa7150cb03a0df1eee70734fd2a311e6
+aa7210422a34db2b1a8085e9a3d61254
+aa7309d837fa8c7188865c81ed4f4b18
+aa78a339e41f8a78be228ce62a6bd5e2
+aa78fef8a47c2e213c86f18111249462
+aa7c640fdcecb35c9e909b846720a3c9
+aa856116bfb065d4aef6426b70414c35
+aa88f995cdd6086b78e60c28d7599b78
+aa8953b9c0cfc171501c0887d557f53b
+aa8dbee4a607a46c169c4b65483f5095
+aa919bfda305ea4f8d7e6993e2c147da
+aa928c0b2cdc42615102e934386714ec
+aa9429843900cdbe80a3b2d2072b8c00
+aaa48e9a63a7029d48b0db26dcfc24ac
+aaa60a8c7bc7d4227e7c732626fc96fd
+aaa7d4535e6f28bfa38596f0b9c58979
+aaac993a6ad8325acb8c2ca7a4f3b4dc
+aaad20cca406dedb93c4a6c96937da91
+aaad80288dde8b48abd4d106fd0baa0b
+aaae39dcc8c14702ad3ac1252cd5af9b
+aaaec8af41400b5425aa88909a99ad34
+aabc0d1b10631c4ed24f2e2cd64e2d2c
+aac301604ebc388f17550eddd56a298b
+aac4e926a05f2746e20aaf34791f1710
+aac96958138ff9e4a9da07565986c7e3
+aacb63dd80bc889a4b66718bad1e5dcc
+aacfb786060e22687c7054f2131f659f
+aad22d001d4f4dea91a076cca09472c6
+aad7a29325659e62b1e040d858a50ad9
+aad8908cd15c7084935489e2904aa8a7
+aadc8a42f28346015200d9c2681aacd5
+aae3e0c0d866b2de371313c6cf6bd1f3
+aae88c4b4ecd8e448cc86b58215ac7a3
+aaea906a33b1ba2efc24ce348cd0b320
+aaf3eb10ee5ef36c60c7e07ec0c57bc0
+aafd84655b1d4a142649e081e628b54f
+aafd9088b32a3c5b0ffd75967d9cfd79
+ab11fef8b20d3c1cdb8e265505614a16
+ab13bf2ae39a5224001823fefc326e33
+ab1dcaa6907d46cc9ddbd359f6948b94
+ab207e8555d07bdb112e36542bc43d08
+ab23f37a476d5ece25036f9039532fde
+ab24b1b9397699dfea0ace179be3cbd4
+ab275ab49cc962bbdf78f2975c31d092
+ab28ba9015a6b8d92f592045e297afa9
+ab2b62dcb1789350bc663992e1466109
+ab2d8538b7317a357caa451049590266
+ab39f6032fc72c4d3b6808fd6656de74
+ab3c556300a80c0446da674ccbe1590f
+ab3d4774f46644206b473acb8663372e
+ab3d6050f237bc6b26c6e99799d34aa8
+ab3d92e13605b778a315969ed6d13f79
+ab4a1437954aea29ac6df34c2044b6dc
+ab4dbac9f96ae9f14aa35b792e438771
+ab4e511b3aaa31e853a17713aa8c1b62
+ab547502c6654a7341626f370c8c1bb2
+ab57fe3151f520a6335e863671737f74
+ab583191dbec06f188c09095cbd6cd62
+ab5b4a626337242e89b47c38eb930893
+ab5e91739495a4529085b93df631a1d0
+ab5f0a63b94cda6c90a0615c6c24782d
+ab62f8f380ecd91b11c588d7f58a1e5f
+ab6602f682faafb05ad3793808afb478
+ab6aa5c71870daa409f2c72777b9e11e
+ab6fccae0cb0ec5a63de70671c805cd8
+ab709e11382cea356ff1e508a7506805
+ab755079ac0b35b8ed4fde1ec5ade45b
+ab7637913bc3be415c79131ecac548e8
+ab781bdb74df08a595424c2991f10f43
+ab796c8922b118fc1b4805c911e4dfa2
+ab7ce75e17897292d8f1b63f56ed2160
+ab8019ebdf6d9b86a48a22e0c0694062
+ab842a5d3d3e59a0d4ae5ced387d1b28
+ab8d936d886d93a24b6a8f60aab7d30c
+ab8ffdb54e0492226702eddee8354e9b
+ab912910ff3465fd5fd042095715ac9b
+ab953aed3fc16cf00619a5b6c7ec5065
+ab9715859c554cdb0d4121239922e224
+ab98a06700af0658c86517888d67cbdf
+ab9dba2d9346915d8a2d8dec235ad6f3
+ab9e0a2b522d7e39458aab500a3ead16
+aba001bb2e74fa171e54a244c8f4237d
+aba8a6d3d4697dc00afc77f730dcd29d
+aba913bf8fecb68046821b21d32a753f
+aba947b0945cf24cff8fa86a03dfe139
+abb142234163d8b88cec2476bc4751cf
+abb75a46a7fb855f41f5f2b2ef1b13a2
+abb8e5b549fafb761720412b838b6e2b
+abc2b43737cdceab87ae36d7c700620b
+abca8bcfa8de4d13c74a46a2e8973d3c
+abcc7afeb67a0716942b7f24da60142c
+abd05cd2a40153675b96ed5053e0b68f
+abd307f26d776bed767633fb3c8b492f
+abd3e23ecf3610d7d5736632bcc9574e
+abd3e2d2803a4311f61bf37171bb75b2
+abdb8ced6946df56347667b94c7a19a3
+abe27c7f7dab0cbea056328712ad83c2
+abe366375a00485e5e5acb3075042e00
+abe4693da90c02553e4dccc6649a4505
+abe52768990a5249f7ded9aa22634f3c
+abed9985e50933fd0b14549ee819afc1
+abf255468eeb1d22b3388e8b0bbf6994
+ac02fe3164ed4066842e0dfd0984a7fe
+ac12c5a936da317bbe80a11a1712d8c6
+ac196f1b80eb226bb649e0ddb540c3a4
+ac1e077c3e4624f46d0faebb67ad7a65
+ac25a190bbde4df80f35972fd5131798
+ac25d8288f2f00ac28b6694540a092f1
+ac264e32fd9f555b3043dd58d7a59796
+ac298bd5036a9dfc9b5665322400915f
+ac3aa89b2785a4a8adb0f90bbe479291
+ac4046a312518289a6cbfbcc9bb005b1
+ac42f67d218bb47d22db32a79f0ca25d
+ac45894560292f5769f2f6f2b55c9478
+ac51cb3273de58d2578353bba4244568
+ac528d33203bfb9ec928745a90919c8c
+ac562754332572391995322b70b9b6b7
+ac579d7a970798a2069f686991de51da
+ac6076904e1b62ea0605d9d615d981b0
+ac60fd6dfcf0808d8a625dd4dd91f9d4
+ac6367ad90687659113bba6748bc54f5
+ac645970af153cb35b54890404cfe169
+ac64f17e5843ca400c4314e3d3903950
+ac68041cb82908286eb17a5b0ebf1614
+ac691685a848326a54db6f8f18686413
+ac6bfc2b1f35723980495cb51e6fd05e
+ac7631d6d4b1685f1dcf317fcb89d66e
+ac765dffbfe1681d0a5984f76a43a213
+ac771fd042e4c9798b3bb59ab12e3dd2
+ac77d68291b8eac9cc4d5425fc42b169
+ac7a5e6503eb2c117277af014acd04f2
+ac7b24cd138c55f9dfb7f7776eb55301
+ac7ca355ffb9428df93e6814f10996eb
+ac86e18e7078d5b4807b864ce2bfba84
+ac87941c0805cb16fd74cec864a93603
+ac87b4643a162d169619097738371bd8
+ac8a97792d87668ebc9eb08a3a3f9ee3
+ac8b8d53f8331417d6eec6e6f2dfad2a
+ac8eb3df595580580f25353eda07bcdc
+ac91b2ae9a20b5c3610c396a94355ec4
+ac9324243e6b465d8d694b25f263d737
+ac997754e918fae7733d6137ef519590
+ac9a755549431fe06844479fc0f34fd9
+aca1252aa52cdb29db67cc7e31056a34
+acaa80b1088bac61a21e9908dd970ebb
+acad05515ba97e152a05eee364f7f751
+acbe7f3439fa7f77b2008f8159458717
+acbf13e847ac2085eb510780dd7b4f33
+acc7b9362edd1dd2a856ceb2a5559d78
+acc7d6543a93c98d6d1787a7cc2c49ce
+acce4d5a212b261cbb7c26d61f45675b
+acd004085ea907d706c6bb1d1c00ce09
+acd9765b5228e5a68c82362e61df7961
+acd984510ecdf5461c112a3c0b2c875a
+acdb56f775256937ea3aee4b64b1956d
+acdb83b6bd1c98d6c8a1fcb19e271aa2
+acdc8615fad5f7937b4d9c96fe7da4d9
+acdd10d2c5003f10146e468aa03beb2f
+acdedbe878e59413ded26c0fd1599393
+ace19407f8860caa886d21086866a44a
+ace967816af932910830304d303cfecc
+aceb9cc5f25dee4a54201e6201636aee
+acee42817574e62951aaf741b5d85702
+acef487932486136bc86f0915f580718
+acf7438e262cecc4a212edc2b63785c6
+acf756e412497b0d23a686541a6d80bb
+acfee8ef9a37a7e626ebaadcea0cb097
+ad080a692a622b71e64d3a1ad135c90f
+ad0c6e9eeb25b982ca6cda00586d051d
+ad11cbbc91ec36ac61404971d9f0b3ac
+ad1553af989087080f628469b9ef3378
+ad20b0aadd7469ec05c462b027ff6323
+ad223922daaee509ab4b533bee252155
+ad2c33edca0d770621373ba0f58808bc
+ad2d9a801f1d5773661f217abfd10085
+ad2e68ba15e601f3aad2ea615008e49d
+ad307afc9013a76437b8149749e98a90
+ad348329ebb9a4fb63c48803a2a6869f
+ad368f9ba7b30e5d57339f85364ae72e
+ad3cde9d38ac8595b775c7c2c3a6cb19
+ad3ee435d3ffa3e6e308039d862ae6fe
+ad455b8784fa0ee9a396ff6b1bd74a31
+ad46879e929314a6b756cdafe0eca759
+ad4c8312d59329ab92a2ce9bc9a6ee5f
+ad4fa03740340c10a2321d1e6fc20b06
+ad4facb83019828863a8b9989e68ed6a
+ad52099399c6e6e10371ece7bba7838e
+ad583964da1b629961316a4158b302a1
+ad5872fb5f8cf9c3ab5e715500b49bc3
+ad5c80db3ecdcd0fd0ecb974734a8335
+ad5f27fdce108ac7e002f4be2d67bc05
+ad610cf84c8d83e7800d634209ff5eca
+ad64512d66b20e6dec2b522b19a865f5
+ad697ecb628335375d33d6514eb666e5
+ad6c8ee1006fc80b74200bd9879ab88c
+ad6feef7a7bc2853ecc2057c2f6fb720
+ad71cd75212a86ebae5f204ecaa3d22a
+ad794692bf7695dd01f79f375fb29fd8
+ad7f12aaebff16ed7f9a2a7a50a6aa6e
+ad7fd9361eafd5005a2e4b57b022b8bc
+ad82f82f770b441ca546e11b402d3b22
+ad8556798acc964026c7497b384984e4
+ad8bd21be0689ed471107992968a1ee8
+ad8db15378fdd0aaa2d19b98b88b6147
+ad8ef6f215d5dd273bfc639d0979e028
+ad95008005643421f80005021169c043
+ad959e15235351395edf7776412406bc
+ad96146886d0e3159f04ee0b10726857
+ad972903c257d964dd2cc8e9e6359726
+ad9980a90384e7e71b5ad14a4cfe5e5c
+ad99df8f1fbf5235cd42430798741305
+ad9b104b914b0c58ac9a53eeb1cc3327
+ada3f3765794d7a1dc9fa3932a2dfef6
+ada68b0af0803a4910bb27c5bee25c56
+adac8b89defa83c8ffb1c0cab96cdb6c
+adb6370ddf7a93a90a75a6d59dd9ccc7
+adc06a24ec0eeceb296fe9eb91421971
+adc1974be058cfdc05e71baf2015b211
+adc460f797783f5377c8d54d19d76170
+adc639271b164694a62408f965420367
+adc734f88e6c66fcc336fe5703c1776d
+adcb168a4f4920eb76fe72de68297bde
+adcbc5246ab68a58ad7fe679c8c298f3
+add2fa8e3624807f5bbdcc19fd36bcdf
+add49ac7c2bf5cf2062719ca1c5650b2
+add54d8a75cd7ad828927843caa3bd69
+add5ed27ebc3f22e0287f25607026b0d
+addceb8d02a96a1ebc5aed1f168cbf5a
+ade14a86707a2576c5e2cba801bf8ab2
+ade4efe4122631da2ee20778cbf8207c
+ade63187e6b3f20dfa42f2feae24c6f4
+ade664148ce481938bf38b23b588f8f6
+ade8643c6d2a18d71141dad6a0e4dc66
+ade960391db6bdad4752e9845a72ea28
+adedc7c241069557e3b6629f268fc244
+adee254ab06cfd81a7b98a9bb468c7b7
+adefb22fa12271199fc33bfa0fae693c
+adf3b65f9f539d0668c69edd6d33dfb5
+ae0068b6d0301f801cf34590e9520296
+ae023a3535192a8b2826918786cb8140
+ae0278fb2fadd69532bc5a35064a39fd
+ae02b08686deab4907156c787e6130a0
+ae04ccd6ef643f10b17146bf2de5217a
+ae0c84296748f1be1b184ebafdcac6a8
+ae0fb8ad3ea7745342cfc76e49951cfc
+ae131cd6ecc3cec04760aeacb769e991
+ae14d4909d2e423dd301b5a0c5b04803
+ae192bdf5fcb12deca3a87a7fd2de7cb
+ae1bb2ed630d0a4ecea9999d3af08afd
+ae21364ad937db64853f70be9384cf29
+ae2166cef4787d2e7700103ff0638f8e
+ae24df404469c8091734d00fafc813aa
+ae27604a14ea53783de69e5c9130a504
+ae2a3dcb26d8ce3e794a5a9b4677a2a3
+ae2d2137a094185486c580d280f3aedd
+ae3081918fd943bbdc453a7814dc7a79
+ae34bfa8c80b39adde7dfaf949958362
+ae3693f203c0be9a08bbeffc36de9281
+ae3879e0841d5358cbdb34a293c2f506
+ae3dacd844f7f360da2a74551def5c61
+ae414c54022163a3ab126f92449d1099
+ae431b939a3c331ef6da5b01e1b2e701
+ae4797d4311d01d45e095393fa215257
+ae4836d71786a0be2a97d6eca5e09bb1
+ae4867312f826c33e1fed2a8154d0f27
+ae4e4ca52249f236d4c5197a798d6427
+ae4fbb531c95dcdb3ea7aa70c0be6a59
+ae53ee7a67650c8e1812afa473fef168
+ae595434ea13223e3f4cc4f69b4af0e1
+ae65f351c83681f0d2ef9afd9386ac7f
+ae67a4420d79f4dc88f3fa0c5b543c83
+ae6bf215481b6fd2895c4362fbcc8fd1
+ae6d99c66fdac4e71677a318a4a193dd
+ae6f14aae96cc6817981032ab634663a
+ae71371b59ab1917b2afbd73d06818eb
+ae71dd72450506109babec4984448c09
+ae88acd7cdd5e5c441e14e916e345c89
+ae922a8c860e7bf2910b2f98b4a9b276
+ae95878a8a2703f78332ca217400a35c
+ae99d123b7f95f4d40613aa4d7f3ef58
+aea0d11e5cf469f7e15445d1dba0ec4f
+aea4c9010becd1b2dcbf04eb46bdaf1b
+aea51813be4710544d81841871fb69ce
+aead83e0a027aa8167440c8bf653027a
+aeadab88a5cca99f1ba7aa3cf9d29d60
+aeb0da2d6263773e3cdbd8d0bd65058c
+aeb1c952199e85aa1085e72ff78e33b1
+aeb4bc743a39e7be65d9c44c13ae6696
+aeb8a5e8a13f3dba28cf8552d97f0413
+aeb95a4940d14f5212b3bd08b39958de
+aeba152442d95b5eccf256b83e2849de
+aebe2648181f6de36855b0ef91d09c72
+aebfddb65449d2b8197e868386154c83
+aec1207b96bb604f422e25be1c260b32
+aec231aa631f46bf60f239fa34d520c4
+aec4b05f6daaad73b9805f9413693104
+aec5c9ac2f3518d52b513bced57692db
+aec67fd75a41f5d09cb4a6110200ca7c
+aecf4eee92e928506022bd29f4c7f8d4
+aed0f5ed42272f679e2964a3455e5606
+aed7511febd66ebd0c2b8c195f79a364
+aeda51ffc3100fe83b330226dc3d13d1
+aedd6ceb948630b608f91555da4033d4
+aee4c779d95b6123921f7e1fc8059426
+aee746816534167083de1cfb3b067fd4
+aeeed96207535fd783eef544abf3970a
+aefbb5bda80b82b5b8d7c4e7f2b2dad0
+aefce60666af56b305caf43be4f0eb58
+af0dfd62cd759715e9a7db665f6e9c52
+af0e8fa14e00075fff1b37bf5597785b
+af123aff193c9c3eedeb4992a19361ec
+af127b546be8bfc59b7a108857999b4b
+af151bc1f80cd923d04080c602cd1cb9
+af17ee6b2a7e8ed7ad54ca51b09a036a
+af18a720531c2f1a914dd963e47286ce
+af18df60b83225839a4ee33d89c94844
+af1b985f6ab0e4e302df88b20ab72bc8
+af1bc6c08b9876410aadaba0f4f74605
+af1d9b2030c56ce93c94a4453dab61f4
+af20a0bcf312ecafcb92d7d8a29600eb
+af2851144f41eaa76970f000fd5c018e
+af28b1255046e259b3f4416ebda9cdc2
+af29a7a9321c0941bf1200a2159f95b3
+af33c04b9f3d17e7bab61102634e3413
+af372365c89f4da8fcdcb987dd636319
+af40d37511714a65920e03ea6dc87591
+af4906392c40cfefe61c56cfc474bc32
+af4ca8db70e2b39f356221bdcaa07602
+af59039f7fd030221accf7065d289c8f
+af5fd4a0b138a7cb3d722648fe9f76e2
+af656357dc7ca3f568e0c6331495128d
+af67d598838637d4c2299843ee81236a
+af683e9bc3d22fcfb02bd187053d9cb8
+af7704910a12b691555ba2cb2bf45155
+af817c74a01837b7025f865389dfc203
+af8220f0b4c16293d034d4d72b3ad6fd
+af8d34f8092bf7e2814f29e7451699df
+af8e95b1b259cfba760c45921413f9d5
+af8f9af08f94ac82a8fbee8e4864be7b
+af9b75e9d5ddcc07ab528ecd4545a9a3
+af9d542362ef4db8540efb0b553eaec4
+afa32d64646cf4fd62b556316ae33e4b
+afa8433d258373b53252d7f3590155f5
+afaa34774aa0c4b0fe3efb999e1bc850
+afab22415a00b452f55ae5536b46a8ac
+afabc7d02b419669e3549cf0fcb8c57e
+afb0d0a02918af4371d90f3af6a4b223
+afb1833c1265f6106c39c3dfe63996a2
+afb3d65b40c59eeed5ddc0b4389a7db2
+afb403d8762ea0459c586a7624c89997
+afb618c4300402a6ce5f4bf84308881a
+afb731fc7719f91be6efc9db877b896b
+afb8a6e143b47e6113fff9acd55d10e7
+afc377db7b09267cff2aa001c1ba03cf
+afc45100957cd7234757c04ad453a8f5
+afc5200bc6c6f8540659cd10501229fa
+afc6533924aee63adb52d7a23869e26e
+afccdd1649487197fefeafff49276bc5
+afd0c3b118ffab2aed4d6075ff82afbf
+afd1c3f291da477ce8cb34ecaed18a4e
+afd1c74be55be88a9781b8f962879d2e
+afd354f01ecc1199b2fcb6edb1bbbfaa
+afdd5c2586eb706e9a01101a4be81a9d
+afde971233393044fb66d1ebefaa67fc
+afe5661f29269bae1e4fc100eb87bdbd
+afec409a1a09dc74f37abe9f24019130
+affa208224faffbaa43022423b1029c9
+affa47858fe22ca285e49d5e7f52b109
+affcbc60522cb62a40244e54e01272a6
+b0023ce545c557990da3292d3352ef75
+b00348062392a28fb6f64f55f8bef5d6
+b003ac024df6081d512dec7048711d50
+b0058d3dd23a8ecc32e4d6b42abc53b2
+b0072bf689bf5b08e0377fa084a65b76
+b0078c7b757653c9c0b7a2288920fa7f
+b00ceca7afb0bdd1a2b1893fc0a2982f
+b00e42a3bc94c064cc6d47ca904a7f8c
+b016968885805a2f4ec1a0e8ad0a7904
+b0182100b92e307f7ca956e565ea383b
+b0189a9cd14937e54239a22fe508f060
+b019c1605f8368e5bd89ed527a4d60a1
+b01afcae08babe504d65e3f59f19bdad
+b022a4e37a5002bcc96270a171460b17
+b0240f075ecf03141e99b871362b2915
+b02668a957b127a28767597757afe13c
+b029d5fff80b63b7d0d62b783b457668
+b029dd4ff5784be726458f57f3bae466
+b034ff0fbd084ba84446212d759f539e
+b035416c67fa432c05b66846195b9da8
+b03921f1d5675449db946cce11341463
+b03af17e55de781a31bd7e75e4d36d5d
+b03c110eb71bf61c60a052aac9cfbd06
+b03dd4051b0cbee245194248d69fbd7e
+b040c895327b8bf967a7ef83ca9a2fa3
+b048c3b0dbbf1c8eeddddad457fb0fae
+b04f262825dc79e7ee0285ad80ecee7e
+b05c7550a2c4cb538286278027e31b78
+b05d81b31ceca0cc8178f2440bec0bbe
+b05da5c12bec67f2967b577f9e61dbb3
+b061a36e84457ce83f7f8d2b398f9754
+b0636799569acae92f33b46faf3fa02a
+b068d4a6dfda7e6cf507078ca3748244
+b069ca76763079792490247f4433c61a
+b069e24f70c23dfdd1908b20d46a26b1
+b06bc21fceb9fe01427f93c14476371b
+b06f5b25100a9f2afa1cbada975ad0a8
+b077d1ff85932951f1032f8e5fe7d495
+b0780a49b7e5b04d3097689b3a5bb0b0
+b07a424b2a2c46de2e76984129e4c841
+b07c677b913c7ff2f6c65de4158bc66e
+b07d97574aeaf22c3601f39151bd98f4
+b08ea19a54932bad3b31c6293caa9ba7
+b0920fdd36227c21f04f84f0db7c869c
+b09724ccb17f368f6dcc7aaba31cfdb1
+b0993338620994c0837ecbb50fac12b4
+b099c5e1e0a96366b4b63fe6c5a6479b
+b09ae8be9397fd08c04b5442c2bc63f1
+b09cc3c319419161c2db3905da1071e1
+b0b190fb0f52ed04bbf8014537f43669
+b0b699fe21e88020f6a1c5a027be1edf
+b0ba0aaabd9b12161d4ad113987b83dc
+b0bca52f0058c1d955ebb636fab93bf9
+b0c297e12429822baff57b741fe5ffdd
+b0c79bb3e1c47ab4b308d8b50d3c7190
+b0cadc7b2cac2b8f2bf4fda135c23c5b
+b0cb5f3428d1aa11ca6bf92017fbc0f5
+b0ceb1dcb24bf679fce27c89318c9cb3
+b0d6090d6235adbf3ad65cfc621ca100
+b0dd7260658158dc6288aadf887f0b20
+b0de0a2550140b6708bfac7fc4d1311e
+b0e21c27cb43325f0d2e4b68774c9e29
+b0e580283d40bc2cddc96bdee652d456
+b0e62eadc8fff702de36db462f916158
+b0e847cdb66477587a793cae3a50849c
+b0ed5168ed64c5ea230e036ee49d17a4
+b0ed70bf96b459883d2562205e142db8
+b0ee816f433717323efdbe06bc385245
+b0f536a2d68e78327547df0aa7fff1f9
+b0f7475cdbe5ba7bde5d9d71d1c71cdc
+b0f7e6fee81aa9edd3b1d6da66741a58
+b0f9433ca106f6737d9f774b1a842286
+b0fe133cebb27f479cd849156143fd4b
+b1006e18bae5edc0640177167e8eddba
+b101e0fa1c94776e95b6bb370b927628
+b10f4a0102dde33b4e41f297074fb404
+b1146dfa8ffbfa6167afb0a0b9b605e0
+b11e01526ae9dfd2e509617e6a33864c
+b121205ed9e49767c22426d9e023fc5e
+b12378cc4e6cc89228ad7b722f7a1025
+b12b7b5f3619697297464b2931fc28c7
+b12c36d89f359589e4b24ebd2294de5a
+b12cfdaed7541d7557cc60af068ace97
+b131041da698d9df6eb243d147e25aaf
+b131c13318299709402bc08df954e568
+b13944daefefe8f1cb4e087700f17654
+b13ce15fe6174a07b39d2b14bf554965
+b1472d0aeb8839b61d94a10ab32d481e
+b14bf89192bc622c4d2cf83939e0b487
+b14c1f25953e74790bfb7979fec8c7ea
+b1507baead651d7e9e0c95eae3cd7063
+b157b93be085c79207c9422cb7e9e33d
+b160ac12c2ad53feab264b045bd64d53
+b164ab74eb5c3d6badcc075e0b95ca55
+b166d51fd6a36efcf180e30482c20793
+b16775eeb4430de4e9411fb6ff3ccead
+b16961ffa3a09580846e0412fc2eec95
+b17207c63ccf0bfdbd655ef3ec5dd384
+b17e33d2688e58b9b1b7f1426c4dc1aa
+b17e4eee58f92715f75b9befeb28a80a
+b1907f4a203ef7a3b1743931cd6e0ff0
+b193e2f5debab5c5c9ee7f313a7dee36
+b19996ab23c7493987fb14d8ac110be2
+b19f22462e4cb12bf3a78156b03f3404
+b19f31da27578b77e120124b7e91b8c7
+b19fb628ff498ad6da4faca70400dfcd
+b1b1f620b43bb68b2e2895f7c521129b
+b1bd0a9bf7ef833367f8ae66155d7c21
+b1c79036c2e37132df6beb4cc7ce22c2
+b1c8b8d0134270d983c63cf05f0681d6
+b1cd4ca15c3e4d2aa16723473f7575ff
+b1cdf4ae749fd3f4b2916f90c207db12
+b1d0e3b5dfe6e31dc1a3296bacdcd723
+b1d9bdc61f8dd3af82e23b610b6f7fe7
+b1dd462253a6860c6197ae6e5dfcc182
+b1dda31c9a8f0e4b43ea77a4417e423e
+b1e060cd82344e3247a9293d4b26a682
+b1e3ce89d84300594e6f13b39a4baeef
+b1e75ef9f125bdd179ed9c88f2c6f3bd
+b1f2fbc855a5aa07de3e63384b7defa1
+b1f346d4add415024bba3a5ccf00bbc8
+b1f59caa1b42ba87703b722c6567f4c1
+b1ff0d219255972e58380c0d1fec53b7
+b2019eb6c10d3df138c7a70b35efbce2
+b2042999ca8596ce5f25f2baba4b2df4
+b206f33679ea56623c70fffb7f6e7339
+b21e8cc81a67417ebd457ab61c912c4b
+b2295934af2a48ab0205ea8f6dab9517
+b22c63290111e73906a8f924d4fc7360
+b233e53c9cd06bfb6efb90f984faef0f
+b235e5b89454bfffda572e6c0b36067f
+b2362dfcf73a0313c923e3b2906ff194
+b2390dcb52d5faf0c620df796efe9269
+b23a6f450f8bd4e65de4f88510d8af29
+b23c91060a9172b162fde7923d1ab0fc
+b23d30dacd203346b063dfe141adf749
+b2403ebc657f8fa7660a798ba6d5f923
+b2407e2717defe784a20a33fa232fb41
+b2413940be04b774de28b69b11a97646
+b245a11852555054217f06148ae9ff7d
+b24c6362c93dfa570c2f7619816f946e
+b24c96a752dd665739f907a0007568b5
+b24d6dafad00052b6748f394dc93f4bd
+b24dc65c6f54f69810dcf5a1743c7e0c
+b24e94f047e5ad1d80333374ad1a36b6
+b253ea3aec6c3f496255b36414194760
+b25816af2c7e4750fc35823e691375b4
+b25f1cb7e7a14cd0cecfa39cc7d100d8
+b263eb9f0c9fe177143877a19c897af1
+b26505c6ad9b5432a9a9aed9f5fd27f3
+b269e042f29c053d8d8746da236c9720
+b26db0bf383246e625d8dcb2929fc2f8
+b26eadc04e5dc2cb71b0081c96a6b289
+b2794d1e3df2b53f09fed8f2dbc1dd18
+b27b74e34e5f9d55452d5c28f6998c74
+b27e4e1c5932fe4f90ec29f6da0752d8
+b28448e7e19e0a27766b400ac289b00d
+b2852e31126c1cb04f8d28269def1b06
+b286aa60904b4f09cf808d95c668b1c5
+b28b018c3de8a202a7a1e5ce6c88b6a0
+b28b9686c0f0154612975e8710e310a4
+b28eb38c4eca1c29e862110737b402ae
+b2906e1841b85dd22f01a303b167bd41
+b293ccd904eff216f617eceac55021c4
+b2957bb54ea6cda70766ee5c79069083
+b299e95512422fb4e9a117a62aa722ee
+b29b12e87e1b352ecc31e46cadb93e4f
+b29c5b8723ef7ba692d60c37b778dc54
+b2a27102e23abf83a48946d92f0d6471
+b2a5672d500b935223f5998d7abc73d1
+b2a9514a459d6608c9048312e0ca208b
+b2b0e347e56e1aaced5debed4deb944f
+b2b1be8dbf9bd3df77b314e58c3ec1de
+b2b7df3af217f92e5883f199ac962a7e
+b2bb59bdc79a692e5b6352f4af2b5ed6
+b2c2bf6c5ac22001c7535c6479baf0b4
+b2c45a006b42f143e8ba23977e89ee86
+b2cb495e14f04eba872f9c52dfc4935d
+b2d32450751a27dac911f847112faf6c
+b2db563db0c255cbd33e88744dbb2eb1
+b2e52b041f272d5f0d70492c2db7cb8f
+b2e59f8f8c979490dcd95de39dcdd5aa
+b2e8c7e8f8924bca673da771889b5326
+b2e8fa0582800341f74fdfd75aef2007
+b2f2949dc60f16d22a45f181b6fc6e66
+b2f32125c2b9ea6b03c035444adb7a40
+b2f9773b4b22d5d0142358a7f433cedb
+b2fafcab71ed8a7c5e5d56c14f03a716
+b30a14cb8792f2c7a54827d6cf7a0160
+b30a8ddb8e5a4bea22c4bd0700510151
+b30af080339e55e1531646746c429dcb
+b30ecdcef6d970087958cecb4d1e5e11
+b31102c192fc4329ad23706a654845a4
+b31cb5b539ab30dad854d8915cca6142
+b31da65a8024cd27f21df8062d05da9c
+b3201365c838a8944a49d11493edf7ff
+b320903d6ee56d8b25f0c5eaca4f3917
+b32380c8bc69d76d631cc7397118ca30
+b32f4cda774346878d27b030e002af0c
+b33a12d455a2b127b13f797c478e0959
+b343417d9448957e249e26fcbb28786a
+b34e7a09d00601cede220fcbc244822a
+b3513727d09c09179e40c4a383a0ee8c
+b35382fa8b5b16009d2a83a351a44bae
+b3576ef3f5c12757a91e324cf3abf627
+b357fbb4df69383b91a6b815af87dc2c
+b35aec3cb72a2e04283007f6f6e56c5c
+b35be7a4c8f7c757d74608be9995e9f3
+b35ec4d713062489be967eb54122a436
+b35fd122927103cd9f100c903e7b4f1e
+b368c6a80d83570d679da36d388efe66
+b36d96665f578ca728f63710668f6b11
+b3722224819f809b0a1c8d1f4a553b4c
+b372599e9cfa3a43e4bed96a75182f03
+b372ba10255f53a089a04f1faa7183f6
+b3731b1f430de79ee8c5c693f42af1b2
+b37455e8605029af34b292bc5791793b
+b376fb1036220f96e52e3f07b8c0f0cd
+b379b06c7131405870ac98db03c9da1b
+b37a2af17bcbc7bbd869c156dd1fd7ba
+b37bf8e513438ec83ab68853c9d8f279
+b381533872948313ef7e22340849b98e
+b3851ba4825844148b27f8c3a9acfbae
+b38b66a31066c8942c0c2b63e1d79fef
+b38bd2666e54749f9174c96e74feb7a3
+b38e86f5351228e3a1e791d0839ec254
+b38f30f933742b76919c55b687096c94
+b392d9c9463ae8c581dfd8d6f2c35730
+b395b879d2dea9e3a52eb2750c37ce47
+b396a416394e7b2a78cb2ac2f542501d
+b397862b0eacb677b0d0bb1f7c8730b6
+b399ec7d1c2af23ed6d671927a346902
+b39a925e242c3a722784c58cc18c355a
+b39ba9182271b0ad6bce9c68687f34cb
+b39c51c6ebfe70f33ab91ef5f5b3ba2e
+b39d91eb144ea2657b90cbdde4055ded
+b39f6909538e91191905245aabc3ee55
+b3a353aea9aec2c321dd31a0686d375b
+b3a437d6b6c8604c2fb733758cfe63c0
+b3a65f8660a7fd2a35d4b30ad17cdc3e
+b3a7dbc4df2ec4f21eca8df202d73ad7
+b3b3dcca29282bd30b4d4e43f62ea505
+b3c0bcb764bc8dd61fee703642206373
+b3c0f70df9d89a6cca003e4e4425c544
+b3c8104287bdd9d55cf13c3cf43e9d95
+b3c9538e0db046f93bf35974ba508732
+b3ca9d6645b8492028eecae144bed957
+b3e4daa9dc1e1e01a687052989c09d42
+b3e86deed6244e60d35e3cb7368fb06c
+b3e915831054ed3db24c914854548f25
+b3f9fece9967896f7e7965035d059b2d
+b4057835bdd097e6a275ff066b747468
+b4059286e09974abc18a56ba7fcc9eee
+b408fc99792547be484d15a1489c4a0c
+b40959f08d54bfadfcd2d1d0cf5f5d2d
+b40dce3711877f0e7854135510f3f8a9
+b41336ac875f4b1ab44d9a3ab61288c5
+b413c3b17aa56ec1183a2580b8f75929
+b417c733f1584d7ad4157a533433b29e
+b41b53695d9294e78ad1db2c484b6110
+b41c82d9583d00cfbd280a47273c9b32
+b427a23d2c25ddd3bca012260e1e3912
+b4290d70b61e0d597332ae1f50633eb6
+b42c057630f7f4f1ad80977ca24c0121
+b42d5b1938638f38ae1f97c45737c314
+b42ee55d0036530e7626cd6e88937c16
+b432216db666cf4c5e7d265d189b9c04
+b4350c822e414a85f403129d630e5274
+b43563040b3669c1a707bb791c265f4e
+b43af6a09b032079b597b1caea126dcd
+b44760993d5e68c1dddd5d4e1202f5bc
+b450f778ab866ee81f2bcd880f9df1ca
+b45637bc8be31c80c3972a69f65d035a
+b4579508a77a30a6c18b309b49203d9e
+b457beb9e98059249b5cbf39c4806fe9
+b4649b456b52a4eec121775323d3c7d7
+b4681ce6f72143bbd086dff973176654
+b46c3b7e1bb9be9a90fb6dede1f5aa86
+b47217003f968a3818468548b7977a5c
+b47bb9810c7a8d91c947651507e9af57
+b47bc6c98fc5934dcef4154de7f45d67
+b481f6b418c637e60f56d40a708452ab
+b4868958d11a557981e978b758e14320
+b4915eb38286404f2671908745165223
+b49a81d18c2a7d0bd3d0e7b5bf89fab4
+b4a3f88cdf326388120d85e075101fcc
+b4a4465ce878e1a27c980c5ea4399e26
+b4a4cf809e635eb019cecfcfd330de46
+b4ab0c96048fd7a61c96ad13abcd2015
+b4b04035a24c51d7943bffec4814ec64
+b4b180e0dad7c69d859ac96af46972ab
+b4b499b76b1cf8375e0f57fd82d86ba4
+b4b6a22183963af9580282dd6c4864cd
+b4b72fb46b8e1a492c76f50a4037eaa0
+b4bbc9f090fa57b28c0207123c5ba2f5
+b4bcc934658663804589e613674f52b3
+b4bddad7772aaa0673b347674cd13396
+b4c038e3cbce873c5ce8937f1708dfef
+b4c07fcbfb5d101cb311a1e4aaac37e0
+b4c3f55c8f7611b27925baf355e8918b
+b4c9645dce46dfd073176194acbd8f3b
+b4d16fc6080150cb7ffeebd2985ad760
+b4d3724f9606189aec0cb6aa31f756d0
+b4d5081d91c9491c6e6a7e07619fe876
+b4d648285c33f8020ed81887dbd150ab
+b4d8525cad03567f8a9fda86c5f2dd5b
+b4dcccb741ee42b8c7b285526884337a
+b4de5daeed519e12b83f30c57df33769
+b4e796e848e3b6231cd756bf8b94710e
+b4e92b72f6a5cde9e5d49636137f584e
+b4e977a45b0e995e4d9b70a0c0d2fa2e
+b4ea68e6a3fe7b3af88ac33e55560e54
+b4eb35b03c731ede85afd662bc157894
+b4ec539c02af8d49a1d0bcc466b14a3d
+b4eeb6bd5d7e3275c6bc0736dc027bdd
+b4f7056302cd9550f895707cd9049578
+b4fb636dfaac80c44c37b30fdffa0282
+b4fd3bc9366a91a4607c09c6e4014bf5
+b4fee6a2458413360c42248ec60ef61d
+b4fffc5075cc9e8009099a3fa36acd70
+b5017363161e802674ba90177b9ee4a9
+b50aecc2dd0b7f8b71413fb9c64c24c6
+b511d597e8c4b7d91ec8269bdc52d9a3
+b5121b742dabdaed7710b3e4181a7236
+b515483dc3b15a8b797e0fb363798792
+b51c5a8b0d46e6992c634197be959060
+b51cee3f88e6b43cdddc469163ce9b26
+b51e41905f088714d3d0bc3cb2cf5755
+b526d99997de432004a6891553ccfec2
+b52d8f2c079035bb0cff087c79c7628d
+b531e9d2e1fb9f16206d24913cd147d2
+b531f8a6127c1e2334669aad2702c422
+b536d62d60865212b07252b613fb66f7
+b53a82529c6235151c720adf9a84639f
+b53acdd4547140209f6f9bc0d78c1362
+b53e512da982d82228fce6750accd1a5
+b5475faeac6ae602fbe1a7f7cf3d734b
+b54858eddfc85c5062756029b5b07905
+b54a13d2e6ab83c2ec5d360e6a30e9a3
+b54c80f2b9f28ed1268c9aa58675ec16
+b54ef082c6949787f56e3f45a5c3efb9
+b552bf5e6737864508ca08a1d3bd3e10
+b5542c2003efc970af3a5f55ba0b8c1e
+b557d5baa473a0f01a5b3b898c38aeaf
+b5592119c2428a65546dee0cc6f7e022
+b55a33b4a109b7e6aa49549fed4120d1
+b55b1469818d69489edbca29ac54a2d2
+b561a3f62045870c2b85853036b59aa3
+b562459e2cfa4f293db33d49bbe267a5
+b562d87d375ce33ad57dfc3d72ae55c9
+b563f09efc2ad25628454b916a08910f
+b5687bdb75fff9332a746932a5d0c07d
+b5697fd50a12bfce80322574b250102a
+b56b262fadbbbbb6a1608e818fa1c1aa
+b56d38c75346d0f941e8aa58a129db31
+b56e2205c2769cb26e89605536a16c5b
+b575f5ff3d60b580b9363a0e2a41d2e1
+b575f6deaed55c35e4785cc6d31f1049
+b5764f8f5f0ceb5842a2525c09a5efe8
+b5775372139709d1e7f1fa20940a2b8f
+b57b40e3183ec68484eb9d9f9fb5e96f
+b57db92095e48f2ab4aca9267e304db6
+b585162e73efab205530a6dd32f3f7fd
+b58ac64489bb38be1efff7a320b48fe3
+b5a0fc784279541f81d64b28ae984dc4
+b5a301d559d88ab3e9d99635df8254aa
+b5a32260676a51bb1b1ea7afa30c8c51
+b5aa91ea815222b8adc599056f83ea49
+b5ac66b401763588f5f07dc51a4df7ef
+b5bbcef3b3509ee2ee88a356aab69722
+b5bdc9e9dba3fb9859c22362d0fccb10
+b5c11f5df3b752249fa4b360c7a20995
+b5c1fe4afdeb13ea6508f7da9fb53cc9
+b5c3e55567e93d10270fb29884697ff8
+b5c99d08c545abaffa7ad813fabde1b5
+b5ca00101275b22e7f52520051127417
+b5cb1333371793b699ccec828b08f773
+b5cfa56fa65818c45f1ff5176065b1bc
+b5d09f7f4b2d6c05c44b164a844325d7
+b5d1cd5a8b5f1fcda7ca245845ddd697
+b5d2659f1e36d48599f4a90685818f49
+b5d279062b0ba10001fe801e40ff90ac
+b5d2f91a7dad51442603ac08c3e28f67
+b5d8bd3c48ec3e0ff9754c4c37937444
+b5db7c99eab404f1dae2508b76161116
+b5dbdfc83c991dfb6717609cd276534e
+b5dd9625f2f49471d2b272869d97fb7f
+b5e47b5b5c65c026bb10488d57c2cb67
+b5e5a37c52fb349213fbc2c6e7a367a3
+b5e6c961a353faefa2a630a7a0b91b4b
+b5ed6b5965eb31765665f5539a6fcd59
+b5fed6bafdd943b37f5ba3140bcd0ff8
+b604d50e77d8dd3fcf925a55fa43e407
+b605670516214e4fdb4ede76ee486c22
+b6065bf5901d6636ba0f3cb1c5e75aa1
+b6120cab2d46fcb726feeea96ef18071
+b6149cfb1c5e2b75869b52035a3b3225
+b61bdc4533e5eb937939114e375fed77
+b61df124faf5f1bc2a39ab1b978db6eb
+b61fd109e41fcd2e0213a44e6d6509cd
+b6331bba73672af033f050618b935291
+b634c547b95b9dc28e41ae0ebe6943bd
+b639495358d62618ef62404e0840a60b
+b63bce1c9631f2e187211de76797daa2
+b640de0c79fd8029d34583137c6c65d0
+b642849fe4b52121fa2786c10f4ba356
+b6434086459942467e936baf3710fc99
+b644dcbc7cd56912173df423e946aa07
+b6495a62eb76e9b0fa16fdded0078bd7
+b64a0e4b07a4659f46eef2c47c2a16fd
+b64c1c474323919a08a986574f187444
+b64cb569fce7eaf525798fed49851a7d
+b64fee61df0c32b70707d22ef70a6885
+b6562cf7ccf3975fd25218fbdf96f60f
+b6574d97eed06ca8eecea1b22ae82541
+b65843aa523573400f4077101624d952
+b6585888bb6f94c91f8a88ae5e1bd6b9
+b65cff8f8f46e4ee0e2889d92be4ae13
+b6667a90211d22ba59f85f81b6005d26
+b66770c6802907ae75c211a8bcdc561f
+b669a4ba8298df432a3448f084d90946
+b66cd0a70fb348fdaac3f4a7dc1c921f
+b67994acd01b4fab48630a1a176219f3
+b67a0d4b1cd27f7a1c9cc187a4c85d29
+b67ca69830c2a0364a3da2eb6314be90
+b67f6231787dbd5a41ae6c77d388fa3c
+b6818eb5f2069ea51198cbf329feff46
+b681bc77e93ff8e79835a16b8255bfd4
+b6824c9940bcdb0b0569a13114b579a0
+b68717367c0e301b4219cb6490555ae5
+b695a5fbf1bfb5a12183910ff74d3d4c
+b6994b323e0c476b6cf0256df81a885c
+b69a57ead54caa03d7a7eec71d0616f2
+b6a24066052492135599082429156ae8
+b6a6ca5da9a2e81772ea87d03b604d31
+b6aa395bccfb94cd8e401989eb14f811
+b6afda3dfd18e00aadc1f9919f268538
+b6b4546a45481a4e896745d3b51ff359
+b6bdbc9e7c602e03c0e9fc40052a51d2
+b6c11c05b2484649bedf0fab599eda94
+b6c52e58ecb6fa892b4e8f0e1309b9bc
+b6c5662b094b37b3183184f299c461a8
+b6cf5a7cd3dfe7a13625bdfaca18eb0a
+b6d022d19e9b137450717bebbf0feca4
+b6d18afe1152647442d4e6c28d431d37
+b6d33ac8e3eb527d04166de681c3aa56
+b6d6b54adfc3022c19aa84388c04d901
+b6dedc92d9e4337ccf4eefe514997861
+b6e408057cac1f6beb7397983fe43807
+b6e7a75cbccf4a48dcb0ade6a0018bab
+b6eb8a5962ff3a198f3d22fb4575be40
+b6ed6ac189fb8c48378e3ce6ccaba302
+b6eece823e50bf6b073f3bd26541e70a
+b6f2e9f00fc3f2d542d224239f2ca13c
+b6f644509d00570fb134bc157080ac71
+b6fb4ae57dec5f638c2a0b725ae1dda9
+b6fc246834effe0116ce5c9ea7cd9962
+b6fde3c37381cd2b75e1d0e1d5fb6b2f
+b6ff2b3add111601d019466269125112
+b7018c6bd8c836d3da7ab1c4434810aa
+b70bc84fd3b06cfc3d2d3d89233ef1e0
+b71378a4027fbdc76646a35d7fee0738
+b71442f6563ad3f90b619385f594b3f8
+b714fc05bf961ce2cf939bb773ceb9f1
+b7174d895b21ff5d0643636456e9a6d4
+b71b6e788ebfbf88b29a62cff3945a14
+b722d3b5f62f8846308f78554da688e4
+b722da0f598dca4e7b5d976a29a5cc99
+b724857f6afd01fe91951044b431ce16
+b725d67e99d644f465f251817abe4e4c
+b72767739ce4b19826c27e6ad40d01c1
+b72a93169db476878d45bdde9b1c0333
+b72ffba4cf385093b327c192f77ebe9d
+b74473f6d717c7f20ea20dda5500e50d
+b7461112f87c3c1676ac9ca6ed239234
+b7464c284be72047e773e8bb5ddf7366
+b7482be8e3b0fbee0a2cbdc51f635503
+b74a0cdc89325f8ad6ba5c4fdeb160eb
+b74d61822330e9b15dd1a3cebbf0bd42
+b74fd017ccfafc319ae5833dda070df5
+b7590b83b54c4963ae2d3ff8f74e9bbc
+b765ea251f92805a6487dd226e6b9453
+b76617223a4efbde6e2f590df7e87b26
+b768001e49b90a090d81851045a96aa6
+b76b009efb46fe20bad875661ae3bbf5
+b76d48e9561cd5eab8178753414326cc
+b774b773b4eb127015b1ced0edf87dd3
+b77529ba410e1efcad163f1e4be78c2f
+b77daf32c1c93efa0ddb11fc97ad0b6d
+b77ebf6a874332ff6a45c9b87f99e510
+b78490001873decd7006ffcee8b9a099
+b78732d8c96532ba15bda3c40f9765f0
+b78ac07dcd59aaf04471de85b050ed5a
+b78ed584b092cc383e74d49ec6a842df
+b78f253bf4339a700e9f0585399279e2
+b7937c5fb23ba8da8eff406f0c4c14dd
+b7939fd331d332e4b8985cd9aafde654
+b7981e743d4a87d5b74b6244cca2f609
+b79b3cc34ecc8c93b61b54a519d790fb
+b79f599dbcb864f64417934021e0a900
+b7a1261cf4722136a80f462ff5d003de
+b7a1bfc7e42afe289d975e8bbe62dbaf
+b7a6614063c540601a8e31b8240bc739
+b7a904a481df8e33e338a42fae183b1a
+b7ab219558d2f13ea4a014a71049e2cc
+b7aed85ef3092ed610d259ea379695dd
+b7b65426c6b2143d52954c2a80ae17a9
+b7b91b1aa361d80fda267649a213a4e8
+b7bb61ef335462344dcd7b228f2c2b06
+b7bb87e8d3d782f0fe0ec0205397297f
+b7c043510b9865764dd8463d099366b2
+b7c6bd332f9652defb6f742121575cac
+b7c9825387c611a832a145c8435b18a6
+b7cddcfdb12d5a2cc6c718dbb19f9d3d
+b7cefef41b6f07008e8125982e21c761
+b7d035b0744aba89d210b76876f5cc76
+b7d5e369473a9f9c66e24e9cc6e82239
+b7dc560c16120eab64dc69d95f184c34
+b7dc9e9525125121db81a1ad0516cebf
+b7dd968ccf0ebcc6506359c237d91789
+b7e097fea412d0ffaeffe7482d3bafc9
+b7e4439c8508f51d511b0f22c2e4fdf4
+b7e6d180209de4ef34778e9831ea5ada
+b7e84922a0a2e466234e7c064401c042
+b7e88b18fde4eb706f25e6de018e6daf
+b7f1d443b6e30649451ac82f314f9ec0
+b7f48614c183cf1c2d1f1c6fccee2159
+b7f5579be26a71146689dc45447d6109
+b7f5d16c4f260c102572fa095c0e014b
+b7f7b1c42128ee5330da892579a91e26
+b7fef3da71d6f0bc314a1fb4c42bf0f3
+b8123a62c559b51814f47e46f0a2d1db
+b8176443e18c8e61d3f1f442e9e51c78
+b817e3f6beed7dbbd71ee6bb32e9f5be
+b817fc56de55e6f02ebcf9a9f5f7b146
+b818ad64e1776b4d71dccd187896464b
+b8197c38d7cb8f2846bf61e313d91855
+b81dcba0273509e87c11dd7c4eadce53
+b82095a70c1e02f465d205ce5840c424
+b82749ab5ccc4657e6fc1d4497a24d84
+b82ea73e39ec97b1ef3668f4c7c30b07
+b83cb322f39e66fc960d2ae0cb8321ed
+b83e7ad7f316fed7f0bc9b67fd1f342f
+b84e663dfaa78f3787aba3cfabcf1fbf
+b85bcb365e48aa1116f4312af3ef7215
+b85c6178d82ae22952c39d44d6efdcb9
+b86346cfd904ac9964df0493a0a8c610
+b864094d19d5986cc21cdcea390dbf0f
+b8644704f00d5bc1b9536ed91848a5f1
+b86b8ab06d3460afd3f600fd06384254
+b86c5336c805d24b7b4dc2a36ddeec22
+b8741f9ab210fc4d3f231b690bf30094
+b875bfedc6f2b2ee4dfcb5d907a97d97
+b87cb56ab4945acdceeae33e5361732e
+b87d3d42efa93861f70a8a1e9c19f788
+b88081f08f8389616b708592637fbb13
+b886776d1670cedba8056ebb074ef68d
+b88832f6ef3fd387becc48e0dfa98935
+b88cc8a53b0c3fdc8ae7f4ae4428a3f8
+b88f94b40c10e1c449b5584d1b660351
+b89463fdb9deaa333c35313a31c97ebd
+b894b3668b85d402c86874a2584ed469
+b895d197391ad65e2659b1bb088f3422
+b898a6b282c5177934cc8a1a37214bef
+b89ce451c6f6762a47b6cd5de32b098e
+b8a317445bce2000993db6a7eb92ea8c
+b8a591a48b48be2e5a9335846ea02130
+b8a7d094b15c03f7896c96a6f40cb710
+b8abb2138a471eeb20215aa7ba7948fd
+b8b6396c5e12dc79c3b36b7c46bf8751
+b8b886a881c069430c3595ff35b4cee5
+b8bcc42578b54f5b4cacb02e837647d9
+b8bf3c2dedce91e8d81e0a7f581a207b
+b8bf6b72acd57f38155d79446fff6e4d
+b8c5b9de601c5b03286fa26dc523549f
+b8c8f7f77b5e103a4ba2f3397c0967d8
+b8ca52cf07eccd108bda5e79368dc750
+b8cb7a9fdc7eaddaf43112f5840705f5
+b8cd7f57f9b52fe90ac4be0c8fe3cc64
+b8d0dee04decc668a24163aee079ef51
+b8d5cda5d50bd4126ef23e73f7df4d1e
+b8d6e93cb3570bbc338a442c5579fa73
+b8d73e600d38a8f3f1cb664d949277ab
+b8dd05c9a7d8c6c7657c5b1d14b203c3
+b8dd35f14f4621552021f93307423480
+b8e4bc22d65b021f1ac9c0e711c4dbe7
+b8e71fbcb37b073babb20a07cc9f95bb
+b8e7786919f4ccba6b9da7cec25291e4
+b8e7848363de2ce8cf64e9ed360fa936
+b8ecfb5f5aed2d30aa21a7a81b06b05d
+b8f14946a441b73586834bf6aebae770
+b8f81b29e3eaca0da5fe9179c870911f
+b8f915cf383c5c570e3fc338badd83bd
+b8fb2c91e26da0bcace87df327968846
+b90518e80ced0ea79e72b6ea53ddfe75
+b90623c2af7e48cb3664446d89321244
+b90d8b47933f866ef2d76f7e73b3af24
+b90e2eca6e5e1b5595be4aa3dc0c4c7e
+b90f7c8343b735dc976bf736eae329d5
+b9199cc12acaf8e032054ff607c83fd8
+b919a7b575c43451b2e92732eca6ded9
+b91b0eb468c2b82913ed45e02fff8bc8
+b91e48e6aa5af6728d3ab2ef15c27e35
+b926e88193c5beb17b1d1520f43b889a
+b9273bb950f7fa0fcbe7f31aeb546d2e
+b92edf765275522e327083f34847700e
+b945e57413a7906380fdec6241ef8f76
+b945eddccd4531a49a75e60f82739af3
+b947ce49975fedacd13e001d1cd657c2
+b94f8e08d217a38d5b6e1540b0536569
+b95d0d06f59a47cb634aa42a9d808ef9
+b9634b249258f434501a78c34dc8b20c
+b96b6b5bf2f170560895d232279a6ead
+b96e3d0d231d0d1091ea3ef7e746dd89
+b9785e6b87f36e1e5527ea8d7af8c709
+b983c7fdc34b28c1ca2ea8dbf8f1063a
+b98534c22fbb64644b0243ea6591b2b9
+b98778e7f2390f3c8a7b36df23fcfa84
+b98a92f98794673a9828ae0e2b7abb85
+b98c36a5150b75e59f5ea6b1ee2ab410
+b993e3fc2ae12b3f3e77b65c7784b726
+b99ebb5979f1900163945c5b7db09e45
+b99fa57d7e1f71529ea6750ff3b76840
+b99fc4b24b67034ef02b1f4bc7218319
+b9a07c087a91ceb2f6b5d889421fbc51
+b9a0b4720451d3f5ad194ca79cd4ad67
+b9a25bf709c9ab1938d644cf9f487b5f
+b9a52d9fd02f40ef4fe41a4335a6c762
+b9aad97e50c4bd33094ea141c872b3a6
+b9b56f8b413caa5a0154f0a7447ccfb6
+b9c0653f025eb7bf4a47d77b7eae6fae
+b9cc3ac275011aa259b067fcdf315fbf
+b9ce4aa2ac8ee309c69eb5b841dd2ee8
+b9d08daf77b8bdcf5c9fa32fa5a36e73
+b9d14dfc1b9cbed103d27cc73c4e21dc
+b9d2eed1eae3e8ee88c4f68a5219967b
+b9da18a697602025d167a83aa8648025
+b9dba162b0c766d9a0fba5f4e832c598
+b9e8f95c87319cb059d7abd7d4f2f6c4
+b9eff3df0c04dc428572f21a8ada9900
+b9f456201cf46155a5a92d96dd2690a9
+b9fd827d79d5478bcd69c4c417a695a9
+b9fea3b009496d1e8609ca86269d1c84
+b9ff6842cda7c92286c06c2ecde8c3ed
+ba00eefa71ba4cb3ebe4e7c066b88444
+ba0273f3a01c74387eef4191ead1fa8f
+ba0c06f62b5d9b545d64e5e98bbb2ca2
+ba119723b31fb8467d98c87652560040
+ba12edb220cc509ecd4b8721c3cf32d1
+ba1987ebfcf643da479cf344c9c2f624
+ba1c516fa3b470acea0646c4a6470f8c
+ba219ca9fc072219b42151075d1f3f1b
+ba3c20f6889c601273b3101114ba61a1
+ba43778f711b1b922b630c3954f432ab
+ba47d9872c4d31f336b795d2434dc614
+ba49637f5c87b169be14a1a3b26bcbfc
+ba4eec446234aaecb82e2dae10bc7325
+ba54ae9701a227376d72dd1560268b0e
+ba5c2279911a0418e970059a88414b08
+ba5cd9c8c6d317459f53456ce51d63b3
+ba5e6da597b8fdf217eb3dd87d997732
+ba64af3bb24b42b0a072f1bcd3bdd262
+ba6515873a161e3988d071a6295d9992
+ba660b30030bb5687762229606c2e69e
+ba692b72d5125d29e07adf9c989de801
+ba72a15bccfe4cbac687b82dcfa49d2e
+ba78b4f758112e2defae1a3475586a83
+ba7d8efe795c88bd955de995665044ce
+ba830176431acd854dfffe086756f04f
+ba8d0b8ca6a227b5cc6dbfb749628917
+ba8f69c0348e29ed99f538c1a26280e8
+ba90c94a738e87035cb6883d10d1f188
+ba941fdd11daa2f0adf55ea7ebe41daf
+ba94eb4b3cddd0c5babc30462c30ae09
+ba9832331311e6da4c795935deb9f235
+ba994c9097fc8a993d9e60f0e3ee7f6c
+ba9c8172244fa5be78346fb8f1d327d2
+baa004b3f1572b62c5b1b5dfc37fbde3
+baa1dda5d74a93f08b5bbfc3b4aef135
+baa74e4490c56d1ca455432e01edd775
+baaa70a3f38d5d6198f87ec3760ce61e
+baae2c61273ba06a179d8a029133ffc5
+bab16ea54cf34e6429dd2daa0a944e04
+babeded7e56bcf094f399459ed1a4efe
+bac108fbaca1ed7220117feaac61a8e4
+bac134ffe9ed37a35eae941cd969bd92
+bac9d89279954004a8cd76676d4883e4
+bad16a6f3b694fcdeeeb6698f4c8eaa5
+bad37e9555a12869ba9648751569dc71
+bad37fc4e2ff156c8fe4e64cd6fb1f3b
+bad643cdc23d2244413db278e8214490
+badb89863438fb6dc644b66cacd12cdb
+badef9b2092f502429e9423e7301dbc5
+bae1b23e2019f4c9b2ba0594d9ab8624
+bae51d3e250294e50fc8be12acf58a00
+bae97cc36cdda7b3df47abbdf86d8de5
+baee6236ef998c27d8039e2bc6adad42
+baef8f12be7881f197a66e7922b98851
+baf008e64f03c22659f8cab5ea95e211
+baf1d325e575f0f6ee0fc80da89ddd09
+baf4253288ad73752bbeb6d62c64739c
+bafa0318438d10d3caaac95bb4038204
+bafb6176ef4c8d05f9d509d8076af046
+bafd4e2bd4286a0adc20497116f58e7d
+bafea387751f8014e08359da50d33028
+baff5dbe83c2c90feb0db4e680cd429c
+bb001c4678fd8180355cd9e83e21ebd7
+bb0ae8c5760153518a3e4afa05da9a5b
+bb0d5fd4d3641a1d664e1aafde8a2d00
+bb1073e32af7e4d9e28938651403a04a
+bb1a2b161b532297cc9d7aac88af8f06
+bb1d4a675756e697202e5f11946964ac
+bb2810b92b3460c41f279223bf8221c0
+bb2c9db5f03126c9b830d54e77114b48
+bb31565ce1a1738395823354cf690251
+bb33bdf5b80f9598663bf544288b60d3
+bb37431c861823d8c4db0f348d1961c2
+bb3757250f97e77d3dad4af77f54043c
+bb3a2b318c1ee70b44aa28e485bb8e93
+bb3ab829942c5386bac7f43e6952ab5d
+bb3c0d25305b3cc6bee690f233bd1744
+bb3e5a483b7252af136a9ba792761318
+bb405ea068510105b81ab865ef2ed1ac
+bb4081d803a2bb97e2ba95af46ec18d8
+bb4b5973caa6a9b82138424f0221b055
+bb4bff5cab67095d662588535ae119c6
+bb501aff29bc405007d5701863506a10
+bb53b417f2bbfabfacf708b36a5d742d
+bb57a43a5789bf0a1149c552cb65eb9c
+bb5a3c15731d68df3a415c1f4fa8944d
+bb5c11b791f795a1a394d92abbd8340b
+bb5f9c0597f8b32bd81a1e4a6b684cbd
+bb6b96f290a43a7ef5155560c3e79d69
+bb6bafed1c9be8a0d6a9b5c6ec1a0f93
+bb6e36681866c8acee06607f54dcec47
+bb75c2b85d9fbb5e810bb001f6db781c
+bb7761bbd57ce4142d6070be3f16d413
+bb784f6a1bfc5af498231079eafd1fca
+bb7a82148c0b6a58adb5638a50bca3bc
+bb859bebca8033774638c670c9f269a7
+bb8874d779c82518341fc279c2c0f3c3
+bb88afae3c77daffe8befbee730b42b4
+bb9000bbdecf9dba1eae618c71d5e905
+bb9716edf6cecbdfa7fd8213274259ce
+bb9bbce7bc8e498387649a5a367e0077
+bba3273dfd073da47f7473651c5df51d
+bba5cf2e249312ef9bb28803f387efe5
+bba7a5ded36653bcabf8f0d6c0dc1e25
+bbb08222ae7b53257c40b4f543521f29
+bbb705fb0025f18dfd7142816b5ef639
+bbb7de5497845d00a68947c4f759fb66
+bbb96cbf838a2aafc7a5cac94ef08568
+bbbaa1a974f14598dc6a28415e97a984
+bbc6fb96b7a883aa73ddf4abfa08d617
+bbca16c458d67283b30e7341b8c6a29e
+bbceb443078694c4d171ee5939545662
+bbcf08fd98935328c7ad792cce85534d
+bbd3e91cb991018db086e2a90985d642
+bbd4a4560f6c6b1a9127df10936ecc48
+bbda51cf8c13a8a58825f4178d0b97ed
+bbe0c7398a803171cabbcc3e428ed1b7
+bbe183677fb87f3b830a0124d9d6cd19
+bbe7c1d3334a0256893702be5d97646f
+bbea1a270dea8439929aa0bfdca6a48f
+bbeaa2c9ee8ab2b55c8ff053bf7b6767
+bbee19ca2913c8be31f49b685b3caba5
+bbeeb017392bf0d36a30d75eb024cd5b
+bbeeee07e9609be79ee564dee1c50377
+bbefa74ab2d857b0f5709e3788eaafe9
+bc0051b6d81246631aa693d89261bb00
+bc0169cd2cca2de2d4c0d6ca5ce6586e
+bc172569bcc96ef42ab2a54e8d1971ef
+bc19c25288b3ed414759533d6d11ac32
+bc1a73afbd65e3ba6119b9f36123596e
+bc1b00dedf235c8c3c72d5ec97a40a02
+bc22a54ac80220c55e554b88f2163e96
+bc23e222cbdaec8983526b7b49d51743
+bc274898bb463d176752b204195f4841
+bc29306185c9f61ee1869affc9c8cb7c
+bc29db5b95e4ecc77863a129eb6e6afb
+bc30525c7107e0904cb32483771117df
+bc3087608aa67e29dcf5fa4396567ea0
+bc3497b0dee58f7462426379e8d62a0c
+bc38ad7396bc25e7f7a99b6a019cda2a
+bc39d33fb044c4266faa1593f505c945
+bc3fb11e5e5b8034e917587b28ddf8fe
+bc43395166bd73040a6fca3b8db0b81b
+bc4b2039c2cad8ed563df871b11abfa1
+bc4b9f25bcfe13b7c5fa5b2d1125fa41
+bc4d8c4544177021e6ccf1896c43ccd5
+bc51fc72a9134f55ca49415bc1ccf9c3
+bc52eafe24b19fbbd79ffb1e1d9a23b3
+bc55dcfe5dbded40eca4674d8442fc4c
+bc5854fa2b4ca600d083dab14b9fc704
+bc5b62b04170f7c207e6c753fa2c776d
+bc5fe81066c6e004967903b4cc84098c
+bc625d9a3d5bb4595699a541949246bb
+bc63da34d4f8c2b59d1c941083e8e0cf
+bc64df93b22f21ca27ea892194f1f081
+bc6690341c3f31633f2867b63650d3e5
+bc6a611a775217d8af8adb1829e9a4bc
+bc6b3662788abaad227e9fe887e8be02
+bc6d1decf7b0cfb40a92612e11c2a419
+bc6da75bf532a7b985f7ce6c05e8ec9c
+bc6e49690876359a2e9e034b4289bbe6
+bc7251ebb5a208b4834c2e5cc68dd173
+bc73d19e5d98d2421fc2eaaa7c9b5f04
+bc764f67f09708a897e15cea8c5a1036
+bc7b09c1d08993d0b178248a42e90d8a
+bc7de8289d0c441f8512059c671a2d20
+bc86ed66bc30df225b248e040881c26c
+bc89a835e2e9d17e74862862bfaebac2
+bc8dcc3a0b3c356b62272aa5be1b7350
+bc8f78ad91b161b39c86e7f60ec1ce70
+bc925aec35475c1ccb1618f5740d5050
+bc9500551e8b9bb28f082db5df179777
+bc969fcabd8efa5607d0a9f2c47b6c04
+bc98fb9045921d5bc386d5decbb32e4e
+bc9c5594b18de4f32ec8a34fea01d7f5
+bca14e10c3ec8953d50795b1b648977f
+bca64765d9c5f2c085daf06745b30a9a
+bca6ac3a16ac6e47ba54d09355147d48
+bca6be4661a0d63e11bb04f6c8514229
+bcaf015da5dd0b7eb4a79be754314a51
+bcb082888afdfe9dac284ccd364a75a4
+bcbfa250f1562cf9ecd0b8696f480af7
+bcc03034ca94a163925f725de8e377c4
+bccb0a8d7b6a9ef904d9a9661602b77e
+bccfdff9365853328320b0e7a480b453
+bcd729a6732c842bc9e784198b5237ee
+bcd747730dc6b2dabfbb1b43d9508a01
+bcda9ec8010c53c3800663a8d2c6fc3b
+bcdce0dc757c8d57ebf893ecc21204ae
+bce09bcfe85b51f45ae667831cdc1491
+bce304b263f944ab86b0902c45494fee
+bce8fcfcc12af0c1b2a30a42e95c8fa6
+bcebba46754e0dbf2204e304d0fa51f3
+bcefd169b74bc275b69569e422eb8662
+bcf78c99583165301f2129ff4475c890
+bcf7de38d5053f0be7a8ccab395e5e0e
+bcf801558e1d56a1f5adaee1f8e874af
+bd02dcce2334ea6b237944c4fc8f5b1c
+bd106277a17b68339711b18d6cf02a21
+bd190ede9175ee6d5c335f52fec2ae54
+bd225f3367d5f4ba42490bfb7fabc2ac
+bd2c67357f39d770faaf7ec249a17bd2
+bd36196ec9a9871bb30291ae60247ea2
+bd38730f29418b36df49065a9217b2ef
+bd3d246b9b6de2a50d394ada65be8283
+bd41d920a4c02e1d35c1e7235162f21d
+bd44db4e0121f2bd9315b94b82da4ea3
+bd4d605a7893ae28a900d0739f73ae99
+bd4f7c2003995dd2cd1312be72043cce
+bd551c12756021127a407aee4bd3cd29
+bd6579e86fd01aa8a2afe0bb51532bdd
+bd69cd548744290fcc7640f23d4f65bd
+bd77107c575b25a8cd1ce223a9696b81
+bd791e179bf92c897d127d8e6a34df8f
+bd7e5eb7babd048aeff454a0abec82ac
+bd8b0655ce0116eeceed0d36a550bc66
+bd9233b3d5de76751a8d54d6bccf7031
+bd94b32f8475a23ca5d080aacc7545ac
+bd96cd1b00a05c61b1e89fffff02f929
+bd98a1946fc6a3d1bdc7bd7cd3a69a8b
+bda3ae6ea7c56b9a2fd986b70a6765eb
+bda9c170ed50bc53f3ffa6b26433987d
+bdb3a4c617b1b6243b800ff7c2731cea
+bdb5ca7d0342f613998748525f7763cf
+bdb900ee9d259d279001ec87477d5c39
+bdbd01278f40e3c439c4101bdbff389c
+bdbd0624ff98e3de531b411bb75e523b
+bdc38e6cb79e1d37a3b2f83e1363370e
+bdca7eaf53a4233474166a4bbf77bb72
+bdcc58f9d18e4688ee255bdbfe8a6c5d
+bdd51699a6e0f8e8809808498126d890
+bdd5366444bdece307b0b03c7df44633
+bdeac028193908ad7d991d264f436f70
+bdec385e84fc77a8740f679cf97d3e8a
+bdef2d965344ddc7b1e51694f44e139b
+bdefb5d19dac655603d2fed72d6caefa
+bdf56cf0bfdcfcf82dc5965c360912db
+bdff461f2217ffa9c894407cde8fa8ce
+be00e0a046819500914a66ed247bbceb
+be055a378ba92bf4a0c0f7a81254ca0d
+be07618beb3dc5e45f17d851fe794c4a
+be09ae91184e890cf5d626191b999863
+be0f1c1b0c309a3009d5ae0d61929ed8
+be1bccd491d83f135cfe6f0cedd9e530
+be1d0e5128788570873924c3bbe26753
+be22696385df848cef30e5794f0fb10d
+be297985c9ae89a785719c214cd02e05
+be2f65c58fe5f8fb438e42569b39022d
+be39e64b7d1b5e0bc03514f271809b2e
+be434b93330a3ced391bddda0cd432e6
+be47b7970e72eece304a67509d16e0b9
+be48ec1485d5045ecf80d3a28fc8ac33
+be50220c4bfcaa522429989f4a727f30
+be6318988bac8951da24553cc1899531
+be6517acc353df9960e7d8ee1478d940
+be69096da808231e839afd6bc827a047
+be6f266070d74edddf27dd2b20248c93
+be6f8ce1da8ca8a2c0e80fd190eacd2f
+be722e26a11388df55372155042f0ba1
+be73be7406d3b6d30de4360ec6111215
+be7859fb476af5eb05a495dcc4a5fd62
+be78c70ea4cdabc8ae2053e03c2c7fac
+be7a0f3a88db1893bae712f540f52582
+be7cba3a10094c351264d6a941c5546f
+be86fd614ced2fef635e870c06ceb22f
+be8e83ca4d1c2bcef8955afb6e88d375
+be9028c17d382dd28a49563892a5f84b
+be90a687591cd8ef5cd66be0538390c1
+be93f40f7574e8245fb5f9b2bdc8c81b
+be95988b30e1af7749aefb285355f6f7
+be96bcd53dd56deef7e2f135751a8f23
+be972fd73b8f9e1576e8cf0aecc6e1d7
+be996b52f43f196670283792a187aa7f
+be9b193ce9e1ce4144f2a43ddca30583
+be9ec304369856228884bad07099b2d0
+bea4e465996e71d46ae511e7d1e92cdd
+bea7b9312c61438d2e428fd5fd89a8c8
+beacd19be8a4204b1f3f00dcbdebdb4c
+beacfa933aec442e6ca101ff121e9b11
+beb3f44ceb81ba65614c2da119fc52db
+beb84f37ba70ca85750edbc44f0e26ac
+beb90e272a4f60c64672fe320593a609
+beb9b5a60f61734a4b7d4fb05d989766
+bebb423830d2538d48effb7526337871
+bec1daa91de3f050425a312621c104e1
+bec1f818840cf064122ef2bcff08fefa
+bec6bcc65b035f987fa34d0dc9db18cb
+bec83f404460602a251abc57459d7e75
+beca63adc4bdc9ced6b649e26365974d
+bed2789000436aa2ba9fa18b3fd9eb05
+bed4c8595526efa3c4e97d2f12db02e0
+bed5962b6e5bc6cc5800ddb58c3f8976
+bedb5b68f14c87de2ebb1431e262ad39
+bedc132473ad752234713ba7832413a5
+bedea6fd3003e973ccebb4c97d1d41a2
+bee3e2ff08e342fa882ed8bb53e7f62c
+bee5a1cbda3e995890e7892259ea5d16
+beedfc57140240aa8cf10d6487e71c1c
+bef0f2841394b37b039cd033e24e8b4a
+bef82295587a00e8cbc9d24b35cb41e1
+bef8556e56d748cda2795c9099074d23
+befa7c4f984a96250a7f292a6b40dece
+befbcd5af790a3534e014684b310c324
+befcc6ddb14c34fd92a04361ca947574
+befcd874971e674cab8caff787226378
+bf00d5a8e22a4918845cae91b4c54af5
+bf037bd468caf91fd03094a778285773
+bf05d961fd7b1483ec69bc8705a3b470
+bf0c38f9eef0ff230af19be771b0a83f
+bf0cc30cc12cddc4ee90ff671bc2d817
+bf18021a3cbbaee34250a8996ce5c82b
+bf1a36587bd35ffa2dfeec52877ac33b
+bf1fe7fa36f1f90dd9af1a08587ccf21
+bf233ce1a481581d687bb62331d0c643
+bf236f619886d1030e9c3e147aebefc2
+bf2452793f3c6cd940f4b079e9da0cd2
+bf308633197ae0d5e3785d5eef1cc598
+bf3134020de007cb427da00e40c44227
+bf327e13d9ace8decb023fcc1d956114
+bf37856897a4b304f24ec175e886b353
+bf37d41cee19dc03a51565d81de620e7
+bf3d06dd2467fac353d4543864e362fc
+bf42d9affd3357ef148d89405ea8acdb
+bf489ffbe8b40ed4c9c2235e2a84d4cf
+bf4a139d336553b84f70049609c17ae9
+bf4b8ccdc42d77b2c7007c5063f73aed
+bf4b94bf10627cafb580b083353b0e38
+bf5404909ffabda7527f1667e6f98bd4
+bf574d2d97f49d7d5b6e7d92d2494d60
+bf59328e9df87531370c449e686eaa54
+bf59b080df02f491cbe7a5fc6ca6c909
+bf5ab540efa0c8a475f89d88e223725d
+bf5e82ad937d584f7270c5288448ecc0
+bf5fb6cf2eb081f5f892839c96831695
+bf6e47b4799db25a3e0d266664483b87
+bf6e79fba544c05b2e82e938c1a2a8c2
+bf750d52efb07c09febae3a3efe035eb
+bf75e7d8271e2c4e4ceada220f04b538
+bf77b952b9348c04c9e0140069028c34
+bf8040f6b358ca6d6133c520dc265983
+bf83d7792c97afeff2bfb0b623a5d352
+bf90cdadf4ef764ccaf46eb84d68b497
+bf9b7491c76d79c02dba8abb3a2dd3a0
+bfa649b59e78a2b9c47749d0531d9321
+bfa7726ba63fbab0d7e4f7105313391e
+bfad2d4b754730ac5aff41b91f82974b
+bfae94de111f72a39ca2171094352c2b
+bfb8763e2be116faeb79b6e9212677cc
+bfbb09c292d383fa9d8511f439aa0079
+bfc09567cbdb82900d45b230162d40f6
+bfc81b189e3bba28810f6e3e77b8617a
+bfce317ac0581d2231858920212ce3bf
+bfd1ff4e0ca22743a54068e55e150e64
+bfda1207f4578cb7223755992c5c8372
+bfdacb9bcb08638699bee8a6456e5f71
+bfde67c959d53992d4cc6280d9bafa87
+bfedc8a2b57e682d807222a622fcf437
+bfedec10da0807975893f485381fa13a
+bfeec72c93bf2626d86dcca4b3cdbf1d
+bff1aef3d5bda9f9aae3cc69ee7cf08c
+bff2e1d16108df81b2162419c362ecf6
+bff62adcc58d0b45a59bf029a0b72e7d
+c00644fde02c45288df875c279269879
+c006885811fca93c23507daed13915a7
+c00e6cbc4440278e95539d280b678419
+c01566650e97e7e78cba94ee7eccd8f5
+c0174a7029f8cc629eb175d0ddfb655e
+c01e8051079c93dfd67bff0b86e240bf
+c0214488215b553d3c93cead9bcb10d9
+c02a8835fc8445e4926e88a07fee402a
+c03a4b32462f82f98a42c03b9a31b7b2
+c03d1d0a8a726af56c9bc8d49d4cc7be
+c048198a8966486be8720352fb7391fb
+c04921bbdf30537beb14ae4445a63dae
+c049aed19ab4e0b848b518bb7ba904f2
+c050df4472a32b17307a0883c517420a
+c0559e025d808d5e79e496af6222910e
+c05ada9089c68cd72957560fcb28b8a4
+c0628c034864261daa74e996e6911f0c
+c06567ee263eb08f7ab4d465dc2e80e5
+c069a2c8c07e5bd64632ac05f51cc837
+c06e61a6a1d2bda60226e14453e32694
+c0740f4f92671c26eb2f30043e9875ad
+c0748908318df7c066de172f934cfb20
+c074969ee21802ac649a0901be836776
+c076df1d91da2ef27732639303c24366
+c0778b27dbb1731ea7e853a1ebcce742
+c07ad06666871da3eee7ccc8b55a55fb
+c08022bd533b48f4e348251629f04c28
+c08547d425be4f6e8eeb2109ac8908ca
+c08a727855754e46a14c5e70a7e7707d
+c08c9ce623c9fc7992a21cf90f5ed0c1
+c091423b7108f7b7af918b4e7d7af6f3
+c092ddf9182ae6fe5f0339219bd265f9
+c0996ab78eac0cd40e335b1bfc232ff9
+c0998607f7b96a66c66f4a589870e4e7
+c09daf097a92e08dca449447330f8640
+c09fb046702fa1a796c9a1582a5daba4
+c0a50b4c8616514ff9db36511516a1ed
+c0a9ca8cd78f4133e98e9ff9c7e968a5
+c0afa2736713bd60f84387fc2b594212
+c0b362e9571ecc43ee4d5228d3641de7
+c0b87163e3faf1ddf623ec258261dd1d
+c0b9a3c49c98c76f83a5c3554458b2e9
+c0bc9f5044c19090e7251d262f25d49a
+c0c7e59b0e8b45de7aa5e074910890f5
+c0d0e54978e27a44fb420e83307cd921
+c0dad5ef711a42ba6e845d7756f35a7a
+c0daf22052e1791076b80a70f197c981
+c0e7089d941e9b6164cff1480852fcc2
+c0ed71a7eb6defa7b7dd97600ab41c1a
+c0eeee7e5aa7fe3fc5822c8f10ac6e04
+c0f0918f7c920074ccdba7bc9285fef5
+c0f50abadaf3c34b5fee45ec878edf04
+c0f8034bf44972cf649b13332c6a3587
+c0f8342cd38765d20c462abfbd809d39
+c0f9354fabe0f78eb2dd5b39572ad4de
+c0fb096baaf2f825c94b569a27b3fe19
+c0fc4d10a8d3162088d5ee794ef11601
+c0ff3c587920fa9d75ad8983705b7701
+c0ffea3a4150d35044a899aea160bf3a
+c100ac72c110cf0d003783d750d216fc
+c1066ffbc53e03550a26d6a37ca2ec17
+c1077a688e25b2b9a3ea8ff09945eab1
+c107bae2b141263f902c688026bd2b1d
+c108d809e8b27d3513f791bb92f2f4a5
+c10a5311bf0fa8ea7f62b2ac271ee36c
+c10ec2d59f876d3d1219f5079267b8f4
+c111e16c61532ff76c48da6d02e0d831
+c119a6f7872019d11011edcedd154c41
+c11aded8b6f784f801e4d784a8345cb2
+c11beef2daece4fb84d5177e3092e309
+c1229e9197d1ecd57b3c1f4d295fa92b
+c123c5bc4281a277e85f64831b51f710
+c123ee95e5584c0b0f2fd44928b35302
+c12aa0f7ca1a45d7351651fab9986222
+c12ba02973b898a30e78e5399adac5c4
+c12d0af94ffff16e65769fc9483025af
+c13c1185a16a43c48750e2d9171c2eee
+c13ce887dbc851ce67020a69d4de82d6
+c14208aa93208fd3f305a9494f05ad40
+c1430ce579fbf59278158a7f1feda13f
+c150999703c98586f7afdc45a5bb9f15
+c1518f7a0b95956f72995b4b748a86f3
+c153864c98b95caa0147d97d1dc1a08e
+c1545f39142cd657988e318c262564b8
+c1571544816c43aa65cf91e67a4bc664
+c15768e4ee823987589974029531adaf
+c15b1386801e7c77546f4544c8b577e2
+c1664ec573c995c2b0c90affd47ea1a2
+c170211abc89b51ee24487e58d228f31
+c1717ccb8b6140ea3da586ccd8bd4f6c
+c1733d993176bcc9678a278cd175281f
+c175afa1ca0c04e578157a21eb546d35
+c179babd30637d465b5fc26216958a49
+c17f3bfad18b0d818e283bcf0461f746
+c183b7a982c69a6a167dcdf70a420e65
+c18506a35f2faebeed0e47045340f1c2
+c1870365534632dffb1b39b5bdf4ba16
+c18a2dbdac98b4377105377c38db75ae
+c18bae9ae7adc0866ab6d1e04c4bd597
+c197845f0983a57f053e82adefa92c3f
+c197cdda7b70347466dd6851dabe94e5
+c199692481f7e5fa335c82009d0c4996
+c19deff62aa073fd829f74ec8fb4e09c
+c1a10a2a11a81e5ce88910e6718990a0
+c1a44dd6741441105765b774baa14a7e
+c1a69f99b44ac974b5981ffe10f8ef24
+c1a7b954ea20d05743edb979508e3e4c
+c1aac2fc27c2803ae5096fb06b1e3fc2
+c1b6fd8f8feb6261de93a914f232cf13
+c1b7bde2b5732146e878f62a28356a99
+c1cc5c9ebfcd48726847640faaed2707
+c1cd800a506a48509920fddda38d07b2
+c1d1c5a65036e6f22e00d1ac4dfea4c1
+c1d36fc1ebc0464ed8ab3184af0f226b
+c1d54d847dc2e90b03cc0f6bf2e5c36f
+c1d72467d2bdd83dc35fee40788d04e6
+c1d7708b609e17e52306a1f20848e9ab
+c1e0a6a97b3288bac6cea66055b7abb1
+c1e16d8e55a68c8b518a3d03d2ccdd0c
+c1e2b443734d99ec7eb7178847395663
+c1e4c72f04221acb6b14030ef8efca79
+c1e76ea8839b654fbd04621cb7cf355c
+c1e8749bc810648e8295d6fc68c888d3
+c1e8b8bdede711b8ac62cd5368486d47
+c1f36e57f5a282814b36b7469ec66736
+c1f6f033fae021fb5595619ad3f2d3ec
+c1fbe9d5a70c68de45ed11d67f7ae92c
+c1fde5bff477c7667250e8aa1e938bf1
+c1ff557c21bf14efb3fd2d9bf727329c
+c1ffeeb374312f4dcdc3ac211d017805
+c201021b9ba276fdf4a5f4ab4db50720
+c20b90b283a9917d0a192d00f47c32d9
+c21ee42a4157974ae225a36d4bdfa701
+c220c8a990d59180348eed7b10f1edc3
+c222b9bdd7e928babfc041e1a9fd54dd
+c2267ee56197db3b74f2bb4df652db9d
+c22737b4f8ca1bbe7b0d3c6b9ac2e797
+c22ca079ab884f4e3eff4309c1174ba1
+c22d3da08a632bef198df6fa216ff222
+c23279dc7e02c5f81073173bda444b17
+c233fc8edbff61860f35c7bd456bb37d
+c23530c136929b12eaf175576cda9639
+c23cf0bc312d621071fe365d3a072c80
+c23f5e292cc29251fbfb8d4921d48c76
+c244ff7816101126e10c9567aeded9ae
+c246bc48223311d06aa33a23f9576112
+c2495604376bf4b0d4c0096dff641f54
+c24abc9659d1c738c84fea437e4c6e0b
+c24ade6ec39aba913215590dd401df23
+c259b8fd04daff7b1d4d328305870332
+c25bf542e639dd5621af19000a4697d9
+c25c9d1d1e50d690d3b2e7e7136f13c9
+c2661e983ba907b40e7ecb57f3a8a35a
+c26ea6fe6912e2fa7625ae97a41e1689
+c2703bebb985b8bf9a61b4f66ed0b7f2
+c272e74a374d24e77a1fc8477e085fe7
+c2730ddd6f1c0bddda0932bda4d47812
+c277cc896e428f65bc43269fefaa43e0
+c278278cf777a1b6c9167546c0ea19c2
+c27aa02f16e3a50963a291eb66894aa5
+c27b7c9a813cfa190fb3578caaabd381
+c288e39d9344691d96127ba4bc31f720
+c293e74c6bfaf1248526be6e16bcd846
+c29668b210fbcf31ee5fa9346af6be65
+c299ffe4436663034bd9f7f303f62b7b
+c29cc9154292abb5f803939aae7a4ccb
+c29ce00fca5e03be4dd7bf2307f3f56f
+c2a0104e63b0576b885a67c71ebde29b
+c2a30d74b4b974f08f29d6e16b292b5f
+c2a96581cb8ef2fbb8fdc32a79a588a4
+c2af3620dee4568e1338c7c2d89ab5bb
+c2aff9dfe1fe6af0e5ce838c916f6ca8
+c2b1005454c72142fa5b832020e121ea
+c2b249f99a92bea61d210c252e597b54
+c2b639152295c208c0a1fd945862330d
+c2b9131490faf2c9bf8680c768d60be8
+c2bb29565f00807271630c3650b75152
+c2c14751575821ac28eb2135d848601e
+c2c546a4b55d1bb6e68aacb0b809945e
+c2ce03c156ff68513893a46912d865a9
+c2db13d0dadfacfbe911e0c9dc3aee8d
+c2e057462f4fec0afbf750715cd68bf1
+c2ed45fc1704e7a5e1d8b19334133306
+c2ed49fd4c15d33849485a9b4054d7f8
+c2f0c87efe1c7f4c1ce642f36c810d68
+c2f23803dff72555bf1ca01923517685
+c2f5d0b96f8da04c256d5841e326e056
+c2f6c6b4aa1152a5fbd0107ce9e7572b
+c2f81ddfdfa2b32e60499cee2adea05a
+c2fa2cb00ba0612c1c6d88649568a810
+c2fc61e30db338089e9f7759efd6d997
+c2fdd3da3760429edad9493948504773
+c2fde8579fe12b1f7c5f45fc9e1a1311
+c3066997e078296a50f39e5b4480927a
+c3088df5b0329802554b01b1f3afdaa7
+c30af07c53ebeb5cee10d3686d19afbd
+c30b62b54ebba29c4a73280dea700fc4
+c30ecf6950a0087dd7acf7400186af65
+c30f2dd44c23502cf3d5d1b3e0f05ac2
+c31696f5c54f9953738e8ba5c21c3840
+c318111f0e8b3a22e39f7e48d34fe783
+c31ab7590b7a627b74e3c20f718f912e
+c31c092d0ab0c98e985913f36d651c71
+c32ad66be4f1bf38fcc8aed5a5465bfb
+c32d56f627a76fb2c55c4d4c8f153f34
+c32fa227b25247ac2230376deba4726a
+c336ecfa407d79f8073dea458383b5e7
+c339f857aa0e6f6ea6a814a07d5dc7c8
+c33ebc107477a6374adf89c5c1552693
+c34170bcabf461e7a44784c8eb48c710
+c34212c1411652919a0e7b8309155f00
+c34cc04d088d32608db06c9adcca36c6
+c34e3b37928dd9aa6983417b49c87621
+c35209acfb3697a103b11221e338a88e
+c355afef105d21178db3a66f35163fd8
+c359755e89002d8bb3f14da9a8d8639d
+c35ab5ae0a6f9d64176b5b97ec2d54a8
+c35e69238ff278f304521307fc140d94
+c362a5f720be9a5eff0dc6783a41a81f
+c372f1e1872f9aba48b7e485316807ad
+c37a30a5e07d62afcb8b142de43b9f80
+c37a49ae06cb6d9d9648b35632518215
+c37c3a1dd81faf0db76049e54c0f11ca
+c3849d8078fd4ea89d99baac28a8d9c5
+c38798aa96489f21c4f72cb0526ae018
+c38da9350ecc780cdac09ed9d673da67
+c38e6452e05b4502036a0d2d7a6cd2de
+c391162f1062b1884bda41814bc6883c
+c391e22b6d09f4f52cfd8e9ff1192ad7
+c3948f8251c4fa9c4ddf65ea2dc2e252
+c397503888b098dfd65558dcabbe8d49
+c39991060fc69964338ccbd445e7a167
+c39a4e717f8c990b35709d5a393d378e
+c39dcca688a1b04556fa8e9205bb5512
+c39eddb90dbe2d3115da7bcc01262b05
+c39f911754be809ba20f47f6f9bd93b3
+c3a4cec00324f18bc9f96c7d31825cf9
+c3b018bf85e7a771bb8f1abcb7f88571
+c3b137f11dca18a44afb514833f0c1b5
+c3bc189d0d3f41bcd20964409491e591
+c3be642d7059ae6a597f4a5fa21a8389
+c3c46e75dc0a979fb547ad4672c79ff2
+c3c7b2934adc9caa3ea006923f599dcb
+c3ca250a07cede92b392d6444ae76861
+c3ca7c2e7c5e7193824ffa396be2e88d
+c3cc24b00ffc603b7c9dc221b096c161
+c3ccf8dd37918d824c00e69dc682af4b
+c3cd1c95d69859ddb4da04b14ce770f0
+c3d038a3b1ae5e02365922a81a585e39
+c3d2617e1adfcf772a0cd43a86e9df9e
+c3d57c30038a88251948419eff73f642
+c3d7bbd2b54c47789bf465106efe880c
+c3ed54b69e85e17aac7e5d8a5debc7a6
+c3f87f0626a0f8c2bbc7f159d982ca10
+c402703b89badac9661b1ac809ec6e6b
+c411bc157437c6a7a1d6a648848de3b9
+c4156f42875f915253ea7e9902de1229
+c416d791a255c0d285b21dcc7cde1e09
+c4199c0de57911950812db778fc996e9
+c41dde305e2ed7b57358bb2432b93fd5
+c42013d775ae7a99f750ccaa44674a44
+c42087c9d335970d66f45f903b1fcaf4
+c421338b7e3af83d4f02bc5bc77ae7b4
+c4252bf3d70b3840e6bb251881e9bb54
+c43151cee513d1afbec884210a119864
+c4320976e5438bcd1cbc0738e68f8653
+c436056ef9aa202808f15b0a3ff5ef86
+c4385ed5e69f2cf5ea4a4d537114d857
+c43ae0db82d0f680f78ae66cbfcc813f
+c43dd5845458724dd27d46dc6136de5d
+c43edf243d77cfc1fc24510090ef860a
+c4425106bcb2d555761ece9557931fdc
+c449421e9f8f31ab93cf296d44e83e33
+c44999712d2e30964d2a73e5c596c448
+c44d30d47604429bf4fafb822eb24b90
+c452fa86fec73fd4e44106778e035ac1
+c4539a6a0c27f56e705b7966fbcc886f
+c455118cbd5e8901653fccffd52672dc
+c463b11d677b31b1e180961bc12707d4
+c46960b6d21454c819221f47d7c63aac
+c46b1092d2a7e6aa68e644e4ad30d321
+c46e1377b8cea2a5fad3cca83d877d6c
+c470bf5d8626ee62c03283f46f7d0d3b
+c4717d04ec723e5817fc900bab996942
+c478cf000e7b3faf85cba57e71d12ffc
+c47c47e100cbf61018e9a860d4e01221
+c47f77d6ee076b723870026b1532567f
+c4857f3ad2128bd3f390b55faacca421
+c4886da36b252a1c39b6ac4ee68c6d3c
+c48873567a93654559d9434b6bf4d272
+c48b34880880261540f9be9fc0ebb031
+c490c814df083e9a8eb35a298104f10d
+c4933f2f3c1776f96b9f60dc33718d0c
+c4939a757c3d0c20a74116560e5af9e1
+c49a1a58b3fc3f4f72639e992df51f98
+c4a9384e620addc23e7f38853459afc3
+c4ab2092d880c46725a109c729ee87f4
+c4b34d21a50eae2ac399191f5fcba12c
+c4b42ce50eef0ef45c07ad31b60397a8
+c4b977e61d0a817a83007dad5551e186
+c4bc66e63b16e3df3bfd270c50bdb488
+c4bdba7c92f8b7c7b3c50f654a3b7d26
+c4bf11d1d39563da815eb1879447421b
+c4c2dc6d86bf1a729339ef3bbfaa16ae
+c4c3b5f4743036d6de3f4d5323ed9004
+c4c8a4ebb01703e9c3a8ad4de1e9df67
+c4c9a0cfc70f524f7c30a0ab2515641e
+c4d499f98d37985b97da5e6dd8404c90
+c4e3cb852eab970a38ac062221fd7158
+c4e51065da7e99ced4d64a059da59150
+c4e53172ea6b222e9e2179ebb258cf51
+c4e7310486e9c6974b11520478188ba8
+c4ee5f6da2d3e058a1847599855e7b59
+c4f3e1be0bd14fc74c245840ae343065
+c4faf0716737705e07d5f1031ae8f251
+c4fe6722603e041578a8b72564f252b8
+c50ebfb88b39645a7570f98d9089822c
+c50f5009215d72770f5233cc6b7183c0
+c525c74dc5878107488bdefad5b9407c
+c52fe07c54359a421c3ffe85d59a5fd3
+c530e658d0ffb6f9a0202e87a05706c0
+c5386e8397753be181e1031b9ad161fe
+c53a012ef91ee82ec8182bc225331e73
+c53adacaa1fee7dc7e3b4cc08af30ad2
+c53b0b704d137c640b7a56980b4eb39e
+c5447887e7fb9a484224f268bf5996d1
+c547a15e3fd649743f5fb7b4f6b81a1f
+c547c67fd5ac3e4e141fd440bd4774ba
+c549ae7cd02c706dc79c3e670efdcd05
+c54de6790c619d3c58f1420d59dd2bc5
+c54ec9a34c9d61c7348930803d854635
+c55167f1db19a8271e829d96265d150a
+c555e0140e1c2ff70b78edcdc6714c02
+c568cd2cb065319f1bece6a6db3fbe21
+c56cfbc2d4614222615aa41c8e059c2a
+c571adcac4f87f9749ab6373f086c982
+c577a6fab0f0307c520f93f60b6dc9d5
+c57a71876f425e95b5a7d1e527609f20
+c57cf893b9980f28e06a24602526e3ad
+c57de47c4d8a1cedf631ab2af8e0959f
+c57ecfe583482b138d34d8bea92e58ed
+c582e6886fc7f35f689218a9dc01f3f6
+c5837ba6b086e93a193c582a8de32e5b
+c586a56b0cf889ce869a197c3f406653
+c58d3bdf1f9173fcd3a4fca1d5c69679
+c58ff3a71814fa00becee9115024d6dd
+c5913891a163b9944264997f6bcdafe7
+c59e524a61b1efaa09ea2f7498517b17
+c59e92f4f3ba8284c18e0e3439562318
+c5ab653a92cb740488a85cce307e3fb3
+c5ae2315bd9876c4f5a4744915e34406
+c5b3e3d971c034e5a82a93a4027ad0e9
+c5bc9e39a7c421affa6b5146079a451f
+c5c008e092336f442cd651a52a3f82aa
+c5c0c7a31049d18114c8df41f699963d
+c5c192782edd08706660a2772d7a091d
+c5c47722b9b06104afb404584e46f4fb
+c5c61059b70f6a98284656b7c31a6e17
+c5ca8e6932ac15a78853f2dc836bb7f6
+c5ca9127c8c727bd36699e168f2e9f7f
+c5cb161f5d934899ecf8fa5c891d3b96
+c5cb20c1dd2d4d5ed1230d86bc281dfa
+c5cbc904c7d25ebf062a9b039d3b6ef9
+c5d563d08a2ce09e2001d99a21c29fc9
+c5d5f3f986892ac5acb27cc2a4e673aa
+c5d938eadc54a5c3d72e25d3a66674c8
+c5daed7ee246bd766317aa9f0bc943b0
+c5e039c9e20862b8345dc7fdebb7febf
+c5e11e5a46c84c0dec6bf8f5cef3c2c6
+c5e3c27f4e673aeb6e688fb0d9887950
+c5e94b735b177c02f5bbc87083d21501
+c5ebcc2395eb661ff434c266ac8e7b08
+c5ed0e8faf4501488fd4db7782a444dc
+c5edac9e6c51ff0a49c007b3624234db
+c5ee0e951ce5c40b4cfb194626fec6bd
+c5f1ee5012fd9c669184abf35a2f6291
+c5ffb5e202698c909877dc749596107f
+c5fff2fc0e902395c50dee991639ded7
+c6005b6496f67e1f8c11e4b495c06a78
+c607232ce5757e87e00ded8014cfbdde
+c608fecd5c00bef5ad0d1dcaa549fcc9
+c60a7559ad4e1f4ec715a972902df8ac
+c60cf58c546bb5d7ea53e335ed99b1ed
+c60d5b54894272925e013177814aa48e
+c613cc8792d23370fe8a78dad8fc4cb4
+c6178eaf4b21bd773ee8b6218c27b7c6
+c618597b4bb58d56693889c2101c2dc9
+c618b8d973cda2b5f42944f4aa0581e7
+c61f477641ac977a949139b5edb22e6d
+c6282bec2477c342af3f8e6032e57b87
+c62a15086143d5df014198423ee4ebda
+c635e7b116e9a604f3027d487d635702
+c63d18482f9403274246751e1d5b6119
+c63fb774d67ceda3482ba7da8f20a1db
+c643279cfded35d12c542c7e8150fc12
+c6447bfa13f859288c154ffce53dd7fe
+c64577b9db3f2006b9dd80a887ca31d9
+c6519fbc9b68223f62e85caad4fb05ce
+c65ce6a4cb0a227f1b1b59f8854c4462
+c65e4248e74664ebf9bee70505d4e6a0
+c65ed1a8e8452f99857e6aa738634855
+c65f55a2661f61cdad217d04f03d159b
+c66f73449f8215d2fce2b3ed931a341f
+c66fad6c7df648d5969fba0c4f02f105
+c6700b0525f6c2ee426b1d8974107705
+c67011898b7c4267b082dd4a8d85238f
+c67259bb9274fa237b8136f7c66673ef
+c67314f9b73a7f20646ec829f9e84792
+c6780e75adb1864e5bc2e3452fb982ba
+c67bd9fea40729a11f31c91ae49fa156
+c68c436a8a26dae11f8c67df7b4dbda6
+c68d1254eb4a2180788975f1bd39a281
+c68da41d74d521055b7afd50d87ff83f
+c6961b3a24b3f5216ece4370978cff07
+c699da7c803f61398439ddde77a4461d
+c69dd3c6a39beab559c172dacad7448b
+c69ed5e1adb7973404d431f4b4a5bb37
+c6a09c694b9d095e7aa01a4fcf172e9b
+c6a399490f33775c8cc6635a8540dfb1
+c6a3c6d4c945d1dc9ec6b5d28c57c109
+c6ab0340832b0a4bc07704616c2ba3b8
+c6af7ee85a28cc2f91747411c0126fbe
+c6b1001b750a6b10825f4494d3c4678a
+c6b2806de12a5a66cc5376e988ca0dab
+c6b805984bfdeafb23cfbeade9221aef
+c6b80c408f5bc8ee061e7736d44324cb
+c6b931563a7dc6131428970870d38da7
+c6bc426932f5e0cf2b7ed0393445f965
+c6bf404bf3479edffd886bccb71d26ec
+c6c25242985cb35843d8ade137334c07
+c6c427426c299205f09d9747ec190332
+c6c4a50375301f62b1af2f331d5875f3
+c6c672c6a78e6d751923c4f178ce14e6
+c6cb0fa1a72ba56a5a4c0efe27f989ec
+c6cb6b20ce632430ae30abf79ea21e9f
+c6cc90aff41bb2a594381be913878249
+c6ce252b2be297db0bdb6b1d9ae9627d
+c6cf0be571ef4de4a55b0ec4b01cea23
+c6cfe9be6835d5ebcc788bf4862b7a9d
+c6d454a489e61d8c3b480696cc770728
+c6d4924cb82d81482e951ff092faf570
+c6da0de9ef3ecd42437b0e0362e51a1e
+c6dbc40140a5c96ff6d985a39e2db80c
+c6e4b1eb0fbd72679fb91aa903ec0c9d
+c6e5a88467e02a2e741728ebe16552fb
+c6e64050119f1194ca921cb472d4470e
+c6e66b37db918ba3c37ed09d37018d8d
+c6e7622ed684d877142be34dc71bb4d1
+c6ec18e06c40d8443c1dc5c713fbe2b9
+c6ecf28956e3124002b4e6a9a81344aa
+c6ed02e7e14293bf59b0bcff341955d6
+c6f0fe7a4d2f6d4b8c74eea5a46d9789
+c6f3bfc245ba29dcf7baf2a59cbb63cc
+c6fb552e975e5daa35d990d47bd20c8e
+c6fcea0b382e244005f8cfbc10800be7
+c6fcee28a929228ece28aa713a941f50
+c6fd5a8ccae7ec96429b3e86585d245c
+c6fe0da6442ca3160f1ddb3af3d0e1c0
+c6fecf38b2f561d33cf52b15fdc48042
+c6ff65e29f53716e8ea5df86649562e5
+c702a340b413e9eed175ed2245642cc7
+c70535372cf63f08f54b5044fc20db14
+c7062774b23a52752e51fa5df78cdf8d
+c70dbe1f7c68539607ea9f81d3206ce7
+c70edf444f9e92d215e4f7964994a1ac
+c71114f65bc1f06b718bfebdc778ed60
+c715d458ee41b8f8938e139441dbdf09
+c715eb673545fc4044bf9411ab43a0ba
+c71f6d315cf35f16ff1cdf16cf0405c3
+c722d71c2bc0447ad5221267938c0c82
+c7284355a3d4519cbb46679fb332817e
+c72ac3d0771281a11b3c12540f35ce48
+c72f177a46a275922a451e3fc21d3c85
+c7322ad004135c3a9121873e6b6a1c45
+c73ad8fd2b7799542ee21fddf1e897d9
+c73dccd08a572489bdc10819f5bb5dc7
+c7436143e7fed0edf980f645319e2010
+c74fb91607a7efd2bd77f2d8f209be23
+c751ca2c54aae9a01f5dfcc338691cb5
+c7539595afcd8da6c86b1297f5457c9d
+c753bee58a25656ec257e8eca5fd9b86
+c75687951ace005462de49fa4c2609ff
+c7584430278bcbddec0f086fb0bb7443
+c75b710431f2ac27a805b99e1c9412e5
+c75ceb97bc08bcbb71f1f028d873fee1
+c75d479638dace5bebd675a76cdc5ce4
+c7642ade620357d1933d84f25ca2c3b0
+c767e6095c17dabcdc0e5efb7ad9a750
+c76d2d4b4d719a6a21a923a2ab8c535c
+c7735f072f8f835c3f2b2626bb830ed1
+c785a2042b83757f917d7cdc3a4f472d
+c78c9221587fe71c1ea20474e0978c75
+c7921ea53e58b7944ca527a32712b529
+c7a027285c1be04cb67b9da83c3c495f
+c7a77340680547b3f9f3b3b466f56a1d
+c7ac03cce0e1436dc6700c13fbaeb1e8
+c7b0b1f2c4b23a0216340ab3fd9af285
+c7b104fd4659def30399dbcb210af942
+c7b4de1a6f8711ad30c694dd70fc2c8d
+c7b63d32d804a071af1b0a836f590232
+c7b6bb2110ec7534530b252440894e8f
+c7bc2bd51ef03d1b1044447d54275a40
+c7c19c54b109d1a13087318dcf0051f4
+c7c4735459531a21f4d8b1f6cd6de919
+c7c8f4c391aa75635f4992751ac13af8
+c7d34d606050d97d251efeb67ced32af
+c7d3c4c00dcf88ca9b1e47e50ddd9010
+c7d568f4e87e2e0b71381c24d47ba0a4
+c7dce23b322623c9221c9f66c43db118
+c7deafd4c683fdd9ab87e061cb4dd08b
+c7e0231e3ff6a3f925fd519dcf73774f
+c7e45738d36157795b31019fa962e7fa
+c7e4668a49a72f16fd2b8863d3fd5a82
+c7e7cdb1ba82eda7e4384d916c571bed
+c7e89f7a3efc8af530845ce85261b51d
+c7ebc1e79a02326a1d60dd1e7ee636ac
+c7ee2c85840800790d7e500c4e2c5373
+c7fdc0a95c04d600accba09aae2e3a9d
+c8010f1f6d8912fecba617d37e6cb05d
+c804baeb5bc541ba5d328ae35f0a8125
+c8086046016f5225055a338281842d7b
+c809ac84e66228c260f360b5c3f42254
+c80ce3bc1c73bdcddf3706a468a7857e
+c8123d7fb18c8eaa9ed80ee37b20c238
+c815808fbb580466b395ba0cc11c78e8
+c820d91c8581f1902523160dd93e3a85
+c826140a3f04aee9a2178d5b669b6605
+c82702d9d59fffa1948d946280fb505c
+c82a2d189f943836c27ca9c5fe29e715
+c82dd07b5259a426e7296e7e9c63021a
+c8308e3876c92a5f112d5392bc35c81a
+c8328cce4f6cb7a5ab35d7aa71cab7b3
+c83293c7d6e3349799309d06a2d41c79
+c834ccee463cc2db98ee91a8e17e33f5
+c835248adb6b18e34a45770d864713f8
+c83649761cbf1c7f1bb1fabf7b5820b6
+c837bc91c77865c507e0a2c04bb4daf9
+c83904ba3e1b991043c3a49a6a75c478
+c8396930f6930c06594ae6ea006f7910
+c83a426d1ef4b068c61a1c07f71b56d7
+c83efd37c2b08d44e2134110ba3f00ec
+c83f9e1923c2d1c0d02108fe99889fa4
+c849a962f01fc1a3be1b81d30c2060b5
+c84b6c14ddd87407aff3de3a1885d89e
+c84c8adddaad155b3ddcbe311a6220c3
+c8501eef9bfef5698f0fe95054a83856
+c8521dfb6ee85de25044c97a0fdcfafd
+c852301e9b2483596139cb808929f0a4
+c85244138d9cb1f20231315c55759eea
+c85403125c4b4cbfa64d97eec70328b6
+c85a70c1840dcd26833090419bdf1490
+c85c5273c96bde994f5e70aaf20f1882
+c85e133534dd9a9d56d14df585cbfbcf
+c85ebd3a1e41bc969f47a0d13334da0f
+c8602ba7337d3ad5c0407e2b41ccd606
+c86155cbd365aa21d5877f81fb802fd4
+c86545926bbe3bc86eda43a3e0400bac
+c8654abd70d5e015e4f74cc26ac063b1
+c86b1f9ce91b56cc3501fa5312a2c91e
+c872988e0ce721e87fe5c8cd8309e4b3
+c87976a05e613d8682059b7d09b8a5f2
+c87b35351acb00b81d44cd2fd8212bda
+c87e04d4198aa2fee677f8d01f5b919c
+c8826f70c0786fa77eafc12a10d76a03
+c88301c7056f1754d84503292a6b8ed6
+c884da28f491ca078e976901f9b7a327
+c888330662eb5f009394c1ecfdc05e98
+c88ade9a2ecc943bde56d61abe1cc673
+c88b09976ba23fd3a5bcbb29280f6a12
+c88bb0264adb0de47044205e155fec1d
+c8904b09657cb77927e72a8429eb110d
+c89147644e10d1e58bd4b476b01c8b8d
+c892b8dae0a858465fa2b1ebe101647c
+c8934796dfe1ddc735a5ff2a56477833
+c8948640f17a05d1d31dc2650dab72d3
+c895b7e2f6c0bb9111c1dee0218ccc79
+c8ab6b2eb580ad63595d8962a01b1da3
+c8ac24963996be2f6ac30c4ff031a543
+c8b0f12e343b3b0ae763153527a0220d
+c8b4b8e5b7dffba06ea08095a51e4044
+c8b6542d2ce75d2488b777580d348bb9
+c8bbc8bdb61a90405210da982bb41b8e
+c8bc96eb5c25170768b0c5036201c1c7
+c8bdfae6b3d263f59c2838bd1e776dc2
+c8bea55512fe02a005793abfe125ba21
+c8beea9df289fa1774975ebf397bdce7
+c8c1d895ae828bb4dbc90b8eec29850a
+c8c68d6211430475a7212e24ad75edc5
+c8c97bea3475b2eb9988ce45c427ff8d
+c8c9f12a7bc1fc700a14ea5e63980373
+c8ce94cf567ebb2767654345c6f0b68e
+c8cf23642bb68acdebf69f7b7818c416
+c8d42e1f22597bb05ef3390f89c94680
+c8d520d44a57c74c3dd2ee4d6769c6ab
+c8dd528c1e2994c710f69c6741b76df3
+c8dfaee82caf34bc38f0f4f6ff3df921
+c8e11d85d4fdb2fceb7c9ffc153800ad
+c8e31531598feed925b6355bad681cbb
+c8e523305bccc2da581d7f17798fd55a
+c8e7420c6a217fe0afa11009174a0aaf
+c8f812e1700a1e978943848b3ded3990
+c8fb69f613e8708c68d3304afec8c028
+c901fc07f7b1036de6bc7103c29f2aa2
+c903d4418bb97cd067d64b47f41d2b3e
+c90638c4bfd289ed524b20d2f0e05185
+c90b28604e8d580529dff0cd7032df98
+c90d8c4554d24db29d7d4374832008e9
+c90f62a8ed4240bcf1db3c900f1a59a2
+c9137cecef25a7bc66c2684d52839389
+c915acf60cdc64e894f1b7de044f1364
+c918c017282b70c1a95fe78f91e3f05b
+c91b5ac998b8cabd048a9e36afde00d1
+c91de6d658d6d36391c00275d8ccbca4
+c91e040bf3306204bcdd84cdf62f5c9d
+c920c149b18d940b09fe26805bf56180
+c92a5a113cc9729a620ab84a375a8a4a
+c92aa641c2942b967523b2699159fc2d
+c930a267e99e4c905a7eac9d45202d7b
+c93254fe2f05f0c7e90471a48b447b81
+c9371a60155a352ce4931fd252f8249a
+c938d8c56b4b4bb80c3ff6209d1d3cf6
+c93d6e4b1a2814434ec1dbe893cb4361
+c9409d7e3053ae782b65bf2d83d44e52
+c940fd9294e4d36243cc992e169d6cab
+c943aaa999a395a962ddb811e9fac405
+c944817c9eec77ab2dc24d68d22f06cf
+c9471c7e47c80b13f71cc3966a6f884c
+c94af86bb168828cd2dcc4f93a606461
+c94b5aa89573d2ad23de547423c5531f
+c94d830daad392e66aec2c82542a3152
+c94e92c2392e59e738dd4ed37d93cec9
+c955c8c4f93f27bb8433ae7350084674
+c958039f4ee6c329482ad42045105401
+c9585d1d5c7a5c87747fc8f33adc06b9
+c95b6d1dce3fef06c8322ca27f3e841b
+c96092c99bbf7c813321e5411b31dbd4
+c966291389117ab8a86117cec9871bce
+c966f607d9d9be581e6ab938956f4c36
+c96a727124d1ee4aba5f76934b655cac
+c9706ae819a2c6728c4ea4343c0c4dbb
+c97118cf064e3fe71c491f7936f098c3
+c9789314bd1bd6a0edd39f9155ea881c
+c97c0606aeb848aca9d2063ee702c6c0
+c97eb85a03d93ce1d4a1493d6cc4c73e
+c97ee1594524cafcb26347f9e7dd4912
+c980388e23b789d7776a94b93e42f472
+c9848145f2d1b74ed7fc25535dccdcca
+c9862da6dae93b5ec0e6343d7a7f9ca0
+c98c8898944d0af968f7ee15114567cd
+c9954314c0a4af38dd26a0d3c4d30543
+c99d64fcc7729d96d1d989f1eff9e437
+c99fec6b22c7f6e8c35e14138cba7694
+c9a65923983f38dea3aed96f15da702e
+c9a83cc7ef35fa9dc69d4d7bdee57eb9
+c9a9e7353d2eb74dd657402946cb62f0
+c9ae773056f7d370502fddfa70488977
+c9bd62feed525fd2cad60dfcd5bb934b
+c9c4575da1d1db0c8e5e38a3bde2a3e0
+c9c750a36f45e640eb05b2e1d65c01ba
+c9cb06caf8ee9cb407b9db57679b2703
+c9d3892d859243b65217fe5924ee3c0c
+c9decaa94f49b65efde0bfabec4ef032
+c9e0c78bf82650a462055f7494e70950
+c9e479668f5783a53e0de5b36a0484e4
+c9eeeed8b519da2b96f6f7674af9a9e8
+c9f5db8bc29714905358df69fae7969d
+c9f601ac68ec6b740f0480587b1acb6d
+ca015d4162dd74eb4f7970a421dedaff
+ca0778635184bea008ed22b32a37e9dd
+ca09e4dad6e596254546de26f20e1fca
+ca0bf022fae68e60549b97a4943051a3
+ca1495863fd5c4483090e4ff8dabaa65
+ca156c46c2babdb3bb0dc5463f62e4ac
+ca190238d2069686066b55b8323b3a08
+ca19ce33742928f9a98decb275efc862
+ca1af0bac10f552724a0ffbd8fdb583c
+ca1e4be1c0117fe91d1e7b4cbd165ec5
+ca2094810ade8065e8ff3a5ed7aabdee
+ca2154575473b239d5762a9dd8bca93b
+ca2b9e4641b24e259ec28c22192cedb3
+ca2c97c052eb63b302d60c4cdbe1b771
+ca2d4315447ad01d7af21e6e77dbebb7
+ca2e6dd2fce8198e7f542fe34d6a64af
+ca30e970eb6d9b8cf4e0df36f7d98f60
+ca31ced64e07d45538084f78593675ed
+ca3287314dc2e6b2ab20af62dd24cce7
+ca360e16b9a34d4e67cf5c15cfa019c8
+ca368c4d5955fc068698f5ce8a1b65e1
+ca4bb2df5b40a894a21e81526ac147f1
+ca517eb11e51ed4f1a3987785673f153
+ca5477e421ce1bdca10c7c533effb796
+ca56725304d72128609e730ad4ba579f
+ca56ae7990fb049e3439ec432596f49d
+ca5afc076122c1c5226dba9f3abb5757
+ca5ce677031a6c8941677dd0329a6f72
+ca5eda939ba4ff11afee9348bd8a4fe1
+ca63bd4a860d4341e0a0fc01093701b1
+ca69ac52f916abca680c5ed1157188bd
+ca6c432527d0e08a09d2486212be6be5
+ca6c7018721faf339e39f064d5f153a5
+ca6d62ea11f510fe8ba7bf4a918ee2b8
+ca72e3a911b80c1293e33153b3e1d9d2
+ca7352bc912e43ab83080131dad9fee8
+ca73a7a2b21cfd0acf0226ca3543a942
+ca74e273fac6008b049087184be71692
+ca74f3dc84e82d3a075a076e4eafda42
+ca77ac1976311f268779d82bc631a748
+ca77d685e42cd5c39f3fd63a42cd996d
+ca7e04dbc311f40706e190a9dd38b9d4
+ca7f90e6fea01789c9236dafb0bf70aa
+ca8bf5ed05247d969c89fdc295664f9d
+ca8d56bcc7e34c01ab47adbdae692195
+ca8e542b92488d1bf0e7f61bf6e6de26
+ca91188bb014900da3c8910a8832beff
+ca94cc6d453ed0b2d072fee031868d39
+caa1f4818591218fca6ee290a2117eb6
+caa26e651942dbae56bc1cb25fb64826
+caa3d85513f5f23f32e4b049dc9753fa
+caa5eb83c86fc7d77e4d6cdd64333c0e
+caa72009577e8f46133720b89daf817b
+caac5af1e2125dec85e3f481a4a50ecc
+caaeafecb563ee0e7847bc52537fbe86
+caaefe8714062df108172a8e16531053
+cab369d3667ccd70b1269f42a4f2bfba
+cab4684f3297f516f5858f0cb2f1a94d
+cab95d9f2b4f3dfa740ef9bd4ef35f9a
+cac11864024754fd487eab172bfd1be7
+cac7bcb09a2f531e71db07f658fcc220
+caca356983925c20a7f1182d8c5bbb56
+cacb31845dc328fcdc12aedaed98ee57
+cacbfa9861bc169c12100837b60e4a22
+cad76650641ef43923d50e791f696de3
+cada2934366802dda0bb05f45ac31a2f
+cadfaf3f77990b6f998930a728ebf1d7
+cae328296e8450f8a2b621b212bb24e7
+caeb9e84ee355bf3254903b332df6413
+caf162834c040c0ce4462b28407bf55d
+caf1ad6b4f75ea69be1f8ec48da57d5a
+caf3dacb3722737b50dd0a3a6aca01e9
+caf45317a4ea42221441746ede18b4a6
+cafe15d7cfa5514fd6e2e11200a083a0
+cb04359b6168da8562ab2008cee999f8
+cb078facace9cf193f2232b47023261c
+cb0c3bf0d4eb67a382f509b65bdf985d
+cb1085b0dde7cdb08d62f2c96257a0e2
+cb1808d59b2dfcac84b69e8fb41f2768
+cb19dc4cecedcc8292a558aa1e267ca6
+cb1a32bd293141d04e7d146406d3ee2e
+cb1fb0701bd0d034f56d3aa3e960f8fa
+cb258ff01589a93c190a66f537592c07
+cb27b6ea49ce86a9a62e631632a5b983
+cb27d80af4943092d63e0ac0175b3129
+cb29ba95e7bbf8d010511beb91b3212c
+cb2edf61ee2ec82c9588a77574b167a0
+cb2f7c3653cfd92f75cb72fccfd25a24
+cb318531b18d23e5d5627eb841ad5552
+cb332a11f7fed43f06b3eb006580f5ae
+cb3f91408bcf36b69fb91cffae517997
+cb454eed48315bf4051d2043f5308d78
+cb4ca49d868e4df000c65290588c9363
+cb4d553068de3a4fcbb4eca6ec4f2ccc
+cb4d85a00552c10686c6a333502cee25
+cb55ef81f8c685a6f50f821f3e7462e7
+cb560091ac67ae3374038e98293d8bf4
+cb5a59ddf499e30ecf8ef398696fa4f4
+cb5a64fb3fefccaf0440bcad851a1790
+cb5bff56c5afacb033644fd2dfc1d935
+cb5d8079b6547c51b68f1d70453933e3
+cb5e0d49e41601fd0db7027ee8fb60a5
+cb6621e2284ea6c6d2fb783248d43d66
+cb6a0046837e7eac42f5c085382c0b8c
+cb71cce2c6007925111bcd1d97630520
+cb7314cf34bc73e19f52a34c06997059
+cb7475a883d17e1d52e39769fc8897b3
+cb7cb819b971c81380255caa55a3e3d4
+cb832efc2f35045f8291539cac901cfe
+cb8b0ba75036eb81ea91fbd518c29ba9
+cb8b174fd84b0bba429d6ea876546a10
+cb8f4c85ad8071e5b74062776d57819c
+cb9269959a6eb3fa6aa9844a886e5c74
+cb93c26e070d7bb72cb6393e56821019
+cb966a10377cb4deac765ad75279f90c
+cb97c1d2e3072898bcbd2360249a5b04
+cb98eb36507fb5b76382a132466b3c78
+cb993f1439dac25119906217071d9d6f
+cb9d26750a35b47639d6aa2f2df27cc8
+cba35b11ed5c9052076f4d5f2d1e3806
+cbbd2b6ffaa57dd6b2dbe5b364ec3ebf
+cbc20591c0e6a3ab97b8921821a8d0c7
+cbc4071a1461a6075638ad0aa8a16b47
+cbc7a15bdac22c8bc2e5f2544c7a33eb
+cbc8d06939bd0dba702d2103fcd255aa
+cbcd93d86aed18c394771e5805278da5
+cbd2cf93b33afb7d6c374c6fcae20fce
+cbd4493806d133055d74b4bc23f163df
+cbd682859b400a2fb46ee83d41538f82
+cbde8a8677860e0f920198d593a59917
+cbdff875ffbbc2214ae6cd9b254b55ff
+cbe0b69393d696c20de52c91f2335653
+cbe3c1175b0f224e2cf8ff17fc97dc50
+cbe4bb839d7abeee70144c43c7190234
+cbe5c84b6b811a8ef4c6b2bb385037e1
+cbe65628977654514b8b0f09c60b96a0
+cbe99bae8dc31db33e874d18e9484bbf
+cbeaaf07be368a0b236eecfea5ce12b9
+cbf2154d7c257871739133a33178b564
+cbf2e72432a816d26c8d0999f20f2930
+cbf6a0c5c41223852213036416632dcb
+cbf90f72b032a6d680f844ecd6df5274
+cbf93c2a7bfcd430c22bb93010b23fe1
+cbfa37241495279b52fe64fb6d8aa8d8
+cbfca1587ba1571bde42c843a7b8957a
+cc02b0100f7a63dfbcce6c09b2e11742
+cc0c600be175b105a15fb741c3b0926a
+cc191afd5ede7ca8b491c13fc96729fb
+cc198d528f67b5b006e4728fdd9ca0fc
+cc23f86d37cb41531d636dcc5f304d4b
+cc244bbb63ed84bd484425f23bf791d2
+cc283ba548942bb55af3f19157d84065
+cc287ebc8a179bdb429a24c12a1accb3
+cc2a6a4e475587fe0db859bd774d55a7
+cc3470a972e67e8dc382259a3d05fd53
+cc369366e2adbc41de87d4d59f11208d
+cc37c13b0d984602f2699d3f16e864ae
+cc37d0d168eab7c032c1ca76e36c77c6
+cc42319c0d2b8c2794e9068f3aea9e6b
+cc440895dc3762cd41792886632a246d
+cc4abb0a32f1d054a122d5c7605b653b
+cc4cdbd542e9aa43fe23c4586615deac
+cc524a87c0a3bb64786346972dbcedc2
+cc52c3e0fa40bf9902fbac333b5d960e
+cc57a3170659d54bd21077294675baf3
+cc5aab7442f5478b548e5165e0d0480b
+cc641cd4a773ad9f4cffb5228f1a85f5
+cc6584f54f0d2ba99cee06f4ccd26211
+cc6be062768e334c846e051b4ebf5776
+cc6e76b559fb864675993b7cd1f2a1d4
+cc780461ea336cefaebb576c3d8c0c78
+cc7909eea2689ad5d2f6f8fa446bcca5
+cc7bfa70c766782f3880da9f99e84aba
+cc7e647f85abb9fb854ed7001637bb6a
+cc809167f76c6d3409d508d7714010b7
+cc882ab91d7f1f86a17fe214bff8943f
+cc89cfec63dfabf2d030da7572ea1dc7
+cc8b808b7a4c0d48fc236d5ee23aeb86
+cc96e4ddbd096a4af52480ea620505af
+cc9914560f6197b696a08c827fd84edd
+cca02436f1954b4097709db575b7606d
+cca798ad16b61787167eb9d365d797eb
+cca79cc5ea29814e98e55837b315d788
+ccaca57bc58df4fec1400e5b9eb43c47
+ccad02fbbf4def4dff2a2c3c612891f4
+ccbb3de62bfccfe4c57e12abbc9bb786
+ccd20e7eb53b92615139c3f94da5430f
+ccd49a5901eafec768b8c12634011b11
+ccd6832b08b8812229715f68dc4e0437
+ccda38420a76c7fad49da5bfffb9f98c
+ccde7328a7d300d0347a6cfc96319428
+cce089abba6eca589534deb1e630ee0b
+cce76f140aa971aa67257d83b24579bd
+cce857265ca25c8c514302eae85cfd3b
+ccee74bd72f7d75c3b0f9d339a4be584
+ccf43be15ae460a5e981c6cc24b568d5
+cd01965c341c41aac6b697c32b7d312d
+cd0d3609f5f761f7dec3d4a0750659bb
+cd15b2614ce031e03c8e4e709b7490e1
+cd16edd9dbe49cbc57fc1b1fe8b8c19a
+cd29c2ef5f2d76f7bd91ef9865cf7f56
+cd2c36e816e8bc64a312c2e5e26ff9a3
+cd36ef0388c59bbef1cb3cfe196d48d7
+cd39800d7d2eef52fbc4fc6b8721a8ef
+cd3c6e9549e14e55807e34dba2d64583
+cd3d636d3a5dba56eb8be46ab8f3aeec
+cd41c9c98015c51091aa1c47e5e008dc
+cd43e0c0212972d5a5533e98d68c1acb
+cd56d1d950dad8ec04a5dcba9d761ac4
+cd586e2dc3fc671f8908e90b9e4728b4
+cd5c7315eb3e4367a943e782b958fd2d
+cd614bad987d92ea4dbaf2f66e11af62
+cd628edf9b1271b1f4de15554ce540a2
+cd65ce1dd745932c97c48fa429698302
+cd65ec9a85993d154696467b2a166f48
+cd6bccdef4046defc710486d329aab96
+cd6d7e155397db5263932aa4f597706a
+cd6f87639cc14c6abd00f307792fa0b9
+cd71f10f69022ecde8f1cdf631e93d66
+cd74991d6af462004ae65a91447b2f6a
+cd7c5a12fc6819693e17f5b662abf4b0
+cd7d24dddae3ba076e5de7527b948f9c
+cd92657c95b7ca8e1d27e627a663f4e5
+cd93d1b0c245b62a9cf7a765cc04934c
+cd965f10e5bf7bd069d3130a98db14c1
+cd98f0ae17e5018b0dccb6ed529836c1
+cda25f28df36e733bcdf6c88bfce3b1a
+cda3f165fc624dc19c6aa8c518e03ed8
+cda8b541f1afe3efc76d3c2ef7323d65
+cda93bb3065213b87d60ad065c5e42a1
+cdae9a5d536184d6346a79fc753efa1e
+cdb3d87a29ca9f284ff3972eda189d21
+cdbb3c222e051f6b00bcfa7fc64eb155
+cdc25c01cfd5d362dfb2791a8d156503
+cdd4103e1ca488bed5a59f4ab18da23a
+cde24efb7826c804b730f82c12770d89
+cde5767c83b73242c1292d5cc2d97096
+cde8dbf31332b79760d17cfe15107863
+cdef951b269e652cb71406c5aa33e939
+cdefa6124dfcf90ecbe9a2be746d4f7c
+cdf80266bece467aba3c378a06a9fc7f
+cdfaa3cb36f58651c81e44c87271271d
+ce155517510f43d7904b75d1a99dbb43
+ce185abb8d4211a4f6098596d3593a68
+ce1d8d31c94aa37dabe8e15236716f5b
+ce1f16a8c364f5e6ddb58290e4b30ac6
+ce2e668aeaf03ed4caf0662595d41eed
+ce307ea56edebd754e1c00b9f4d2c175
+ce339cf109d2408eb074267329d4e336
+ce34e579c37e7afedd17d326b8fe5ac6
+ce361e3fd4d2c04530e39d5a44f9d823
+ce3c0f1c135a81faf17b6a3a6a0b4ee2
+ce49cee67d3fa5050b7439242e0c39c0
+ce4ca8bd41d50c4db6cad8c7c75a51f8
+ce4cc12d319e7b3675495eb63208df31
+ce5354ff079a64b85076d6de263a5a92
+ce53f62ba78fab4005780cb29b85d5c8
+ce55116425d6a5e7946b9c62421ad49e
+ce59b2f21985eba4ca4135ebfa5d3da9
+ce5c929460b066e849651e9365781225
+ce65d6605c1171d2e66cdec017b18226
+ce667431c4d8d69cfb8a6a9fe806af75
+ce66b7939459a29edcedbd547ae32471
+ce6994bed569cba6e1a15acae4b10e97
+ce753a955aa8902276da9b5563ce57fb
+ce7770801757389f8049ce2e42ba8c39
+ce7944898cb8191507a6f5563373abc2
+ce7ff5160ef01648e82988d78b2d815d
+ce803b30961ff2b8b96651918bb2241b
+ce840ee90f1ce760b801ca3d99a86794
+ce85638abe130967da0d3fbe12754715
+ce860c2356a2c9215f456aaf6a43811d
+ce86acbe833ca40a28b4db58eb8ad1b2
+ce8f51ed1a082d3428760f14379d179d
+ce8f71463b78742d0bc854e130acedb4
+ce96e53e72d4f5aa98517a57c042d9a9
+ce9f1edd56b89d15dfb1a826e7bea11d
+ceb97b3b651567d2eaabcc9a28f45caf
+cec141212789f7c449a84a82fe7402e8
+cec7280bc9c0c76b2793b0752b308552
+ceca246651679761c6a631fffca56a7a
+ced0692e69f651b7eda7240fb703b965
+ced1f19c1e9aa471e60eb2757bad7db2
+ced37f9c5bb9ee20dca2cce1cb83e52d
+cedb093b2b4c3757d3994d541fb7f0b8
+ceddcdf9245fd235d2887ca1d54a77e5
+ceddf9bf53971397c64a29553933c256
+cee40efc9451f3104bb894b683b9112c
+cee54c6989ce27445dfb410d4aff7e52
+cee5649c61e6a3b384a6adc91a6a0a0d
+ceeca37380c88670154458b12b138f40
+ceef58856688e93ceff24ce81e9107d4
+cef2888c9177d60cecfd607ac2273265
+cef58ff59038992cf3ee95903466dcf4
+cef9d360cf5eee09944305c0057af288
+cefd7cbc7938a390747baab664014c17
+ceff39b070918fdf7f5cfdda2fc8ac75
+ceff55fecbf926bedb8b639e301eb27e
+ceff96a33262daa063585524b60c3b55
+cf00acfecbee8794d3db0759eb9e16e7
+cf0f728d6dc8412c314567e9b783822c
+cf11c18cc7f4c7559806afe4adcbb065
+cf12d0d807080a99c9bff90075ff02e2
+cf197d4efbaec721a79718b77e0892e3
+cf1b5e4788946255298f0d63007c1788
+cf1fd8a636ebb34e0bb2c39273b2264d
+cf2dfff9499b79ef1ca832635e1948b5
+cf30628ff8390b998561dbd3e7b42e97
+cf3d889e493e2339a08b0300acf93ff1
+cf448487423d7148fdf2355b08698e14
+cf4eafe187784b07782839b1c7b39a92
+cf51fe98f5d26cb01248a32cca41d747
+cf5b212c2891aab3bfc6f66047cf68e2
+cf5f157d38139708ee6034e3a9f24677
+cf607dda9f8ef33c99ac204a08cf47b1
+cf62be85cc30ef3a7f94e53d6a4764c7
+cf6b5f769ae6cf06dda78e1ec78cef1a
+cf6d6fc53b346753928d429c752c783b
+cf72667687674ddf429292d7a50b5e6a
+cf7352653b38b8893b3d3eed48543d49
+cf7416ef3d3702ab508d4d1e5321c91b
+cf7833ef2c25b790b9e3eca4e0d1ea96
+cf78d5a2927ca22ad3004c16f6b7174b
+cf824c1ac3317e3ae17f74634f94e17d
+cf90371b86d295a773a7253dfb73bc18
+cf96a5133a5baed24b47d9db5d0afae0
+cf9c2d6f360a8c2523a594f201642e71
+cfa6d043c7d090aedef9ced2e18c4999
+cfa84e9c1e12a4ec6f16b41ba05eee96
+cfaa95c985adeb14e83d298d513d7068
+cfad42504b8193ec2de85c1fa51e5be8
+cfb02f2f466af7f6c5b8cd2c7ca26f76
+cfb508d42949f56c1c2994e133977711
+cfbb8f24c5e339f0162690b83e614199
+cfc791a55049cf5b81450700092d2718
+cfc79fff40a21401db702a00c177d95f
+cfc8fe38e6774584886ac3e48f47768c
+cfccda6d4c42b6b90356c503d2139bf4
+cfd49db5f77b2a8c9661af173d371e69
+cfd6c34498a1f64cce5fa82dead2309b
+cfd706ae9092c045dcf46d88ad98c3bf
+cfde934bab75cfc713554212aaac2fa3
+cfdf67c396f50b8c59a10a86f69bf94a
+cfe2bb8060082b0011aad76d705c92e7
+cfe8483dc66457b4bc87a19802a1bd80
+cfecc31e35d34e0d4ead71d94f554be7
+cfece79120c6ec56c7cd7d5778adf5bf
+cfecf7d9c8d20283a73cdc351659b5c6
+d00137aba9c23f66cec0fc38ec014da5
+d003d1cbf255e1aa7bf48e2412eedfc0
+d00785fb08b7c5d94fcc27b062a09357
+d00a939b9f028f67b111dca6ba80ae13
+d01dd26a60e85260c2ddfb2e8605c3b7
+d01e5c6eac0184d1dfc76e3010203436
+d01f867df34510993478a44fa3176f52
+d0283da776fe5aa2739f5018ce9b3c89
+d030ea5e6d0951e99bf4ece9b955891f
+d032384af848ca4e9f394b7d7c857ad1
+d037e549bb8a062570c6e923a32d929e
+d03ae6100ff3e2a39c71748b1ace1b54
+d04e6ca02c9886f3d689e380ffeec1d2
+d058343eb61f8d449c30e50df70284ab
+d059fbd290ae160f185eb264d85164a8
+d05be6a6a40f0f0c2f42a0e85dcdaf16
+d067c266e331ec701ade4448516164e5
+d06c78eeb37c097e2ff550d37cdc25b8
+d06f69b8eb4b60fe9e63ef0ecf0f664b
+d0740a9f91d4892d13b00b8bca0b34c1
+d074ae709e0260bcbe9c44a3b57c9bee
+d08160abc8441d1e373215cbb51bb53d
+d09015f441acf7abc2c17b695376bfa6
+d092abf1a8d58e4bf5802dacd1873f6c
+d0931015acf8bf7879ba1041d89331e8
+d09579bd2cf696ef6b9fe17498a82d60
+d0957c1b0a4dc8d04ee50e4aeb89c51d
+d0960af6f1e13de9d633754b07b46b22
+d0b0874ccc0a89b85cfdd584095e8eb8
+d0be17aba6949ab7adf2cecfcc8e3ee4
+d0c24ce95ca2162a9758ef3fd035a036
+d0c5ac6a9b17767009ce2c032a5c2820
+d0c5c7e979cb8060d8c6eda47d8b8dad
+d0cb499a383d22090f7396ad1e911fd3
+d0cfe94a0f0af304be6f7b39439d5405
+d0d3552f13005fd7b8ba2238d2b322b3
+d0d71c7a84b01cad15c5be93cc200121
+d0da18badfe6bc8c13848bdb12467966
+d0da8eaf000cf7e1438f6b2f9df76015
+d0daa8e3ee3933f6d43b5fbef0611973
+d0dd3580e3ce09fad7e60e6acd156946
+d0decccfa57f61f3037d5b728ff8f335
+d0df72d9d60d3aecd6fe512e0a6fd5c2
+d0e2f6fa5f841c61206ca787c9e9bd16
+d0e50a2a945501d9f29d5e0b272f928c
+d0e7beab8ccdabbed3ec06f6fdef323a
+d0e8b80665ea8304b6b590100a6e594f
+d0e93348dc58560d895de517748d1611
+d0ebc795a9c2069f12b2ecfb8ac34e63
+d0f14ffa96c660732862fe607cb63aa5
+d0f7534c660655100294bc92f2dc7956
+d0fbe2e4af8bfd380529acb634874ec7
+d0fcc29f06d2f38639683aa0cdf47bc9
+d0fd3fa096ef037c4ba5fca9ad4abd2b
+d100b1ec9aaedf7de3f7378a0e909cb3
+d10219895df02a6d68229d447c553fa3
+d107e0056dc6919be184b595f3a94a8f
+d107f52f252e930e09826230dad6bc06
+d10a1e889baa17e210438d13b92a5141
+d10ade6eb79842f4e0210e1b793cb0c3
+d11148facada0a3bcad62394a3e332b6
+d11b5d60f0a871a90972f55f9e3bb1c1
+d11bbe2f51182a0d1b09e1e8c5aa91af
+d11d60596ccd2ee0273e9fe36fcc8fa6
+d11dd96714ed457d5158ae5d5028c999
+d11e802406367bed53a2bb020454981c
+d123a67dd337ec696e7d8d83675ed242
+d12445c0d56ae133787ee34300c90a27
+d1250b3df7085d771b2f011bfdb32b69
+d12733b3bc742549205da818a931f7c6
+d1296c1283cff880f35fb0b7af6528dc
+d1297dd1666cd79b001d00b4841ec3d3
+d12d74758844d42072951ff5476732aa
+d12fcaeccdc513f263e3cce81da97301
+d13be8c7fdc35c105075451e33d50bfc
+d14152f666bf2312e2ecc84bbe31e5bc
+d142e8aafe486b18cc447d7b4b893860
+d14317e2550cb009782939a00e5a5bb9
+d1436af2af979e7c8a54e306813cd030
+d143ac8411004532c0ef59981dcb8fd4
+d14a9f8cee2598cba68b50f92b8b3800
+d14d7b5fcd4c17d4142732f3abc074f9
+d15365e5e73390c6bf5ee7c11df8eb8b
+d158271ed488573fb8f8981c7443fdaf
+d161e93adfa6849cb9f7eb8f9cc8406c
+d16e76862fa4a27fa3dc8eb027596230
+d16f092af17c82dcb7336eea51e77b45
+d16f108ea6ff919f006086890ae311cd
+d17123b9af8c5bcbce761298405c239c
+d1718c9b6d02f9993f975a3e83dbe6fe
+d17472ef993241b391921b570daaf9ed
+d17be78f918542eead17ec230bafa20d
+d17df0f17a8b336a1a89c5a5e42abe80
+d181545ea04bbe68c6607f4b3392027c
+d186e4cb8802072434144577431f928b
+d187263fec0a79f32bb6d6781d01fa18
+d187ab22dbb23b5b2f02314926e7bea9
+d18b57cab9575869f11edcefb7a79c9c
+d18b7884ebf5f2e8d6b613810ac63c78
+d191b5666562f34ca0426227232ab809
+d1938041d3d67e1b0a83828f97ce125d
+d194b7ae743807caf452942c064bea18
+d19bbef3095df639d1ae8cd6fd7707d2
+d1a55fcb25e6ec1ef740a54357ecbdc1
+d1a6899af4a02591c2188ddc563a370d
+d1b40abd6bcd116f3c602325e5bf20b3
+d1b71bc1d9aa9721f493956beaa071b8
+d1ba9d738606caf4e8295b76ce162da8
+d1bae303bb06a6ba80b23464ca6634f9
+d1bae669be1fd867c025fd82b00f55a7
+d1bb8cc9908a4fd36b030d3e3a489a52
+d1bbff7fd94587d5a9e09e7eb7a69af1
+d1bdf33d619d6d46e81d1b01c721283b
+d1c354463ff151cee3d4059fcc3d1e29
+d1c798ab86b03849420f15fd23632d85
+d1c980c24af9e918585c91f6d494cee4
+d1cdfed97e4fbe8be001c04bc328f558
+d1d1b1dadcfd8844b4fe38126bf313cc
+d1d6017c330f806ee81e9201e0b2cc00
+d1d8e466abf5f3beafa196383fac806e
+d1d9597ffb45939261fc774c9c28e023
+d1da00a0a652dab36c7fb6d8ca17d01c
+d1da353f9c987d56279619566d7a69da
+d1da457ad304376a2fb761340a8b47b4
+d1de216b19ea7179911a5898b48b3fac
+d1df1a1c66b60b88cf26d46da9cf8a32
+d1e09a4f4d0e7812a0c50ed15a833eb7
+d1e3432da751bcb6fc193fba72880074
+d1e526b2246d9319b10ce77950ec670b
+d1e75f551773176efb13431560c9bfbd
+d1e76a1725aead00aded3a90539dd293
+d1e7bd7fe23703ab5337ab80902796c7
+d1e7d93a7a88a400b6655de738fe19c3
+d1e98a5e78171f2fff41cd0fdf1a0ee1
+d1eec1b06a0912e13ff47410152b81eb
+d1f5f71e0822df2acc4965a2a81dd13b
+d206324608f9280d0d9652869a3980d6
+d212d7302fea1dd56677b12afb7e7ed1
+d212f1d9d066ad96acef091339164b84
+d2197b07d5cac2cafe8e3556658c657c
+d225ac9a781e1a57bcc7412190dac791
+d22bad17b29704a938353c42591778e0
+d233735b89b2bf5687a3dadd74e221ba
+d243385c9faca72dac6593e0520d26bf
+d246c8d2d6ab81d8e51ae1b5e21846f8
+d24a81e874a01bea21e8ff3723aca967
+d2512f1c67f4598fe3409d8108c56973
+d2534104a8b7ce69e3a950295dacc437
+d2558e8a307dc01fe6a8007f9b608de2
+d25d4f0d8c175f0912f8c6916e201995
+d26ac869b328a2a27221f6d1aeb7ce7c
+d26dbd1afb71bd419c89418960c38f84
+d27502e9e81852f0f329c1273f90ba41
+d275fe8fc18869f4cb961b3d13cca288
+d277c198d087d8d48a2b6b6118e68a18
+d27b3ad4a0510f176b0d407f71f78e8a
+d27bb3b15382b8f12be448744ba38605
+d284ea21f94a9a7a06e49e4deaaffcbb
+d2883b6ccb3841ba3c242b65a6a8a1cc
+d2894147bdc91d2afd029a5960a22fde
+d28b9cc11848a758fee0f05b08e28d90
+d28ce9ea4e9d01e802570017090c7188
+d28f90f6cabceb95eb28f1ad1ddf339b
+d294d2fdfb00c88e09ae60c67a1addc4
+d2974e2e2bf8a5802a09f778170d3f4d
+d299aa1cea1ee15a3ec4b755d4d90163
+d29c8e692e7f8c266ef53205af88403e
+d2a439d19e64afed27b7ec41cadd8925
+d2a4a8ea3a41883dada0d7eab1c267cf
+d2a7c76b170e5c1f886336e450bff02c
+d2a922e1180383982eb208a2903d1ec9
+d2afbafd767971a35b33dbce65e88ca3
+d2b0d2c15798501a8302b47d058b4efc
+d2b2412de314097c62b62e819f14f176
+d2b2e98794f24076da675ca965f0b8e1
+d2b5326be56115402151ad0d259d63fb
+d2b959920ccb9e0c8529eca8e9c89acc
+d2bd9d6e195c31b8502bb090cd736d05
+d2bec79bfd9bbc3c3e9e6ea4a19493f3
+d2c46fa6321a948aab9f7098cd0bb8db
+d2c535d6a5a1ca30e1b6edc0998276a3
+d2c8b907a1c7395149aee3f2529819b9
+d2cc42cf231abc805ad16be2d8f3655e
+d2cf0c6535f15b1a05605bacfa7cb734
+d2d27886c386155898cf7aacebbd8713
+d2d5db7a88832721bc1c9a132ec7452b
+d2d91261cbdaadcfe9f5f4a117f7af34
+d2e33b7fbc1a52ba67cfbccb5f8e389a
+d2e946f5a868eb9d91643d964d5573ec
+d2ec0f20643a8bf8e1ddac20f0980c73
+d2ecef2d13f876e00d794d587eb04150
+d2ed6078db599f41408aac35c6d3a863
+d2edd2093407d0949828cf420231ba44
+d2ee2cfbce277e9401b28257497f5ae9
+d2f2a789972cc1f0f8820e1bb54223d2
+d2f9f5d7f6b0517b6851791b071a3d6a
+d2faf3297051bd0a34f9b08b1415a2a4
+d3009a981c26a43a7d9c9527d980de0d
+d300f0c184edf18b392f0687bad2cb9b
+d301ee94294448a36397926176ecfff5
+d30db9738bee61da462205f612b10489
+d31004b1702091998a9369f192cfbad7
+d31420ed27ff25aad1a8f24b697edd68
+d315f2f210aece2af3c5ddd9092c7e92
+d31bfc8e4b7dd99bb0c1b4064c07c697
+d31c91ae1042c1e22fa7972991686b8c
+d32067006c4083d92c9a3e9ed690d41c
+d32905aa7aa6547f37ff909f12e00d4e
+d32b9d6aface685b2ceadc2d740f5362
+d32e20c60cadff4838dbe1e383b55477
+d331d2193f392a9ade297a2350f4eb6e
+d3355d9f7b57dec4667b328f31938813
+d33cfa004fee22d6587c7271f19d05c8
+d353335b6f55255700361872d3afe09c
+d35710c796fddbbf29a3d73c3da796a2
+d35b2d239dd22ab5be6420910a3e848f
+d35ce58b693c2e495cf74e016f8e44f6
+d3615e708d1d533ef36d0f44e2037561
+d362987a326f020536a4786fff89d3b4
+d36a16cbd4cfaf8da2006e4ea089641a
+d36b08d99464df020718916e6b537143
+d373f99a2ed653c9bad9c685a648d2eb
+d3743ece113244b68460337023cfb56a
+d3787c923bd35e1d48327c3a51ff0681
+d3805e84b8eb97023e2c5fd8cc08ee5d
+d3883a59acf382cd79ec50cbbdf65a81
+d388ab8c33699ec8fc24395e293b9869
+d38af91c73c93bb5f3817aa8bdd8e91a
+d3907461da7d7adbb2186c1c3d5550a2
+d39233adf938cf839635432b1018101f
+d3970d5948a53e0033ae68f7fb06e5a6
+d3989dab019a20a832d2327cfa1a00ab
+d3991dad2de59297d48ba0a85a8a8dab
+d39b58b8f761d50aef9150cc32c7788c
+d39d8b21778e3784f27fe0bcd6b14f4e
+d39e3126d6c99ff6e5b15f597127b711
+d3a364009e4faaea823e52da869b155b
+d3ad6cf6bb5910e8ca960baaf630a6ae
+d3b0868e1fe61103f452712ec3cff58b
+d3ba0e5dcadfe43abd606dbd9e9f83f5
+d3bf038e57ee5352136676586e0243cd
+d3c22de93616e9c28b7a85b676e128f8
+d3c2d6328418890b7c490a51af448371
+d3c5110f78b3ceb8505ce48449edecca
+d3c6df1c29fe43edc84f78c68974eb07
+d3c7e2205802a9ccc2d19ebd97d7d9ab
+d3ca11a445914c5cbd886ac1aa71a10f
+d3ca5c4e7c7377ec997ced0c4764179c
+d3d3abc0b6fa3a653be6188677555237
+d3d4b7031fc91b6976174eea2a05082d
+d3d6b5a1b75e1739fbcac0b25940b6e9
+d3d8adb788aeb43231d60945e43809fe
+d3d92e0cc3f03950ce7b78f02b71722b
+d3dc2590c728a42d6fdf7ce0871d9496
+d3de57bf2b452efee5a167ab93039976
+d3dfb0b7a0f49b9e2276c8847bebd9cf
+d3e597fe4c1ed61b252e4d7cf46b78c9
+d3e5a8fc06d3ad09a0432976ab11571d
+d3e78b5c0f8dcf92b296c1b6163807fe
+d3eab3ef95bd64ac8ca3c48451a37401
+d3eabb691b04398f659cfb94b8775142
+d3efcc7cfa9ee37631b6febdf2154935
+d3f1348cf27706f1d77ddae286344e52
+d3f63bb266aa4ee329383eb9c0c32826
+d3f7902e0995ad2550a35453aa1d65bc
+d3fd585d61f73877dbbd3896c0780c61
+d3fe14f9bae32f134eb70dce358f8bc2
+d4022d68bb61cc9fd11ce27a8f7fdfec
+d40d8de53a55770855bb6f158681ae14
+d410a57c21658181e0575dd13a74f37b
+d4154c065d995abde27cf9d767044ddf
+d416476dab13291297bc5396d426ff1c
+d41918de88385de56d30fe53004ddfcb
+d429208466e0656ffc0664d8008a54bc
+d42de439ded5724355f737c3da6924b4
+d4322e6e3a81aad0fc41c49e4c4477ee
+d4362ffb3c323fef9c1abc3a56d15bda
+d4370e5f55acabcab3368a6a0313e300
+d4383a6331038dabd8b3d8388af18de0
+d438b88f6531dc5634585e482eb4a5d9
+d43b3e47534276b4e787c8895b1f17d3
+d43d800d419b161a32bed57ea07ab2e2
+d43ed56cac64d0644508fa4c1f382e5c
+d441e8e856839f6b5bfab0aaf566fb67
+d4449e01ad1007c964cdd52811907d8b
+d44bbf7e3378a17428e2359803b831c3
+d44f88d358ce94a1053d275f69953c20
+d450aee266587b5389478340bd5b3af3
+d454f0e451c0e06ec8e41fd415075039
+d4584039e91246f97a375c31e1928490
+d45cf10e958a224bf3ee492635c45abd
+d45efa03214aa319bb65114b1b5f3429
+d4647071071c6ca0a55fa12ca8cc6c21
+d4669e9b2f115a4836c9bee661905303
+d4669f4f1d63eb7d1a17340bf8f415b7
+d47146a3ed42568e5ec16cd4e30eee69
+d47750afe02dcf23f169c36e3c3cc504
+d477dc94adb88c66844c36bc7a769d8e
+d4792c82a0f4ab3c7d97f41f2cff15e3
+d479d12afad1ac59fd4e4f0867be3fd7
+d47d99a82dcaf4733c2b9820a242a0d5
+d47e409972875598e6cffa60980cf368
+d47ebb01b3560eba62ab339e3f86cdaf
+d48645332ec053ae431a15096d49b875
+d48948e714cebbcb60b38f5e96c14982
+d48bb09e30ac5a767a612595691ce486
+d48c52a14cb52948a5ef9b434fb56cd3
+d49a934efcd8f0fc5d70353fd645dca9
+d4a86deac504fe1bf806a2c10d2bdd96
+d4aa173ddcba0e9c5813d09056bc3b56
+d4ad399f3b5e1f7e7b9f533b27624ec0
+d4be707553f579c5571e4bc573185cf9
+d4c215a5aca74771dff2ea27051ddaca
+d4c431da3f187f3d469ab1afc274510c
+d4c48debc6c4a3723841c978afc4837c
+d4c4f6e41f5a236e2130b0d5bfd97620
+d4c565b0f62c162ad740aa61d1fca3a9
+d4c6166ea4fdcb979de707bb94dadeb3
+d4ca13fe9a6702282c613cc059bc76cf
+d4cd858a1a00f0408dc78e1df88c086a
+d4ce9d7105a0ae217b0c73ebcb106557
+d4cfafeb137c4f808c6cda0d5fb16301
+d4d09b2941e39fd39988dfc4134c5ef7
+d4d590d33dacceeca2aa79e01026050a
+d4d9de96c3d0ab9e375ad329b0446d07
+d4daddabec903440a97e59456a971868
+d4e2a074856db201a05efb5d2ec2a749
+d4e53816c9bf2a03972757e8ef102691
+d4efb53332d116e7e6c971cfae6ff89d
+d4f8885fc4fff9efaa90c5e28152df4b
+d4f8afd033d4caeb406837b15d6591d4
+d4fcf3a70707ec884527936bfccfbe60
+d501132d2c8c72e17965b5c1015a2087
+d5025f065590648dde547c21aac60b1e
+d502e060a4211cb74af463f277d4fbec
+d505cba74304758bc5b4a20f1dcc44f0
+d507a3bfc9662ff2eff3ccac3ad6a161
+d509bab3b75c0420b93d20ee78650c90
+d50b371bca837aef59f78ca7e0cf9a48
+d50c3faa5183e592322aa0404ba95fb3
+d50e6cf7eb88deae981454363525a7a7
+d51bf75d22924f6536ff34f965d212fa
+d523260923041f8cd140692d0c869821
+d523fc31955ad270ded62e5606e1b72e
+d52531125814e61d5bb9a01f0f1ed4fd
+d527f8105e760c52c137a59d58a4f72c
+d52940214ce09f99d623cb6f0c0921bb
+d52d7eb47907f5fa493e1325b1d205cf
+d5311c379baed55cc78f3429ab4500ca
+d5345ac1ee7c082627c9fbd740727fab
+d53950b8a7bbbb778318059d2bc1baa0
+d53aca0e386d2fa37f69cd9f3aea8921
+d53be08e832b9546cd94159c311db613
+d53bff6cb1decf2a90ee551a7b35e86d
+d54b32ebce9601acb4ed14af17e2a9eb
+d54c546baa36e4fdd91dad20569d5a77
+d55afb5badf230d6c800cd810fc621a9
+d562594124eb91f3c3fd7d3ea114f800
+d56f098db7af5ea8dd2955f777da40c1
+d571252a060d5983e2a8615344a5de37
+d577937172efc8f39b0503ed97f7bc3b
+d57babed438b9effda531857073cf792
+d57e98538eb1e5af75cf8e365e1eed5e
+d5843c51020444d959291ea1ce715876
+d58a5cc0f1be66a2d8aacc446604d70f
+d58e02cc49ebdced9a6e376e754c0563
+d58fca799048238ed2bb343bc5401dd2
+d59512c6fa06fc3b5c0ff9005bcca9f6
+d59815397e825d61b879351f4d52d974
+d598f3781fbf961c67e76824663afbdd
+d5a9c9be5b6e7fa79f9ebabbca94c5a6
+d5ac67ea0f81c96ade5d1bc19c34a22b
+d5b26b8921f8748dbc2dcf9a1181f308
+d5bc3d269b625c373d9268a2dc088e33
+d5bef5f537bbe91412d6b18c6f69d8e5
+d5c04b11d84840984fb9f1e946f33fd8
+d5c8f4f97ddbdd7e15fdf2f9d905a266
+d5ceec8a30fade99c315d2af3a3dfc00
+d5d143677e330148c56e760934b854d4
+d5d237ccf5758877b612526f1f87464e
+d5d3267c18eaffafb5c65da526abfef5
+d5df68b62365afab9cbf6e7669ac80ab
+d5e0bc6affd96532f4c0c28cddde3bcc
+d5e555b7fbe17def9d74b353cc9bfbd5
+d5e686307096d16041ece80b05de0e20
+d5eee44130f4ee9e5c65a68998642729
+d5f01433a982ae41ab75e40fe3c52742
+d6038df4019f454aae7a4e43253927ae
+d603ad23629db20930cc2d30903eb0b5
+d6071486bdaeb3fd1ef8acbb1a4dd980
+d609b3847a0176320ae9652547e44c04
+d60fa929e69f4fdb78f06673eb36fad3
+d623ba3f2d4aae81be37c590e250d488
+d6257fc3202e5ee80bd8cd2fe7f5a41f
+d6322e32ec2fab492e03b6d17d0e163a
+d638a7ce0b866057b0cec3f93852fc9d
+d63908bfb2b96e191f50e25b4306e5bf
+d63b33edc1932756c87eefa4c4bfb5c3
+d63c7b5fdb539ea62331a1030f57022a
+d63d6748cc359a441d9dc14c386adeff
+d63d80278927f3016c925f3fb7c47657
+d6494c2b4775427fef7d418ca5b5ae62
+d64a85875bbca160b1baa7fec6dba421
+d64bbab35046015ceca0cb380bfc0d0f
+d64bd8ea7c969476074fcdacafaaa42a
+d64d71dea4c77ddb8834d5173d01dc39
+d65ad89c23565df47c2e16f8ec34f377
+d65d19bafa797d0e282140095ec916d0
+d65df5bdf15392d25db24381ace273b5
+d66001b231369bbb0d6b951d121e6a67
+d6706a27c8f99bdf0be7da8c5d95eaf0
+d6807a82170184780ade053616779426
+d6818144ec5782361f5b63e5d6eca8c7
+d684f52bda20b17b676170d1571c9f3b
+d687048642b5f04ed4d516e8e63dc2fd
+d68a34e1efecac1d66ffe7d06b247d7a
+d68ed923bf96dbac7e721c61bb96f6a7
+d691ffbc2841a00bb9a481b2e94d123e
+d694ece2a4b12697c5d0bf44d5d583f3
+d69a74d67d8be7e4236f1c4ed20d588e
+d69cb7500eb9d4237a7f5e968b652e18
+d69cf7192452e63cda4f51a4c3a56f6e
+d6a20c6b848c9065b10a19d9003b2410
+d6a5be584ec5ff92f823fe925f00c56c
+d6a646d1a4edb48050f7ca52e62a1e28
+d6a925ab949bc4dfb253eacd7103723d
+d6ab9049ab4de843dae0ea5985d73603
+d6ad6d06ae0e601fb75fa5d835073c0a
+d6b4eae42159b9a3816cb68d9ff5a9e0
+d6b6690c7068239b700948b048db29be
+d6b8e543007305bf8ce05e3bbd9fef60
+d6bc0798776e5ba06807b5ab2ca229b1
+d6bd9d091685636970d75657e609eadf
+d6be85a42697fb96f84a760add1030a7
+d6bf5411bfbe493c530156ab3aad4e2e
+d6c0ed8483aa7ff51743da3fdbc812aa
+d6c1782fa0159c7d674b7e1aeeadb105
+d6cb5b72b2bff6038a179a544533fb11
+d6cec3739f24ba0488efcb0bce4181d2
+d6cecf69ff57aa0177aa795e95bb2d71
+d6d2297eb9c8f51b5ad732deafae32b5
+d6d40175fd9c2b152391a5a43cc138b2
+d6d80d01d0306a41d484201bfba86e2f
+d6d9f2cfa27c0360a7561b0b837d3a15
+d6de79026784b77457a3e27dddab2e89
+d6df2ca9f414f62a854f621b35d00077
+d6e378d551ae117c4fafdad94ea05e65
+d6e3ba5bb1646dc66c9407386a203690
+d6e6cb97ffd2bceb882428a1ec51c4a9
+d6eb30066a7d1639145c30ceb781d45d
+d6eb9487917032b1e2b6e079044229b7
+d6ed5952f8c69bdbd0c793b530df01a5
+d6ef367abbec57155195efab72fbc607
+d6ef47f65be251156155fbed7dee8d68
+d6f2b48d61dfa99595b8dcc0abf9fa10
+d6f54bd7fcda27146adaa61d94fef23b
+d6f74404081ed88c09af09e44dd061ed
+d6f98b01f17c23554821843c800af69b
+d6fb382cdffc9d7aef3ae9d41c4709de
+d6fd4da7e8b1dad6452ff5277d5dadba
+d6fdff53dd0d63caf0943ab64ebb7959
+d709e40dafc19739190f714f2a98a6c4
+d70d38e00a87a03ede4b40fc90a6efc5
+d70e20d0bb71e9d036acd83ae9fc0d78
+d70f54c0abac7c36908520b1a6bbf3fa
+d715a25052ebab71a19e2a10e60923a5
+d71a550b66884ee8c88ad6343216ec5b
+d71e3418d44e2363778d5d1fde340179
+d723a7d284b0988cbf83165bd213aa0e
+d72bbbb9fba0c1f3f7a81c45553a9285
+d735cba164bc1db1f5388e7bd9e8884e
+d7360e95d09847c0ce25e188c971ba46
+d736c131076b3cd535ae18142c77b639
+d73e8e1d86c88b398c26a53891892f40
+d74049a46d0ca4a6e131875611764715
+d74841ba3b385c489fa495accefbe968
+d748ca44367de33ee1c818caf3c8a1ef
+d74dfc6ba8790442395f3f86e1a9c225
+d74e2f1413125eb5b13221a2ae3a10c7
+d7504843db26582021b69fe198fbc597
+d751c9cc89171e777148c215e79b03f5
+d751f4c079cc76eb51a0814cb8da67ed
+d75eaa22e2908fd05acf9d51d0da9d2c
+d7673052b09f7446a035d63bb94e622d
+d7700208e7d79e46389d61dad341dae7
+d770c5e88d1239c6dd3b16c5cbf85beb
+d775b40be79c696ea0eb81aba936dfbb
+d776016be41e922ead34846aaee0c9ee
+d77fa03353ce2332020b9d89906512dc
+d78289056b5f89e4d8e8c6f52124daa3
+d788ff47fe1173c74a7d292e0365d3ae
+d78c542bfec5f8862e63e5bbe9ca0816
+d79011dd7b69483541eea81646990626
+d7902f64fc5384ac249e59946515f619
+d7911ea2c7231f38bdd6b997b958b5f0
+d793746231867fe1dedca07d2bbca15e
+d79380fdd94701e621e4569692a79a5e
+d79857c356a1a04775fe99c3ee508469
+d79a400dc305cb6dce92484bf731b640
+d79cbc8e907c83df7b0d5cc99c689a4a
+d79f80250d2f9503ce7e87f6ba60782c
+d7a02239f37371daab0f81b6d26092a3
+d7a5f1267f73bd191e0ce20559a98b55
+d7a803fc6f11639ee9c64f2839de7b36
+d7ab28b79da0a8ec76b9cce393eba4fd
+d7b541d081a66933adcea9cf2235ecf6
+d7b5cec664694e992deb46abcba543d9
+d7b6d8073aae0e5f62a7bd327366d70a
+d7c0b9a6ed7e6aa9ad232df7e9553b6b
+d7c3ec461e5e4264a999ec6ae9cca6bb
+d7ca4fcec335aeba194da2edd275b418
+d7cb672c573fa3f7a9f9d01e20903128
+d7d22247bdd131503288c4b31b83e0a9
+d7d25241d5248f6db834d30f852506c5
+d7d67896c3180dc66c141aa064b78395
+d7dab2b218d444af58fb9f190a836ab0
+d7dedad771fc082261a072697dde7cb5
+d7deedb5887acb67e66ddf4a4ce81aa8
+d7e975d29d859d9fff6972adde94c16f
+d7e97bfb093664d3ef17fc5bffc5806b
+d7eb12b493dd980d6dfa0963336b3d9b
+d7f2edfa823c4c93fd266721c239f824
+d7f8d1e82eb2d02afad7a4c4a50240ac
+d7fa100b1dc7dc09888398e27c9627ad
+d7fe6a0124e0ba305772a11412b88c6e
+d8019edd432f0c6bda89b6f059fa811e
+d808a0f36b72fc1150fc148902ddb1db
+d80b5c3b3c81b8888d57219083921e2a
+d80e6875fc05ecf7f18a5e02b42419ec
+d810ce4c4250df6a47449a56889e5ca2
+d813a02f69b67c15c264de9de662d4e6
+d8156fa7f4ce075ae2ff5648c7af70af
+d81699faca6949282a6123cec95ea15f
+d8187e607bf10a8f378c9cef9991a9ca
+d819d2cc7e08764cb602eec73b2faced
+d81b1fe3257f1447d70c69b2547a89af
+d82cb7d018ca4621ce5251a70824c32d
+d82ee7ee6da08806cc4b939a109116ee
+d830d16cd5f9004e2a117b4b1f44ad9e
+d8322877fef9442c54599c60faa46a4b
+d83285b92da81f943e6bb4fdb2851a78
+d8345036bd7025c0a63d413d2a8caad5
+d83567685c737fd9be5183ca518e3675
+d83a59bd7155f88bc53a51a809ebd2bc
+d844a5ae46d6422c2671d1441f34b03f
+d84e949be3427539ba2e6b96263a4a8d
+d8549c4c7755eee2c32c47228a6865e0
+d85fcfa0d18f5f38e722c3eb571d0fd6
+d8649e07aa146d0b1438b78729c0d6c7
+d8655ee6b3079f313e2f2bc0a2b85330
+d868991cc8fbb07864d74adf19768d69
+d86dcf799343c9ba02a770cfe400ea8f
+d872927d76a7c4ac047f1f0e26d9e008
+d873c873066f9bd4ba3f1392541c9d1b
+d87a10d761ad4b0925f5d100fda8e5a9
+d87f10a4f615144fcedc8ac46a01b20c
+d88163826f8cbe7412194391fd0e6d33
+d8927654b04bdf9b3c667e529a186c85
+d896d85dee88698aeb1bb3be657e79c4
+d8a1995833b5d2a177a297f3d2c1efeb
+d8a31d7906c297a4048e9a31b29b67cc
+d8a3f287b9666242567c774bba4dcae3
+d8a544c2c500d436687e15958deac97b
+d8a91bf210de063ec1f7990c04459d77
+d8b370f44b0739969b02987f70766f45
+d8b8441ba49e36e098e7991096281dd6
+d8b98b292c8bec0a17c8a3bc9e334587
+d8bbc4ae1f3a5d9eded34f1f777207ab
+d8bdbf53c5b70c7f2fa20371f892972b
+d8c603818314d248c07d3f72c722e660
+d8c8e44bcc02ec8a2a8ae4272fcc2cb7
+d8c9156f667a2a22c53d4b42b2ffe8a7
+d8cc6c2ffa5ad10607f680a72490412f
+d8da7edca4a954d4ac1ea53985878ec2
+d8e1e9febe9d6928806ac258ad6e7d18
+d8e2879b919ff5b80616912f7fb5e8a7
+d8e79fc93d982abc40d4cca9883a6f81
+d8ea0458a00b5b90e98fb92e4677bf6b
+d8ec26932f87b9a5f520a21f51714b7f
+d8ee5fbb48a2823cefa1e0bd39d990c1
+d8f19b008b0c61baefee24b5d347e772
+d8f1affae7af4bec5861723978cea54a
+d8f6f9f3bf26ed61f18b5e20a1fd5e01
+d8ffbf9b4f1d4acbd414860b192d2f25
+d902e21232c7ea8f0b1340880d48f7f2
+d905ae05dac5b176e39d67b33ebb1b21
+d9066c31aeefb22779f5f9d8328ce20c
+d909746215299870da61d4fc0ee230a9
+d90bf9f5a938e125509eefd9bab32f68
+d90e6b83d3e603f49aafc50f6bbbaa99
+d90efde66eb5aa235c92a9e0e5d4b422
+d914cddcf9dd2eaef8b40e40120cfba3
+d918230b0bbce3280c35cb42539cda6c
+d919332afeae73aaf50cd21b2df7dc56
+d921395e87643bb4912e61baf5e35761
+d92215e88c6accb67170dceea64c3f62
+d924ecda071cf0e24f5b54565661bdbd
+d92539e31255a2c22343beee70dfa2ee
+d92749265f65b19a811400391ea49879
+d92be2b1f771a2780a15ffe26a43bb54
+d9379fd640b2d995e27ea7feed45e393
+d93e30d364ed06b54bf21990f979cabb
+d940ba01aed56ace3c972a9c4a3cd8d9
+d94250328ad47c13febb0997ec000de2
+d943d7a77d73e1d90594715d4ea47be3
+d94457ad204976b3006701b4861db57a
+d94989dc39c2dcba447d2d6ca5e8adfe
+d94d4d7ea9acf9d0f755c2978bd73110
+d94eadcbac42b117f9ecba3175b2620a
+d95e6b90e7fc80c171a03149d5965c80
+d9609e23dd9d5d2b38a94fa971488508
+d9624b06e42b1b0722e2e9cc0363e2d5
+d96366c11e4eabbcdb98210a3af6c44c
+d9667dc7af364868ab8c429016ce4f7b
+d9702538dff23d97568b1820b45a8054
+d97488b516254827f09efc511edf1f99
+d977eec604737d13fc28db1082eb81ed
+d978b1ad84a994a137569908dda7fae9
+d97a1d4e95c23b7f31deb2a6fa792b16
+d97f71e7eeac655045dffea6b962831b
+d98465bdb0aa39efc77c83f35d169bdc
+d985bfa5b3cd2fcf6751579ad75ac553
+d9893459048f005c46d163ece0a85126
+d993f7c1bc5e9c2078e850bd2d9d6e57
+d995d7500f74962bf3a89515faefd29f
+d998337d38b91f34c5333e1cd0b60f09
+d99909efe7771eb50e6f75062dc60669
+d99ca886536ef41346f288e3de682a89
+d9a09d38f6aa5564d7a9dead278835a5
+d9a54c25c70a1b390476f0c202e640b4
+d9af211718814a204caba598295ef75d
+d9b200940a1bf8a8268d6c5651e04b8c
+d9b2c09acf6bc653b67d029a0e13bf01
+d9b6e43450c496083b56cdcbdea5d9d1
+d9ba3ee4f2e37dd2fc7551315b960dfb
+d9bb775675837f2306c2104511c22307
+d9c2146019ab635efc4cd9732dc90fda
+d9c2be38f653c9c030b660f877713560
+d9c5db4d5ecbabaf6ce7ce9e0940520f
+d9c92a63b9cb48f9ff7c1ade81bb0f0a
+d9d448e6b2cf3ce2079e714ab01e03fb
+d9d4734371a8ce1b6ce4d45138d6128f
+d9e4bd6fe997ed4ac1b7710d6f492c8e
+d9ee57d304f818fa194765bfac712871
+d9f448a4a538efc0c15127b357f22933
+d9f648b08499bded3711ec7752637f68
+d9f843e103b1400847808a353898629b
+d9fee879d3b4dc8569d0b92ccc70b24c
+d9ff91c1febb18c331681f91019416f4
+d9ffc2214cb1b572cf5f9fd95493a177
+da015c0da2a905bb7a165289f1fd04c9
+da116a76dd54e8eb0bd559f0aa79085c
+da12faede1e1769ca4edf3d8f4d85f9c
+da1a2c9d585a449d85d2e928d0c71af5
+da20ae228d000b845e1c32818a57bed0
+da250b4be9573de5b0ac65b4e57c2a76
+da25a6b4e9d0826db2ccc5da321e972b
+da25e4d8c742c40a86f176a4c4e104bc
+da27b4aa7fb1393e23e924bbda382c54
+da320863ddfb53f82904f8e823599b99
+da3401a0bde4e258e7387405e0b33385
+da34fbc2344a3d97957b09a378067612
+da36b8852bf58755b86d8fa15d985e96
+da36ffc316d799fc186208b0dd1212be
+da394e4d21f565ba02c876dbc55f9a6b
+da3c0d8e138eb0d03ba0813c30a83ccd
+da3f42ed0760b7ee3422444df60bd6e3
+da45dac2185312b580afb74f0f76aa32
+da45ed91acc8de6ff65c44c8bb96a81d
+da47645fbe37efed7ca399f7e3d3e409
+da47ef00be52dafe208965cff24b185a
+da4da2b3118af1cfcd2f27f447e72c04
+da4f681fe9fef5e91514607933ba2903
+da55cf117cca98d21df71b1c1e76b969
+da56f16c1cc41d32c6b51292a02de826
+da5f7c2671accb13980fbed1bd1f844e
+da65596e663dfbb244373f0b0c3a3f5a
+da659a5b63e119351854180407f42c70
+da66a92aa2ff2adff4c3c1225e3c746f
+da6c9b0404cd62b57e6924498eca7c1e
+da6f79343e506c7371f78bb1281fa5b5
+da741b879fbd3fd7a9f6361b0d728c6a
+da7e4ad8e63621f81fe757885c67c9ba
+da7f2c17821f42de29f8fe6d3fa5c8e7
+da855fb318d92b8c51306391716be430
+da8a0e3215f61c34eb4bd200a679e06e
+da93abcaece7512c44ec28159d2a3be7
+da94f39ac31f5b6caa73a8448fcfa08b
+da997b22c6c38da028e6c8f1db668fd1
+da9f21dac400e7588543f9fd9ab70ed7
+da9fa15f9b5364c306170c42f59ab46b
+daac421020a08c69f02e6b75732d96e3
+dab5794acfa95a929396b911438c3571
+dabad457f971451f94af3a01b5dc9df6
+dabb31568c6b6620b45724e61362ddaa
+dac2e148c0c0b58d00ff607b1662918d
+dac31ed8a48236485f436ac0b7a03ac9
+dac5d2c2cfe708d572111e22edaa22ba
+dac8396b64655593451f33c37e041442
+daca9a57d70cc3af4abe00bfd2cc4a6f
+daceec2e69a3943679ecb4cc3e66308a
+dad5e442f39e4545277188c272856f3a
+dad992b172e7dca39c9f34c54adcf593
+dad99b3f96802b3a93fd9752f67f0941
+dadbf71da5b8bc6e7a55e4da08b42b6b
+dade40cac8b541e58189d8137d5f38cf
+dadea8506ceb1792fa4e2f6f3c1cf302
+dae17b08f05610f92c5fe08a2eeac75a
+dae73e2ac6886fec52168cceaf992496
+dae7b5dbcae8a5054261fc039822b183
+daef54c0b701bbb60060210802c204b4
+daf567e2386547907fea3f7e240eb5f0
+daf6a428b0bd511c94e6bc408e2335ad
+dafa97df7b0b9adafc399fc28f592de2
+dafad16a345c8ac4b4c2edd065919557
+dafd11bd033e0dad4a97eae736425c5f
+db08c90149a32bf855246428b941c950
+db0a8b61fbf4f95f9f74e371fd784c41
+db11d36f80e623cface30b2e2cb4582e
+db16f63e8f8a0f1d9d363966aa17c5bb
+db1cffb66e27ca2a5548bb4057561d1b
+db1e81a9ef39ebfb638f61cb27702140
+db25e7de904ca5b74e262318a1301775
+db297c8a800eb727d50af12a48962abf
+db29e56036b17ea80d9db306ca2440d3
+db2f3c106ea91ceaf9f6b6eeb0b2e5eb
+db312b92c3079d6012d373daa48a71bb
+db36eb30437f8346ac31aa03b7961b89
+db37f43b809a74660d815c7b0ce48db3
+db381b4414f950ad7bad1114bbeb177c
+db3fec346bbe722c3f312d6b2f19e2ba
+db4b1b12ef30b17cd8112218335fada3
+db4bc4ca97e8045350a1fc2000ec59f0
+db50adab8d6255bf9b9da5ffa8b9a97b
+db51263ce6d0ecf6af166694b13852ea
+db523f690f938f21f1aa4b7692440499
+db56448652d6220290ebf630ff41d182
+db5e63928a133666181d5e00b3f39d19
+db5e7bdeebdf5c2e828f9a860d74f7e5
+db5f00831622650dc157ffa3267317cd
+db67bbea5cd9bde353b2eeed70ede5bf
+db6b969582378074bde40ebee7daee99
+db6be3cc8ac53c8c3982e20e47c7a689
+db6cadf5df481dbc4a58e58111dbad93
+db7156ebb69fa994380b1c41b3c3e4cb
+db71ac79322da359c244a3e88e3a5571
+db765f4c1a99c347c80a2977e479c27d
+db79157417624be81eceab7db9912b1f
+db81e21b96ecc18eaf26bb7127e4c670
+db82ef2f6b0c96b5ddbb7efac450938b
+db88fa27797cf8046f0edc3d44fbb5cd
+db8d1f5d185dfceb5cd521985507e70f
+db96702c32e4cdd7c841172a45a1ce6e
+db992b9a991b786bbe2e696dbc03069d
+dba5596c284ada069d209ffdcd2ad120
+dba6ef363301742e43fbde3b43a07f4f
+dbaa92e468cb71ca638ba822eb24ebfd
+dbb55b662faf07b01a533b27305ff452
+dbbcc2a005d39fac6e6ce8feb608544d
+dbc8afec7754fbe26dadb7c67a257736
+dbcc69af9a8e07333e9c821748b6a4e3
+dbcff5e344c48be01711c9ef89c57de1
+dbd9325a368223b91a3173eb764f306a
+dbddde0ee1c10d12b6f9e3bbef0e00a1
+dbdf4082316f72ac35b7489634bdb29d
+dbe5e1608cee5fe4ea900118f678b8f0
+dbed3701dadbbaf0c9dd23f08cdb44c2
+dbf08bb9cc3c92bc32b3f93a52294060
+dc073e202becb13926f081c685968a1a
+dc0a06a4957dbf732badf8ed45f3e789
+dc0a7cc1a21a48f72e7f8dfa27e862b0
+dc0aacc043e2eec0382e698a4f1ee837
+dc0b28fac11c8334d63906445faa92d9
+dc0eecf20a1f4ae4fcfec40c973bd565
+dc0f707000ccf7e0591d39cec0b8fdfc
+dc1199f92c5e8a23d0a1b66988c2eb1b
+dc1503500d1029e34a9009a2d1a1b085
+dc1fd54ee2cb7e1d321928e92247ea6c
+dc20ea5ff120bbbef60ea0b8651be8f9
+dc36d0651d2cc30bba8e56b0d485c933
+dc3edc1ac37d1cf5e4b0d61925cea2ea
+dc41c22cf4598807e5cb167e0feee00d
+dc4860916db188163e8f67ca8f27a07f
+dc4a3573d9c08eef89b90c44b14588ea
+dc598d1eef7549c7277ee749055788e8
+dc6078dfe90668def4d5562476c536f2
+dc64230e626f160c7613c07cb6d4004b
+dc6ae39a553d69a19983616c2cd10806
+dc6b02d243d693f0686dab8136e25a65
+dc71ebcdc321a4471103977c22f4e710
+dc753bf2d2b5b59cdf6e92fad590baa4
+dc7b2f57c44c1d98dafb23b71212544a
+dc7b37c7452174a1e2a3333ff289fa04
+dc80f9a9d36e46197c6b58b1b5afb01e
+dc8dd782cc06b269bd30b3cf43774e43
+dc8e04b7733bb653cc9bbfbdc3e13c09
+dc92742f5a003ef6f6fe05187408a0be
+dc94e2aed50b894979e6c9f72b7da8f3
+dca0fc8fa5ddcb8de3b3bdd350be798f
+dca6d845abd21aa85405ff9a569a9e61
+dca8e4173257f70b7204e1eaf42851ce
+dcaa5248a1461a6ab52d910a49f89d8b
+dcaf41fbedfd6e6eeffeb3d9fab31f3f
+dcb3af3ed13f7989e0d50e72ebca3d8b
+dcb620a490be49e0416267ff80d70ae8
+dcbd9b9f8fb42e2cd2853a9ee0bcee00
+dcbf3b4497516ebbda068124980faefb
+dcca532fa8fe9efdc91d6e010461b07e
+dcce839dae90bae619521bf682a7eb5c
+dcd2d5640c9ecfcd8c5182ccd55e97c8
+dcd3de39f563d065bb4e92804aacdd3d
+dcd8559edbbd6331cc07f900d81824f2
+dcda185cb4ee1a1448700d41d4a1f8d0
+dcdb6cbaf5ac8ab194e17516bc6cf772
+dce30d7682ad8e5dcc1e48ae60d3d366
+dce511498dd45c5d66044cf39e8fda42
+dcea51da3f6397f4ace7c06f27eaa95c
+dceaec176112fde09502f50f0929be51
+dcee7f36674cfe9e01510b6c51b578ae
+dcf39fdfe3b8fe7209ab84f668afbaa8
+dcfcf63a407686db00d59a883a074cd9
+dcfdea73a6ccea8094fa4c500bc6d67a
+dd0288d3fe0ab648e55ac420ed251839
+dd0be8da626cfb23fc5761cc5a383ba6
+dd0c763b8c848f2e845bf1ecd8458647
+dd0e0bf4ccec40665ccf3bd76b97d348
+dd123e8193226b009e42a7efc18b78b8
+dd1dbfb858f5fa6943047f353bd718ad
+dd1f19dbb0e728c01e937918e4e4474f
+dd2799598dc410ff1dd05d99a80faf25
+dd29c41cdcd4af9aed6113021c6df120
+dd29cd1b9b565d835321ebf0fea28c4b
+dd2dbe453c9a88426f96f2177c5b981e
+dd2e9ac5ed4f88c2f8564eaaa0dfb9f0
+dd32c17440516a0ff4ddccc6284bd24a
+dd3ab8b9e7ffbc43d6a6935025af81a0
+dd3e657fb116820c824981bc438d215e
+dd429b2e9de41d9a4c6cba85c960320d
+dd4989664be488445564dd6daf18f2c5
+dd4cf29506031e4f3719a7c782ecb649
+dd51e2cbb8134cd5481700abc6ef7d66
+dd5afd2b56dbcb4c4f987aa8f937b2c5
+dd63ad35ab38da7ad093a0427942989e
+dd752d369801691f99cf4c38f40de786
+dd756d67a4c6313967cd7b35295e7252
+dd8541005934d67f437b66c7d5e05a32
+dd85503d98d0c7eaf6e54e5b9c24c3eb
+dd87163490d3087e1db4e611023ccc72
+dd8d4c74d5a1c5f66bf2d94c5c2ac3ac
+dd93b4f911f45ec22cf1866245245b3a
+dd9649701b91f35422a7049708c548c8
+dd9b7f1a65ee495486e8398506bf0c3f
+dda055250ecbe406b444060ceaf67d06
+dda133c8147959ef9035b9b234d5b399
+dda136c29ab46a9aac36fe53ddf1af0c
+dda2583593896c005113b4c4f35d918a
+dda273194a5bd824847b35a94b785ffb
+dda9e01bbb0d9098d6d83a417fb26425
+ddaa1db63e4dc2e59f55b4e12ac8775a
+ddac06c450a6bf7afaf6ae0dc69ef1c0
+ddae9431eee3e00e648b159bca5d49cf
+ddb0d9360922c16b52eeb42bf067f19b
+ddba33398c0d268f6dd48a31081e4df0
+ddbaea5f6ca3f2571bc48ca2014e19cb
+ddbb52f10f1adb8324ad2800eb31e79b
+ddbbde185b36f53af6534351c8b04e78
+ddc17ae9986d59192261c9a238fdb2e9
+ddc4327949a90e273e310d18201e7980
+ddcab7f17ece5e9481bbbeb7d54d827c
+ddcd1b70e18c86ba60b3d21749d7cdb9
+ddce1e84125e7b35ed26c8d1ab868721
+ddce2180972e10abca55ffbf332013af
+ddcf03f5b60f1692432331054a2a14c0
+ddd7083e96bdc010a77fd8f683459d1c
+dddb661bc45641f6458a4933586b8599
+dddbcb0a2f862d105b512d6c540cdad8
+dddf0a2086151c9c013a9686bc3de760
+dde79e63019a6b57ce4ad81c97eeaf43
+dde9f0f95789b08a2ce840cfb773c864
+dded62310346ad81d69d781a565d0440
+ddf01f87d4f6f5b2833b577b3dc79c49
+ddf563956e50383c7cc718705feb71f6
+ddf898c4e9d89704454b7ff74e6b0bab
+de0522882468b04ff421166af127ceee
+de0547c068ccc028ba4cc1f28ed915a5
+de07245562d4661822b66aa05d465a09
+de0eb817ac24d1f9590a7db6c2706031
+de1198bab70f9882fd864b71b355179f
+de12350bc71026755f9cad2188b2036b
+de1a7256739baeef8468e0e859c90e05
+de1bcc8421481f9c47046233eebc8ca4
+de1c152d5fe6563077be2962c98c3eb2
+de20a88c3e30f880dd16b26dd6a6d9e4
+de24b20cbc15bb5669ea1283a4e6a497
+de28ec6967f3ecdc036b8494082c087d
+de2932aaeb6f6807a55af5801d67fb2d
+de2fad058af5c797af7af45cdc1df3e4
+de2fbb79c32a6406c59f7b5437df94d1
+de3050cb40450ef10052b8bc9ff731e2
+de30eacdd67264fd9bdadd51e684dbf8
+de32caadd2e24037cfc1bb83dc8472e6
+de37011f1fde88f49c5830e00367b198
+de41dbb63d6460d33120270181f26486
+de4a0ef8a4b7c887d4a310ffa443d80c
+de4c2f5cffabfa9e96142d143ee06ccb
+de554b9cc5d266ee3f5d450df524d122
+de5fc7a92c45e0e22998f3f2fedc37c0
+de617a8bdd5d8ba456a3b9863475344d
+de6296763515eda2d9daf40af0aadb35
+de63d16942396376e6d69d2101c9b14e
+de63eb8afb9b7dd56df2d0bbae15a74e
+de666e7872e3553313f7ac3018edf3ce
+de696d2f15a582e4fbf0e77ba55b097e
+de69f83dc878641b593e4fc9b0e3d44d
+de7121943692b1d1cb3617c4f4096ea3
+de7631a56d2af81e91e3619475a1f3ef
+de7747f182f7b5ba4ab253fd90922955
+de787d032e0d3faec43c86679039ebad
+de7aae9501f1111d7d5555d106ecd59a
+de8a8e0582af19ca7478f70d7907e444
+de8b484401b2befdbab76788f78b9cb1
+de8b9020e9148c7d58056bd00ca290ba
+de8cd9abd8a6b8564c503008fa80b454
+de91283b7180955608701743f0091c26
+de91f578044ef2546b7d0bb9d7f0db60
+de93a1849f37d802ff1c6e339969d264
+deb3be931bcb7df18211063803c0de66
+deba871f40d8c1c13aaf872a07bdacd4
+dec0e2bc26194a22aee36c61639ea941
+dec205de8532cbf5c79e07c60e35aee6
+dec345f0790b7782474c838312240bb1
+decdab4415516362fa9256a8c31adee6
+ded168536533c1ffeb13d1aea4c3810c
+ded8ec58f5cbfa08ff2ab2e18eceae45
+dedb616afd65a8d77feb326242fed2f5
+dedd758cd74411e6197ac8450f0b983e
+dede891801cd3501f20daeb3433be0ff
+dedf23bd43762b7927c074f4e8379824
+dee3879f69756fb3720955606cb9af4f
+dee38bcb1245eb3ff220433820e7ca54
+dee76fb4736a17b55583a721fe3ec5df
+dee90170e83b7785b0f1162b1734e60f
+deeae1b1a201074d80153c7507112477
+def028c3989b155d3942d50cba64836b
+def4aec227096bdeacf937314f77fe88
+def5ce5870b16967c55c83d96967c5ed
+defe914b5259e9450f4518e01fe8b127
+df06f38a0db8a7d57805069f370d9988
+df0a3de0a4e396179c9f6a104339c3bc
+df0f69e98d236f002c932870f8500f00
+df1245c2749871f109adf896a6236bff
+df17194df770e656f5077692b3a22e31
+df17d47172c7a01302b8658d701291f7
+df1c1521b176fcea5be689123cdee50b
+df22d0a2069ef9ffe206e01bef802b00
+df236aa2a9a49af011a36fb28cb3d710
+df23da74ef635a9b9a27d5f5be3ac7e3
+df2657c882d35a497ae1157ef961d59c
+df310647dd51a6d7509819882e6559a2
+df33fc7dfe8f57b39e25fcc4ef2cdc77
+df34d75e6a699e17ac101fb18a060183
+df441578be29e676816cd63663c358a5
+df47cf07a3681719bc6e14b20ada82b8
+df4f74bf972ffd4eb708e36a3841601e
+df4faffc90f0f8a569fa0daf74a798e0
+df5080eee7cf7ff33635d4523623180c
+df53e6f78306c9f78cabec880d0cb00d
+df58c9ff7abb0eb2d7e6a6356fe333af
+df60b82e4507b29c624eb11d3a140ede
+df65a5c1f0fe997623934e7837935f6e
+df6b53df196526d36fff3c9114d447d6
+df6b8f0e1739204f9c19f61217e09b84
+df6ca321440ea9a8061bcd0087dea18b
+df75a9ea1641de3092b574e13c4d7f66
+df7a4e7a5c624b83b8e55915ec95e637
+df7aafe1d05bd531a3d078786036bf8a
+df7b853289247a694ee7d76abba0d03c
+df83009042a2d4001174c58a7c866b10
+df84a395884b4c7744bcff1683c672fb
+df8605c79ac7323df1720be35be3018e
+df8c05c8deb00a4a0ea4d11b2742cc8b
+df8d594e7e97ee4b8692f6686b3de2c9
+df905241da642fede0390fa167a84f25
+df9185bb89c9bc361da9c79b2bbc6f8e
+df9346437d5079235f564e8b4228feeb
+df956cc9889d3aa1d92667a16cce778b
+df9a0d769276ea02e7ab6165ea249cad
+dfacbfad93ed14ba41cef3b25217d9aa
+dfad2bfeeaafae79a5fa613bfe44c010
+dfadcdc2bf043ed3d889ad9a2d60446e
+dfb0d67df7c8e5bcb50099b443f7fa47
+dfb34e8476804d45586eeaa216f343ae
+dfb5dda48c3effc9eb646f1ff5833689
+dfbae1161cebed893da94d8216620565
+dfbd466270d3438202b1fe3cd3b62da0
+dfbf6ebee47ec23b8ba68246b7858f1f
+dfc0de7f1f8decd685e57d26cbff0af4
+dfc28eaae27ee5bdc44149142967998a
+dfc35e528b8922017b2bec12b4a3072c
+dfc57e405c60c3d56b6273d9cf63c085
+dfc71755b1a7c63256f8081dd3c347c0
+dfcbe8a8b64241421efad6f89bf6fb94
+dfd0d6ebea1fad4611ef569198f34e92
+dfd551e3a12917381ebd30456ffbeab8
+dfd601f1e7e5b598f2eceb6602efe8e5
+dfd84953139680963f110827563084b3
+dfdeffba346e7bce90b684fadc21008c
+dfe16cbe4d26ff5619870314b2371595
+dfeb9f4ae93eebffadf26ea17f735a80
+dfefbf7dc9dd7591d5c38d7852dbad4e
+dff021af78ed0f8bfb34cf469c5d25e4
+dff77364d51fb40cc715d5f0eef4280b
+dff9e46afd2703ab80101d3911da84dd
+e0090608f2961196bee84f3c358a9126
+e00d1d7deb3663032e4b7d27fdb69b6e
+e00e6ecc0cc7ca58e38ad55fa9d0f71a
+e020d24783462fba39456a886632f123
+e02103b229d71bfa5badb2113b905c64
+e024c22940b2144a024a33b1263e1299
+e02cd8ea3cc0ba73145f3b8f452c7f01
+e02f7eb32abe0cdb718ab64d337997a0
+e02f848678a17e3ec55ab71cef8ff786
+e03053620a2c864e29cc869480479009
+e03bbecc3082df3f8ddf33af1901c09c
+e03e39f6d4fead0b47cdf2f59df123ed
+e03edd6ec07f052977e800234aa84a61
+e047459c0ff805c8e76f3e7914f3e015
+e04c7f6fb3b6dd143f343174335dfe74
+e04ce4e7c29b6a2457ea0efa1959d160
+e0514f0005dfa6a66778a37d1b4edc3a
+e053f1d621829553ca3d3da4127977bd
+e05d0ae404b3847e4042d0a2f4e623c7
+e05fd9691635a052d250aa8e0065c3f0
+e0657178bfb5592d0d54a51a9b072206
+e067ec7f5596010fe8eed689f1a4551a
+e0682aab748c34311229b3cceaf74592
+e072456e566556d3006c7fd12bd65c3f
+e073134631cdae70973d69812d72a889
+e07a2d2c7c2565cebdfc7db42bd47fc1
+e07a4f6437ff08ef003df274e6d9b32c
+e0821adbbbb6528a46439cacae81f3f0
+e0869bda88b43259b0e4bf541ce94e5b
+e0888f01667527d9053f2d00cd6c7a40
+e09684f21173033d61c96ad9afe1bc81
+e09e1d564d16bcb8705a0aac0e5db546
+e0a173cc77785cd18de84c8c76492813
+e0a5430ea035205115636b4eaa6147c0
+e0a55856987831db961e83bdb6e46e4c
+e0b3c0697b5591c4b4b4515bff54abf6
+e0be8e4572753d435844efbedec42bf3
+e0bebc7db2f9f759c4658e7fd4c27ed7
+e0bf7738cd335b82e7431cdd27b896a8
+e0c009f9f08af9e755298d7cbb7ca5eb
+e0c685b3aa6b892a7578e2850652892d
+e0c905dae4c9dfba19ebe90a4876b17d
+e0cac92a31a996114b9680e504923af9
+e0ce5a01525b40a00bc3715849da8bef
+e0cf7d5f47c66dec862f5e737fb5984a
+e0d179f43d207a3a3c067a593f16f451
+e0d3d2a9a48b61abb7e5a61f3c6cf086
+e0d81196253f4488526bf02fda47e141
+e0daeaa1e7ed291d842add725c675967
+e0dcec7f7326e8cef57b18452d253ef5
+e0dd3c3790cacc29bf0ff68b34c2e49a
+e0e04191d19d9c343807fa2dcb148e8c
+e0ea8b4057a0b5dc963f1b70c45b13e2
+e0f1edc503b08a0de388187d36e604c4
+e0f5c90ef8c96bc105223b008d62402d
+e103b7845ed5396b68d8369240aec0e9
+e104ee17c4efebd6d3f1ece3ab97f198
+e107e02a1d410b8837220b93f703aab4
+e1139b597c80333d7bb3792ffc35dd6c
+e117de4e11db21ea84f7209117d6c534
+e1196fc5d43629c1951f1fa5b0df4747
+e11bbe28838ddc6ec26efa3136260a49
+e11cf226cfd9cdfe92e05816eaa07151
+e125691d1406407ee69e6968a118351d
+e1263f65821d69d136dae8e4744b12b3
+e12680dd94dfde39f2fb2f2e7fcb07bb
+e1281e67b956c86dddaa877ab8aaf5bf
+e12d75db22c422d36f6b20303b1e5a5d
+e13055bd65c3dbd08aee44ff0d93e8de
+e130673d57a35fd98ed0e63c34956c7b
+e13686a7e67f2446d9881df2815c2199
+e138797899e364025ec529071cae6918
+e13cf245c47077ac0497ffe7fb0b29e5
+e1466559711836355f080b160669f5fd
+e1480fd076cfa8e48b9a57a71733f77c
+e14ef642fd4f0dea66ca3ea3cecb5fde
+e15bd12a2d340650953715dc91b3e550
+e15e95f8fbe3c4133b6f4396be678601
+e160b16a52e2e3d54c530770e9cfd0f9
+e164d978d261df81d6bcbbe26a04750f
+e167d50236c9fb9c04ba7bf071293511
+e1684d441133b33a0aeedd8524743e77
+e168f615e4141466abaf19d415f27699
+e16be6c84ff76df369713bc86c153873
+e16cb8b58da6065be9d24c323eb3ffeb
+e1722956de181dbb662aba8e3a0f6e10
+e172540c05c9180e86cccdbd52aca5ce
+e17305e53c65a5f3af933759d899bd9c
+e178d7061017b47743d1550647b24581
+e17aac871188a22ed7a63f1230b4eac7
+e17d2a87901143319fee1025ba4aee43
+e180034c5febb1895c0e587056c0b386
+e18033a0a82165249fc4398fca2ec65d
+e182a8b5f6c4f573e4839374348790b8
+e187b6ed58163b25cfd6338f92d98067
+e18ab32d7c43cf6670407b3e8e9680ca
+e18b4a77b446a54b13c6dc8028fdfb90
+e18ca862a988bf58a537a24110a1ac49
+e19110e5a945b4650c1557a7e9ef04a9
+e197a09e47a5a8054b3ef78ba6652899
+e19c6c21f83c59a17477fdd8973ab9a0
+e19e83ea459ecb9efa1c4b804ddb9c11
+e1a11b1fe14a9a2f3f89d9fae3300285
+e1a39809b8e30016d7521a4e49a1d3b0
+e1a612e4f739bfaa326f128a8013da62
+e1a94f022eb43bf9b58abad10cf613c0
+e1b0e80443e69a8bac8f210c446c9e0f
+e1b15cd3448c8f95d5b27617e9491091
+e1b2e63cb68a7c8ff39ed4e8dd3f1770
+e1b4d8000bfbe87f7cc5bc02eb858ed2
+e1b6638c49ee74bd79949570d7a4bf14
+e1b6ae581a234431bd042fce2981d124
+e1becdf0d8433cd06dc56aabb5d35981
+e1c3102eb2e47c21348f6eb52245f5f8
+e1c49f658f6265dba73a14e2832a73e5
+e1c832654ecdd28540d6ad8416f4e317
+e1c86e72e9c3cfdb6663b5d1fb2e6930
+e1cfa78c54f912474a6c02ce62e59260
+e1d0660a25c160217bc9359289a1210e
+e1d6503a1874b63826bc861d9fe30966
+e1dd7f5cf84b2e6bcd5c7f5c98243ab5
+e1df33022d815530990fff83693783d6
+e1e02ecd25f989dd30d728623dc66eff
+e1e482fc3a01a8ddcbb058615445e905
+e1ec81ad17116b85d1dedb82a63022ef
+e1ecd4ec4e952d0f416691b88532e121
+e1eea60854e6c6651e295e93dc9f0ac6
+e1ef12d39fb4fd9a635da49c03e157f2
+e1ef3c28b7ada72501a5e75fc160587a
+e1f67f8bc0ee70bbd9024158d63056ec
+e1f7a2d03a3cee1cba74e29ae95e9905
+e1fdcbc61068ec6dd3517225a97ba299
+e1ffd2edb8b7f628340041bad918dfb9
+e202ea8e8621a5dc81e83d45247e2aa7
+e20e5d9d707c42da30f0e0948b58c788
+e20f9d9e4081de967a22f0068922c349
+e21116620a622a6f4d487a0cb6c7fc20
+e2123af97eca391fbfec6d0a997be553
+e212c5c2714593ebac12171ea63ae890
+e216675592f289e67ace39ce4b07d20a
+e2191dcbf0fed4e1eff9825c8c51f4f8
+e21d2d1b4272c087169984add9879276
+e220f9d32edc685f5269851d37e662c9
+e225716664eb621ebab42107cb23f75d
+e225cf97732d06c497b5cdb2ddc62bed
+e227a0a5ae135a02f5c30e32b36d42db
+e22b6c7d30473d655408931e31018a4e
+e231cadefe8c2794a7df1907386104be
+e23534ae42495d891ad3664ba07b7495
+e23a6c419eac71660b98db978adff3fa
+e23cb564bde403922634d42078553383
+e242126b4b1e20798a4504f994a0c9a4
+e24fe098a45a85b3e1aa1565e9bd8c03
+e255e0bc3327380ed5ffc569a89e458f
+e2579b3d711b8e485f4bbf76196a3987
+e2595c12641437d9b0f9de1311232789
+e26051b77e64187222b8c19a26c15633
+e262b5ccb9f1d574958a898be0f4df18
+e26397d4706c65f44fde0a25e1bb0f99
+e2662988c0a481a5ebf179201718918e
+e272987ab92e8770aca13576b1380710
+e279f201542c25f87cb10cfe044faaf2
+e27a87e9be5af4d93d8eb84cec8d68c8
+e282654f19aa9699651abb3877eb324c
+e28376098f1f25de6969923e125b68be
+e28972cf2ab32e39e515daed1caa9cd0
+e28d581ef4bc736accd5cf251585fd4a
+e28fd30df6b92caa4ddcf9f134f96423
+e29018cf123845defe03b1971bae3006
+e295ec3ffeba003e08d8ebacb11d99f4
+e29c5bc36ed2c7db847d2205c89e0cb9
+e2a6ccbac30710d6d45022b9f08bc60c
+e2aa853de99b4f65bb0ff692f7868a7f
+e2ac55f6592c656f8ef7bfb68d93c7fb
+e2b0234da4278a28eb14c7ea48757d3f
+e2b04590767725a6361b74db7bda803e
+e2b31e3e00cf10f1b4a00f0695c42960
+e2be05c0ceb581653fafc5e1c255fe02
+e2c2b659cc72daed3372f685efe00b3a
+e2c6bf02012f6fff08b77039841c71a9
+e2c78a00b93d65dc9990df61510a470b
+e2c884c85c432377e1421cb7c5a2423a
+e2cd5f08cc735d38fa7b6ce3f4858bbb
+e2d413e6524c4249533b820847c6fcf2
+e2d53f79bae5800fde7cac8bab87f5a7
+e2deab68f98ea7dee9ef77828f32a342
+e2e177c543d67a73541fcd3256a19cd3
+e2e4b698d9257bb2d3eb4723740b6ba2
+e2e7aba8207ce0d02352597e1e190bf9
+e2f0cb086cb948143159411bf0c4885b
+e2f4898c4759f461a9c522776f578a5f
+e2f9cece325b6ed7bab6fae48abc5fa8
+e2fdb193213f6b209e12e99fc81fe96a
+e2fde053cd315020ca374f7849196080
+e30ce6f8df1a08d3193f8944cac8d182
+e31496edca2d7403a2d872156fa2656c
+e32aca6287cb252006a3ef32f137badb
+e33488386246c24bf4f4bda2411b6595
+e33709231adaeb9a12f03b836709f311
+e33b76d160d249fffdf8b9fa3a02a315
+e33ea710d821140ff8c263389f9cbd9f
+e33f30f3c27425900fe2b6af63386b84
+e34111218cc6caadb783620a40ed9d03
+e3459eff4c08074578ef0c26dda8825d
+e34d2677ace1244143803eb4c039208c
+e34f97fd447e113b6d84dbf953b539ad
+e35a0dbe76bf10c29c16a176e585bb54
+e35c722339662e24da6e284cd6ec66bd
+e361409cbe10c49befbf678f2f23148c
+e3692d235588b448f2b2a33699a69d93
+e36a6c4cc8b11c3829c36460da7d4233
+e36ea3060194ad834d2f86228ae75bea
+e375c5f23f1313cb48d0439369b15384
+e37844a89e96a9946085c11d47393e98
+e3789566761f923b35384399e6c7b710
+e37923d1e5770565af38461ac47f1b51
+e37a7ce5127dc268a88544a8dde82312
+e37b018cb1a069bc8dd97a3959973ef3
+e37c1cc10e55f30d98c651f988e60c1b
+e37c888ee08bc3cd99f727d7e915fe05
+e381e185748237ff952d31a991a3bb2a
+e386f3b967fcaed24e072e4e0c353b61
+e392521c44f4b91367ddf872f016f42a
+e392cce1253454048e158c0cada26bbf
+e39407886707c9be63b3ceb21b1213ec
+e3952ad396d1e0b41996106990d6a6fe
+e395bf165f8221d077e63088887f640e
+e39654683565664e8ee3451f2c25a55f
+e39ed76870aa6e854e7a3268ba7c9a16
+e39f46d0ed673559a0c02a4786cc5f37
+e3a0d73b6172b3e0458ad80323742e04
+e3a4589351c84a45b41e0576fe4b8b4b
+e3a6ab57f8cf9c8ea0fcc2969ea6e964
+e3aa1b028b85482c563f159777e016e2
+e3aafd05d87352ef1cabcd913846f593
+e3ab6b0f5242ac6e8e767a8a60c72587
+e3b31fc066d0c891bbfd8c02b69fd3a0
+e3b5eaf20d6ec38febf2ed7f870f0ebc
+e3b60cfc9ebc0bf71317bd1c4af7426f
+e3b62884fc04a4e9e6b5bf95fb18827a
+e3b79580dbd00c033b9d6e593801b2c3
+e3b870f3fdb98a8062e622d23c80204b
+e3c0cc1c8e9c4181d4e3e360716648a7
+e3cdfbec4940dd075b19f0e40f95cf33
+e3cf103c84d7759a81e4616a1bf310c5
+e3d7a66f7a236d87fff6f9952be73355
+e3dfb56dcf6b6c7f9e37436d9121cdf7
+e3e0b8df2e15f360479ae9c64a5a35b5
+e40252054393fa8482d8edf63361b2f9
+e40b151b2ff1f384249b6ea08fc58a53
+e40ce85030c5bde4f0f32689ab921bbb
+e41566b8fcdfabee3e4a357f96de7490
+e4167094acfa90109a3630e2a487ec43
+e41cfd737c544bce19a24819ab0e6907
+e42c8358c081203a6763385628a9f495
+e42e3a759a7cb51ec7f53ffeadef724e
+e42f0a01a0653b167d699be1177f913d
+e43081d357b537cc4558fa711bc012de
+e434e96cdf8a8766db72eb322dc10457
+e434fd64dc1cb7829af5edfef1459c0a
+e438d559d795c5e2eb377c5aec9c1bb1
+e43eafa75d8d8ed3d536349c3c93b300
+e446fa34fc2cb59bc9e9b03af21117bb
+e447981117fc89fe56f8f5b819c7c808
+e44856b3ae33f237c1a66f1029a55b56
+e44e4de9a0d8c8202b9a4374a812c328
+e45340763a211b17b1311578539a3bf6
+e4579e5fdb91438821c4a7b87ea52346
+e458909a946c512ce1f36935e7b576b8
+e459c234d8c2dee1683143e857845aef
+e45e5e5daca690d403ffdc2ec2f2f53d
+e461800bbd6e237819f0d11cd850d361
+e466536fdb69abb1e402d6d87da08b41
+e4670871ca2c34cc221e4523b12623b6
+e46f820f4fc103e947bc06d94f3fca6b
+e474746acd66ed666d2d45b16eaa1ffe
+e474b1ad958b6621eacef7407d388dae
+e47d7f331014a7bee769d714d26262c2
+e48118d18eafad8aab2c81dd132f173e
+e48312cdfe66a345a0e1d8e5d05dc86d
+e48d4acca1e518156e34e560aadc44cc
+e4921478b733ed83f21c48af9b60287d
+e4942458d10835d2519228866275974c
+e494ef7935e692c16b5a944514a9f5ca
+e49682ee83c07c3d791d5425890cea5a
+e49a9164573da7eec2b4cbddb9c238d4
+e4a12ea0d9bb8718a92bf5214753b698
+e4a5903794a779ff79880e395283f865
+e4a702cd3ef76a98695e45851e2839f6
+e4a82a117460261beae528291a864c13
+e4ac0d394b4ce4adb2fbd190f9bc480e
+e4acb8268aa897f97065ac62793ca109
+e4b0955aaf1bfb3b1b35da286184d431
+e4b7397e0baf78723737fe74dcd1a509
+e4bd7f8cad4c7cbfb6dd3276cf6c224c
+e4c85da362f699c0d65bab4a8547213e
+e4d34b129414d3f684447a345c1e7d8f
+e4d8e3b300f7bb4ac3e16d5c51a7c859
+e4da535dbdd477e9b2e7c8aeac402d74
+e4dac23780f9ad51a82e20b4d96f7a22
+e4dfad3f23faa1a46ba0fb8d5a1d7a22
+e4e37013b31f0cbb254a402f75002e36
+e4e962daca4e0de2ea6888dd284ea863
+e4f9557e6c90b5b4e7b8bb492cba5257
+e4fc9ef16e0590b7cbff32167c9e1170
+e4ffc798d028319aa627d3c28c0851a0
+e5006b220f7ff16e35ece89956b31bc5
+e50471920608fec75df91865b0d37a68
+e50adf7eaf606c1ed50f8a5a6906b7fd
+e50bb5abc6798c3d26b883ebd7fd5ace
+e50c70b32cbbeabf8f0472989357a363
+e5127b63ed3dd69583def2afa51fc542
+e5134c610bd2091b4aa4377351fedfd1
+e513f1ce95e843522f8ae8b1393fd5b2
+e518a4c4f341b15291f1130bea6bbd1f
+e51aee761ffd39e4dfaed4dca5335962
+e51b9e8526a8d5b41d7012a168cf0c26
+e5271a5ca6303e6a00894f788528e04c
+e5291749f9bad0b0c692ab8eef89be9c
+e52df4cc71b34d278a422a3fa1a250b2
+e530e8c9abb9d2138ba6536f020f4828
+e533d3e5a2a1631fd44c6aa8087d8589
+e53530f63447c911b5572fd9ed0ad205
+e535be89c7e8fe68e82992b672d65ec8
+e53cf13cacaadeafd23dc5644c359053
+e54333be78c06261c81519ad04ed2434
+e550ee59b9391c3383c2d12684bb9674
+e55d432722084ce27db39b26282e2440
+e55f70dfefe38daaa5c7b8e3ed403039
+e5619542de23dff6d2d2e210d4bf26ab
+e562c54cd152e5ca0f5f6ee92036bba8
+e562db0826f14359a25bf6dd2d9aadd2
+e56578789e7f4338516035a51cb85eb3
+e56a110e77815b4afe38b0e5c3b45fe8
+e56ecbcfef35cfc312bafdeb320acf4e
+e57373a5f7220bc8349df6f32a6eabd8
+e574ff2bb8658c7d282f2c46bbf9b983
+e57d06f40f50fb622ad1e9ce8706af4a
+e57e52fb37e7654422f59528cdedb0f0
+e58111ccacdd78b2bc51e19a4c060dd5
+e585c60321c8beeb44fce427da548870
+e586846f5b0d459ec7c720a3febd125f
+e589904a23de9eda6fce42dc8ab60567
+e58d072aa639557f1e5088b55acdd320
+e58e3cf6cdae67e6a0e3afc052513cd5
+e58f79a87ab2c886094fb3627e360f76
+e591c56738b10291a9b5f94a34638839
+e591e1cd123c401d9961ad14caa6b3fe
+e5934f62c94a747fb0d12895844ca6d1
+e594e276b92a616b5924fccacb16ab68
+e59a6238e33af6068f4b5f8c5a83e0bd
+e5a79e07b1862c517dd3de385cd75e31
+e5a89ce3fa86f38dcd944655431189be
+e5af39eea58173567f5e6d19c9170eeb
+e5b05bb8f2788b32cc5745eb81a17800
+e5b2c88cd4d116dac0af512938f67f64
+e5b4288a3ab64569992ecab8cfce458c
+e5b8abeea023545ffc753b3a1a50fe11
+e5c6c2a41430a2484bc5a1c3d9279171
+e5c83c14032eaa0abf90774d0e780b4d
+e5cab9bb6ef849ed4e3d2a1763cc22fc
+e5cf58b5b0ab643c8142e1022451ab04
+e5d028d1720d6fb33d93e71d18925187
+e5d57a7389509e369373217eff7c5977
+e5d6766426e2e1b3f9de50c71ae1d77d
+e5d7281230b6779e85167ecb426e1eed
+e5da6cfad43f77e7ffb3db0f7e2885e5
+e5dbb5e906c6ed23a4061652a63d0d1e
+e5debc22bbd3e9e57f4046fa5d3df3cb
+e5e11b2163fa91afd5ca01fbe967eb2d
+e5e18cc92c3f47666816071742f9ac3a
+e5efc49ee0e69029d4f2d62981261dd1
+e5f3cb30c3fe02eda1c5bf4a08056356
+e5f61ab0ae1c5ea2a5a7a86e1a23f6db
+e5f64ee143f76d964299f17ca0d270b1
+e5f9779842c967282f4d76f6e203508b
+e5fbc2e55f647fe098760778d75f0bf4
+e5fcec949c6fd1da27d5c03fe42fde48
+e5fdab0994c53495e611cb6727310641
+e60cb6a5e50ae470d36a89056bc6a077
+e610a94ddf698913d6b0b2297f622223
+e614868521da51859786eaead8328701
+e618bc0ed55a72d4020623d86b283e39
+e61f7734ca8c0f2fcf9b166411dd0e54
+e621058f22ba4a6459d8c585690dfc9a
+e62200cf708bbf3dac9f3d699f2f12e6
+e628ada71ab7b6eca855f8da419f2905
+e62cf481753b2900b594db6f1f1df99d
+e62eb760373845a4ca9a60e5b9bef647
+e6303d5059ca664d624f7cf4f551c5ad
+e63077712c3878a0dd9086bc4d8a324d
+e638e84fce91126bcc79dda42a13699f
+e63c90127c38ad79ca29ab93c8a61bc1
+e63cb71bc2ca58ce403a30fe2b18b442
+e63efc508f554df4c5ad29342f9a6800
+e63f5e7ec880a2ee555635a3f35f2393
+e6414e1bb3201197314608688b337b17
+e6492cb562112826cac6836ad81b26f8
+e6498eeaa5a9d1687d319bb25e9e3953
+e649d0516bfb58c566fbf6fe1df04d5a
+e64ccee491e84c2309feb649b5b151fd
+e64f0a44d5ada135dfc7503d643c273b
+e650e0c7ff9c8cebf76c901dcc0ad52a
+e65375f900b64a664e726344af7857ba
+e65bab9335eee7afcd5177379a8b3e59
+e65be4f9ead08ec0527043a840266f16
+e65f25a8d168d8005235ca40c07c5348
+e66108cb04944626e1e529d71417584e
+e6734fab79d6b73478cdda9f80220f2e
+e675a5c8b679e4060bd2637a7ab0a73d
+e67cd7e2b182e1d401549036575d2980
+e67efaabf258338ee3a007b18344ebc1
+e680373d27b3498a4af2fff2e060a6ce
+e68606096ed60d2767fca3eec107f828
+e6863b530b9d52e24441f41cfa3a9ef0
+e6895bdd4ab555cac0e088f856c8fcbe
+e68af299d861cca9c55b5dcde3825e50
+e6941a00eb3b15dc874645b744b4ef6b
+e69468bb91083ed483ac4b4cefee8e1e
+e69761ede92373ad876e59bedbbdf472
+e69f8cffea3a8ead836963758ecceac9
+e69fcbf3b1316ba060805f0f2ab138f3
+e6a638e317453e0d7ccb39882b6dfa89
+e6a6d6895cdd319eb8aff36a163370ad
+e6a863173938fd9ea21d418667fa4da0
+e6a99510fa7392ceefd191079a91a82b
+e6aa96e6b230548d82d48417d94a19d6
+e6abf3896c51c9ec3bdfd64133c61fe4
+e6ad46a4b4a8002ef9eebd0dde8c74a8
+e6b13481dd372af1a7898a9c04cef65a
+e6b3dea55ed1b04d58491f8b8a57d872
+e6bbf552069f36858e5381c81b7d36c6
+e6c37572c78fe7f932720f1af00d15c9
+e6cea313b0c2b1230908d8c34163795a
+e6cef5b2d261dc3f678dc1d07131c886
+e6d3a9ad43736a33d803097a9ba5b2a0
+e6d3c2064cafc85de6746a1d6cbbf572
+e6d4c4c560bb4fe833531ed3e590b15f
+e6d580af8fe4858e318728c77f1049e8
+e6d7d2133c68c24cd581e95357b1055c
+e6dd3f82676bf2ca791fce9d8b648d3c
+e6df4435b64a9c99438d3e1ba1c95630
+e6df6feefd00c23ce4989ac9010b7292
+e6e2e1cc646ad7c3a3656ed329a7a901
+e6e7918cfdcec340cf7fda58620e5071
+e6e9e97801f4776916acc0c52dd6031c
+e6ee9a5fefe704f3fc3daf4f05720fa2
+e6f9dbba94ad6804171f2bd740a60923
+e6fb8b7e7c69bc4db0d430b1115e611c
+e6fb9ee979c8871ad0337ad752d259ce
+e6fbd802b65cefffe9b25ed27bd324a1
+e6ff888ac4d54284ca1c677471a65bff
+e701b96f79bb348d97f1cd439a8c032a
+e70a55ec6061c465de69646a0423d2a9
+e70bbd59a1ae4e88d43355ae110466db
+e7125cc74539071479fedce554eadbe9
+e712834f26f3e13a23acf2b3932923ee
+e71297cf62a6cb1204e7fec89959d7c5
+e713100f9ee1b398115eb223b2c38b4c
+e714106ce672c954723962c13449e040
+e726119a397264aa2325a20f664567f7
+e72b48f647b908b68a8d11a210f81deb
+e72b8ef7383309380ba914b5019eb75c
+e733cf595e317cc06b046ed3ebd32745
+e7393c8abae8b8676deebace8c6c711f
+e73d6ba8a713a5b70ff3a3b9ee8b12e3
+e73fc1b64daa6e23fa94be10701a991b
+e74678f410bfcc4e5c599180552a11d3
+e748a8c37c6d480b48d1d083bd2532d0
+e752121dfc0413366060e012ff4d18d4
+e757477a031aabe890b15275942153b0
+e762247960251a8018893bda034db5a1
+e764927021a9fdd3531ca6abd16beab5
+e769627a2947acd1102a546805d74c32
+e76c3353bc9edb77064735033f64fabe
+e76f242e5f3c4c000063421313d9f2d8
+e77244eb3f31876eeeef6b636aeabd86
+e77a0fe6aee9db67a545ddfca3b870c9
+e781800704b94f8d6439dcec652be5ad
+e781ed41ad214c5385c52673f740ceb7
+e787aff2d8d4ab52702f99fd40b76f03
+e78811d39f6c5caecb610567a757f4ab
+e78e1dcb5b50be9a5f5e013af70a0a1b
+e79d3bc1ab4a9ca8ba826b60330d3b9b
+e7a079e0fb75a87a50ffbbc525f0fffd
+e7a4382d7f3851aca597c59948b3a0b5
+e7a4807599066785c7ffeda5de96c671
+e7a4c06dcd65741d51d55a952538afb2
+e7a9d88b3bcce0d985806741522aacbb
+e7b74511ab42b37a68994edb3831efd9
+e7b82e03c7206b1565db2feee33c4be9
+e7bb1eb946107d5145f5aa1af8ce73d5
+e7be5939e1ba730be787f71d5b2f2175
+e7c1c32a9e4c0027c390f589ed274450
+e7c353325464cd49ec33a82c7be32fed
+e7c59dffd86e3b52e3d249594ae50f1f
+e7c6100cca0ad0ca23a4f78bd6ad0ad6
+e7c96fd65a3a22a237951704d6fcd05c
+e7cc08a56dfc1d841267a67a6f3c6e1b
+e7cf52ccf3ee828d918c9e8cfc361471
+e7cfe4208784fc2b05e2711e27329724
+e7d020cec12b1307e390ce4c67b315df
+e7d3874b605300e156b102a0de4b242d
+e7dffb2f4dc326d0ab67ea3ec258c690
+e7e5993bc230140cff23adf73628f0b7
+e7e9c5a0cd1e5b2f3aebda8d0786fb8a
+e7ee7e382a0c90c83f183c3c8ccc2445
+e7f14d64630247db8c909a875e42fa4f
+e7f460706167da81d5ffa64de90239f3
+e7fb3867822fe4d5bdbaf7e25be31a37
+e8006ffb5834e3a4b211287fa351e3ce
+e8011d85bd328ab8ae6f9b1e450f9b7b
+e801865f09ee0a2c223a92aa1899a629
+e802d2d1e17d864a8ed83aa92ac1ce5c
+e803a3ed9a891dfc4aa6cba0f3f88138
+e80543d34b267c504b14aa6801dc2557
+e80721a3620025732d0a40f0dc6f345c
+e808cedec52cdc27012ec60c3b134599
+e80c6e798beacccf0090fb5132e5ba23
+e80fbe3b51ee37c354e64dcf56f853d5
+e8170b6cdcdd12e0052cdc2c12085d70
+e819060a92d73bed455b321c84f53790
+e825b0b0d0308d80e127d942bbb699c4
+e826350decad7c086d4bb2c60beb354c
+e82db3242c894325ea8463eae5a00820
+e82db718c49b171405c0f1cc0c41317b
+e830571032c6ecef920f2143fb0ff476
+e8336976f4dd2105db684450d44ba4bb
+e8381e8576911159830a1c235542813b
+e83a214dd89f4b726e693dddc9412406
+e83af9b10ec171df91235b5d199f2a40
+e83f557f7715b00ae301c3bc793e787d
+e83f8f6542496e3c9a783f4d44ba9d5d
+e840d04c3892abed49d0f1422ec54085
+e8429119d8c1081609db3d227e7945ee
+e843ea49f3b248d3e4938c16b8acf6d4
+e84426d4f9d77876d6d6bd56e6c67f20
+e847147b25afb9a415b31aeaf9ef2833
+e84a736d8b28a49d9753983df14b3988
+e851a7ae33e07ea0e7e2f380ec11593b
+e8534731d29418164f09f88d9b4c0279
+e854a54cb051f1d23a5247cc05f377f4
+e8558c3d1a39debda0553ff720210458
+e8579c2d01e9bdab30f6809324a8cb78
+e85af7abb8994417eb19ebbddf3e499a
+e85ccbe8816235800628467eb99f6a37
+e85ce98555b4d8cec3cbab58559eff5c
+e85e55d8fbe35941c8170e9a9f39b410
+e867186297d9b15372df8a9aff940a20
+e8712d537ce74d94cbd88fff78542a26
+e874d7017e2143fd352b2a5ef35d5791
+e88035056a27f023a799a9ee7246edbb
+e8810bc91563597fd1731562312d0a91
+e883e87904754aea2c5681b704167916
+e88453586ab588908a17e5b3ba6b9b45
+e885d8342b55f1174e99f57c4331820c
+e88e8c32825c242d3fcf676059917948
+e88f3b791557f217aa8f559f959da9c1
+e89f18f0e6dafe1573fbf92f71efd8dd
+e8a06bb2bb32f8a00e296d6dbdd231a7
+e8a1106798486f5783cf43c72381bdb8
+e8a1a322c4a2786ac1395e23aa598d78
+e8aa155fa57006b470817d1dc2777904
+e8ae025149d061d4752b5e2bb17fe953
+e8affceea88cbad27f88d2594c233314
+e8b074752055a509d31bf7385f377103
+e8b507a3f0abb142fe305be73ef58ded
+e8b93e07a14e7f03166a2addbbff1327
+e8ba003ef8549e909e75465205d75874
+e8be95f868fa967b9cfc58b3f735b51c
+e8c3175adf7ba8cad9d69fcd61ec6fa2
+e8c586885792aea6bd1916bde997455d
+e8c82779db22db188b8fd80214167397
+e8ca10f05cb76b4d213099528aba87c2
+e8d7fc269594d4fbd790965c7ce19e76
+e8dd6ee4c7e9bb74aa2c83201f6e5c16
+e8dde1420f3cb489792b7756b61e61b0
+e8ecd0380d1bafaf0b796536d7c7fc67
+e8eeceb162e8213648d912dfc17f2a16
+e8efe4c5857fc6176e0dc17a917a7668
+e9075ed38ae51209b2645d3b5af91230
+e90a116018a8384e9bb1783fbcc2edd4
+e90af448f8e23790218d5688243d56c8
+e90cda6163c68d9a0c35f6b9f9eb5b85
+e90d0180c9cd3bf1e1fe6ea42503fde3
+e911a7093e3d6ab250f1468932599d84
+e9128d4fa22046799efa7de7956b5b3e
+e91313fd91feaad254ed23070a7c0f1b
+e91a144d12b885df10c77375b6d1e0f7
+e91b593dc41289b53df8ffd7e145baa1
+e924ef3c2fc3f189a04e665b33c7d248
+e938857393c688c52cf3627772ce0eb8
+e93fe34e7ee155c79fa27171ed02da23
+e9416ba3275a47b24e1a4797323a37fe
+e94770d16edaae92b154e996811e09b7
+e94c64ebacc8431baf9f6f17c9207abd
+e952fbc0749b8af931f0db3e6ff0248a
+e954e13600848c6bd508a33d1b6c5523
+e95f429f770c221123726850b8ef6e6e
+e9641714540a2899b873cc6b10b8608d
+e964181bc2b1f9018e5f15a88676e95a
+e96845ff7c99ebf4220d41ae11209e4c
+e969b504cbe54a2537843d36d8e46ec4
+e9751e17381af314cd65f4c32f64f47c
+e976fb79b4d98284b0c8c95b7db8f1fc
+e97c4d511e639e2eea75c72f45e8c571
+e97f8c10965dfa4634780c4d54a8b9eb
+e9813099efadf886ed3f70266573bdce
+e9833e1428eaf52674068b01f20a3920
+e9835d7e0ee1a64dc662b9f40a3e4031
+e98d15adb06e4361abd787fa196ff628
+e992dd996485bab3f48a9cc41357cc04
+e997af7c57ece4ad3885b47deccd5380
+e99809e905d01337fc84a7c3521eb49a
+e9a238ec51d656266c0d1e1dcdbc7c86
+e9a763f3a02d31a83cf2a4f4efe703db
+e9a85913a527412c129cddb23d5a3afb
+e9abba148572d9999962128f92578e94
+e9ac8769715d5c5a92a2fa5b6532d24d
+e9ad05b2506387b635f12f212ad7e87d
+e9ad3bd3e05ef6016d774c6941f34b13
+e9b83482bc8aa0fa0d23beddf2ed9836
+e9ba80a2dd82e63b972b6dbdc5865c95
+e9c0c7f57fe22eff13833bd4d6e92eba
+e9c0f36fc5965a44664f9879db8a09f1
+e9c46256e11b450478c7b677c3bdab3f
+e9c58211f982922acc679fc370d05ac7
+e9d3a3515f8a98f06d0539c84aac522c
+e9d6486058f6b71ef53348f304a2674e
+e9dc169abd52e93e49a519155be79a7d
+e9dc4edfb3a49d5142dac341b1178643
+e9e04f85b35ffeb16e62dcedd5599527
+e9f60b81103fa0fcc9d1e467be9d5eb7
+ea01c443d06c6f43b0d2f6085d4ef217
+ea042f2a741a247c55fa04777a8ba6fb
+ea0e7a20260cc6956e5de6dd8949b41e
+ea0f14462e6a6b28fc4424c2f791a270
+ea146cf3ca918b38f549b09febd9b359
+ea1ea0fe3995a95bd9467640d384dcdc
+ea2418453e907f62815e5bf24c61d59d
+ea3303006cf9682e6cc92d1353c9d924
+ea34523fc72622684a90d8618750e1d7
+ea3572ebad77d86156ac9786934f7418
+ea359305a42de000e7928dc4e1a8b37e
+ea3cd9594a222cf560cec820d6b21e4b
+ea46d8209b3624b5bfe1fbc50f2edf5c
+ea4898c72e871d366bc99b53d257ae6f
+ea4eecfc638d6208b80173c78fef0804
+ea531aeb7098ebeab3cd9769777e0bc5
+ea5c5909cbf3948a720a5b7b5114a237
+ea5d73c1e5acce3e1ed748e2890a785d
+ea6082c71913bfe5486ca3faaf65c7f7
+ea6218de12db3ab4a89b19eff7ec80f3
+ea64e480dd6b8fd9ee0fa24841de6711
+ea65959bf5a716aead6fba13f634aaff
+ea711f363601620329b55caa63540add
+ea72e0d8103cc3219da363b7c91ffd77
+ea7389994b09372d484cfdfb67942b92
+ea74e07ea5c6aa53584dbda6c819f141
+ea7893effb2fd2a4dfc9908733c85f94
+ea81016888c288f55ad8f763849656fe
+ea84e942c01ca0261280a1303549e5e0
+ea88166d4203952d2d2241c7c437c2fa
+ea89a393b666e752ccce285fc1da41dc
+ea8f487b415b607f0217242d0b2eef38
+ea8f70bbb2772a842003ca99cb31146f
+ea90cee9f556db8ed4362dec2f93c980
+ea9b19d9c83633b3e1e018599754d478
+ea9d593687a9583b1338411e4cddc833
+eaa036da7fe3634a50130217e321140e
+eaa2401df8ab1a19c7c69526bbcbd106
+eaa6abff5a4adae8e42e2c323dd7af63
+eaa6d1f6b70197f464ba05b7a5a7b558
+eaa7290dffd8180729feae2eb3fdded9
+eaabc63a57edf7362a72e3b396bccf91
+eaac83e0bfbc36b788bf80d07d550565
+eaae9e76a931cb2c0e9bcc45af896116
+eab367ba94d10d2db701115725627f43
+eab9f815c995e65877d2eb62650aabc0
+eabf1060cf023287e121cc7cd17e68a4
+eabf64ce017315be375c90dc6896c3a3
+eace0eef94d323e137ab199a486a0e17
+eacfc8393e2836278b9466b6bc836fd1
+ead4bf399b7dbd1b4be4b9727cb3aa08
+ead588f2dba3ef699573a125b4f98df6
+eade17d99b32e7d0157d9958ee5ba6e0
+eadf0478de92c7fa5ae3693d53d639b0
+eae69929e8157eea4f78352feca21297
+eae9887a6c53d18112ab340aa4dd1a5f
+eb011db7666fa82189396d52311b5a5f
+eb0293ef67743e4d3cafb67f78bbbada
+eb069dee47b3e12272bb1298cae095fa
+eb088e85a158f295b8cf855cfc93c553
+eb09012dc0d25c6f29ea8d215ccfbc61
+eb09a3ee1cfd71f23d5e0cda8c23f868
+eb0f103a9fb95fb00ce936cca6efe986
+eb1101552493ecbd2cbcf9f5933e1e02
+eb123dd91617d541cd9f40f4d649a764
+eb15a1e27856b94a24750951861854b4
+eb16c9cdf13dc653edff10ccef9612e8
+eb26bd3c710ace0d2020630ac75eb3e6
+eb29f9293c10afb7f937d9b729af9c80
+eb319178e8b6a2c47f1acb7735011c50
+eb34de72e0046fbb44b1da8340b56a82
+eb35e2e0e897ab027a3d8aa848e6c721
+eb38285142dfdb56757ff23df7523e89
+eb40b5bd70f4f834d4da99f560acc693
+eb464dc781c64aca17103bc92ac6e9d3
+eb53bfb7a4cf1304042157c0a80be377
+eb57130470b8029aa8fc9cecc059b34a
+eb58a291a6f7a36b87911372734cded3
+eb594d74c073de6b7dcb73c5dd8a7d4a
+eb5ac7b239fe060e7b531d2447ff0072
+eb606f024488bd96838adc6506f2a4fe
+eb61ea35cdc47e0d8da414ea9313f666
+eb622b510cc4e606524200b048683b64
+eb657fda8b533ea2c5b3e05054eac679
+eb6d3cde5cba8ca58260761aa0a530e0
+eb6fe4f587453c3fe9e136734722cdb6
+eb72dbdaa5c0406d0e9d034c209ede72
+eb7a41b5c044c9e97adc4f7f04fb95cf
+eb8005e17df1ceb535a3010e14842f14
+eb8306d2243adf671a63580bd2ada29b
+eb8955d1a0290f28d90d471d80ca1d0e
+eb915142f7e077ef68e80607d3c1d702
+eb91b3e3975f0d09f89526365f46409f
+eb95d55daaff567b73e7648f1d3f3714
+eb9a1427a9a90c245fa729b26b2be9cc
+eb9c4a28baa60e6961c3518a3d07d526
+eba19ea3c86201cb68e934e8cf291816
+eba5860f8aec590cbe911536aff1db8b
+eba5d25e206cc1f6e885b9efcf16018f
+ebab373b279f96da69af44eecb051f27
+ebb34a6cb0e4dae1ee0dba61c94e09f1
+ebb5e094606644e8e897203741e9109e
+ebb745e6d202bfb13ce81c3e5d4f2a73
+ebbf6039ff4ec149691fb8007ae2f868
+ebcd1230042235c0a064e22b49a79041
+ebd81f72ed40aa52a67eb4fa19d1e40c
+ebd8270b2fdcf20abcd9186b6bb2dc1b
+ebd827b6f817ed620a991e9af0affba8
+ebdce99a6127e79d405e3a75573e025d
+ebe21f1c3496b5a04bf753f6393677c5
+ebe5f98255ae768309044ca99f1d593f
+ebea63ddc966d99ea66323c7d89c103e
+ebea92c5d548dc161df65c45dff51308
+ebeb64b448b2b161bd430f2c08f50f2d
+ebebfcabc3f9c343b9b5d9f3125e7446
+ebf7af608d1aa72d33270af812a30d1f
+ebfa5e2997b0ca24fc2e57656400f6bf
+ebfecc270ebed0ecf3534bca8eeb26dd
+ec0399fddedfedddc23113ef1140a0da
+ec06ddb7dfba6fc54ad050d2954c3107
+ec07d049b6201670233821841d4d2a8c
+ec0b63e34607b14f6d40e2880aceca21
+ec0d0a1d9e4dd27c898abb783f8c7647
+ec1712141dd457351235b6365993edc7
+ec2920abd7aaac85f27957fa5c422dcc
+ec2d78de53b2e1d28fa542ed373eefc2
+ec387e7aa60047de01e1eda32979a535
+ec38931fec026344c00eb76d9890f09a
+ec3f0c075ce7c05e929372e5acff2be3
+ec40969996f9463ab0efe766d84666ee
+ec420085d52414c73fb950b763534832
+ec4ae0a2e17969ae0e14b1913633410c
+ec52674c82119c5335ec4f59106d6519
+ec5679c3c9c0838da87f4ca02de47ea4
+ec56fdc025fd671c70eb79e93a6d5e6b
+ec5b097bbe01d1629fc10d33fdd809fb
+ec5c055e8e07faef43cf027c93f2ac63
+ec6144bc96b25616ae5deeebf56b604b
+ec621d602552e74c25727a825db5b397
+ec62c9ac2160965614c2c7704a68479f
+ec722ed19d527aa7b7779df63f320c1b
+ec7b48a5c985d68b4dbc360b68481b4f
+ec7d3feed7adf98eec79324ab0bdd455
+ec7e5c316215c7003e2cccb8f02d2738
+ec7ec0fe330b84163d075e5cacf34891
+ec850482498b6a9ea265093deffa85bc
+ec86105cb3f1b25b1ffa5b1ac44bfee9
+ec88007b20815ec87fda456237985aa6
+ec89eea7862342e13f2d58869c59550f
+ec961c6133c11637f0a24e8529fd8b86
+ec9d11d3729062e012489830bb568c49
+ec9ff1773b843f396c3d30b60b5a3baf
+eca4278a981f64ab0e3a28171cd6f1ee
+eca58849b783959f6e4eb45d97c18a74
+eca9fd0ef067cdafbe9fb57d1c0cba25
+ecad1fcae6c1a82bae09264e82459f88
+ecaf92b2b1928e952dc8c3db3472cfa8
+ecb3763f2fb89bb3758415673de6af43
+ecb9cbf8b0d61edd06328509a07b1ed7
+ecb9e6135ed35ddd7f3551bce1763bf3
+ecbba9799e895894f17cf847d07fc860
+ecbc76ede79625253b073e561d792797
+ecbe1f419a203ff8b64d3bf42cbb7c55
+ecc0caa8264b12925cafe2639f6574a9
+ecc17a8f558f47c0b2393111e58c208b
+ecc2e2a0bb3224944939fad6999e14e6
+ecc4aa2c78712f9b8cabc78230e59bcc
+ecc5677fa8903bc55b1028dd6fa8797e
+ecc88cf6faa820d4c0ed5d8f2de8aa51
+ecc8df3acebb436c87d21a91b2b42374
+eccaeadb9597f6c2a457057f12da8d35
+ecce00c2df2425c810e704f5284f4bb1
+eccf0cfbf0aa74ad8b6468967a4d6aca
+ecd1700135a39ddfb9143f05279ae0ab
+ecd9ba7a7bd8c4cf777bd0bdacb89e01
+ece39d32213bb85259da3c35ce531cab
+ecec33d3fda55dfad62b03593b4a5c82
+ecf39d8b4d226c50bb6485da2bfc641c
+ecf61517c01dfbf2d4852fa35379d887
+ecf74bd6b44b3429a513266420ddc40c
+ecfe9e42f0fb3a81c27b906bb0dce61a
+ed037fad58d76f67b522ce84839a8779
+ed1604193cd33b6ebc197ef4e59d6b38
+ed1608ed641f7e4f00fc4bebaa7f38d2
+ed1c4e300bce98c0683382e76317ad6c
+ed23fc67243f9950e0b21d5910595208
+ed290a55a3a4d2de8b08d11715972375
+ed2f2c40271ca7ee7792c2824e09ce05
+ed346c869699cf46fc00d01c539c89ba
+ed35bf1cae60198deede544b43e4854f
+ed3dde1e493a260a0bc01f029de66733
+ed44009a1a89f08c823dd2dc63db6238
+ed49250a18c9bfbcb10557bfb5203933
+ed4ecde49277fa4c626f5f7ba19a6709
+ed550c928a6a45f76cd5da34aa98af61
+ed55ff6d0188682675954eaec21ea360
+ed584bcb2c2032392fa9991383e7068e
+ed5f953826c43170fd9027a9109ca5d7
+ed689fdd283e2bc42bb935222dde2e25
+ed6ef0412472f41540426497bdb7483b
+ed6f0ff961acd7a0009e9b0631858b99
+ed7635b640857ed5a329d2cc22bf2a21
+ed791857607121cae3da59a5d50d9eda
+ed7acafb9ee2198d3c1d81a71ac5d1e7
+ed7b045b5661fd9bd1d440a457140d54
+ed7bb7519faf689d8ccddd0b8c739a0b
+ed7c0d4d6498af2f6a1f820cc0fb3de0
+ed829807bfb92dc6a4ca5c5c3b6ca984
+ed83795a5d14fde17d911437df4ec9fd
+ed89f1427ae478246dd331350effc303
+ed8acfc3e46d3c50c4bc8289a49f0144
+ed8cffa2cc73f5bdc89d0b3bf80ccc4c
+ed920e3d0ded8cd646294507f6ea2d08
+ed96de03a650134f32e7133c265a068e
+ed996bfbf24580fbe5d951941f46b2c9
+ed9b96c510eb62e0e066b91a067055d4
+ed9c8d83511f48f086575fe5610e6775
+eda1a0340a42a041b5ff47f0788dcfce
+eda57f4fa83b5f9c52e420f67b05179a
+eda58a7dbc5bec6e5d1cc44a235f5bda
+eda5dbd7053463be40e95dd68f7c2672
+eda7d7421fb548971a8059be90d0eeae
+eda85ec28a76326bf6eee98942abf15e
+edad549efe7071a306b442568b371dfc
+edb1d6db125d2238f1b0261571519120
+edb291cd29b4a9ce98dd1b7d5f8d46a2
+edb4510fa6460105bd1def204c4e0e23
+edb85989ab9c231617f1a6c8cbf788aa
+edba128c9585da288367b907f0c385b8
+edc5f826f0277ec9e4fbf2166c6a6759
+edc8fa8f948548b1e7a46c35aeecb9a5
+edcdd20e4cab62ea6e7261c228e9f5d4
+eddbcb14280502430c213441f257c3d7
+ede22fb0b4459ee288ae204defb4d1a4
+ede5ba329320213c6224889ecc6bf23d
+edf1e6b4fc221d811a431750ef70980e
+edf464a5ef51a499c8bd639322cfac60
+edfc1e95fad851c16325a5d55559945b
+edfce63fa7af2390c4aa18fa1a1b0c70
+edfdd8224c976fe2dfab797a0b1154a7
+ee006c8f86ec5b7ef627d5e56ca0b44a
+ee01eafa8620fbc671103a9f608ef159
+ee0556236d0cbab513c97baae5ed2abd
+ee0cfb628cb3c0934d3fe0ac9deead55
+ee18ea954e40247873784defd8721303
+ee1a4b3552fc828bb53c7f66b51ce61b
+ee1d921ed1452df8a6112c99ae43b99d
+ee1f581abd53953f5d8fd508a9d2fb4e
+ee33c2f4a8baeaf210423dc26acb8041
+ee38f739a19c3aae3e0f32d771f5e393
+ee3e19d4ddcd009161b17f14f4dcd46e
+ee4f5aaca0a75bcb1b1886ab8b67dfcd
+ee57fc881012647b32d6915cb9ad1ebb
+ee5db9be5a19de2a8da55168e0a9a83a
+ee6c996d6ac4c1b6873ee3d90fe04202
+ee7040afdf185d951a67a8d36c277ab7
+ee7067c84ef21e458438beb133d1db83
+ee75448142e288a0641070641f4e124b
+ee77886f08ffbce7eeb0d4557371bc80
+ee7af704370b0304453de25294b7a709
+ee7fc988719de4fdeec7c9a8034849f1
+ee83eb93de40be686c45ba5930818c93
+ee88c17517fd5f46c9ce709abc3b4f6f
+ee8ad0cbb240e7b4feb947a2a22bef83
+ee8dd3cfd9a067a2b25a76bedf35ca0f
+ee95077fab6349c1249f4eb3c5072c63
+ee96d4ec7ad26c16db43561bd98fabda
+ee984630d377560f36857c9c4ebed81e
+eea0b03cf79875ddd0b7f312ef3f7026
+eeac79b6584a965f69acdbaa4d20c725
+eead89bf5095dd517b7497fd236408c6
+eeaff5e8246c7d32d64725165dc216e1
+eeb2f0a832776acde876a2e9725dd52d
+eeb2f1bd8bc40b2df59d450da924acd8
+eeb4ab9ecb929359ad6744bde33c2541
+eeb5d415a6f79e7a799935ba9bc79ee4
+eeb5d6991eae35dc4c9a29fe0aca043e
+eebadc6807c38365c94ce34addfe2f68
+eebc8d45dc3ec5d79cdfb7a53e1c1cf1
+eec97d595a9b567e284fae84e1475d98
+eecf4a8e341798ad86dad2721f47ff3b
+eecf6189ee351308e4d0d65548621887
+eed06327e26705e502489eb9756d6c5f
+eed13184423c6c57942059634fb42868
+eed471f1ccbb63ad815530b672ea3f85
+eedb9ac2e82615476d14103baf361899
+eedfa45c9dfb702a475ee7012e1de357
+eee701b2d127ede6c8af5d68a1e70340
+eeec1522b596f21e8194bd65d3db3a38
+eeedf5e90f6a66ed873e37470b874009
+eeee0466cfcf3b389194886dacc46ec7
+eef178f7fdef9f7fdb5cfbb0d5ece62d
+ef03cbbde505f786fdc22b7557673661
+ef08f00ef8fa24563164859440a0b01b
+ef0b8cc4d9fc9f2453d2bdf6139be783
+ef0d3b02ff4f9f789f782800aa332411
+ef0ec695047e96e551c05368131a3cd5
+ef13040b37a91449bdaa05bdcb49c9ef
+ef1330987fd12835dc1b8633ff95ceab
+ef1405deee8e2a1d4201ec58b611ec61
+ef15779facb0ab1d375136bf7c97b42d
+ef17d619ed663bf6ff7d8739cd1e330b
+ef1a4a77247512e53356e292c8619614
+ef1ad4146fe7eda8b88a8eee1ebcb134
+ef1f215f9b78073cfdba59e761f7aa5b
+ef201b0c0671841f1477e111fe61c78e
+ef217a74506c840f5cf1132c79b5869c
+ef24caebd516c961bc765095a5ee4d21
+ef25af4d5f699871f89e93e1fe03c682
+ef2c517ee5cde8d29185238f3d1e3967
+ef2ced26a1c9cf1e16514d5d2520d324
+ef2f17db436cd2da45fd7d3d8ad4d10d
+ef382e9ab8dfde24dfa96fec50f08c4e
+ef423276a20d1e06e815b308d6f38106
+ef4522b94c84d942a61312fcb1891dc0
+ef49d088a01421291b6869aebca0aeb3
+ef5061608aae63fe42e6cc06d812e280
+ef5cad1ae210e815e2428888c4814564
+ef6223b586623009445f6ed05baac2eb
+ef630e646bc928563614d487a96d40c5
+ef63c75fa93e1c5d09afefa3070682f0
+ef6a2afc27ee362dede9a91548ba2a24
+ef6bf406ea3ad4a10a73f8fcfdb88612
+ef6d6f1d7a6094fccc068055058fcc76
+ef6fbbbca0031a189d61823111ee326b
+ef7249ba3188eb9408645a05e9415c33
+ef74096648bf03f76c920fcb33e26cc0
+ef7476ac0860f321acf26417127de35f
+ef7a1501ba8b410576dbf5036b33ac5f
+ef7b0ec494c7f8eba6f2f9884a4336bb
+ef7b4781326faadf01cc09c235862d3a
+ef7cdc91d193fcae278c94351b34886d
+ef8147837e866111e700815fa2456f86
+ef83b236f2d16aa1a8f6f6e0b055f322
+ef88adb17b5d86a6d36658de805e645c
+ef90d7e2cc86943d0b5f4fa7262839e9
+ef91fce6c45c5e7a9bbac85d09582226
+ef94536f55e09dcd23523f705a17763f
+ef960833fbf7ae798e0461e01948ef90
+ef9a07ebc464967cf4b170b83cc77007
+ef9f74182cbddd241f2ef7d1d9501cb2
+efa9823d325ec5b8ac674d520a3b3bd7
+efaa89d1b3f75db504312185e9f9415e
+efaf168bdd4ba45c6dea432cf7790fc5
+efb1d92870a835efd11dd37a60abce16
+efb5ee0c0ec047e2e7014c024fdf3132
+efb807e643c6bdca8ed38e0ffcf04514
+efb81bca84c9d5027d116db686e83958
+efbec6f302f9074d8d9b9f7f2f0568d5
+efbf5ee52880fd1e4572d9cc7e079ba8
+efc1caabf4d6f79426341701c906b459
+efc33f33a82b256fdd433a6987f9c3b1
+efca9aa27c0f408f4a1a401ffeba5fdb
+efcbc541d6ae9cd041dd5788d2abb7eb
+efcc14f17c5ef50222067065871a2c80
+efcd052816f6c1cfd03570e5a5a20364
+efe08a006631effb2bedf98eabef18b8
+efe445e822a5f32e583b467ce029a45f
+efe59ab1ef7324f746b15f9b37498aa9
+efe83bf7cfa2e4b0dfa356c01b5635f3
+eff3bcafda516eb9a9167e8dd8b68f29
+eff68abb18ce770141c38d00db03344d
+eff9f458c15bfb6a8e2e0bf8fbc4b898
+f002f3b02ce018f5e4b112892811548e
+f0114806a5e6d1ec4639d0a9aa0b992e
+f01359a898ed3fed4810ee5eb1ea1ead
+f014175e0e171abcedff06b3800c75c2
+f016438daa8b1b5642d6598d8641c200
+f016e6cedc9e77cbfdefa101eb822c2d
+f01956e2a93fc4d517604fe7703304a1
+f01cd77e537b9a441dd1246afde1c973
+f02968f62d43a1b7c4fb32358b9bd1f1
+f0304a44cb41ea349d8c7677a417303d
+f03351872ea1dc142c8489f397a4749a
+f0347afdd1413aa208bbe8872c0d872d
+f03a982a37a034d0664b69e22db7dceb
+f03b52a80e8f06d201732340ba8fa1ed
+f03c8e0ace27427329abca59b1510ff3
+f03cf744a7d04ec465d54a3708c69977
+f04011559f32d9ef21132841390c3f0d
+f0446b79d4d915690834e2f783de5ad3
+f04d536dd976356d4f5adf503826ddda
+f05f77f3acbbdbeb32301fa9c2468c45
+f0616fb65f2f4c038cea1d1d4ad7a53d
+f062b9acfdc42f309eeadd6a8fb55ae6
+f06620fa44f6e9dd1d759f30a5eb8c28
+f0665391025ff2564eb656346d30147f
+f06b3035d8a61797a04ab55ae882132a
+f0761cdf2a8d015a98c2d7a0a981820a
+f07abaf4fb2a6497a2959cd8140b9528
+f0866c3de928b89e3339db843427828d
+f087a180efdf5332274edbbc48b0e765
+f08af5515f4ff6d1fbd5f366c481a6c1
+f091219c8e290618a83591c658068a56
+f0949f528214c17da4096a46ed4357c7
+f09b173c5f03fe098db2ba689b69a448
+f0a1a98d5e1e7455fc6780b983def29f
+f0a390275f62f9dc1177c46f9bd057e9
+f0a9771358e279aa4ebc022ecf2f85a1
+f0a99cfecb39132f469e4fbd9fecebbd
+f0ac18af2421fded1e82a67115ad47bb
+f0b147a1b6e8f84c86c1e504f7c833de
+f0b4dcdaf08deec0e97e10782f79afc6
+f0b962d451880b5f7790d8c63e2cf366
+f0c83965f8c42f4a4094d4863d8c59a9
+f0c9eaa80bf32654382e3878c3645bb3
+f0d009a588006fbe6f144fdc53f4ed75
+f0dcbe5600bd89f84a8ce982d827d5d7
+f0e067f769671a888ccb728a046a6c1b
+f0ead7fa9d8abe9f2905a81a7903ce0e
+f0ebdba814abc854600c71ea44cd20b5
+f0eeba02eca4b8ae8a9ed6501b3c801b
+f0f285bd9b31c8cfeb266b908a6d9741
+f0f88b1e16b60761129ad6de5bb92cdb
+f10168ee3198c17f8ee0c2e6566aa1f9
+f1021b4e4d686773624e26cc423971c3
+f103a79412935fa01833d01b6a81c011
+f104bb15657cb4a857eba1846e58d88e
+f106ba86083f603f695a30644c7775f4
+f10bea1a3623592c343a0f03df3fae30
+f10e42df74605914db317d308d5ef1f3
+f10f9d6238792d69ec6160c67ecd75d9
+f1194dcc7cff9c8152b384566d6af1a3
+f11bfe4eeb544259b46dc6c737332321
+f121e976ebf3dce5090c9cfe210384fa
+f1263ab84bff9063ebd053341c4a5b47
+f129159075373f8d823fdc0e1f8d9d0c
+f129c64363c4b9f4aa2543c55f8d0eaf
+f12edbba01fafcb1124d6e65b7441535
+f12fb7fbd213ca6ec6e39dbc9eb6cde9
+f12fff5b7b39c8bfa156e24f69cab2fd
+f1318abce5af533e9ac1e1cd97941687
+f13802fe58323e16281f8212349df294
+f13911b1e96eef8d0359e66e22cfd90c
+f1407fede683d782d17bf4f224120020
+f1419ae89c26cc2202a863d8a2e696e5
+f1450de7cc2a1f1f0dd763fdd0fd57c6
+f14546bd0999f28a65d519d13c273759
+f146bf4f9a210e0fec9fb69978a77053
+f1476644f4039eae058e66dfd7836f90
+f148e60d1958c95a2ad552a5da186669
+f14b92a03c3b3aa87e4cc21651aac0a2
+f14c5e1988ca5125598d8617dd4613f6
+f15a03f9c8860eee81816e6656f71e02
+f15acf77ad329dd4c6b53d5e6f1e7fe2
+f160fd14a296c7e4c511621351992554
+f165587c2ce755b7b43924bb8b6f1144
+f165b3d0f9427928354903bbf0f683cb
+f1707c07820e7116d7695df705e30810
+f174739cdad911eec8e7d53a08a1925e
+f1775e07c5f19fe28f28a89bc74d2d3d
+f17e69c6492ce7ee18b43810aa609e06
+f17e9a9bd24182e07c6e6a639127b267
+f184c206cc0c2e3e86f6d926916f7233
+f192b54c5403ab271ad030e30b071961
+f194ff2fab20b66079085f343c6512ee
+f195f762fd3303671088a2d0876d481c
+f1992a053a5cbaa18969e4821afe3b5a
+f19b11caf7602c708f17e3a5e84fe92d
+f19cb57cdd8d562ad1524d51763724b4
+f1a4a9cee02447b9d8f058349007d3b6
+f1a5b5298634d9a9d9ee8f39f5c2c9d4
+f1a84aae6266f5778225f99a5e04c90a
+f1adf33c01078405869c0f45577d773b
+f1b668333e70d0c25c8f5ad0740b4d84
+f1b87a1636410db3a7e894fa5f6d9b6e
+f1bd7cdd4dc275ae30438327b135a7d0
+f1be4a15a7cda0eb1298d9b5bde8fa04
+f1be68315e1679728bc7294b50d50440
+f1c112e9d0fbb8d08879f8d5c8eb20e2
+f1c927e633aa3f2b6add8f30453839cf
+f1cc2d37c5627907f36a7cf78fcb5e42
+f1cc7bf6bda34a6796c05e8b962ee539
+f1cd2c770dcc04c50bfc9d07f462c279
+f1d1415fc0cff2ce1bbcbfc47931d9d6
+f1d793eb2a0be53ae096d0b1780f57f8
+f1d819018d5502e2d08837edb50da392
+f1d9813a045742dba81dd7ea92e5ef14
+f1dc8b926726d45586dcc27ec9cefa00
+f1de1ff3b3d21d4f79673ab36b37da26
+f1df59f1a9834960889cdcef79d070a4
+f1e4975bb58a8c7ee06c70ceb1049245
+f1e638bdde443954f52385fded5d6964
+f1ebd83cdbb1e67a3fc3b1af1cc54094
+f1ef4714a4f7ed212f8b05f7d9a2fb53
+f1fc6d3fd4cdca6e420dbc41245a8980
+f201457ea88831c9aad70993e287de4d
+f201b5fa222fffb54acffc94d1bf6992
+f2051c78f0ca5fd0eb9a05882d500733
+f205ecb46bafc16fa0aa18a47dfb6727
+f20c48438b91cbb64a5afb31506f362d
+f210567c2723d495bdffa1cd3d9461a5
+f21233a554494402309a89b99419ffe8
+f2139c4f7e04a446d73ed7292ebf6d47
+f215eb391d4b1ef8c6a27e5cc51bef90
+f218ff313ba0902454d85ba7da578af1
+f22614dff995026ee77a326918ce2c7d
+f22a2fe424aab20e1281093794fa8c26
+f22d785f05a9d164e0c8cb8832cdaa4a
+f22f98e4f2b6d6b995c5280a4e137a88
+f230834ffece5b42237956addc94fd5e
+f2322489daac1b16efd54a027350653c
+f236167233204bff1df48e965f1fd7aa
+f23aaf7b9ac598b56a30e528c2204a00
+f23adcbe8f76dff9d00fe417e2ea4f0a
+f23e91a9a8e67790d038d18de203163f
+f243fff5dc55575824cba54cc09413b5
+f248943404b018107fcbb92fe891dc97
+f24f193a1cd6ff28881193d3bd649c3d
+f251a2c8ba136cea440d16230bdd86ad
+f251d96bdaa6558b2a7adbfb05a5bc72
+f255490e8ffbde3c0e0e16ab40ee6583
+f25c7c5e43d09e2068e21708ce42be6e
+f260f830ed36c0f0e439eb0572feb89b
+f26381c21160e66374c69ec1335958de
+f26700ba690c02fa9cde5f681a070c04
+f268708bf1fae25ed35f0e0c0306c35d
+f269babc272b4f848234a50f8b179046
+f26bcb6a29fd5c9cea50e48d43581178
+f26fab95cf53b4a3a4d26f48f1bd7bbc
+f2702d449db609d98a693a9cf05773f5
+f2719aa375054f88b066a89b96da1145
+f27bc8008cc77583349797e34ff5ee18
+f283c7d143a72288491f02f6669b4a48
+f283f93c1dbe4f1ef3f37eaed2498bc3
+f29356d6d58da4cf8bf355ec46c1ce0a
+f2940ec3e5bb9817c473153c77cb8f80
+f296c6422933d672e2ea5a5935e1d28b
+f29a423eafd147a67ca98b2642f1628b
+f2a3f85b353b963c33fc9ca9c993caa6
+f2a663366706da5182812899d27b27c8
+f2bcfcc15eb10396d1f84f2fec84c3d3
+f2c54ca5d89fbd1888a2ad09b3db8270
+f2ca9d8450d61623f3f8ff9343f7f994
+f2d28e9f226a4dfecc5261c10bd51b38
+f2d61a33c1974962a604cfcc5b175a45
+f2dba92486d6b0dcf549a3c27f0f1b3a
+f2dc14e873d73403334d7be4598f90bd
+f2dcc5b31a7a3424ccf8e8a3a7887352
+f2dd479dd240f0c0043db63b508ad78d
+f2e2ddf530418b008bcbde2abf486af2
+f2e35bac2bb19cc208dd79e3d7fc14ab
+f2ef15f4026d535458f5ea6cf0a320ff
+f2efd9f2ce308dda6c40606e22530482
+f2f1d167f331ea47af7e25e4d567704e
+f2f4d8b981780d9e7a7fa47af64c8806
+f2f6ce4870f107344d3b05d5d93d41c2
+f2f735638785f8a3dbbdb06992eddc73
+f2fa42c4f6f8ad2387cd52dc0b388aa0
+f2fb2aa71a5801e8088d7cb85a2b079e
+f2fd69bfb850bcc66eff341f8d470d5b
+f2fed8fe8c4b9c629f6eaf72b3db53ca
+f30881d6cc265cc526a50a9854e2eb47
+f308c8c8995cefb8c72a930eeff62643
+f317535dcb1c1fcd8d6e03e79acce760
+f31b692c7992bfa474b1b106d223dba1
+f31cde1944fb770c870b7e4e7c5f957a
+f32325045d6698dd90d76eab0c74a1a8
+f3238186feb50539b86c4df14885fe80
+f32b88838460565af0d6103f598b3603
+f32ef8deedb495695c4d799294f10d55
+f33662146458234cebd658471e4c2d14
+f338f12bd6ba576174a8adf215914260
+f33bbed475d6e2ec26ee27253312606d
+f33ccee674eb9c6402cd410ff8785ad8
+f342e46da8731ea5a701804e36a77185
+f34337710006282f39dcabc8b2264adb
+f346aa077cb84c1da139c96d45c15ea1
+f34aff251243a8824dec9a0e57c3df81
+f34b82082ba25971f8367aecd9abb998
+f34f51ab286de0fc6ad79c123280a71c
+f3519a491550beff36e35d14bd260e4b
+f3539e7bba67d49b04903a35749db6e3
+f35aaf42c442449fb90fd41aea4e0f7d
+f3600ecbd0ab1c670e6306137a8d0354
+f3659220ad8f4e2ed45ba58cec9f1745
+f3712ccc5176e63c5ecfa4071d1f10ee
+f3760d8cca250773202f7901f300b36a
+f3760e30759fa17ad6810127ac5e2b8c
+f3850c7dc24f589bf419085dbb43c4a8
+f38570d6f2aa64982349daf790d94331
+f3874ac1a3236de7905764f2a3f63a9d
+f38af6fe429b4db4f7b4c69c803c4d9d
+f391c7e780ac080b653aeccab717f067
+f3967d41fb95ae9c10a7d17baab3bb33
+f398a5a61fffbac4bc42030bb8f52beb
+f39a157821d96c25af5c6c6021f206e9
+f39aee193504054899561cc3051896ff
+f39bbae14a5c758f77207a1122fae1e3
+f3a40c96f661433696aee365b3381c9b
+f3b0c666754fabe5e045f2aa26c372cd
+f3b966fa0977b968c88ab04ffe38a4ad
+f3befa984d1b1b7c6242dfb203c9b93d
+f3c2701c6140db301b2d824433544017
+f3c7a87e4f2b0e035db526518f89b502
+f3c8a7c8d280e51b8ac6a61e5c2b2798
+f3cee695ac8c7881a211cde849bedb5b
+f3cf08203fb9b17f00ce9b723744031c
+f3d234431f0c88521f5a7a5734b2e216
+f3d5e00ce6bdb0e18c04171777565f8a
+f3d972d7e55fa513c1716b393822f272
+f3eb4b9e25e3001cb25b97b21b8fe5e2
+f3ec3f5f3b18be74d3cd8b93f690f14d
+f3ed750618add38c0174e63e9f6a7dd5
+f3efaf4df8b72ddab0bbcf57886d8cb7
+f3f8274c31ab468d29861d2c5fb18feb
+f3fcaaf1fe41ffc78f604120d9a52bd7
+f3fcbe6dfd593cc171a0e09599e96adb
+f3fe3b68a04507a302d9255067a0785f
+f3ffc49f18807d6bdd2822abce610302
+f401bdb97aaa8018944df2a84524b037
+f408d68913d23e293d055b7ee0e5a276
+f409c74795522e756ec82ea915756353
+f415ee156d2d78573a19450e703a8994
+f418538c955f8ae706409feb7475b1e8
+f42a07cb34d521644f2382d9d94352eb
+f42b245c3a20a68f269e3514a2c5dcf1
+f42f45b3a01f4751a9699056beb49529
+f43244a199acbf0165744dbb9eeaaae8
+f4379e9453a4b5e0ddc1c7a1f827b4b1
+f4385de801b365a756346968cb09668f
+f43b0a557dc3564b86aa904b4bd5cca6
+f44c2c0a8443bb4d759e275d6e39a03c
+f454c9727c0d7de722b5d37ee17aed7a
+f45ba296932e7ea6ceaf57c43ac64850
+f45bb2a5befacadc748e52b5b52ed70a
+f45c10117b233746ebead2359bf1ef33
+f467e5e129151a994265b78830727346
+f46e6ed50105eb643907a98aa3975c72
+f470ed6c4221add74465de13e98f88bb
+f4794aa7fc2b844a96bb2bf1a09a48b8
+f47a0311eab7aa9ddde46cf326e24f8e
+f47c9229b3c19e99bef78eff24a698f9
+f47d463435efa4a438ecf091d87cdb84
+f481312137481cbb0cf2e31a5f2ea517
+f4825c602e88942559f1a8bd7eeec8e6
+f4894934b5113a5fb04e4d419be1360a
+f48b91c087890949427cbc275afbc857
+f4a5369e5e3075215251e605c6127a34
+f4aa3a047d07d0fd5fe49ce0c452733e
+f4aba6c73ce579bc8546d00c3d33d3e6
+f4b3a09d630f3d54301993a53e4b44a5
+f4b6d37437d6f065ff4f0669ee03b1d7
+f4c2d1c8420ba432822968bff0d11ed9
+f4c37681cc2fb17c2de8cc0a12b85670
+f4c64ed80653381f807731e806bce11d
+f4cecd509bd1230a283e70e8f191aeea
+f4cf3984631fa8df37a0434379fb2e4e
+f4d1186943a42747d980279c277cf14c
+f4d1e634d9bd563caf65c71f8d37e1ad
+f4d368d943e2b7fe2831734f3b4337da
+f4d59317bc07a13d16211f2064dfba50
+f4d8148e1aa0f25d0e4dcd2334ad0a86
+f4e1270f665e89fad953ec10ec7b6636
+f4e135a6b0566d24e78187448c52e957
+f4e1e20d642f0954305cfdd4a9509006
+f4e35382b734e979628729c6198b5613
+f4eafd4666b07855464bf3564df9d2e3
+f4f583bb5a19fcbdd534947aebbc43a6
+f4f5a2ef68d5edd3f1a655b48806998a
+f4f9d99b02d81d36b971ba400c5dc77e
+f4fea4c26b8796d581556defd96026f5
+f50151d4be8ca575714daf84eb1eda0a
+f505e48a3b9b570c1211d9306b5b9a91
+f50772c610f23a9ab4b759d0de5cf95b
+f50a508c2b525bbc8b5a0f6a9003f801
+f50db41a3a7dc4a9f4d90383f70363a5
+f51080a0b8e44d358390a75aaf076936
+f5110a23f8b746d3b3a7e0f84d07b347
+f514bdeefd0791ac731e2c0d86e3c7f2
+f5183b91f3182a7bcbabd76eb43b8447
+f51f6a96417b959da827b8fe670a3105
+f521e2aa436ee425c378e687e2663085
+f526e5216827978dfc41951d8dbdc5c5
+f527d7c8e5ffa3afda67f156059deef0
+f5283275f7d5e312bc8bb4380930567e
+f52c5b227c1cf565508b30fc97a99bbd
+f5307615ea38db98214bf9fa24ef0ba2
+f5324e2b8e2c447dd5cacfa91bae05c2
+f534a49c6b232023481613d3e1a4317f
+f5360dcf7ff1c8e6fa3cf586c52a103a
+f536882135e737babe9dc5c480b7cd46
+f53c71ec17067d03cecd68165c2bdc60
+f5423c1f3178d6f8842d808b5d39f052
+f542b47d00e79e3414cf7767b7a13eea
+f54a3742d806e4786b3f800cffa58857
+f54f210519038b5d28fa780c4fb8ffa6
+f55709f5febc992545f7b025d648f299
+f55cc2992713c756b2391749ac786756
+f560c8a07b34b53bdcdfd11fe40728f9
+f5626f73bb6ab25c3fc11ba2f9fe6975
+f56cc61e87f8a314cc070fca2cb010fb
+f577217484a75a1f24d99331f9b568ec
+f577b6fa3054d843c0349cd068174419
+f579bd2266eaa885904a9d5fe5ad215a
+f57cc7cfb0f0450e2e4f41c1032d0bdb
+f57ef3b7b370b81b93b4783aa51801c7
+f5807009b90c772eb78ebfd938bb9486
+f5849791119ec54d7cf797286adbba8d
+f58d8be400ba7a8df88ad40633afe01a
+f591cc3835d869cffdc1b17870b96ed1
+f5924a38ce377d851b182be3b63a5c5d
+f5994435d691e50f81b48b7eab46af08
+f59f0f7c4fb29f25549802118dcbd33d
+f59f93b849591b3b0f27f3ee9e5668f1
+f5a413b2ad93af416b1b767fada2b949
+f5a7fa812ac67d23be78c8d8e90b6aa7
+f5b183e77cf0f00ae9ba96661cb3c19e
+f5b1c63d3c35820088e82792088b188c
+f5b23256a1def25d97477ce53d51df2f
+f5b4936502c820799c6374a0df89d27f
+f5b912d0f24ff9c4bec4a21fbac5fb67
+f5bc183f1d2992908140d041fbbe4668
+f5bce866dee9660b7557b5188c275706
+f5be38ca4433ca47ded89aee6c042c62
+f5bed41b3fc020ca9a9a1293441bea94
+f5bf151cfa1b70ad7a2daf0802d39b74
+f5c1e570e066241f852e32386ac3cf8f
+f5c58d2b79589a94c52e082b2ade9a78
+f5c5f83c754ec91b5571cf898a5c7cc8
+f5c8bbe6edbb743e4adff048d39e3eb5
+f5ca8ed57a7fac7644fa42b01084eca1
+f5cb591c4d6c711fe5d0acedd87ca439
+f5da7c5257f29e4510b14d468bcd6c3d
+f5dbcc8ebc597acc2fb6cff177bf2efd
+f5ddde7ed7d4fd2d8540b350f2b69d5e
+f5deb167ea5cfe2bee7725e7835955b6
+f5e0bef451da7eb167762125911b39f1
+f5e4de9d1d2463ea81b923d3f4bbada4
+f5ea8a00b617ccc79532c035d43f4de4
+f5f1737bd8071b71426c27d81f35d983
+f5fa98ade7069c5cd188c5989b356968
+f5fc5739290e31c31295e6ef2a139afd
+f5fe08d2b8f2cb075868bfdc79130272
+f5fe69fbc0a236093375a2e4415d1053
+f5ff2076ff3a831c1f8d76d3ba1a4c78
+f6025d346d9dbb8c4a8237ab01c019eb
+f60535577ff1f14f2eaafac806597411
+f608124ba52a311d57b33dbf32f95776
+f60a67b7a477046b4790f7bd4dc83e13
+f61169e349aa479ce02c36d96647cd01
+f611f50c8fbc245d187c1ee5dea87a74
+f61c35e11e8bb00bd8a4f7b427566f34
+f61ef535e7f7397945d16ca3a014ca87
+f624444e1ade8c87a625a941e09a4ad4
+f6246ee2746368ff7071b4a2f662d866
+f62b609a0ea76a5b52769eb1e6a75e0c
+f62c7cccd98cb263a6e600093a392245
+f631d1d00e5916d357f9dbc45129de92
+f6320eec85a46aa190605b0865c5c54a
+f6355b04e423eecc1cd8aec680bb731c
+f636b14f826c3edbae7a54bb1199f358
+f6377fe44f8127417416648a3ef9f91a
+f63a16d36870a6b9f50e13544f6294f2
+f63a6f9a235767637b1d1dc12a99c131
+f63b3b17f4c13cdaa3d830c6b975dcfd
+f63de614251a27f88fbd66671dd13f48
+f641fe46b391b75195c33d3135201547
+f64500912148caa49c4a0969e46edb03
+f6455ce1b020e05ccb9c86eb52d66a66
+f6461a6249f0ae5be98d6f623efd54f9
+f646d6b5db7eabe0972e48f53ffcd766
+f649339d9d565cb32267d80cec65bf25
+f64ab672142108f5e35c433a3811ada1
+f64c64c0e36502c8e18e250e1b1cc7cb
+f64c7a5db9cae7cb1caba91778ed3876
+f65659ab61e9029bb688f065794d476b
+f6581ecc0e1cd1608afc7f683ae82ccd
+f6670e57e49dbe96c8bbe2625fbb14c9
+f667b4fb7192dcbe5029560c65d926e4
+f66d4b03d98403a733303e664385eecc
+f66f3bb13da6c9aa3d4a6f2f02d363fe
+f6779cb70600de511d00150c57db8f86
+f6817f298ce1a4e805aaace59ab5419f
+f68f6896e12975dec6d6ceacc10405e1
+f6945e6c3a8039599c7191de19d0118b
+f695ffae7e7d7cf53924b52b9130d1b0
+f6a116236ca8a6ad1c032e7fc1baf8bf
+f6abb7d9d89df6253cb8d757fc33bfa5
+f6ad4a80adb097e23d45cb65a90ebd6b
+f6b5ad8ee5fe35eb639744ece3a342c1
+f6bac9becbee61f37ae1e0c3b9aec92a
+f6bc67bf4cae63ef24b71d2484390678
+f6bf250ff5a219287da9792b03e24de6
+f6c2139e968f0304ea60d0e0c0b8d294
+f6c26fc3a6a0f202ec45fe6a9b9b10b3
+f6c6dc9b03a88609819a49e34c4e4288
+f6ca235a6c4b05dcd1e885fbdae45f1c
+f6d18f905d60fc7b46923679b79e58fa
+f6d754413849b0eed4e5aa981aea2745
+f6d7e95947f14ab8e35e4228334922bf
+f6e2529decf814f8031d57a4374360eb
+f6e467b35d5f92e8759abfcca34e0087
+f6f3fbd5998c086a9f4f3a61b0eba7b1
+f6f435ad3e80313800d5251973a9e3fa
+f6f68d49ae37fd334a552ba324455fdb
+f6fc8fe7c0894fd0076aa765668762e4
+f7023f686f1981090d80b1d9fcd88dc1
+f703e7d307fec9a465df5f2053d83244
+f7084d0dd54fddb9b73f89c77803ecd4
+f7126f8523c107f7ae1737eb065654a8
+f71a30c22504d1ace33bcc747edee685
+f71b4bcb905b228a0334dbda6b924896
+f720afe896abbf76e54d007b839c03d5
+f7267220509d9292907102028270e66d
+f72b8b0d4660d934c1578d581de0fcb1
+f73056cd898d694504d89bc196bbe0a2
+f7376bb06dad23e2ff94525b9d758dd3
+f73c279e4cab87b5c34c0584b103a4ce
+f73c6a783e58ba6272a3ed15a6641933
+f7442d9627d8c9a4c58e249c1c6da641
+f7447f2f888774a1400ea4591ef26fed
+f749444ab4fc76d5ba8e7336437aef16
+f75b4c5182774430950b0949d2bd2841
+f75caaf95b7828cdd4040b6cfcc0c8af
+f761c54567315a5151bb47c95aca90c4
+f762fa1956dee3d27a4fec021348fc61
+f763e1f635dd63c8057a1bcb17812486
+f7680243c5d215e1223ace7770076e36
+f76c67a43295f40b7694643beb35296d
+f76e352612fec84937ef5b4716ec88a5
+f770167f40a151c53a7a2992108c1629
+f7729efb3e20e2cef52d77f147b9d8eb
+f77937b62d392cc4ed342bba4f3e0786
+f77df6c8a9131cb8aa457734ad45f9d6
+f78015a7c718436351786797b35b8149
+f78146dd5187746f87ba49bc0e2aa15d
+f7816f8df4231c5b97cc77e4833dbd91
+f7851ae69ac0e0e8f8271f12a5e2c9b2
+f78709bbac3e57ecdb2be240821b77c6
+f78a753d217cd17a7f4693f1f95be63b
+f791cf3ea537b691ab4834ff2d8462fc
+f7975bad0a78cc7e9c716a8d346ff59a
+f79b1b4c9efa662c91ec7db6ebda8f97
+f7a20485b63536aff4170b457cc43bfe
+f7a4af8f06680d894cf434894184fdff
+f7a6275abb2fe0d978f00c186228ec3c
+f7b0ce131184ce1ff8971fcf4da5bed1
+f7bc7a1dbc2b421ab5c615b43a961357
+f7c08c6740ddff122de9325c6522762c
+f7c376c3a5e371377a84b3e261f3c973
+f7c4e335765f5b261ac8d85f098f574e
+f7cabd1e7cdf163e5bee31377340482e
+f7cfb8fe1fd1592b46accf4d83454ff4
+f7d4a989d34d44b8a4d565cde239822a
+f7e1e4b14eaca6cc4ed08f6cf4414c9f
+f7e361bb8d6d0fd716823310719530b4
+f7e4ad4dfbeb8c06de1d5fadc9023361
+f7e7abfcb75bb658d8958b947f3d524c
+f7e8538d5a7d2999ae8cbbee250a2022
+f7eb58ce3b6b6a48e6d2e4f2351be31f
+f7f6e2ad349fbc2a596e61111bf7b36e
+f7f99ba2603ab425145afd6493b4ab19
+f7f9bc57fcc41ed8184cd17392f0bb44
+f800053b99794880a17205d2bb5505ab
+f8008824e3fe31f75c5e24f91b15a437
+f803a4f059d510604e973050f19f6899
+f8045045c3802bcb2e5ce2fba2226acb
+f80862d7144ee9c8f4ddc1d0ff94fd2d
+f809a30f8022fbc0dea997a640fb168f
+f80eb72043c1917fd909335d225084a3
+f80fd38cb8a46dd6ebff68a8126efd9f
+f8104252e1ffbfb9a2d647727f8e394c
+f8147a80b6570c85575bca5addb5170c
+f81b8de4cd2bf792c196bff8c75b4f49
+f81bb44519ccb358fd953fc837eb87c6
+f82759dfdc562df9a08b2925e3a138af
+f82bf8eba6784abccb5df8c17b28f8ba
+f82e782e1c5c9cbb94c7ee7286b15ab2
+f82e9a23ad7b7a89fec3aa9ebada7cbd
+f8374b3be72cad36fadb16f5a67e8930
+f8377e1feae3876ab6e2d6382fc9ee08
+f842c0b796e648771bf2cf8680be934e
+f847f8c5ee48c2ad59e216f2afef362a
+f8526e53f50cfcd2e541679e49dafd9b
+f85616d6dc9be9d277c0b1ee3b73779d
+f85934fd1b19a332e6ba7a845913520b
+f85bf2fa52d06843295dc693136d7f86
+f861425461a11646eef936605a96017d
+f8625472588cd171d8e35df46831721e
+f863ad0ab3de92cd826b68436d24fbdd
+f86b739e7a8ed4feebd7083d549703d7
+f87203792ab7bc3fd3ad53089758f531
+f87ccebe49000fdabb03ddcf187fbaa9
+f87e24fc16b6186e7391d4af238543f7
+f887b2ced44e50d3be1249b1f0d4e39a
+f887ef0a7dda73b68859e853c9270de5
+f88bc35242dadf4266c97034385db908
+f88dda5c9f5fad47e0c0f3bb795b4d38
+f894ad8d3233058974673b3b9a4cf264
+f89de52bb851f81a500cc7f49f0cc728
+f89ec72f8c0cd06366cac01570d51081
+f8a44c5d0ebd96a0e3b95359649f011d
+f8a48695bb68edb93d6b1a5bf7b439e2
+f8a653aba017d4fb5adb4c959e5cfd41
+f8b04faaa2fca17ea5486ca2365db6a3
+f8b2d0adff80ef5ac0a95dfbb8299653
+f8b825ce9e0800df2d0cdad8f4b37b15
+f8beac1e6683aa0b780c71ac5552aee2
+f8c5aab3ea82d98f8374b5360927743f
+f8c940caa27e5e313e8e0f96c3ecfb3c
+f8cdc68a41ee9f4145353d1fb6286a73
+f8cf0e229a4da1640b81b051635d791a
+f8d26dd0655ef2c22841953936d596ef
+f8d8ca25d60823be1639cdcdfac4f089
+f8dfce0fc9e8e4f85872b63d92f34790
+f8e3c28b4e167391841a2f25ffed10fd
+f8ebe35c6ac4cc180ca649167a5f3fef
+f8ecbae645dde4129d22f553e0d1ff9b
+f8f3ffffbbfab75d237d91ef2822ab61
+f8fc1af17297eae160c7aa9d58a80a81
+f904a9eac07d65d1af0441f343af7127
+f90a1802daa91598675695bf0042bf28
+f90cea63174ed8b6bf37ae3dd47cc599
+f914c77c720289ad55b3e675ff64816d
+f91d4097c44ba6c933855464c3f2fffc
+f925ec176a8cc6f95051d78c17dbc3e0
+f928eb5de7baabdb19edd59d4aede584
+f92be485ccb75afd4a4aeca85adac330
+f92ce1b907f57cf7417d3cc513ba9b11
+f92ce3870ebcd93499c261334d356c39
+f92ed45a55f6a290a356c7766a312740
+f92f9a797f9f0456615610a63228ab63
+f932b78fcc16bce005aae67fdc8a1ca8
+f936ae9c9763b2eeb6abbc4f567159b9
+f937500d9feb283eaf4b3f14082644ab
+f93c7e0fa38531754911868231824468
+f93d6dafcd5e3011941f0602228ce1c4
+f9429199f5c547be10f0e3ab14fc70dd
+f946683e3190902b4b03e1b708cf1f52
+f94881e3cc6aa183f79fec0ef95ec0b5
+f950ea11fbd89f1a70b99a3c37607533
+f953952b7b25dce96cf62663d8dfb65f
+f95719cdab587dc7e7699e3b2125a8ca
+f9605fb6d2b3e92526591d297d9c6a2b
+f9629708dfd934f2cc3fd512f73a1bcf
+f979692cb7b47c5a74dcdf028b9d4922
+f97abeba1fec66fb3c66d8c58696ef59
+f97bf35fd10f13f068f6fb9e15bcc102
+f98777010985cbbdde90b537ae428341
+f98ce57c07016f0874788382bc088c7d
+f98d1f1c28a0a9727951a3ecaea39579
+f99473b28103684d33e0382a8e98a7d9
+f9a092372b94401be06d9636a3c93e26
+f9a0c4f3b68e81b71a6fff9314d9bc5e
+f9a5a5256cec98688a6d9a600df1d661
+f9a887dfa1207631e00a57d30e985b3a
+f9ac22a88db652c7a48df6ec86a5c18b
+f9afed3a019041446aa348bc14b5f14b
+f9b4e7d296c50859cfc4c6a6c1d31bbe
+f9b8c07a0c3a0862e00b9440f897a9a8
+f9bdf4edf2e14cabfcaf06c54c24bb42
+f9bec4a585840c71f1e7c7e5f83c919c
+f9c0174dbba276924761761a633406af
+f9c7b8b705922be28c4017625846df95
+f9c922f7327dd763312b17eed3bc31ea
+f9ca4f576f9cbd85c4f9e3c8ca7d1283
+f9d17038b999da5e31e62b7d5bcbf37f
+f9d642b56791e9ba85d6f569a5218700
+f9dc7709335aa2327f12c6f13ac57800
+f9de7244b5124a7f7cf3c068318147f2
+f9e05a49b1c65b3b1f7daed40b42107c
+f9e0da0085e9033ac59e8c462e2b7252
+f9e2b3179ca9b381e2690ab260ff92bb
+f9e57a9922b397b9fc1f733e7fb23af1
+f9e655a58eb17503647ffbf279a3d3d4
+f9e710b4a12208131fd0dff96c5c93b5
+f9e777c6711d01321fb8308fecab7973
+f9e8ddd1073f417f1499649b8fbaba58
+f9ecb795d1c0722f3dc1cd2a434f8483
+f9f5196d60bce1820e27223893cccb79
+f9f6aec4f9b4ea0553155fed99ef92d1
+f9f9f4b2bbcbcb7209cdb0a435b5f4be
+f9ff5c6d6df78cd4ec9592b82e0e18cd
+fa00a113b96b0825b2b8c9d1bc9244a7
+fa01978eb0d0845e4abbaee8e881054f
+fa0300b8b6ffd74ddf4ecfde9b24767e
+fa05bd051937ce962fc8fac0a0cc238c
+fa09b86333873f6729f75709d56cfb46
+fa0a3dac18ba0f7c5d960898e8b68676
+fa10ac4285c465d1cc809365fe62e8c4
+fa147ad566ecfaab951592498b0592bb
+fa2235e6c7dd64dad660dacd29080e98
+fa26fc9237dcbe3594c56a258f595a02
+fa2c9efb2c8345d4ce2c308e7da95f84
+fa2e0146cf3ddcb7e596bee98dda06de
+fa375b28e5e18293da1434b5464285e3
+fa38bffe134be83bd39185e90993609e
+fa3d8a8aca899b81713dc29c23735f2b
+fa3e46c14d9169c593470b4008c92740
+fa3eef8810fa44e47c4dc4bc7bf6298e
+fa4359b74375cdf5fc3f73bb26fb97e9
+fa4730776e7d079a9dfa4450c36b845d
+fa48b4026b6527b9114de27d81be0ccb
+fa4e75bad011d2ec7e59614babd5091a
+fa504151f0cd4dce604203f8f6f068d8
+fa50d867826ef72dae443150db769c5a
+fa51fefd0c43129c34355d5787673519
+fa54d1579db788c31e03f54ee411510f
+fa5a11ad3c10a3e4a3d5d9f40ab294da
+fa5d576ac9f02c4fe0feca132a5c2b8f
+fa6a0fd2c085cdb1135bc08c0aa33fa3
+fa7296ab1e87045ceceb9093e5f321bb
+fa7d8040580cf953d2002e6b1a0e1d6b
+fa838b620ffc6b8f65235e3b3d453c49
+fa8e6aec8ff3735a84d076e3a5cd4a24
+fa8eb7da7e8e5669e6e5969cd9194d71
+fa962c1f8d72f95920253d5ca587e7ab
+fa995e6763115c07533e2bbce9486ddf
+fa9da74959283856c15192f2f6714b5a
+faa700453d118197208de1a1a8fcaaef
+faa89e951f6c93d085b942ae1de6db80
+faaa3dd2f6970b3257c3bdc5035892bf
+fab49f99ef09f4d2a7bec718e82e0a95
+fab65b0acd689ba2ae111a2679f5b91c
+fab7895af41d9dbac71400a4127c4ed9
+fab9871f7a6683cbf2560359f23c77f2
+fab9eff579527152a4434a79a6738315
+fabb5c59a47c7a13db14960d9e13374c
+fac4b5017bc451994fdc79fadf675541
+fac6184d18bb11a485959d3b5d494dce
+fac8ab022c0e7ef6276d69369ec7989f
+fad67e54c7d83f57c2637d31577ae9fe
+fad7ef725b8aed057408a25958c250a3
+fad896395e8fc5a91ed882ee2e47fe72
+fae0d439a8a45e7ae8ab5286c046020b
+fae604a99d67644fd3f1b42183b62337
+faeb936e46621cf84418c76a3219240b
+faee4ea9afc5d2e1a47d207a5309895e
+faf1af78fab2487a3e097868f6d07fee
+faff8f3380fb9c707df85ddea1d5fb45
+fb017f1a3666def770a8b8629b4f9b50
+fb01d1026f994c768f9f86b04fd04941
+fb081a7fda5be8d93755d385268051a1
+fb089f94f283a813f545caa87ff855c1
+fb1295221427a79c0340399a2ba63711
+fb13611a83817133b6cafdef4d48c91f
+fb1924537552ddc531b7000ba4c8a74a
+fb19db90990df53fc882727217547611
+fb1c0c27470c8e0a25698ea751732275
+fb1f9d9d695c2da150674ffb33a83ea7
+fb22ea4fa551ab36438f29ffb35cf939
+fb29dc7ea8d1c118b759f974d08e741a
+fb31f9d26247df95578ae094e3cbd187
+fb338021445092021ca507eecf36a8f4
+fb407f6b15679aa83a9e59db6815d177
+fb46fb5d28a3ca9b504a4420bb04f596
+fb5434842ff1a0fcc3b3da0f5ae6625e
+fb563f4db0b89516da60531ce32e6a47
+fb56eae47ee161a8346258c28b234d99
+fb57f8fc44f509852a161595319d3bfd
+fb5b973d3fbc66756ea86f086e66d4d2
+fb5e6495e1b8fc7f2e8f66704a4b6ce8
+fb5f84772a3dae6ac3b39aca11c3df0b
+fb62f780373a8d9a7cf57664839762ae
+fb65c7dc03e3df76b395b43e43577a5a
+fb6bb9d71a3c98a7a7235e89e7940b12
+fb6fa1323406695e86969a294249f273
+fb733df05d7adb6e632d4dc62712f850
+fb762801e562c83026b0bb4c3fc510a1
+fb7a0c166bb6f3070c8d6ec05dd0f8c7
+fb808c8c3ab523bd27f8a31dc27b7535
+fb824b004559da48703d3249de88ab3b
+fb8b8a136ba81a6abd9acdd4506e2d40
+fb9017301e4a6d603b5c3584294cceef
+fb939cdb55b13de3e0db4246f7e1c777
+fb97e4d8a1cdcd62713aa842dd5dea65
+fb98a84ac5ee650d6e626b5feaa2c618
+fb9ec1e839409bfc2c8048b14bf3e9a7
+fba0a5f8bfc01b482bc2cb6d5b9c7e4b
+fba2f9fbf07892cc8198eb63fe89bdd4
+fba3e0ecab90f9d11b34e7a2747dc13e
+fba3ed6e2bdaccdda6c28e3ed7012bc6
+fba6a6676fa921f1de8afb231f9d409e
+fbad716f8440017866f39025f1a196cb
+fbb18042bdf4151aa95b310ec9ffeb38
+fbb4bfef808258683f6af955fe7e7d69
+fbb5c9d75a7b91e1850ee5f05839a5b1
+fbbb0c620cf88cc86737fb8c5c64906a
+fbc0edaef1a3a9437d8234915af6346f
+fbc3f6922d51f42329a2fa3b7e213d4b
+fbc5926241d54fdcb75347dd62b0cb9f
+fbc8bd87f82556a18cfe91e635c9e681
+fbd00d79d6a00541b2165011f3c7dab0
+fbd0b5f6043d4a994be075d87964e8bc
+fbd1e40cceb5e688700b226430098357
+fbd92a61d0b67861072a62db1c55b3a9
+fbdbf09237afebcbcbb5e7c5e714068b
+fbdc6b1f6022752ce6168a436ee4c95f
+fbe262f49b737942364b8ab3ef0b3f34
+fbe460cc3760db27829031cd98595d7c
+fbe5f3c9ac4ae1782061ef8451cbf46e
+fbe656e2a158a085f289478e63bc7475
+fbe75d82be35cdde11a46e5e894586d2
+fbea1b01e5507201b1390e29b7690d0f
+fbf23a68067e1f5e3f41898bf8f76270
+fbf296136e6a1f1da969d632d5358a42
+fbf92432e88a9f67ae281693f5c99ffd
+fc00697d792e85e3f43e9173fc71333f
+fc012b03b1bcba86faf4a545dc8f0a59
+fc023fb452f2c9271d62e7e187d5e6c9
+fc0be796b417926fa5c2a2809abf64c3
+fc0c3f161bac218703c2ed52d7b2a784
+fc0d0a15bec6adaead413017a0d5343e
+fc1089c8edc35d903af0607a01640cd0
+fc11552d8e395597f16b7e8fd79aaeac
+fc11bb3ac7377c3c88db6756715d741c
+fc11cd6ce6881fd29d4156eedbde93fb
+fc18ef9b365239a411eddf6457e96e49
+fc1a19ca4b90f4fb51701516b2481239
+fc1d3170cccfcd5853e37ce4729107f2
+fc1fa3629c39cf3eff868749b06a16b5
+fc22b71b75d299ae32ac627f8e92326e
+fc24531bb6b612960177a8305bccb1f1
+fc249a2f1396870057ad45e8f4dda017
+fc265c7558c0a907d10e94d1c6888adc
+fc29b3db67ec4b38313d7e9f67f332ca
+fc346a9803ad3e72813cb498dafc2ba3
+fc3810de7638d218c98364ae47a7109b
+fc3954813a6b9d0ca92adf723fd138d3
+fc3d6b560e667372413eb7fe1691b268
+fc409791dd1c9a1e44e796ca6981a045
+fc4bf1623d66390b1366584ca5f7da74
+fc4f1c036e6bc21579f05fae9bce8f37
+fc50c3f708976b7c64a0ad23c82831b3
+fc52233641d5c613b209b2dbe9a1f2b2
+fc56476e538ca38cd9243b9958e7aeae
+fc5b01c5714e419acc0b0dd6c38f3fdf
+fc5dc67c759865b4b78746cd03535ed4
+fc6174e5864957af53ee1ea3ac4a3c09
+fc62f433628e389a705e80c6574a0f66
+fc666250be329f146fbc66be050be2b4
+fc6972ba8e5919bd95caf3d9de543cc1
+fc6d9d2eb6a74a6716b7eb91a2292bde
+fc6f15fb4a3b4b43d6915576cf096a15
+fc7c9d2b1d2b1b20d2c70e083d0646e9
+fc8174ee6ccfeb7f8ba09969aef9b8d0
+fc83fb0fa8953deb39a6cc1b39b3b8c6
+fc8b97c3cb6b15cebc69a67c2f1d191f
+fc8dd5d947e330bc8d31ba285ede5a51
+fc964a0290259c9fc605555ec4a3bd56
+fc97296809c1ea00d1cfa01ed2e25624
+fc9af53c64a1d60eef0cbb3ce8830792
+fcb2b215a71e03d202fab51a0e2499d7
+fcb303d15bc83e94107edc4999effccf
+fcb8b4fd1f1b539c8a2414ad366da700
+fcb8d5bb3b6f8cfeeae8a036679cc493
+fcbbe2a3a73071f3745cc2d3e981e7f2
+fcbdf00155ecaa74b63995e4c93aaa0e
+fcbe9d6dc973d2971dadbe2e26d3be5e
+fcc4a872257764710fb9ac92fe749a89
+fcc627633ae4161d4badf8e9b85abcde
+fcc6f18734966d80b1133b3dd79a4be6
+fcc80212b0e0c37640c2e3b94280043b
+fcc9168258a8c53a2679b4398f948315
+fcd1763cb95a5438899dae62e7523beb
+fcd2f2364532fc6cf60e576b7d633e25
+fcdc5c5445ce000e8d63836f98f1bdb3
+fcdd5911b681da6d764f3aef53cfea04
+fce2d214a8fe8ad5dd3d6fa99ca726bf
+fce6d08af9e01cb22975a6a3f90009e1
+fce8998efd3b636c509409a2e7efc626
+fceaca71a86fa5d2c1426060747bbc5b
+fcefbd9a7a8ee10f0851b948c1509901
+fcf166e9b07084c52e56d34024090b14
+fcf20d0afbc70c3a330ae6fe844cbb01
+fcf2a5e325d5281021d1c2845810ff63
+fcf3aedad8a7456e973e09e59af6f0de
+fcfbcc7af26872f24884bbf80b75ff5f
+fcfccb533264308cbb6ac8ab417829c2
+fd01caaf3181668ca5d9fbd65c7a1d15
+fd062d7de1d16fb189f9f65e90d51b11
+fd062ea33c146f6d661037cea6ce1ccf
+fd079c54537a30c2a7dffec6a24db0e3
+fd081dff2a98ab0716cc0d155aabbed4
+fd09a6d593307e81dcb8f63bca026e97
+fd0ee0e2b08abc61a6e4a6e61b2aad81
+fd12ef2c93a6ef7653f4898ab27df971
+fd1444c780f9a969ef9e55e13b671471
+fd19932a5d81fc9dcabaabac2daca035
+fd1d462854c3bbe7680e06d8a07b0c6a
+fd20c40371557ad7c8d7f70d7a1a7848
+fd22f429a80fac17c52fa8a167fcb63b
+fd2339a6b83dd628bd7939008799ffa5
+fd2afefdf0c2424907763863344c2705
+fd2b300f150e011e41968639c7378f3c
+fd31876328cf86684aa47a739c4bda76
+fd32c4a85aa34dbe51f7449ca6da635e
+fd39eb64b0d7d81cdd3a4dc3f44d967b
+fd39ffd0a8d646e4f76bfd385833543f
+fd3ad774c3a8bef21e804a02548eb5db
+fd3c17a3afc6c89ec23a6e37fa3ba25a
+fd3eda09ffc6ec510f85c88538cf5395
+fd40fb17ae3fcf90935276128090012f
+fd423f903e5a1a3d746f29268f929176
+fd4428791ef1f8e75569c3e1c715da1f
+fd49f52370378aae74152cb3b1ecceb7
+fd674f4ff1506c34cf7d9d17002ee155
+fd6cd47d9c0ffc6995130247c7797809
+fd6ef63b6dab9c54ad53096023cb78c8
+fd6f3500071e747c33de5634631aa088
+fd7001af36561310393623fe0bd63175
+fd714515b0cfa2579a273479a2b714dd
+fd7e8c69413803d51ba18f4c13630e52
+fd818b2f6a65e374126bee689c310b52
+fd875fdecacbb7fce43e07b03998c349
+fd881b09ba980e349c818dd359ed0a73
+fd9acd7d1fe07fa97bd8998e6c684bba
+fd9b31a7e8f15614a5749f5801b31176
+fda49b7d8969d5760164d276776c10fe
+fda909dc0854bb78e6fe4f31c9112736
+fda9c4ade4e315c7326d1c43dde1be9f
+fdaae9c5ef125fc3491609555e79e250
+fdabd8d997b19e8aa416b0f702022bc1
+fdaed45bf9dc61e5087990fbc93d23e9
+fdaf465c0f20e3730fca93abfae8ebc6
+fdb1a394b867ed58a8d302e84658a58c
+fdb3694ef71ee5c58a626bec8ef73877
+fdb68d18a66772632620d9a9ad353d35
+fdb7fe66042032dae5cec18eb101e47f
+fdbda8736e7cac9c9e2102f7d0dd0771
+fdc3572c7bb890a3a1666c80cb1b58e3
+fdc4afe74880691b7169e6b4c64beba7
+fdc68b6ede67cf3fac8bef036d5cb5df
+fdd0d66794b9c01bd98deeff7f237bf9
+fdd55c04f1ecd3e9a0170a89a7eb6fc5
+fddc66a6049f15a2ac9335112ea003e3
+fddcc782ef4ebbee16a19255274b883d
+fde0f771e88d4515d5258d35537c23d1
+fde36d1747ec7cc9085fc9b8d4df64b5
+fde50e611451e737af52951b44b38c39
+fde572e2a46017a209cb7363a10c74eb
+fde6d86a03d44f660cebc6029379425d
+fdea716637af928698d158ebd9f0b036
+fdecd15430104d1cc27995cacdd24d23
+fdee3f95389ca55e49e52f273699b691
+fdf739b8a12074a524e3bc88fda1dce0
+fdf76759174cfd9e25fdd23e5f263c08
+fe0128aad8de6412eb103e1241abf185
+fe01ee02f1ea0ceaef1ef4363a81405f
+fe08303214ad9353127c14b6f0da073a
+fe0aac9a17699e7e1dd875fe76204fee
+fe0c2fa96596de96d9b24e73b865997d
+fe0c7500f6d98963f0b8ab0da4fc1bf4
+fe0da82b3d7d9db97e96f4aaa0d6a33e
+fe13f7c7b4306585439160e287089067
+fe15fffee1fd535463472be3dc09c360
+fe1d88ab6c33ffec4e6fdb12f9c46ee6
+fe2c5574c9c0292ab8981f8fd8dea837
+fe3350bb4c1483910f4ef6befddfa83f
+fe3522355333a257462c6a998da6c027
+fe37af1121acc8eea99038b5449dc42c
+fe3faed9ba35d7700a1829ac43040da8
+fe4302edc88561f0247d0ef1dac0d5d4
+fe439b38a13bdb8df49b6a46ed2a3f40
+fe4861c16670b46abdb7233b498d6b09
+fe48fce1093ac1ea73fe5801af68a64a
+fe4afebbc7eff059485846198c812a91
+fe56878bf49aba131992500c61be3d4f
+fe5978aba3beaac52dc67e93fde647ed
+fe5a207aa868d990e1cc99df1ff69ca1
+fe5c07a8cbb65ad630626e793bae9c07
+fe689ff12979381ac939a82725f14b5c
+fe6b58abff1e0181af3593b3352ac5c2
+fe748248c18a90a4be14d0edd16fe09d
+fe7e14d43bbfc0e0aa5aaffdd4077eb6
+fe826116269e6dc22fac3d927e9aee48
+fe84cabfed2b16dd6f95ab2bc663d163
+fe85fba83877d96aa1202b631a24fbc9
+fe8b84c3fd2e847cde501e2b52205bc8
+fe8d31e8c9cdccfa8dfb9a0c39c48afb
+fe96f563555fa64109ff27de153f8e68
+fe9af4332c84650bd962178c16209751
+fe9e03a8013f452eab395047fd68a2f8
+fea28a5746e45a0b12e676b1ca44bc14
+fea6e0fef29727269d12a02d03e8879b
+fea7a3e4b1b779bb0ae814a765b8e873
+fea9044824bc24433835d73fb2baa82b
+feb3f68293b09c5b146eb73ea1bfcafe
+feb73e866bfe9a24f4380b89b41c91dc
+febc05bb5e2619915f57bed34496a080
+febe63320d1abf4e5788f4b92cb1a14e
+fec1627ebbf9c2e0b19aa1abdeace369
+fec31e3c5636d034db7d77b74d392837
+fece65a24bfa92478d9aabb5e10524ea
+fed13a5e2c97591385ea77a3f810bd70
+fed288c923ce63d5747893c906b4d2ff
+fedb8d83f3f99ba774319ee865b2bcec
+fee08b0ce490733f4f40ef2c16eac7a4
+feea4177c2112af966a5966d9f415fa4
+feec501f02a92df68cac935fe8c7d80f
+fef4175f6ca217ce3c1b15deae3526ff
+fef5aa3e1897be2436e28c5aded3c308
+fef7884338438ce445b9a64408d5836a
+fef8a361d5dfd33ecba109224ee5038f
+ff08b4ab5b226db93ae9358bcb6aa73f
+ff0962b8540fc4426d9ab8c5f804ad8e
+ff0a6713c28202159927b3463281355e
+ff0a962245ae339a165e36b643aeeaf4
+ff0afc56b8e788821134edbcc68ff703
+ff116155f8c0f0b3c51d17f26beb6911
+ff136b6f3da2ccacfe8286dd561c9289
+ff17ed00fa3884f4cc42a4b0f0c2465f
+ff1949fc0a0ca76f47548b466bfb635b
+ff247086752290cccbf4ad67b5250166
+ff24f928bdcb760355e825b5df17e010
+ff25b948ce2fe844ab640c4d6ece3538
+ff2607011d5a5d67351c9b17c5ab745b
+ff266ec90858b3d0dcbc0a7441b41d84
+ff2a0cfd542bd2318d7bc0f64a78d0f6
+ff2b3eb773a53660896b753067686cb3
+ff2c8c5bd21d88286073227b2b17fa9e
+ff2efaf48bb3a229db962e709c6d95de
+ff35d32a44f393599be1a3ab2ee94a9b
+ff37d21a9793558aa890ba553a2c2270
+ff408bbf0e9dcce6d4fe50a85371ea31
+ff47df32fa4daec649bdb7680a342718
+ff495554aab44edb5e62f653fac69326
+ff5185fa1dce45618224432060810e3b
+ff556f0b9393c3fed13e0f5d36ab8dae
+ff61bb10d210bb0c72b0ecc6f45b722d
+ff6286edf5e8e6eeebad4ffe5bb97b22
+ff6347cdbff3d963ef396f29c89dd5d2
+ff6afe7f97c8b8df422be97d48feaddb
+ff6c6c2075f04020241e54dfc5db741a
+ff6d609348ba719262b24664d4005fb9
+ff72d7b87a310e97ec6c9c8e8ce4ab77
+ff748c8f9d27f8f484613fbdaad00799
+ff7ae88de9f08a441636378aa4ea2021
+ff7c4a92fe91727f62cc71270016a319
+ff7f7dd10730508f2921ac5a14d6d5c7
+ff8204a4476dc0467dbadae97251bf6e
+ff831717a5809c0c5e8a97e514f6294c
+ff864d7a4df0d05b11a501d58831b14a
+ff87819215bc249e2383f3c6eca832c1
+ff8acd12b05e64f5a6277f1cab9e9b1b
+ff91d654536517f0d97b9fc32ccf8d47
+ff93ee2524c5ceeb4f9e875a8f4524b8
+ff96cfc14006dcd5cd2f5055e02692a7
+ff9b66d3fa3e59ad7d5187c5fc32dfce
+ff9d56068b5c792c2412a86509af099d
+ffa4c7c35cbb60bd5bac8c2692805b67
+ffa622a3c8bf0e0afc36215800b166a9
+ffab254e799fd42d10da6d4c9f32bf1c
+ffab9ae62ea5149cebc770d69ab1fc3b
+ffabb8920899a83a51d88dc6ba5e2596
+ffaea6b3910d55fd6763508599326c3a
+ffb6c4885c962d804ee755a94076b408
+ffb74266d52bf044cff87eab01bcf2dc
+ffb7dcf84173252ff46d602e9f22e577
+ffbb402e50ae32f31b0f54368603c4ff
+ffbcac9b7410e5c1d7c0c92b424d8a44
+ffbcd43aa88c27bbaf8e2fd6907faaed
+ffcc56cfee46a62a7594c29b401bf8cf
+ffd38187be49ba0f49119fe65b09cd0e
+ffd3d80bf0be1a7d0603e9be1e6405ac
+ffd68d3fb874785c712b4d0e9a3e3c9d
+ffdbabfd29ea1c65e46c7e60c16766ad
+ffdcb5d25eaf9605a33859fc29c1f3a2
+ffddc05f4964f02f82dae3920d11eb63
+ffe00046a95ddb1b7ac23ede3b3ba5ba
+ffe69d829af68ded36787aadf32bbf95
+ffe78d515dbed78b8a75415c98633ce1
+fff0ab39c75cf5441fcece68da746c7c
+fff2eed02d5934c5443f7b727bcdb8bd
+fffccc91ba9f9e0e5d63a0167a71701b \ No newline at end of file
diff --git a/searx/data/engines_languages.json b/searx/data/engines_languages.json
index 83701e6..e293eb1 100644
--- a/searx/data/engines_languages.json
+++ b/searx/data/engines_languages.json
@@ -34,7 +34,6 @@
"de-CH",
"de-DE",
"de-LI",
- "de-LU",
"dv-MV",
"el-GR",
"en-AG",
@@ -51,7 +50,6 @@
"en-CK",
"en-CX",
"en-CY",
- "en-DM",
"en-FJ",
"en-FK",
"en-FM",
@@ -74,6 +72,7 @@
"en-LR",
"en-LS",
"en-MH",
+ "en-MM",
"en-MP",
"en-MS",
"en-MU",
@@ -115,6 +114,7 @@
"es-CL",
"es-CO",
"es-CR",
+ "es-DM",
"es-DO",
"es-EC",
"es-ES",
@@ -155,6 +155,7 @@
"fr-GP",
"fr-HT",
"fr-KM",
+ "fr-LU",
"fr-MC",
"fr-MF",
"fr-MG",
@@ -273,7 +274,6 @@
"de-CH",
"de-DE",
"de-LI",
- "de-LU",
"dv-MV",
"el-GR",
"en-AG",
@@ -290,7 +290,6 @@
"en-CK",
"en-CX",
"en-CY",
- "en-DM",
"en-FJ",
"en-FK",
"en-FM",
@@ -313,6 +312,7 @@
"en-LR",
"en-LS",
"en-MH",
+ "en-MM",
"en-MP",
"en-MS",
"en-MU",
@@ -354,6 +354,7 @@
"es-CL",
"es-CO",
"es-CR",
+ "es-DM",
"es-DO",
"es-EC",
"es-ES",
@@ -394,6 +395,7 @@
"fr-GP",
"fr-HT",
"fr-KM",
+ "fr-LU",
"fr-MC",
"fr-MF",
"fr-MG",
@@ -512,7 +514,6 @@
"de-CH",
"de-DE",
"de-LI",
- "de-LU",
"dv-MV",
"el-GR",
"en-AG",
@@ -529,7 +530,6 @@
"en-CK",
"en-CX",
"en-CY",
- "en-DM",
"en-FJ",
"en-FK",
"en-FM",
@@ -552,6 +552,7 @@
"en-LR",
"en-LS",
"en-MH",
+ "en-MM",
"en-MP",
"en-MS",
"en-MU",
@@ -593,6 +594,7 @@
"es-CL",
"es-CO",
"es-CR",
+ "es-DM",
"es-DO",
"es-EC",
"es-ES",
@@ -633,6 +635,7 @@
"fr-GP",
"fr-HT",
"fr-KM",
+ "fr-LU",
"fr-MC",
"fr-MF",
"fr-MG",
@@ -751,7 +754,6 @@
"de-CH",
"de-DE",
"de-LI",
- "de-LU",
"dv-MV",
"el-GR",
"en-AG",
@@ -768,7 +770,6 @@
"en-CK",
"en-CX",
"en-CY",
- "en-DM",
"en-FJ",
"en-FK",
"en-FM",
@@ -791,6 +792,7 @@
"en-LR",
"en-LS",
"en-MH",
+ "en-MM",
"en-MP",
"en-MS",
"en-MU",
@@ -832,6 +834,7 @@
"es-CL",
"es-CO",
"es-CR",
+ "es-DM",
"es-DO",
"es-EC",
"es-ES",
@@ -872,6 +875,7 @@
"fr-GP",
"fr-HT",
"fr-KM",
+ "fr-LU",
"fr-MC",
"fr-MF",
"fr-MG",
@@ -24752,7 +24756,6 @@
"wt-WT",
"zh-CN"
],
- "gigablast": [],
"google": {
"af": {
"name": "Afrikaans"
@@ -24761,19 +24764,19 @@
"name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629"
},
"be": {
- "name": "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f"
+ "name": "\u0431\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f"
},
"bg": {
- "name": "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"
+ "name": "\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"
},
"ca": {
- "name": "Catal\u00e0"
+ "name": "catal\u00e0"
},
"cs": {
- "name": "\u010ce\u0161tina"
+ "name": "\u010de\u0161tina"
},
"da": {
- "name": "Dansk"
+ "name": "dansk"
},
"de": {
"name": "Deutsch"
@@ -24785,43 +24788,43 @@
"name": "English"
},
"eo": {
- "name": "Esperanto"
+ "name": "esperanto"
},
"es": {
- "name": "Espa\u00f1ol"
+ "name": "espa\u00f1ol"
},
"et": {
- "name": "Eesti"
+ "name": "eesti"
},
"fa": {
"name": "\u0641\u0627\u0631\u0633\u06cc"
},
"fi": {
- "name": "Suomi"
+ "name": "suomi"
},
"fr": {
- "name": "Fran\u00e7ais"
+ "name": "fran\u00e7ais"
},
"hi": {
"name": "\u0939\u093f\u0928\u094d\u0926\u0940"
},
"hr": {
- "name": "Hrvatski"
+ "name": "hrvatski"
},
"hu": {
- "name": "Magyar"
+ "name": "magyar"
},
"hy": {
- "name": "\u0540\u0561\u0575\u0565\u0580\u0565\u0576"
+ "name": "\u0570\u0561\u0575\u0565\u0580\u0565\u0576"
},
"id": {
"name": "Indonesia"
},
"is": {
- "name": "\u00cdslenska"
+ "name": "\u00edslenska"
},
"it": {
- "name": "Italiano"
+ "name": "italiano"
},
"iw": {
"name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
@@ -24833,40 +24836,40 @@
"name": "\ud55c\uad6d\uc5b4"
},
"lt": {
- "name": "Lietuvi\u0173"
+ "name": "lietuvi\u0173"
},
"lv": {
- "name": "Latvie\u0161u"
+ "name": "latvie\u0161u"
},
"nl": {
"name": "Nederlands"
},
"no": {
- "name": "Norsk"
+ "name": "norsk"
},
"pl": {
- "name": "Polski"
+ "name": "polski"
},
"pt": {
- "name": "Portugu\u00eas"
+ "name": "portugu\u00eas"
},
"ro": {
- "name": "Rom\u00e2n\u0103"
+ "name": "rom\u00e2n\u0103"
},
"ru": {
- "name": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"
+ "name": "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"
},
"sk": {
- "name": "Sloven\u010dina"
+ "name": "sloven\u010dina"
},
"sl": {
- "name": "Sloven\u0161\u010dina"
+ "name": "sloven\u0161\u010dina"
},
"sr": {
- "name": "\u0421\u0440\u043f\u0441\u043a\u0438"
+ "name": "\u0441\u0440\u043f\u0441\u043a\u0438"
},
"sv": {
- "name": "Svenska"
+ "name": "svenska"
},
"sw": {
"name": "Kiswahili"
@@ -24881,7 +24884,147 @@
"name": "T\u00fcrk\u00e7e"
},
"uk": {
- "name": "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"
+ "name": "\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"
+ },
+ "vi": {
+ "name": "Ti\u1ebfng Vi\u1ec7t"
+ },
+ "zh-CN": {
+ "name": "\u4e2d\u6587 (\u7b80\u4f53)"
+ },
+ "zh-TW": {
+ "name": "\u4e2d\u6587 (\u7e41\u9ad4)"
+ }
+ },
+ "google images": {
+ "af": {
+ "name": "Afrikaans"
+ },
+ "ar": {
+ "name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629"
+ },
+ "be": {
+ "name": "\u0431\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f"
+ },
+ "bg": {
+ "name": "\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"
+ },
+ "ca": {
+ "name": "catal\u00e0"
+ },
+ "cs": {
+ "name": "\u010de\u0161tina"
+ },
+ "da": {
+ "name": "dansk"
+ },
+ "de": {
+ "name": "Deutsch"
+ },
+ "el": {
+ "name": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"
+ },
+ "en": {
+ "name": "English"
+ },
+ "eo": {
+ "name": "esperanto"
+ },
+ "es": {
+ "name": "espa\u00f1ol"
+ },
+ "et": {
+ "name": "eesti"
+ },
+ "fa": {
+ "name": "\u0641\u0627\u0631\u0633\u06cc"
+ },
+ "fi": {
+ "name": "suomi"
+ },
+ "fr": {
+ "name": "fran\u00e7ais"
+ },
+ "hi": {
+ "name": "\u0939\u093f\u0928\u094d\u0926\u0940"
+ },
+ "hr": {
+ "name": "hrvatski"
+ },
+ "hu": {
+ "name": "magyar"
+ },
+ "hy": {
+ "name": "\u0570\u0561\u0575\u0565\u0580\u0565\u0576"
+ },
+ "id": {
+ "name": "Indonesia"
+ },
+ "is": {
+ "name": "\u00edslenska"
+ },
+ "it": {
+ "name": "italiano"
+ },
+ "iw": {
+ "name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
+ },
+ "ja": {
+ "name": "\u65e5\u672c\u8a9e"
+ },
+ "ko": {
+ "name": "\ud55c\uad6d\uc5b4"
+ },
+ "lt": {
+ "name": "lietuvi\u0173"
+ },
+ "lv": {
+ "name": "latvie\u0161u"
+ },
+ "nl": {
+ "name": "Nederlands"
+ },
+ "no": {
+ "name": "norsk"
+ },
+ "pl": {
+ "name": "polski"
+ },
+ "pt": {
+ "name": "portugu\u00eas"
+ },
+ "ro": {
+ "name": "rom\u00e2n\u0103"
+ },
+ "ru": {
+ "name": "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"
+ },
+ "sk": {
+ "name": "sloven\u010dina"
+ },
+ "sl": {
+ "name": "sloven\u0161\u010dina"
+ },
+ "sr": {
+ "name": "\u0441\u0440\u043f\u0441\u043a\u0438"
+ },
+ "sv": {
+ "name": "svenska"
+ },
+ "sw": {
+ "name": "Kiswahili"
+ },
+ "th": {
+ "name": "\u0e44\u0e17\u0e22"
+ },
+ "tl": {
+ "name": "Filipino"
+ },
+ "tr": {
+ "name": "T\u00fcrk\u00e7e"
+ },
+ "uk": {
+ "name": "\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"
},
"vi": {
"name": "Ti\u1ebfng Vi\u1ec7t"
@@ -24901,19 +25044,19 @@
"name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629"
},
"be": {
- "name": "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f"
+ "name": "\u0431\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f"
},
"bg": {
- "name": "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"
+ "name": "\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"
},
"ca": {
- "name": "Catal\u00e0"
+ "name": "catal\u00e0"
},
"cs": {
- "name": "\u010ce\u0161tina"
+ "name": "\u010de\u0161tina"
},
"da": {
- "name": "Dansk"
+ "name": "dansk"
},
"de": {
"name": "Deutsch"
@@ -24925,43 +25068,43 @@
"name": "English"
},
"eo": {
- "name": "Esperanto"
+ "name": "esperanto"
},
"es": {
- "name": "Espa\u00f1ol"
+ "name": "espa\u00f1ol"
},
"et": {
- "name": "Eesti"
+ "name": "eesti"
},
"fa": {
"name": "\u0641\u0627\u0631\u0633\u06cc"
},
"fi": {
- "name": "Suomi"
+ "name": "suomi"
},
"fr": {
- "name": "Fran\u00e7ais"
+ "name": "fran\u00e7ais"
},
"hi": {
"name": "\u0939\u093f\u0928\u094d\u0926\u0940"
},
"hr": {
- "name": "Hrvatski"
+ "name": "hrvatski"
},
"hu": {
- "name": "Magyar"
+ "name": "magyar"
},
"hy": {
- "name": "\u0540\u0561\u0575\u0565\u0580\u0565\u0576"
+ "name": "\u0570\u0561\u0575\u0565\u0580\u0565\u0576"
},
"id": {
"name": "Indonesia"
},
"is": {
- "name": "\u00cdslenska"
+ "name": "\u00edslenska"
},
"it": {
- "name": "Italiano"
+ "name": "italiano"
},
"iw": {
"name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
@@ -24973,40 +25116,40 @@
"name": "\ud55c\uad6d\uc5b4"
},
"lt": {
- "name": "Lietuvi\u0173"
+ "name": "lietuvi\u0173"
},
"lv": {
- "name": "Latvie\u0161u"
+ "name": "latvie\u0161u"
},
"nl": {
"name": "Nederlands"
},
"no": {
- "name": "Norsk"
+ "name": "norsk"
},
"pl": {
- "name": "Polski"
+ "name": "polski"
},
"pt": {
- "name": "Portugu\u00eas"
+ "name": "portugu\u00eas"
},
"ro": {
- "name": "Rom\u00e2n\u0103"
+ "name": "rom\u00e2n\u0103"
},
"ru": {
- "name": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"
+ "name": "\u0440\u0443\u0441\u0441\u043a\u0438\u0439"
},
"sk": {
- "name": "Sloven\u010dina"
+ "name": "sloven\u010dina"
},
"sl": {
- "name": "Sloven\u0161\u010dina"
+ "name": "sloven\u0161\u010dina"
},
"sr": {
- "name": "\u0421\u0440\u043f\u0441\u043a\u0438"
+ "name": "\u0441\u0440\u043f\u0441\u043a\u0438"
},
"sv": {
- "name": "Svenska"
+ "name": "svenska"
},
"sw": {
"name": "Kiswahili"
@@ -25021,7 +25164,7 @@
"name": "T\u00fcrk\u00e7e"
},
"uk": {
- "name": "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"
+ "name": "\u0443\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"
},
"vi": {
"name": "Ti\u1ebfng Vi\u1ec7t"
@@ -25033,405 +25176,1321 @@
"name": "\u4e2d\u6587 (\u7e41\u9ad4)"
}
},
- "qwant": [
- "bg-BG",
- "br-FR",
- "ca-AD",
- "ca-ES",
- "ca-FR",
- "co-FR",
- "cs-CZ",
- "cy-GB",
- "da-DK",
- "de-AT",
- "de-CH",
- "de-DE",
- "el-GR",
- "en-AU",
- "en-CA",
- "en-GB",
- "en-IE",
- "en-IN",
- "en-MY",
- "en-NZ",
- "en-PH",
- "en-SG",
- "en-US",
- "es-AD",
- "es-AR",
- "es-CL",
- "es-ES",
- "es-MX",
- "et-EE",
- "eu-ES",
- "eu-FR",
- "fi-FI",
- "fr-AD",
- "fr-BE",
- "fr-CA",
- "fr-CH",
- "fr-FR",
- "gd-GB",
- "he-IL",
- "hu-HU",
- "it-CH",
- "it-IT",
- "ja-JP",
- "ko-KR",
- "ms-MY",
- "nl-BE",
- "nl-NL",
- "no-NO",
- "pl-PL",
- "pt-AD",
- "pt-BR",
- "pt-PT",
- "ro-RO",
- "ru-RU",
- "sv-SE",
- "th-TH",
- "tr-TR"
- ],
- "qwant images": [
- "bg-BG",
- "br-FR",
- "ca-AD",
- "ca-ES",
- "ca-FR",
- "co-FR",
- "cs-CZ",
- "cy-GB",
- "da-DK",
- "de-AT",
- "de-CH",
- "de-DE",
- "el-GR",
- "en-AU",
- "en-CA",
- "en-GB",
- "en-IE",
- "en-IN",
- "en-MY",
- "en-NZ",
- "en-PH",
- "en-SG",
- "en-US",
- "es-AD",
- "es-AR",
- "es-CL",
- "es-ES",
- "es-MX",
- "et-EE",
- "eu-ES",
- "eu-FR",
- "fi-FI",
- "fr-AD",
- "fr-BE",
- "fr-CA",
- "fr-CH",
- "fr-FR",
- "gd-GB",
- "he-IL",
- "hu-HU",
- "it-CH",
- "it-IT",
- "ja-JP",
- "ko-KR",
- "ms-MY",
- "nl-BE",
- "nl-NL",
- "no-NO",
- "pl-PL",
- "pt-AD",
- "pt-BR",
- "pt-PT",
- "ro-RO",
- "ru-RU",
- "sv-SE",
- "th-TH",
- "tr-TR"
- ],
- "qwant news": [
- "bg-BG",
- "br-FR",
- "ca-AD",
- "ca-ES",
- "ca-FR",
- "co-FR",
- "cs-CZ",
- "cy-GB",
- "da-DK",
- "de-AT",
- "de-CH",
- "de-DE",
- "el-GR",
- "en-AU",
- "en-CA",
- "en-GB",
- "en-IE",
- "en-IN",
- "en-MY",
- "en-NZ",
- "en-PH",
- "en-SG",
- "en-US",
- "es-AD",
- "es-AR",
- "es-CL",
- "es-ES",
- "es-MX",
- "et-EE",
- "eu-ES",
- "eu-FR",
- "fi-FI",
- "fr-AD",
- "fr-BE",
- "fr-CA",
- "fr-CH",
- "fr-FR",
- "gd-GB",
- "he-IL",
- "hu-HU",
- "it-CH",
- "it-IT",
- "ja-JP",
- "ko-KR",
- "ms-MY",
- "nl-BE",
- "nl-NL",
- "no-NO",
- "pl-PL",
- "pt-AD",
- "pt-BR",
- "pt-PT",
- "ro-RO",
- "ru-RU",
- "sv-SE",
- "th-TH",
- "tr-TR"
- ],
- "qwant social": [
- "bg-BG",
- "br-FR",
- "ca-AD",
- "ca-ES",
- "ca-FR",
- "co-FR",
- "cs-CZ",
- "cy-GB",
- "da-DK",
- "de-AT",
- "de-CH",
- "de-DE",
- "el-GR",
- "en-AU",
- "en-CA",
- "en-GB",
- "en-IE",
- "en-IN",
- "en-MY",
- "en-NZ",
- "en-PH",
- "en-SG",
- "en-US",
- "es-AD",
- "es-AR",
- "es-CL",
- "es-ES",
- "es-MX",
- "et-EE",
- "eu-ES",
- "eu-FR",
- "fi-FI",
- "fr-AD",
- "fr-BE",
- "fr-CA",
- "fr-CH",
- "fr-FR",
- "gd-GB",
- "he-IL",
- "hu-HU",
- "it-CH",
- "it-IT",
- "ja-JP",
- "ko-KR",
- "ms-MY",
- "nl-BE",
- "nl-NL",
- "no-NO",
- "pl-PL",
- "pt-AD",
- "pt-BR",
- "pt-PT",
- "ro-RO",
- "ru-RU",
- "sv-SE",
- "th-TH",
- "tr-TR"
+ "peertube": [
+ "aa",
+ "ab",
+ "af",
+ "ak",
+ "am",
+ "an",
+ "ar",
+ "as",
+ "ase",
+ "av",
+ "avk",
+ "ay",
+ "az",
+ "ba",
+ "be",
+ "bfi",
+ "bg",
+ "bi",
+ "bm",
+ "bn",
+ "bo",
+ "br",
+ "bs",
+ "bzs",
+ "ca",
+ "ce",
+ "ch",
+ "co",
+ "cr",
+ "cs",
+ "cse",
+ "csl",
+ "cv",
+ "cy",
+ "da",
+ "de",
+ "dsl",
+ "dv",
+ "dz",
+ "ee",
+ "el",
+ "en",
+ "eo",
+ "es",
+ "et",
+ "eu",
+ "fa",
+ "ff",
+ "fi",
+ "fj",
+ "fo",
+ "fr",
+ "fsl",
+ "fy",
+ "ga",
+ "gd",
+ "gl",
+ "gn",
+ "gsg",
+ "gu",
+ "gv",
+ "ha",
+ "he",
+ "hi",
+ "ho",
+ "hr",
+ "ht",
+ "hu",
+ "hy",
+ "hz",
+ "id",
+ "ig",
+ "ii",
+ "ik",
+ "is",
+ "it",
+ "iu",
+ "ja",
+ "jbo",
+ "jsl",
+ "jv",
+ "ka",
+ "kg",
+ "ki",
+ "kj",
+ "kk",
+ "kl",
+ "km",
+ "kn",
+ "ko",
+ "kr",
+ "ks",
+ "ku",
+ "kv",
+ "kw",
+ "ky",
+ "lb",
+ "lg",
+ "li",
+ "ln",
+ "lo",
+ "lt",
+ "lu",
+ "lv",
+ "mg",
+ "mh",
+ "mi",
+ "mk",
+ "ml",
+ "mn",
+ "mr",
+ "ms",
+ "mt",
+ "my",
+ "na",
+ "nb",
+ "nd",
+ "ne",
+ "ng",
+ "nl",
+ "nn",
+ "no",
+ "nr",
+ "nv",
+ "ny",
+ "oc",
+ "oj",
+ "om",
+ "or",
+ "os",
+ "pa",
+ "pks",
+ "pl",
+ "ps",
+ "pt",
+ "qu",
+ "rm",
+ "rn",
+ "ro",
+ "rsl",
+ "ru",
+ "rw",
+ "sc",
+ "sd",
+ "sdl",
+ "se",
+ "sfs",
+ "sg",
+ "sh",
+ "si",
+ "sk",
+ "sl",
+ "sm",
+ "sn",
+ "so",
+ "sq",
+ "sr",
+ "ss",
+ "st",
+ "su",
+ "sv",
+ "sw",
+ "swl",
+ "ta",
+ "te",
+ "tg",
+ "th",
+ "ti",
+ "tk",
+ "tl",
+ "tlh",
+ "tn",
+ "to",
+ "tr",
+ "ts",
+ "tt",
+ "tw",
+ "ty",
+ "ug",
+ "uk",
+ "ur",
+ "uz",
+ "ve",
+ "vi",
+ "wa",
+ "wo",
+ "xh",
+ "yi",
+ "yo",
+ "za",
+ "zh",
+ "zu"
],
+ "qwant": {
+ "bg-BG": {
+ "name": "\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438 \u0435\u0437\u0438\u043a"
+ },
+ "br-FR": {
+ "name": "Brezhoneg"
+ },
+ "ca-AD": {
+ "name": "Catal\u00e0"
+ },
+ "ca-ES": {
+ "name": "Catal\u00e0"
+ },
+ "ca-FR": {
+ "name": "Catal\u00e0"
+ },
+ "co-FR": {
+ "name": "Corsu"
+ },
+ "cs-CZ": {
+ "name": "\u010cesky"
+ },
+ "cy-GB": {
+ "name": "Welsh"
+ },
+ "da-DK": {
+ "name": "Dansk"
+ },
+ "de-AT": {
+ "name": "Deutsch"
+ },
+ "de-CH": {
+ "name": "Deutsch"
+ },
+ "de-DE": {
+ "name": "Deutsch"
+ },
+ "el-GR": {
+ "name": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"
+ },
+ "en-AU": {
+ "name": "English"
+ },
+ "en-CA": {
+ "name": "English"
+ },
+ "en-GB": {
+ "name": "English"
+ },
+ "en-IE": {
+ "name": "English"
+ },
+ "en-IN": {
+ "name": "English"
+ },
+ "en-MY": {
+ "name": "English"
+ },
+ "en-NZ": {
+ "name": "English"
+ },
+ "en-PH": {
+ "name": "English"
+ },
+ "en-SG": {
+ "name": "English"
+ },
+ "en-US": {
+ "name": "English"
+ },
+ "es-AD": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-AR": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-CL": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-ES": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-MX": {
+ "name": "Espa\u00f1ol"
+ },
+ "et-EE": {
+ "name": "Eesti keel"
+ },
+ "eu-ES": {
+ "name": "Euskara"
+ },
+ "eu-FR": {
+ "name": "Euskara"
+ },
+ "fi-FI": {
+ "name": "Suomen kieli"
+ },
+ "fr-AD": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-BE": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-CA": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-CH": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-FR": {
+ "name": "Fran\u00e7ais"
+ },
+ "gd-GB": {
+ "name": "Scottish"
+ },
+ "he-IL": {
+ "name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
+ },
+ "hu-HU": {
+ "name": "magyar"
+ },
+ "it-CH": {
+ "name": "Italiano"
+ },
+ "it-IT": {
+ "name": "Italiano"
+ },
+ "ja-JP": {
+ "name": "\u65e5\u672c\u8a9e (\u306b\u307b\u3093\u3054)"
+ },
+ "ko-KR": {
+ "name": "\ud55c\uad6d\uc5b4"
+ },
+ "ms-MY": {
+ "name": "\u0628\u0647\u0627\u0633 \u0645\u0644\u0627\u064a\u0648"
+ },
+ "nb-NO": {
+ "name": "Norsk"
+ },
+ "nl-BE": {
+ "name": "Nederlands"
+ },
+ "nl-NL": {
+ "name": "Nederlands"
+ },
+ "pl-PL": {
+ "name": "Polski"
+ },
+ "pt-AD": {
+ "name": "Portugu\u00eas"
+ },
+ "pt-BR": {
+ "name": "Portugu\u00eas"
+ },
+ "pt-PT": {
+ "name": "Portugu\u00eas"
+ },
+ "ro-RO": {
+ "name": "Rom\u00e2n\u0103"
+ },
+ "ru-RU": {
+ "name": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"
+ },
+ "sv-SE": {
+ "name": "Svenska"
+ },
+ "th-TH": {
+ "name": "\u0e44\u0e17\u0e22"
+ },
+ "tr-TR": {
+ "name": "T\u00fcrk\u00e7e"
+ }
+ },
+ "qwant images": {
+ "bg-BG": {
+ "name": "\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438 \u0435\u0437\u0438\u043a"
+ },
+ "br-FR": {
+ "name": "Brezhoneg"
+ },
+ "ca-AD": {
+ "name": "Catal\u00e0"
+ },
+ "ca-ES": {
+ "name": "Catal\u00e0"
+ },
+ "ca-FR": {
+ "name": "Catal\u00e0"
+ },
+ "co-FR": {
+ "name": "Corsu"
+ },
+ "cs-CZ": {
+ "name": "\u010cesky"
+ },
+ "cy-GB": {
+ "name": "Welsh"
+ },
+ "da-DK": {
+ "name": "Dansk"
+ },
+ "de-AT": {
+ "name": "Deutsch"
+ },
+ "de-CH": {
+ "name": "Deutsch"
+ },
+ "de-DE": {
+ "name": "Deutsch"
+ },
+ "el-GR": {
+ "name": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"
+ },
+ "en-AU": {
+ "name": "English"
+ },
+ "en-CA": {
+ "name": "English"
+ },
+ "en-GB": {
+ "name": "English"
+ },
+ "en-IE": {
+ "name": "English"
+ },
+ "en-IN": {
+ "name": "English"
+ },
+ "en-MY": {
+ "name": "English"
+ },
+ "en-NZ": {
+ "name": "English"
+ },
+ "en-PH": {
+ "name": "English"
+ },
+ "en-SG": {
+ "name": "English"
+ },
+ "en-US": {
+ "name": "English"
+ },
+ "es-AD": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-AR": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-CL": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-ES": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-MX": {
+ "name": "Espa\u00f1ol"
+ },
+ "et-EE": {
+ "name": "Eesti keel"
+ },
+ "eu-ES": {
+ "name": "Euskara"
+ },
+ "eu-FR": {
+ "name": "Euskara"
+ },
+ "fi-FI": {
+ "name": "Suomen kieli"
+ },
+ "fr-AD": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-BE": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-CA": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-CH": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-FR": {
+ "name": "Fran\u00e7ais"
+ },
+ "gd-GB": {
+ "name": "Scottish"
+ },
+ "he-IL": {
+ "name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
+ },
+ "hu-HU": {
+ "name": "magyar"
+ },
+ "it-CH": {
+ "name": "Italiano"
+ },
+ "it-IT": {
+ "name": "Italiano"
+ },
+ "ja-JP": {
+ "name": "\u65e5\u672c\u8a9e (\u306b\u307b\u3093\u3054)"
+ },
+ "ko-KR": {
+ "name": "\ud55c\uad6d\uc5b4"
+ },
+ "ms-MY": {
+ "name": "\u0628\u0647\u0627\u0633 \u0645\u0644\u0627\u064a\u0648"
+ },
+ "nb-NO": {
+ "name": "Norsk"
+ },
+ "nl-BE": {
+ "name": "Nederlands"
+ },
+ "nl-NL": {
+ "name": "Nederlands"
+ },
+ "pl-PL": {
+ "name": "Polski"
+ },
+ "pt-AD": {
+ "name": "Portugu\u00eas"
+ },
+ "pt-BR": {
+ "name": "Portugu\u00eas"
+ },
+ "pt-PT": {
+ "name": "Portugu\u00eas"
+ },
+ "ro-RO": {
+ "name": "Rom\u00e2n\u0103"
+ },
+ "ru-RU": {
+ "name": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"
+ },
+ "sv-SE": {
+ "name": "Svenska"
+ },
+ "th-TH": {
+ "name": "\u0e44\u0e17\u0e22"
+ },
+ "tr-TR": {
+ "name": "T\u00fcrk\u00e7e"
+ }
+ },
+ "qwant news": {
+ "bg-BG": {
+ "name": "\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438 \u0435\u0437\u0438\u043a"
+ },
+ "br-FR": {
+ "name": "Brezhoneg"
+ },
+ "ca-AD": {
+ "name": "Catal\u00e0"
+ },
+ "ca-ES": {
+ "name": "Catal\u00e0"
+ },
+ "ca-FR": {
+ "name": "Catal\u00e0"
+ },
+ "co-FR": {
+ "name": "Corsu"
+ },
+ "cs-CZ": {
+ "name": "\u010cesky"
+ },
+ "cy-GB": {
+ "name": "Welsh"
+ },
+ "da-DK": {
+ "name": "Dansk"
+ },
+ "de-AT": {
+ "name": "Deutsch"
+ },
+ "de-CH": {
+ "name": "Deutsch"
+ },
+ "de-DE": {
+ "name": "Deutsch"
+ },
+ "el-GR": {
+ "name": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"
+ },
+ "en-AU": {
+ "name": "English"
+ },
+ "en-CA": {
+ "name": "English"
+ },
+ "en-GB": {
+ "name": "English"
+ },
+ "en-IE": {
+ "name": "English"
+ },
+ "en-IN": {
+ "name": "English"
+ },
+ "en-MY": {
+ "name": "English"
+ },
+ "en-NZ": {
+ "name": "English"
+ },
+ "en-PH": {
+ "name": "English"
+ },
+ "en-SG": {
+ "name": "English"
+ },
+ "en-US": {
+ "name": "English"
+ },
+ "es-AD": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-AR": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-CL": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-ES": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-MX": {
+ "name": "Espa\u00f1ol"
+ },
+ "et-EE": {
+ "name": "Eesti keel"
+ },
+ "eu-ES": {
+ "name": "Euskara"
+ },
+ "eu-FR": {
+ "name": "Euskara"
+ },
+ "fi-FI": {
+ "name": "Suomen kieli"
+ },
+ "fr-AD": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-BE": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-CA": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-CH": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-FR": {
+ "name": "Fran\u00e7ais"
+ },
+ "gd-GB": {
+ "name": "Scottish"
+ },
+ "he-IL": {
+ "name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
+ },
+ "hu-HU": {
+ "name": "magyar"
+ },
+ "it-CH": {
+ "name": "Italiano"
+ },
+ "it-IT": {
+ "name": "Italiano"
+ },
+ "ja-JP": {
+ "name": "\u65e5\u672c\u8a9e (\u306b\u307b\u3093\u3054)"
+ },
+ "ko-KR": {
+ "name": "\ud55c\uad6d\uc5b4"
+ },
+ "ms-MY": {
+ "name": "\u0628\u0647\u0627\u0633 \u0645\u0644\u0627\u064a\u0648"
+ },
+ "nb-NO": {
+ "name": "Norsk"
+ },
+ "nl-BE": {
+ "name": "Nederlands"
+ },
+ "nl-NL": {
+ "name": "Nederlands"
+ },
+ "pl-PL": {
+ "name": "Polski"
+ },
+ "pt-AD": {
+ "name": "Portugu\u00eas"
+ },
+ "pt-BR": {
+ "name": "Portugu\u00eas"
+ },
+ "pt-PT": {
+ "name": "Portugu\u00eas"
+ },
+ "ro-RO": {
+ "name": "Rom\u00e2n\u0103"
+ },
+ "ru-RU": {
+ "name": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"
+ },
+ "sv-SE": {
+ "name": "Svenska"
+ },
+ "th-TH": {
+ "name": "\u0e44\u0e17\u0e22"
+ },
+ "tr-TR": {
+ "name": "T\u00fcrk\u00e7e"
+ }
+ },
+ "qwant social": {
+ "bg-BG": {
+ "name": "\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438 \u0435\u0437\u0438\u043a"
+ },
+ "br-FR": {
+ "name": "Brezhoneg"
+ },
+ "ca-AD": {
+ "name": "Catal\u00e0"
+ },
+ "ca-ES": {
+ "name": "Catal\u00e0"
+ },
+ "ca-FR": {
+ "name": "Catal\u00e0"
+ },
+ "co-FR": {
+ "name": "Corsu"
+ },
+ "cs-CZ": {
+ "name": "\u010cesky"
+ },
+ "cy-GB": {
+ "name": "Welsh"
+ },
+ "da-DK": {
+ "name": "Dansk"
+ },
+ "de-AT": {
+ "name": "Deutsch"
+ },
+ "de-CH": {
+ "name": "Deutsch"
+ },
+ "de-DE": {
+ "name": "Deutsch"
+ },
+ "el-GR": {
+ "name": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"
+ },
+ "en-AU": {
+ "name": "English"
+ },
+ "en-CA": {
+ "name": "English"
+ },
+ "en-GB": {
+ "name": "English"
+ },
+ "en-IE": {
+ "name": "English"
+ },
+ "en-IN": {
+ "name": "English"
+ },
+ "en-MY": {
+ "name": "English"
+ },
+ "en-NZ": {
+ "name": "English"
+ },
+ "en-PH": {
+ "name": "English"
+ },
+ "en-SG": {
+ "name": "English"
+ },
+ "en-US": {
+ "name": "English"
+ },
+ "es-AD": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-AR": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-CL": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-ES": {
+ "name": "Espa\u00f1ol"
+ },
+ "es-MX": {
+ "name": "Espa\u00f1ol"
+ },
+ "et-EE": {
+ "name": "Eesti keel"
+ },
+ "eu-ES": {
+ "name": "Euskara"
+ },
+ "eu-FR": {
+ "name": "Euskara"
+ },
+ "fi-FI": {
+ "name": "Suomen kieli"
+ },
+ "fr-AD": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-BE": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-CA": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-CH": {
+ "name": "Fran\u00e7ais"
+ },
+ "fr-FR": {
+ "name": "Fran\u00e7ais"
+ },
+ "gd-GB": {
+ "name": "Scottish"
+ },
+ "he-IL": {
+ "name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
+ },
+ "hu-HU": {
+ "name": "magyar"
+ },
+ "it-CH": {
+ "name": "Italiano"
+ },
+ "it-IT": {
+ "name": "Italiano"
+ },
+ "ja-JP": {
+ "name": "\u65e5\u672c\u8a9e (\u306b\u307b\u3093\u3054)"
+ },
+ "ko-KR": {
+ "name": "\ud55c\uad6d\uc5b4"
+ },
+ "ms-MY": {
+ "name": "\u0628\u0647\u0627\u0633 \u0645\u0644\u0627\u064a\u0648"
+ },
+ "nb-NO": {
+ "name": "Norsk"
+ },
+ "nl-BE": {
+ "name": "Nederlands"
+ },
+ "nl-NL": {
+ "name": "Nederlands"
+ },
+ "pl-PL": {
+ "name": "Polski"
+ },
+ "pt-AD": {
+ "name": "Portugu\u00eas"
+ },
+ "pt-BR": {
+ "name": "Portugu\u00eas"
+ },
+ "pt-PT": {
+ "name": "Portugu\u00eas"
+ },
+ "ro-RO": {
+ "name": "Rom\u00e2n\u0103"
+ },
+ "ru-RU": {
+ "name": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"
+ },
+ "sv-SE": {
+ "name": "Svenska"
+ },
+ "th-TH": {
+ "name": "\u0e44\u0e17\u0e22"
+ },
+ "tr-TR": {
+ "name": "T\u00fcrk\u00e7e"
+ }
+ },
+ "startpage": {
+ "af": {
+ "alias": "afrikaans"
+ },
+ "am": {
+ "alias": "amharic"
+ },
+ "ar": {
+ "alias": "arabic"
+ },
+ "az": {
+ "alias": "azerbaijani"
+ },
+ "be": {
+ "alias": "belarusian"
+ },
+ "bg": {
+ "alias": "bulgarian"
+ },
+ "bn": {
+ "alias": "bengali"
+ },
+ "bs": {
+ "alias": "bosnian"
+ },
+ "ca": {
+ "alias": "catalan"
+ },
+ "cs": {
+ "alias": "czech"
+ },
+ "cy": {
+ "alias": "welsh"
+ },
+ "da": {
+ "alias": "dansk"
+ },
+ "de": {
+ "alias": "deutsch"
+ },
+ "el": {
+ "alias": "greek"
+ },
+ "en": {
+ "alias": "english"
+ },
+ "en-GB": {
+ "alias": "english_uk"
+ },
+ "eo": {
+ "alias": "esperanto"
+ },
+ "es": {
+ "alias": "espanol"
+ },
+ "et": {
+ "alias": "estonian"
+ },
+ "eu": {
+ "alias": "basque"
+ },
+ "fa": {
+ "alias": "persian"
+ },
+ "fi": {
+ "alias": "suomi"
+ },
+ "fo": {
+ "alias": "faroese"
+ },
+ "fr": {
+ "alias": "francais"
+ },
+ "fy": {
+ "alias": "frisian"
+ },
+ "ga": {
+ "alias": "irish"
+ },
+ "gd": {
+ "alias": "gaelic"
+ },
+ "gl": {
+ "alias": "galician"
+ },
+ "gu": {
+ "alias": "gujarati"
+ },
+ "he": {
+ "alias": "hebrew"
+ },
+ "hi": {
+ "alias": "hindi"
+ },
+ "hr": {
+ "alias": "croatian"
+ },
+ "hu": {
+ "alias": "hungarian"
+ },
+ "ia": {
+ "alias": "interlingua"
+ },
+ "id": {
+ "alias": "indonesian"
+ },
+ "is": {
+ "alias": "icelandic"
+ },
+ "it": {
+ "alias": "italiano"
+ },
+ "ja": {
+ "alias": "nihongo"
+ },
+ "jv": {
+ "alias": "javanese"
+ },
+ "ka": {
+ "alias": "georgian"
+ },
+ "kn": {
+ "alias": "kannada"
+ },
+ "ko": {
+ "alias": "hangul"
+ },
+ "la": {
+ "alias": "latin"
+ },
+ "lt": {
+ "alias": "lithuanian"
+ },
+ "lv": {
+ "alias": "latvian"
+ },
+ "mai": {
+ "alias": "bihari"
+ },
+ "mk": {
+ "alias": "macedonian"
+ },
+ "ml": {
+ "alias": "malayam"
+ },
+ "mr": {
+ "alias": "marathi"
+ },
+ "ms": {
+ "alias": "malay"
+ },
+ "mt": {
+ "alias": "maltese"
+ },
+ "nb": {
+ "alias": "norsk"
+ },
+ "ne": {
+ "alias": "nepali"
+ },
+ "nl": {
+ "alias": "nederlands"
+ },
+ "oc": {
+ "alias": "occitan"
+ },
+ "pa": {
+ "alias": "punjabi"
+ },
+ "pl": {
+ "alias": "polski"
+ },
+ "pt": {
+ "alias": "portugues"
+ },
+ "ro": {
+ "alias": "romanian"
+ },
+ "ru": {
+ "alias": "russian"
+ },
+ "si": {
+ "alias": "sinhalese"
+ },
+ "sk": {
+ "alias": "slovak"
+ },
+ "sl": {
+ "alias": "slovenian"
+ },
+ "sq": {
+ "alias": "albanian"
+ },
+ "sr": {
+ "alias": "serbian"
+ },
+ "su": {
+ "alias": "sudanese"
+ },
+ "sv": {
+ "alias": "svenska"
+ },
+ "sw": {
+ "alias": "swahili"
+ },
+ "ta": {
+ "alias": "tamil"
+ },
+ "te": {
+ "alias": "telugu"
+ },
+ "th": {
+ "alias": "thai"
+ },
+ "ti": {
+ "alias": "tigrinya"
+ },
+ "tl": {
+ "alias": "tagalog"
+ },
+ "tr": {
+ "alias": "turkce"
+ },
+ "uk": {
+ "alias": "ukrainian"
+ },
+ "ur": {
+ "alias": "urdu"
+ },
+ "uz": {
+ "alias": "uzbek"
+ },
+ "vi": {
+ "alias": "vietnamese"
+ },
+ "xh": {
+ "alias": "xhosa"
+ },
+ "zh": {
+ "alias": "jiantizhongwen"
+ },
+ "zh-HK": {
+ "alias": "fantizhengwen"
+ },
+ "zh-TW": {
+ "alias": "fantizhengwen"
+ },
+ "zu": {
+ "alias": "zulu"
+ }
+ },
"wikidata": {
"ab": {
- "articles": 6031,
+ "articles": 6080,
"english_name": "Abkhazian",
"name": "\u0410\u04a7\u0441\u0443\u0430"
},
"ace": {
- "articles": 10323,
+ "articles": 10353,
"english_name": "Acehnese",
"name": "Bahsa Ac\u00e8h"
},
"ady": {
- "articles": 417,
+ "articles": 421,
"english_name": "Adyghe",
"name": "\u0410\u0434\u044b\u0433\u044d\u0431\u0437\u044d"
},
"af": {
- "articles": 89423,
+ "articles": 93802,
"english_name": "Afrikaans",
"name": "Afrikaans"
},
"ak": {
- "articles": 795,
+ "articles": 879,
"english_name": "Akan",
"name": "Akana"
},
"als": {
- "articles": 27014,
+ "articles": 27329,
"english_name": "Alemannic",
"name": "Alemannisch"
},
"am": {
- "articles": 14831,
+ "articles": 14875,
"english_name": "Amharic",
"name": "\u12a0\u121b\u122d\u129b"
},
"an": {
- "articles": 36741,
+ "articles": 38095,
"english_name": "Aragonese",
"name": "Aragon\u00e9s"
},
"ang": {
- "articles": 3187,
+ "articles": 3294,
"english_name": "Anglo-Saxon",
"name": "Englisc"
},
"ar": {
- "articles": 1031588,
+ "articles": 1065982,
"english_name": "Arabic",
"name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629"
},
"arc": {
- "articles": 1647,
+ "articles": 1764,
"english_name": "Aramaic",
"name": "\u0710\u072a\u0721\u071d\u0710"
},
+ "ary": {
+ "articles": 2554,
+ "english_name": "Moroccan Arabic",
+ "name": "\u0627\u0644\u062f\u0627\u0631\u062c\u0629"
+ },
"arz": {
- "articles": 145685,
+ "articles": 1098659,
"english_name": "Egyptian Arabic",
"name": "\u0645\u0635\u0631\u0649 (Ma\u1e63ri)"
},
"as": {
- "articles": 6381,
+ "articles": 7230,
"english_name": "Assamese",
"name": "\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be"
},
"ast": {
- "articles": 100187,
+ "articles": 107460,
"english_name": "Asturian",
"name": "Asturianu"
},
"atj": {
- "articles": 1167,
+ "articles": 1259,
"english_name": "Atikamekw",
"name": "Atikamekw"
},
"av": {
- "articles": 2422,
+ "articles": 2451,
"english_name": "Avar",
"name": "\u0410\u0432\u0430\u0440"
},
+ "avk": {
+ "articles": 9484,
+ "english_name": "Kotava",
+ "name": "Kotava"
+ },
+ "awa": {
+ "articles": 2406,
+ "english_name": "Awadhi",
+ "name": "\u0905\u0935\u0927\u0940"
+ },
"ay": {
- "articles": 4646,
+ "articles": 4852,
"english_name": "Aymara",
"name": "Aymar"
},
"az": {
- "articles": 155848,
+ "articles": 173155,
"english_name": "Azerbaijani",
"name": "Az\u0259rbaycanca"
},
"azb": {
- "articles": 183161,
+ "articles": 239463,
"english_name": "South Azerbaijani",
"name": "\u062a\u06c6\u0631\u06a9\u062c\u0647"
},
"ba": {
- "articles": 51161,
+ "articles": 53560,
"english_name": "Bashkir",
"name": "\u0411\u0430\u0448\u04a1\u043e\u0440\u0442"
},
"ban": {
- "articles": 2258,
+ "articles": 3899,
"english_name": "Balinese",
"name": "Bali"
},
"bar": {
- "articles": 30891,
+ "articles": 31394,
"english_name": "Bavarian",
"name": "Boarisch"
},
"bat-smg": {
- "articles": 16858,
+ "articles": 16893,
"english_name": "Samogitian",
"name": "\u017demait\u0117\u0161ka"
},
"bcl": {
- "articles": 9363,
+ "articles": 10546,
"english_name": "Central Bicolano",
"name": "Bikol"
},
"be": {
- "articles": 185723,
+ "articles": 195563,
"english_name": "Belarusian",
"name": "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f"
},
"be-tarask": {
- "articles": 69136,
+ "articles": 71188,
"english_name": "Belarusian (Tara\u0161kievica)",
"name": "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430)"
},
"bg": {
- "articles": 260031,
+ "articles": 265773,
"english_name": "Bulgarian",
"name": "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"
},
"bh": {
- "articles": 7033,
+ "articles": 7342,
"english_name": "Bhojpuri",
"name": "\u092d\u094b\u091c\u092a\u0941\u0930\u0940"
},
"bi": {
- "articles": 1219,
+ "articles": 1230,
"english_name": "Bislama",
"name": "Bislama"
},
"bjn": {
- "articles": 2973,
+ "articles": 3256,
"english_name": "Banjar",
"name": "Bahasa Banjar"
},
"bm": {
- "articles": 664,
+ "articles": 668,
"english_name": "Bambara",
"name": "Bamanankan"
},
"bn": {
- "articles": 82134,
+ "articles": 94608,
"english_name": "Bengali",
"name": "\u09ac\u09be\u0982\u09b2\u09be"
},
"bo": {
- "articles": 5887,
+ "articles": 5920,
"english_name": "Tibetan",
"name": "\u0f56\u0f7c\u0f51\u0f0b\u0f66\u0f90\u0f51"
},
@@ -25441,147 +26500,147 @@
"name": "\u0987\u09ae\u09be\u09b0 \u09a0\u09be\u09b0/\u09ac\u09bf\u09b7\u09cd\u09a3\u09c1\u09aa\u09cd\u09b0\u09bf\u09af\u09bc\u09be \u09ae\u09a3\u09bf\u09aa\u09c1\u09b0\u09c0"
},
"br": {
- "articles": 67768,
+ "articles": 68639,
"english_name": "Breton",
"name": "Brezhoneg"
},
"bs": {
- "articles": 82109,
+ "articles": 84169,
"english_name": "Bosnian",
"name": "Bosanski"
},
"bug": {
- "articles": 14127,
+ "articles": 14139,
"english_name": "Buginese",
"name": "Basa Ugi"
},
"bxr": {
- "articles": 2163,
+ "articles": 2172,
"english_name": "Buryat",
"name": "\u0411\u0443\u0440\u044f\u0430\u0434"
},
"ca": {
- "articles": 638512,
+ "articles": 657438,
"english_name": "Catalan",
"name": "Catal\u00e0"
},
"cbk-zam": {
- "articles": 3023,
+ "articles": 3092,
"english_name": "Zamboanga Chavacano",
"name": "Chavacano de Zamboanga"
},
"cdo": {
- "articles": 15445,
+ "articles": 15490,
"english_name": "Min Dong",
"name": "M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304"
},
"ce": {
- "articles": 254141,
+ "articles": 288306,
"english_name": "Chechen",
"name": "\u041d\u043e\u0445\u0447\u0438\u0439\u043d"
},
"ceb": {
- "articles": 5378538,
+ "articles": 5337062,
"english_name": "Cebuano",
"name": "Sinugboanong Binisaya"
},
"ch": {
- "articles": 513,
+ "articles": 512,
"english_name": "Chamorro",
"name": "Chamoru"
},
"chr": {
- "articles": 834,
+ "articles": 917,
"english_name": "Cherokee",
"name": "\u13e3\u13b3\u13a9"
},
"chy": {
- "articles": 618,
+ "articles": 619,
"english_name": "Cheyenne",
"name": "Tsets\u00eahest\u00e2hese"
},
"ckb": {
- "articles": 25522,
+ "articles": 26975,
"english_name": "Sorani",
"name": "Soran\u00ee / \u06a9\u0648\u0631\u062f\u06cc"
},
"co": {
- "articles": 5788,
+ "articles": 5824,
"english_name": "Corsican",
"name": "Corsu"
},
"cr": {
- "articles": 104,
+ "articles": 117,
"english_name": "Cree",
"name": "Nehiyaw"
},
"crh": {
- "articles": 7046,
+ "articles": 7713,
"english_name": "Crimean Tatar",
"name": "Q\u0131r\u0131mtatarca"
},
"cs": {
- "articles": 447036,
+ "articles": 462637,
"english_name": "Czech",
"name": "\u010ce\u0161tina"
},
"csb": {
- "articles": 5329,
+ "articles": 5352,
"english_name": "Kashubian",
"name": "Kasz\u00ebbsczi"
},
"cu": {
- "articles": 702,
+ "articles": 743,
"english_name": "Old Church Slavonic",
"name": "\u0421\u043b\u043e\u0432\u0463\u043d\u044c\u0441\u043a\u044a"
},
"cv": {
- "articles": 42681,
+ "articles": 43604,
"english_name": "Chuvash",
"name": "\u0427\u0103\u0432\u0430\u0448"
},
"cy": {
- "articles": 107176,
+ "articles": 131787,
"english_name": "Welsh",
"name": "Cymraeg"
},
"da": {
- "articles": 257264,
+ "articles": 261215,
"english_name": "Danish",
"name": "Dansk"
},
"de": {
- "articles": 2402917,
+ "articles": 2481560,
"english_name": "German",
"name": "Deutsch"
},
"din": {
- "articles": 122,
+ "articles": 116,
"english_name": "Dinka",
"name": "Thu\u0254\u014bj\u00e4\u014b"
},
"diq": {
- "articles": 13329,
+ "articles": 31162,
"english_name": "Zazaki",
"name": "Zazaki"
},
"dsb": {
- "articles": 3251,
+ "articles": 3279,
"english_name": "Lower Sorbian",
"name": "Dolnoserbski"
},
"dty": {
- "articles": 3242,
+ "articles": 3287,
"english_name": "Doteli",
"name": "\u0921\u094b\u091f\u0947\u0932\u0940"
},
"dv": {
- "articles": 3002,
+ "articles": 2958,
"english_name": "Divehi",
"name": "\u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0"
},
"dz": {
- "articles": 222,
+ "articles": 219,
"english_name": "Dzongkha",
"name": "\u0f47\u0f7c\u0f44\u0f0b\u0f41"
},
@@ -25591,102 +26650,102 @@
"name": "E\u028begbe"
},
"el": {
- "articles": 174096,
+ "articles": 181844,
"english_name": "Greek",
"name": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"
},
"eml": {
- "articles": 12321,
+ "articles": 12521,
"english_name": "Emilian-Romagnol",
"name": "Emili\u00e0n e rumagn\u00f2l"
},
"en": {
- "articles": 6023622,
+ "articles": 6161541,
"english_name": "English",
"name": "English"
},
"eo": {
- "articles": 275593,
+ "articles": 285283,
"english_name": "Esperanto",
"name": "Esperanto"
},
"es": {
- "articles": 1579723,
+ "articles": 1627598,
"english_name": "Spanish",
"name": "Espa\u00f1ol"
},
"et": {
- "articles": 206326,
+ "articles": 212088,
"english_name": "Estonian",
"name": "Eesti"
},
"eu": {
- "articles": 351277,
+ "articles": 364789,
"english_name": "Basque",
"name": "Euskara"
},
"ext": {
- "articles": 3180,
+ "articles": 3222,
"english_name": "Extremaduran",
"name": "Estreme\u00f1u"
},
"fa": {
- "articles": 712079,
+ "articles": 746175,
"english_name": "Persian",
"name": "\u0641\u0627\u0631\u0633\u06cc"
},
"ff": {
- "articles": 238,
+ "articles": 272,
"english_name": "Fula",
"name": "Fulfulde"
},
"fi": {
- "articles": 479513,
+ "articles": 493428,
"english_name": "Finnish",
"name": "Suomi"
},
"fiu-vro": {
- "articles": 5533,
+ "articles": 5589,
"english_name": "V\u00f5ro",
"name": "V\u00f5ro"
},
"fj": {
- "articles": 780,
+ "articles": 1070,
"english_name": "Fijian",
"name": "Na Vosa Vakaviti"
},
"fo": {
- "articles": 13305,
+ "articles": 13378,
"english_name": "Faroese",
"name": "F\u00f8royskt"
},
"fr": {
- "articles": 2184872,
+ "articles": 2250872,
"english_name": "French",
"name": "Fran\u00e7ais"
},
"frp": {
- "articles": 3529,
+ "articles": 4080,
"english_name": "Franco-Proven\u00e7al",
"name": "Arpitan"
},
"frr": {
- "articles": 10273,
+ "articles": 11200,
"english_name": "North Frisian",
"name": "Nordfriisk"
},
"fur": {
- "articles": 3338,
+ "articles": 3419,
"english_name": "Friulian",
"name": "Furlan"
},
"fy": {
- "articles": 43513,
+ "articles": 44493,
"english_name": "West Frisian",
"name": "Frysk"
},
"ga": {
- "articles": 52203,
+ "articles": 53585,
"english_name": "Irish",
"name": "Gaeilge"
},
@@ -25696,137 +26755,137 @@
"name": "Gagauz"
},
"gan": {
- "articles": 6430,
+ "articles": 6435,
"english_name": "Gan",
"name": "\u8d1b\u8a9e"
},
"gcr": {
- "articles": 1001,
+ "articles": 1033,
"english_name": "Guianan Creole",
"name": "Kriy\u00f2l Gwiyannen"
},
"gd": {
- "articles": 15062,
+ "articles": 15136,
"english_name": "Scottish Gaelic",
"name": "G\u00e0idhlig"
},
"gl": {
- "articles": 161843,
+ "articles": 166957,
"english_name": "Galician",
"name": "Galego"
},
"glk": {
- "articles": 5931,
+ "articles": 6029,
"english_name": "Gilaki",
"name": "\u06af\u06cc\u0644\u06a9\u06cc"
},
"gn": {
- "articles": 3763,
+ "articles": 3863,
"english_name": "Guarani",
"name": "Ava\u00f1e'\u1ebd"
},
"gom": {
- "articles": 3723,
+ "articles": 3719,
"english_name": "Goan Konkani",
"name": "\u0917\u094b\u0902\u092f\u091a\u0940 \u0915\u094b\u0902\u0915\u0923\u0940 / G\u00f5ychi Konknni"
},
"gor": {
- "articles": 2542,
+ "articles": 9012,
"english_name": "Gorontalo",
"name": "Hulontalo"
},
"got": {
- "articles": 825,
+ "articles": 829,
"english_name": "Gothic",
"name": "\ud800\udf32\ud800\udf3f\ud800\udf44\ud800\udf39\ud800\udf43\ud800\udf3a"
},
"gu": {
- "articles": 28880,
+ "articles": 29088,
"english_name": "Gujarati",
"name": "\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0"
},
"gv": {
- "articles": 4996,
+ "articles": 5010,
"english_name": "Manx",
"name": "Gaelg"
},
"ha": {
- "articles": 4631,
+ "articles": 5321,
"english_name": "Hausa",
"name": "Hausa / \u0647\u064e\u0648\u064f\u0633\u064e"
},
"hak": {
- "articles": 9277,
+ "articles": 9366,
"english_name": "Hakka",
"name": "Hak-k\u00e2-fa / \u5ba2\u5bb6\u8a71"
},
"haw": {
- "articles": 3862,
+ "articles": 2265,
"english_name": "Hawaiian",
"name": "Hawai`i"
},
"he": {
- "articles": 259224,
+ "articles": 275788,
"english_name": "Hebrew",
"name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
},
"hi": {
- "articles": 136564,
+ "articles": 141828,
"english_name": "Hindi",
"name": "\u0939\u093f\u0928\u094d\u0926\u0940"
},
"hif": {
- "articles": 9774,
+ "articles": 9797,
"english_name": "Fiji Hindi",
"name": "Fiji Hindi"
},
"hr": {
- "articles": 215457,
+ "articles": 222117,
"english_name": "Croatian",
"name": "Hrvatski"
},
"hsb": {
- "articles": 13546,
+ "articles": 13598,
"english_name": "Upper Sorbian",
"name": "Hornjoserbsce"
},
"ht": {
- "articles": 57995,
+ "articles": 59897,
"english_name": "Haitian",
"name": "Kr\u00e8yol ayisyen"
},
"hu": {
- "articles": 465402,
+ "articles": 475889,
"english_name": "Hungarian",
"name": "Magyar"
},
"hy": {
- "articles": 264666,
+ "articles": 275577,
"english_name": "Armenian",
"name": "\u0540\u0561\u0575\u0565\u0580\u0565\u0576"
},
"hyw": {
- "articles": 7830,
+ "articles": 8406,
"english_name": "Western Armenian",
"name": "\u0531\u0580\u0565\u0582\u0574\u057f\u0561\u0570\u0561\u0575\u0565\u0580\u0567\u0576"
},
"ia": {
- "articles": 22095,
+ "articles": 22551,
"english_name": "Interlingua",
"name": "Interlingua"
},
"id": {
- "articles": 521569,
+ "articles": 545873,
"english_name": "Indonesian",
"name": "Bahasa Indonesia"
},
"ie": {
- "articles": 4723,
+ "articles": 5079,
"english_name": "Interlingue",
"name": "Interlingue"
},
"ig": {
- "articles": 1448,
+ "articles": 1533,
"english_name": "Igbo",
"name": "Igbo"
},
@@ -25836,112 +26895,112 @@
"name": "I\u00f1upiak"
},
"ilo": {
- "articles": 14221,
+ "articles": 15249,
"english_name": "Ilokano",
"name": "Ilokano"
},
"inh": {
- "articles": 1225,
+ "articles": 1478,
"english_name": "Ingush",
"name": "\u0413\u04cf\u0430\u043b\u0433\u04cf\u0430\u0439"
},
"io": {
- "articles": 29252,
+ "articles": 29785,
"english_name": "Ido",
"name": "Ido"
},
"is": {
- "articles": 49128,
+ "articles": 50516,
"english_name": "Icelandic",
"name": "\u00cdslenska"
},
"it": {
- "articles": 1585945,
+ "articles": 1636112,
"english_name": "Italian",
"name": "Italiano"
},
"iu": {
- "articles": 403,
+ "articles": 472,
"english_name": "Inuktitut",
"name": "\u1403\u14c4\u1483\u144e\u1450\u1466"
},
"ja": {
- "articles": 1192319,
+ "articles": 1228979,
"english_name": "Japanese",
"name": "\u65e5\u672c\u8a9e"
},
"jam": {
- "articles": 1648,
+ "articles": 1660,
"english_name": "Jamaican Patois",
"name": "Jumiekan Kryuol"
},
"jbo": {
- "articles": 1251,
+ "articles": 1256,
"english_name": "Lojban",
"name": "Lojban"
},
"jv": {
- "articles": 57275,
+ "articles": 58065,
"english_name": "Javanese",
"name": "Basa Jawa"
},
"ka": {
- "articles": 135269,
+ "articles": 140486,
"english_name": "Georgian",
"name": "\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8"
},
"kaa": {
- "articles": 1883,
+ "articles": 1865,
"english_name": "Karakalpak",
"name": "Qaraqalpaqsha"
},
"kab": {
- "articles": 4508,
+ "articles": 4832,
"english_name": "Kabyle",
"name": "Taqbaylit"
},
"kbd": {
- "articles": 1584,
+ "articles": 1586,
"english_name": "Kabardian Circassian",
"name": "\u0410\u0434\u044b\u0433\u044d\u0431\u0437\u044d (Adighabze)"
},
"kbp": {
- "articles": 1603,
+ "articles": 1612,
"english_name": "Kabiye",
"name": "Kab\u0269y\u025b"
},
"kg": {
- "articles": 1197,
+ "articles": 1212,
"english_name": "Kongo",
"name": "KiKongo"
},
"ki": {
- "articles": 1368,
+ "articles": 1366,
"english_name": "Kikuyu",
"name": "G\u0129k\u0169y\u0169"
},
"kk": {
- "articles": 226239,
+ "articles": 227051,
"english_name": "Kazakh",
"name": "\u049a\u0430\u0437\u0430\u049b\u0448\u0430"
},
"kl": {
- "articles": 1669,
+ "articles": 833,
"english_name": "Greenlandic",
"name": "Kalaallisut"
},
"km": {
- "articles": 7994,
+ "articles": 8292,
"english_name": "Khmer",
"name": "\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a"
},
"kn": {
- "articles": 25796,
+ "articles": 26549,
"english_name": "Kannada",
"name": "\u0c95\u0ca8\u0ccd\u0ca8\u0ca1"
},
"ko": {
- "articles": 485688,
+ "articles": 520943,
"english_name": "Korean",
"name": "\ud55c\uad6d\uc5b4"
},
@@ -25951,482 +27010,487 @@
"name": "\u041f\u0435\u0440\u0435\u043c \u041a\u043e\u043c\u0438 (Perem Komi)"
},
"krc": {
- "articles": 2037,
+ "articles": 2048,
"english_name": "Karachay-Balkar",
"name": "\u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440 (Qarachay-Malqar)"
},
"ks": {
- "articles": 368,
+ "articles": 422,
"english_name": "Kashmiri",
"name": "\u0915\u0936\u094d\u092e\u0940\u0930\u0940 / \u0643\u0634\u0645\u064a\u0631\u064a"
},
"ksh": {
- "articles": 2861,
+ "articles": 2878,
"english_name": "Ripuarian",
"name": "Ripoarisch"
},
"ku": {
- "articles": 26872,
+ "articles": 31973,
"english_name": "Kurdish",
"name": "Kurd\u00ee / \u0643\u0648\u0631\u062f\u06cc"
},
"kv": {
- "articles": 5333,
+ "articles": 5347,
"english_name": "Komi",
"name": "\u041a\u043e\u043c\u0438"
},
"kw": {
- "articles": 3939,
+ "articles": 4196,
"english_name": "Cornish",
"name": "Kernewek/Karnuack"
},
"ky": {
- "articles": 79759,
+ "articles": 80385,
"english_name": "Kirghiz",
"name": "\u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430"
},
"la": {
- "articles": 132249,
+ "articles": 133666,
"english_name": "Latin",
"name": "Latina"
},
"lad": {
- "articles": 3545,
+ "articles": 3554,
"english_name": "Ladino",
"name": "Dzhudezmo"
},
"lb": {
- "articles": 57817,
+ "articles": 58769,
"english_name": "Luxembourgish",
"name": "L\u00ebtzebuergesch"
},
"lbe": {
- "articles": 1220,
+ "articles": 1223,
"english_name": "Lak",
"name": "\u041b\u0430\u043a\u043a\u0443"
},
"lez": {
- "articles": 4057,
+ "articles": 4109,
"english_name": "Lezgian",
"name": "\u041b\u0435\u0437\u0433\u0438 \u0447\u0406\u0430\u043b (Lezgi \u010d\u2019al)"
},
"lfn": {
- "articles": 3723,
+ "articles": 3983,
"english_name": "Lingua Franca Nova",
"name": "Lingua franca nova"
},
"lg": {
- "articles": 1178,
+ "articles": 1197,
"english_name": "Luganda",
"name": "Luganda"
},
"li": {
- "articles": 12737,
+ "articles": 13098,
"english_name": "Limburgish",
"name": "Limburgs"
},
"lij": {
- "articles": 3682,
+ "articles": 4277,
"english_name": "Ligurian",
"name": "L\u00edguru"
},
+ "lld": {
+ "articles": 916,
+ "english_name": "Ladin",
+ "name": "Ladin"
+ },
"lmo": {
- "articles": 39359,
+ "articles": 42884,
"english_name": "Lombard",
"name": "Lumbaart"
},
"ln": {
- "articles": 3136,
+ "articles": 3175,
"english_name": "Lingala",
"name": "Lingala"
},
"lo": {
- "articles": 3545,
+ "articles": 3566,
"english_name": "Lao",
"name": "\u0ea5\u0eb2\u0ea7"
},
"lrc": {
- "articles": 5360,
+ "articles": 5714,
"english_name": "Northern Luri",
"name": "\u0644\u06ca\u0631\u06cc \u0634\u0648\u0645\u0627\u0644\u06cc"
},
"lt": {
- "articles": 198453,
+ "articles": 200796,
"english_name": "Lithuanian",
"name": "Lietuvi\u0173"
},
"ltg": {
- "articles": 932,
+ "articles": 1001,
"english_name": "Latgalian",
"name": "Latga\u013cu"
},
"lv": {
- "articles": 100417,
+ "articles": 103239,
"english_name": "Latvian",
"name": "Latvie\u0161u"
},
"mai": {
- "articles": 13512,
+ "articles": 13600,
"english_name": "Maithili",
"name": "\u092e\u0948\u0925\u093f\u0932\u0940"
},
"map-bms": {
- "articles": 13344,
+ "articles": 13382,
"english_name": "Banyumasan",
"name": "Basa Banyumasan"
},
"mdf": {
- "articles": 1192,
+ "articles": 1196,
"english_name": "Moksha",
"name": "\u041c\u043e\u043a\u0448\u0435\u043d\u044c (Mokshanj K\u00e4lj)"
},
"mg": {
- "articles": 92711,
+ "articles": 93211,
"english_name": "Malagasy",
"name": "Malagasy"
},
"mhr": {
- "articles": 10108,
+ "articles": 10204,
"english_name": "Meadow Mari",
"name": "\u041e\u043b\u044b\u043a \u041c\u0430\u0440\u0438\u0439 (Olyk Marij)"
},
"mi": {
- "articles": 7159,
+ "articles": 7166,
"english_name": "Maori",
"name": "M\u0101ori"
},
"min": {
- "articles": 223738,
+ "articles": 224076,
"english_name": "Minangkabau",
"name": "Minangkabau"
},
"mk": {
- "articles": 104303,
+ "articles": 108023,
"english_name": "Macedonian",
"name": "\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438"
},
"ml": {
- "articles": 67743,
+ "articles": 70483,
"english_name": "Malayalam",
"name": "\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02"
},
"mn": {
- "articles": 19010,
+ "articles": 19621,
"english_name": "Mongolian",
"name": "\u041c\u043e\u043d\u0433\u043e\u043b"
},
"mnw": {
- "articles": 470,
+ "articles": 642,
"english_name": "Mon",
"name": "\u1019\u1014\u103a"
},
"mr": {
- "articles": 56283,
+ "articles": 61619,
"english_name": "Marathi",
"name": "\u092e\u0930\u093e\u0920\u0940"
},
"mrj": {
- "articles": 10270,
+ "articles": 10275,
"english_name": "Hill Mari",
"name": "\u041a\u044b\u0440\u044b\u043a \u041c\u0430\u0440\u044b (Kyryk Mary)"
},
"ms": {
- "articles": 334930,
+ "articles": 343278,
"english_name": "Malay",
"name": "Bahasa Melayu"
},
"mt": {
- "articles": 3442,
+ "articles": 3614,
"english_name": "Maltese",
"name": "Malti"
},
"mwl": {
- "articles": 3783,
+ "articles": 3828,
"english_name": "Mirandese",
"name": "Mirand\u00e9s"
},
"my": {
- "articles": 45399,
+ "articles": 47334,
"english_name": "Burmese",
"name": "\u1019\u103c\u1014\u103a\u1019\u102c\u1018\u102c\u101e\u102c"
},
"myv": {
- "articles": 5871,
+ "articles": 6196,
"english_name": "Erzya",
"name": "\u042d\u0440\u0437\u044f\u043d\u044c (Erzjanj Kelj)"
},
"mzn": {
- "articles": 13127,
+ "articles": 13157,
"english_name": "Mazandarani",
"name": "\u0645\u064e\u0632\u0650\u0631\u0648\u0646\u064a"
},
"na": {
- "articles": 1309,
+ "articles": 1483,
"english_name": "Nauruan",
"name": "dorerin Naoero"
},
"nah": {
- "articles": 6976,
+ "articles": 7003,
"english_name": "Nahuatl",
"name": "N\u0101huatl"
},
"nap": {
- "articles": 14561,
+ "articles": 14609,
"english_name": "Neapolitan",
"name": "Nnapulitano"
},
"nds": {
- "articles": 61186,
+ "articles": 75834,
"english_name": "Low Saxon",
"name": "Plattd\u00fc\u00fctsch"
},
"nds-nl": {
- "articles": 6935,
+ "articles": 7082,
"english_name": "Dutch Low Saxon",
"name": "Nedersaksisch"
},
"ne": {
- "articles": 34031,
+ "articles": 33466,
"english_name": "Nepali",
"name": "\u0928\u0947\u092a\u093e\u0932\u0940"
},
"new": {
- "articles": 72233,
+ "articles": 72289,
"english_name": "Newar",
"name": "\u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e"
},
"nl": {
- "articles": 1998535,
+ "articles": 2032886,
"english_name": "Dutch",
"name": "Nederlands"
},
"nn": {
- "articles": 151775,
+ "articles": 154370,
"english_name": "Norwegian (Nynorsk)",
"name": "Nynorsk"
},
"no": {
- "articles": 529258,
+ "articles": 546147,
"english_name": "Norwegian (Bokm\u00e5l)",
"name": "Norsk (Bokm\u00e5l)"
},
"nov": {
- "articles": 1671,
+ "articles": 1679,
"english_name": "Novial",
"name": "Novial"
},
"nqo": {
- "articles": 546,
+ "articles": 718,
"english_name": "N\u2019Ko",
"name": "\u07d2\u07de\u07cf"
},
"nrm": {
- "articles": 4332,
+ "articles": 4515,
"english_name": "Norman",
"name": "Nouormand/Normaund"
},
"nso": {
- "articles": 8175,
+ "articles": 8205,
"english_name": "Northern Sotho",
"name": "Sepedi"
},
"nv": {
- "articles": 12264,
+ "articles": 15865,
"english_name": "Navajo",
"name": "Din\u00e9 bizaad"
},
"ny": {
- "articles": 565,
+ "articles": 733,
"english_name": "Chichewa",
"name": "Chichewa"
},
"oc": {
- "articles": 87871,
+ "articles": 86161,
"english_name": "Occitan",
"name": "Occitan"
},
"olo": {
- "articles": 3236,
+ "articles": 3403,
"english_name": "Livvi-Karelian",
"name": "Karjalan"
},
"om": {
- "articles": 786,
+ "articles": 847,
"english_name": "Oromo",
"name": "Oromoo"
},
"or": {
- "articles": 15579,
+ "articles": 15917,
"english_name": "Oriya",
"name": "\u0b13\u0b21\u0b3c\u0b3f\u0b06"
},
"os": {
- "articles": 11884,
+ "articles": 12458,
"english_name": "Ossetian",
"name": "\u0418\u0440\u043e\u043d\u0430\u0443"
},
"pa": {
- "articles": 33858,
+ "articles": 34752,
"english_name": "Punjabi",
"name": "\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40"
},
"pag": {
- "articles": 2531,
+ "articles": 2663,
"english_name": "Pangasinan",
"name": "Pangasinan"
},
"pam": {
- "articles": 8637,
+ "articles": 8695,
"english_name": "Kapampangan",
"name": "Kapampangan"
},
"pap": {
- "articles": 1941,
+ "articles": 2002,
"english_name": "Papiamentu",
"name": "Papiamentu"
},
"pcd": {
- "articles": 4666,
+ "articles": 4865,
"english_name": "Picard",
"name": "Picard"
},
"pdc": {
- "articles": 1877,
+ "articles": 1894,
"english_name": "Pennsylvania German",
"name": "Deitsch"
},
"pfl": {
- "articles": 2647,
+ "articles": 2678,
"english_name": "Palatinate German",
"name": "P\u00e4lzisch"
},
"pi": {
- "articles": 2540,
+ "articles": 2543,
"english_name": "Pali",
"name": "\u092a\u093e\u0934\u093f"
},
"pih": {
- "articles": 796,
+ "articles": 803,
"english_name": "Norfolk",
"name": "Norfuk"
},
"pl": {
- "articles": 1386843,
+ "articles": 1427892,
"english_name": "Polish",
"name": "Polski"
},
"pms": {
- "articles": 64598,
+ "articles": 64812,
"english_name": "Piedmontese",
"name": "Piemont\u00e8is"
},
"pnb": {
- "articles": 52557,
+ "articles": 53656,
"english_name": "Western Punjabi",
"name": "\u0634\u0627\u06c1 \u0645\u06a9\u06be\u06cc \u067e\u0646\u062c\u0627\u0628\u06cc (Sh\u0101hmukh\u012b Pa\u00f1j\u0101b\u012b)"
},
"pnt": {
- "articles": 467,
+ "articles": 469,
"english_name": "Pontic",
"name": "\u03a0\u03bf\u03bd\u03c4\u03b9\u03b1\u03ba\u03ac"
},
"ps": {
- "articles": 10997,
+ "articles": 11544,
"english_name": "Pashto",
"name": "\u067e\u069a\u062a\u0648"
},
"pt": {
- "articles": 1021915,
+ "articles": 1043641,
"english_name": "Portuguese",
"name": "Portugu\u00eas"
},
"qu": {
- "articles": 21801,
+ "articles": 22691,
"english_name": "Quechua",
"name": "Runa Simi"
},
"rm": {
- "articles": 3646,
+ "articles": 3695,
"english_name": "Romansh",
"name": "Rumantsch"
},
"rmy": {
- "articles": 675,
+ "articles": 676,
"english_name": "Romani",
"name": "romani - \u0930\u094b\u092e\u093e\u0928\u0940"
},
"rn": {
- "articles": 615,
+ "articles": 616,
"english_name": "Kirundi",
"name": "Kirundi"
},
"ro": {
- "articles": 404592,
+ "articles": 412071,
"english_name": "Romanian",
"name": "Rom\u00e2n\u0103"
},
"roa-rup": {
- "articles": 1225,
+ "articles": 1233,
"english_name": "Aromanian",
"name": "Arm\u00e3neashce"
},
"roa-tara": {
- "articles": 9249,
+ "articles": 9302,
"english_name": "Tarantino",
"name": "Tarand\u00edne"
},
"ru": {
- "articles": 1600812,
+ "articles": 1661738,
"english_name": "Russian",
"name": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"
},
"rue": {
- "articles": 7447,
+ "articles": 7813,
"english_name": "Rusyn",
"name": "\u0420\u0443\u0441\u0438\u043d\u044c\u0441\u043a\u044b\u0439"
},
"rw": {
- "articles": 1820,
+ "articles": 1852,
"english_name": "Kinyarwanda",
"name": "Ikinyarwanda"
},
"sa": {
- "articles": 11456,
+ "articles": 11462,
"english_name": "Sanskrit",
"name": "\u0938\u0902\u0938\u094d\u0915\u0943\u0924\u092e\u094d"
},
"sah": {
- "articles": 12199,
+ "articles": 12759,
"english_name": "Sakha",
"name": "\u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430 (Saxa Tyla)"
},
"sat": {
- "articles": 2470,
+ "articles": 4535,
"english_name": "Santali",
"name": "\u1c65\u1c5f\u1c71\u1c5b\u1c5f\u1c72\u1c64"
},
"sc": {
- "articles": 6371,
+ "articles": 6686,
"english_name": "Sardinian",
"name": "Sardu"
},
"scn": {
- "articles": 26074,
+ "articles": 26058,
"english_name": "Sicilian",
"name": "Sicilianu"
},
"sco": {
- "articles": 56578,
+ "articles": 56568,
"english_name": "Scots",
"name": "Scots"
},
"sd": {
- "articles": 12956,
+ "articles": 13687,
"english_name": "Sindhi",
"name": "\u0633\u0646\u068c\u064a\u060c \u0633\u0646\u062f\u06be\u06cc \u060c \u0938\u093f\u0928\u094d\u0927"
},
"se": {
- "articles": 7612,
+ "articles": 7695,
"english_name": "Northern Sami",
"name": "S\u00e1megiella"
},
@@ -26436,297 +27500,297 @@
"name": "S\u00e4ng\u00f6"
},
"sh": {
- "articles": 451031,
+ "articles": 452863,
"english_name": "Serbo-Croatian",
"name": "Srpskohrvatski / \u0421\u0440\u043f\u0441\u043a\u043e\u0445\u0440\u0432\u0430\u0442\u0441\u043a\u0438"
},
"shn": {
- "articles": 6745,
+ "articles": 7293,
"english_name": "Shan",
"name": "\u101c\u102d\u1075\u103a\u1088\u1010\u1086\u1038"
},
"si": {
- "articles": 15543,
+ "articles": 15855,
"english_name": "Sinhalese",
"name": "\u0dc3\u0dd2\u0d82\u0dc4\u0dbd"
},
"simple": {
- "articles": 155539,
+ "articles": 172453,
"english_name": "Simple English",
"name": "Simple English"
},
"sk": {
- "articles": 232538,
+ "articles": 234573,
"english_name": "Slovak",
"name": "Sloven\u010dina"
},
"sl": {
- "articles": 167090,
+ "articles": 169414,
"english_name": "Slovenian",
"name": "Sloven\u0161\u010dina"
},
"sm": {
- "articles": 823,
+ "articles": 855,
"english_name": "Samoan",
"name": "Gagana Samoa"
},
"sn": {
- "articles": 4996,
+ "articles": 5991,
"english_name": "Shona",
"name": "chiShona"
},
"so": {
- "articles": 5752,
+ "articles": 5805,
"english_name": "Somali",
"name": "Soomaali"
},
"sq": {
- "articles": 78674,
+ "articles": 81381,
"english_name": "Albanian",
"name": "Shqip"
},
"sr": {
- "articles": 630590,
+ "articles": 638284,
"english_name": "Serbian",
"name": "\u0421\u0440\u043f\u0441\u043a\u0438 / Srpski"
},
"srn": {
- "articles": 1076,
+ "articles": 1070,
"english_name": "Sranan",
"name": "Sranantongo"
},
"ss": {
- "articles": 504,
+ "articles": 518,
"english_name": "Swati",
"name": "SiSwati"
},
"st": {
- "articles": 656,
+ "articles": 768,
"english_name": "Sesotho",
"name": "Sesotho"
},
"stq": {
- "articles": 4017,
+ "articles": 4025,
"english_name": "Saterland Frisian",
"name": "Seeltersk"
},
"su": {
- "articles": 59863,
+ "articles": 60527,
"english_name": "Sundanese",
"name": "Basa Sunda"
},
"sv": {
- "articles": 3738262,
+ "articles": 3675733,
"english_name": "Swedish",
"name": "Svenska"
},
"sw": {
- "articles": 55888,
+ "articles": 60346,
"english_name": "Swahili",
"name": "Kiswahili"
},
"szl": {
- "articles": 51946,
+ "articles": 52432,
"english_name": "Silesian",
"name": "\u015al\u016fnski"
},
"szy": {
- "articles": 1748,
+ "articles": 1812,
"english_name": "Sakizaya",
"name": "Sakizaya"
},
"ta": {
- "articles": 127284,
+ "articles": 131072,
"english_name": "Tamil",
"name": "\u0ba4\u0bae\u0bbf\u0bb4\u0bcd"
},
"tcy": {
- "articles": 1271,
+ "articles": 1397,
"english_name": "Tulu",
"name": "\u0ca4\u0cc1\u0cb3\u0cc1"
},
"te": {
- "articles": 70262,
+ "articles": 69631,
"english_name": "Telugu",
"name": "\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41"
},
"tet": {
- "articles": 1475,
+ "articles": 1479,
"english_name": "Tetum",
"name": "Tetun"
},
"tg": {
- "articles": 100460,
+ "articles": 101656,
"english_name": "Tajik",
"name": "\u0422\u043e\u04b7\u0438\u043a\u04e3"
},
"th": {
- "articles": 135321,
+ "articles": 139928,
"english_name": "Thai",
"name": "\u0e44\u0e17\u0e22"
},
"ti": {
- "articles": 202,
+ "articles": 195,
"english_name": "Tigrinya",
"name": "\u1275\u130d\u122d\u129b"
},
"tk": {
- "articles": 5656,
+ "articles": 5821,
"english_name": "Turkmen",
"name": "T\u00fcrkmen"
},
"tl": {
- "articles": 75674,
+ "articles": 68568,
"english_name": "Tagalog",
"name": "Tagalog"
},
"tn": {
- "articles": 710,
+ "articles": 712,
"english_name": "Tswana",
"name": "Setswana"
},
"to": {
- "articles": 1738,
+ "articles": 1740,
"english_name": "Tongan",
"name": "faka Tonga"
},
"tpi": {
- "articles": 1608,
+ "articles": 1625,
"english_name": "Tok Pisin",
"name": "Tok Pisin"
},
"tr": {
- "articles": 343072,
+ "articles": 365938,
"english_name": "Turkish",
"name": "T\u00fcrk\u00e7e"
},
"ts": {
- "articles": 683,
+ "articles": 700,
"english_name": "Tsonga",
"name": "Xitsonga"
},
"tt": {
- "articles": 89002,
+ "articles": 136712,
"english_name": "Tatar",
"name": "Tatar\u00e7a / \u0422\u0430\u0442\u0430\u0440\u0447\u0430"
},
"tum": {
- "articles": 586,
+ "articles": 588,
"english_name": "Tumbuka",
"name": "chiTumbuka"
},
"tw": {
- "articles": 705,
+ "articles": 708,
"english_name": "Twi",
"name": "Twi"
},
"ty": {
- "articles": 1204,
+ "articles": 1208,
"english_name": "Tahitian",
"name": "Reo M\u0101`ohi"
},
"tyv": {
- "articles": 1987,
+ "articles": 2704,
"english_name": "Tuvan",
"name": "\u0422\u044b\u0432\u0430"
},
"udm": {
- "articles": 4848,
+ "articles": 4948,
"english_name": "Udmurt",
"name": "\u0423\u0434\u043c\u0443\u0440\u0442 \u043a\u044b\u043b"
},
"ug": {
- "articles": 4244,
+ "articles": 4364,
"english_name": "Uyghur",
"name": "\u0626\u06c7\u064a\u063a\u06c7\u0631 \u062a\u0649\u0644\u0649"
},
"uk": {
- "articles": 993357,
+ "articles": 1044506,
"english_name": "Ukrainian",
"name": "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"
},
"ur": {
- "articles": 151971,
+ "articles": 157319,
"english_name": "Urdu",
"name": "\u0627\u0631\u062f\u0648"
},
"uz": {
- "articles": 133433,
+ "articles": 136238,
"english_name": "Uzbek",
"name": "O\u2018zbek"
},
"ve": {
- "articles": 368,
+ "articles": 370,
"english_name": "Venda",
"name": "Tshivenda"
},
"vec": {
- "articles": 19577,
+ "articles": 62971,
"english_name": "Venetian",
"name": "V\u00e8neto"
},
"vep": {
- "articles": 6395,
+ "articles": 6601,
"english_name": "Vepsian",
"name": "Veps\u00e4n"
},
"vi": {
- "articles": 1241042,
+ "articles": 1255776,
"english_name": "Vietnamese",
"name": "Ti\u1ebfng Vi\u1ec7t"
},
"vls": {
- "articles": 7079,
+ "articles": 7225,
"english_name": "West Flemish",
"name": "West-Vlams"
},
"vo": {
- "articles": 124156,
+ "articles": 125021,
"english_name": "Volap\u00fck",
"name": "Volap\u00fck"
},
"wa": {
- "articles": 15627,
+ "articles": 14141,
"english_name": "Walloon",
"name": "Walon"
},
"war": {
- "articles": 1263934,
+ "articles": 1264408,
"english_name": "Waray-Waray",
"name": "Winaray"
},
"wo": {
- "articles": 1387,
+ "articles": 1421,
"english_name": "Wolof",
"name": "Wolof"
},
"wuu": {
- "articles": 28716,
+ "articles": 39058,
"english_name": "Wu",
"name": "\u5434\u8bed"
},
"xal": {
- "articles": 2082,
+ "articles": 2085,
"english_name": "Kalmyk",
"name": "\u0425\u0430\u043b\u044c\u043c\u0433"
},
"xh": {
- "articles": 1043,
+ "articles": 1057,
"english_name": "Xhosa",
"name": "isiXhosa"
},
"xmf": {
- "articles": 13670,
+ "articles": 14930,
"english_name": "Mingrelian",
"name": "\u10db\u10d0\u10e0\u10d2\u10d0\u10da\u10e3\u10e0\u10d8 (Margaluri)"
},
"yi": {
- "articles": 14999,
+ "articles": 14885,
"english_name": "Yiddish",
"name": "\u05d9\u05d9\u05b4\u05d3\u05d9\u05e9"
},
"yo": {
- "articles": 32321,
+ "articles": 32714,
"english_name": "Yoruba",
"name": "Yor\u00f9b\u00e1"
},
@@ -26736,199 +27800,214 @@
"name": "Cuengh"
},
"zea": {
- "articles": 4727,
+ "articles": 4741,
"english_name": "Zeelandic",
"name": "Ze\u00eauws"
},
"zh": {
- "articles": 1099382,
+ "articles": 1147282,
"english_name": "Chinese",
"name": "\u4e2d\u6587"
},
"zh-classical": {
- "articles": 10187,
+ "articles": 10450,
"english_name": "Classical Chinese",
"name": "\u53e4\u6587 / \u6587\u8a00\u6587"
},
"zh-min-nan": {
- "articles": 264433,
+ "articles": 405686,
"english_name": "Min Nan",
"name": "B\u00e2n-l\u00e2m-g\u00fa"
},
"zh-yue": {
- "articles": 77673,
+ "articles": 102328,
"english_name": "Cantonese",
"name": "\u7cb5\u8a9e"
},
"zu": {
- "articles": 1382,
+ "articles": 4070,
"english_name": "Zulu",
"name": "isiZulu"
}
},
"wikipedia": {
"ab": {
- "articles": 6031,
+ "articles": 6080,
"english_name": "Abkhazian",
"name": "\u0410\u04a7\u0441\u0443\u0430"
},
"ace": {
- "articles": 10323,
+ "articles": 10353,
"english_name": "Acehnese",
"name": "Bahsa Ac\u00e8h"
},
"ady": {
- "articles": 417,
+ "articles": 421,
"english_name": "Adyghe",
"name": "\u0410\u0434\u044b\u0433\u044d\u0431\u0437\u044d"
},
"af": {
- "articles": 89423,
+ "articles": 93802,
"english_name": "Afrikaans",
"name": "Afrikaans"
},
"ak": {
- "articles": 795,
+ "articles": 879,
"english_name": "Akan",
"name": "Akana"
},
"als": {
- "articles": 27014,
+ "articles": 27329,
"english_name": "Alemannic",
"name": "Alemannisch"
},
"am": {
- "articles": 14831,
+ "articles": 14875,
"english_name": "Amharic",
"name": "\u12a0\u121b\u122d\u129b"
},
"an": {
- "articles": 36741,
+ "articles": 38095,
"english_name": "Aragonese",
"name": "Aragon\u00e9s"
},
"ang": {
- "articles": 3187,
+ "articles": 3294,
"english_name": "Anglo-Saxon",
"name": "Englisc"
},
"ar": {
- "articles": 1031588,
+ "articles": 1065982,
"english_name": "Arabic",
"name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629"
},
"arc": {
- "articles": 1647,
+ "articles": 1764,
"english_name": "Aramaic",
"name": "\u0710\u072a\u0721\u071d\u0710"
},
+ "ary": {
+ "articles": 2554,
+ "english_name": "Moroccan Arabic",
+ "name": "\u0627\u0644\u062f\u0627\u0631\u062c\u0629"
+ },
"arz": {
- "articles": 145685,
+ "articles": 1098659,
"english_name": "Egyptian Arabic",
"name": "\u0645\u0635\u0631\u0649 (Ma\u1e63ri)"
},
"as": {
- "articles": 6381,
+ "articles": 7230,
"english_name": "Assamese",
"name": "\u0985\u09b8\u09ae\u09c0\u09af\u09bc\u09be"
},
"ast": {
- "articles": 100187,
+ "articles": 107460,
"english_name": "Asturian",
"name": "Asturianu"
},
"atj": {
- "articles": 1167,
+ "articles": 1259,
"english_name": "Atikamekw",
"name": "Atikamekw"
},
"av": {
- "articles": 2422,
+ "articles": 2451,
"english_name": "Avar",
"name": "\u0410\u0432\u0430\u0440"
},
+ "avk": {
+ "articles": 9484,
+ "english_name": "Kotava",
+ "name": "Kotava"
+ },
+ "awa": {
+ "articles": 2406,
+ "english_name": "Awadhi",
+ "name": "\u0905\u0935\u0927\u0940"
+ },
"ay": {
- "articles": 4646,
+ "articles": 4852,
"english_name": "Aymara",
"name": "Aymar"
},
"az": {
- "articles": 155848,
+ "articles": 173155,
"english_name": "Azerbaijani",
"name": "Az\u0259rbaycanca"
},
"azb": {
- "articles": 183161,
+ "articles": 239463,
"english_name": "South Azerbaijani",
"name": "\u062a\u06c6\u0631\u06a9\u062c\u0647"
},
"ba": {
- "articles": 51161,
+ "articles": 53560,
"english_name": "Bashkir",
"name": "\u0411\u0430\u0448\u04a1\u043e\u0440\u0442"
},
"ban": {
- "articles": 2258,
+ "articles": 3899,
"english_name": "Balinese",
"name": "Bali"
},
"bar": {
- "articles": 30891,
+ "articles": 31394,
"english_name": "Bavarian",
"name": "Boarisch"
},
"bat-smg": {
- "articles": 16858,
+ "articles": 16893,
"english_name": "Samogitian",
"name": "\u017demait\u0117\u0161ka"
},
"bcl": {
- "articles": 9363,
+ "articles": 10546,
"english_name": "Central Bicolano",
"name": "Bikol"
},
"be": {
- "articles": 185723,
+ "articles": 195563,
"english_name": "Belarusian",
"name": "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f"
},
"be-tarask": {
- "articles": 69136,
+ "articles": 71188,
"english_name": "Belarusian (Tara\u0161kievica)",
"name": "\u0411\u0435\u043b\u0430\u0440\u0443\u0441\u043a\u0430\u044f (\u0442\u0430\u0440\u0430\u0448\u043a\u0435\u0432\u0456\u0446\u0430)"
},
"bg": {
- "articles": 260031,
+ "articles": 265773,
"english_name": "Bulgarian",
"name": "\u0411\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438"
},
"bh": {
- "articles": 7033,
+ "articles": 7342,
"english_name": "Bhojpuri",
"name": "\u092d\u094b\u091c\u092a\u0941\u0930\u0940"
},
"bi": {
- "articles": 1219,
+ "articles": 1230,
"english_name": "Bislama",
"name": "Bislama"
},
"bjn": {
- "articles": 2973,
+ "articles": 3256,
"english_name": "Banjar",
"name": "Bahasa Banjar"
},
"bm": {
- "articles": 664,
+ "articles": 668,
"english_name": "Bambara",
"name": "Bamanankan"
},
"bn": {
- "articles": 82134,
+ "articles": 94608,
"english_name": "Bengali",
"name": "\u09ac\u09be\u0982\u09b2\u09be"
},
"bo": {
- "articles": 5887,
+ "articles": 5920,
"english_name": "Tibetan",
"name": "\u0f56\u0f7c\u0f51\u0f0b\u0f66\u0f90\u0f51"
},
@@ -26938,147 +28017,147 @@
"name": "\u0987\u09ae\u09be\u09b0 \u09a0\u09be\u09b0/\u09ac\u09bf\u09b7\u09cd\u09a3\u09c1\u09aa\u09cd\u09b0\u09bf\u09af\u09bc\u09be \u09ae\u09a3\u09bf\u09aa\u09c1\u09b0\u09c0"
},
"br": {
- "articles": 67768,
+ "articles": 68639,
"english_name": "Breton",
"name": "Brezhoneg"
},
"bs": {
- "articles": 82109,
+ "articles": 84169,
"english_name": "Bosnian",
"name": "Bosanski"
},
"bug": {
- "articles": 14127,
+ "articles": 14139,
"english_name": "Buginese",
"name": "Basa Ugi"
},
"bxr": {
- "articles": 2163,
+ "articles": 2172,
"english_name": "Buryat",
"name": "\u0411\u0443\u0440\u044f\u0430\u0434"
},
"ca": {
- "articles": 638512,
+ "articles": 657438,
"english_name": "Catalan",
"name": "Catal\u00e0"
},
"cbk-zam": {
- "articles": 3023,
+ "articles": 3092,
"english_name": "Zamboanga Chavacano",
"name": "Chavacano de Zamboanga"
},
"cdo": {
- "articles": 15445,
+ "articles": 15490,
"english_name": "Min Dong",
"name": "M\u00ecng-d\u0115\u0324ng-ng\u1e73\u0304"
},
"ce": {
- "articles": 254141,
+ "articles": 288306,
"english_name": "Chechen",
"name": "\u041d\u043e\u0445\u0447\u0438\u0439\u043d"
},
"ceb": {
- "articles": 5378538,
+ "articles": 5337062,
"english_name": "Cebuano",
"name": "Sinugboanong Binisaya"
},
"ch": {
- "articles": 513,
+ "articles": 512,
"english_name": "Chamorro",
"name": "Chamoru"
},
"chr": {
- "articles": 834,
+ "articles": 917,
"english_name": "Cherokee",
"name": "\u13e3\u13b3\u13a9"
},
"chy": {
- "articles": 618,
+ "articles": 619,
"english_name": "Cheyenne",
"name": "Tsets\u00eahest\u00e2hese"
},
"ckb": {
- "articles": 25522,
+ "articles": 26975,
"english_name": "Sorani",
"name": "Soran\u00ee / \u06a9\u0648\u0631\u062f\u06cc"
},
"co": {
- "articles": 5788,
+ "articles": 5824,
"english_name": "Corsican",
"name": "Corsu"
},
"cr": {
- "articles": 104,
+ "articles": 117,
"english_name": "Cree",
"name": "Nehiyaw"
},
"crh": {
- "articles": 7046,
+ "articles": 7713,
"english_name": "Crimean Tatar",
"name": "Q\u0131r\u0131mtatarca"
},
"cs": {
- "articles": 447036,
+ "articles": 462637,
"english_name": "Czech",
"name": "\u010ce\u0161tina"
},
"csb": {
- "articles": 5329,
+ "articles": 5352,
"english_name": "Kashubian",
"name": "Kasz\u00ebbsczi"
},
"cu": {
- "articles": 702,
+ "articles": 743,
"english_name": "Old Church Slavonic",
"name": "\u0421\u043b\u043e\u0432\u0463\u043d\u044c\u0441\u043a\u044a"
},
"cv": {
- "articles": 42681,
+ "articles": 43604,
"english_name": "Chuvash",
"name": "\u0427\u0103\u0432\u0430\u0448"
},
"cy": {
- "articles": 107176,
+ "articles": 131787,
"english_name": "Welsh",
"name": "Cymraeg"
},
"da": {
- "articles": 257264,
+ "articles": 261215,
"english_name": "Danish",
"name": "Dansk"
},
"de": {
- "articles": 2402917,
+ "articles": 2481560,
"english_name": "German",
"name": "Deutsch"
},
"din": {
- "articles": 122,
+ "articles": 116,
"english_name": "Dinka",
"name": "Thu\u0254\u014bj\u00e4\u014b"
},
"diq": {
- "articles": 13329,
+ "articles": 31162,
"english_name": "Zazaki",
"name": "Zazaki"
},
"dsb": {
- "articles": 3251,
+ "articles": 3279,
"english_name": "Lower Sorbian",
"name": "Dolnoserbski"
},
"dty": {
- "articles": 3242,
+ "articles": 3287,
"english_name": "Doteli",
"name": "\u0921\u094b\u091f\u0947\u0932\u0940"
},
"dv": {
- "articles": 3002,
+ "articles": 2958,
"english_name": "Divehi",
"name": "\u078b\u07a8\u0788\u07ac\u0780\u07a8\u0784\u07a6\u0790\u07b0"
},
"dz": {
- "articles": 222,
+ "articles": 219,
"english_name": "Dzongkha",
"name": "\u0f47\u0f7c\u0f44\u0f0b\u0f41"
},
@@ -27088,102 +28167,102 @@
"name": "E\u028begbe"
},
"el": {
- "articles": 174096,
+ "articles": 181844,
"english_name": "Greek",
"name": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"
},
"eml": {
- "articles": 12321,
+ "articles": 12521,
"english_name": "Emilian-Romagnol",
"name": "Emili\u00e0n e rumagn\u00f2l"
},
"en": {
- "articles": 6023622,
+ "articles": 6161541,
"english_name": "English",
"name": "English"
},
"eo": {
- "articles": 275593,
+ "articles": 285283,
"english_name": "Esperanto",
"name": "Esperanto"
},
"es": {
- "articles": 1579723,
+ "articles": 1627598,
"english_name": "Spanish",
"name": "Espa\u00f1ol"
},
"et": {
- "articles": 206326,
+ "articles": 212088,
"english_name": "Estonian",
"name": "Eesti"
},
"eu": {
- "articles": 351277,
+ "articles": 364789,
"english_name": "Basque",
"name": "Euskara"
},
"ext": {
- "articles": 3180,
+ "articles": 3222,
"english_name": "Extremaduran",
"name": "Estreme\u00f1u"
},
"fa": {
- "articles": 712079,
+ "articles": 746175,
"english_name": "Persian",
"name": "\u0641\u0627\u0631\u0633\u06cc"
},
"ff": {
- "articles": 238,
+ "articles": 272,
"english_name": "Fula",
"name": "Fulfulde"
},
"fi": {
- "articles": 479513,
+ "articles": 493428,
"english_name": "Finnish",
"name": "Suomi"
},
"fiu-vro": {
- "articles": 5533,
+ "articles": 5589,
"english_name": "V\u00f5ro",
"name": "V\u00f5ro"
},
"fj": {
- "articles": 780,
+ "articles": 1070,
"english_name": "Fijian",
"name": "Na Vosa Vakaviti"
},
"fo": {
- "articles": 13305,
+ "articles": 13378,
"english_name": "Faroese",
"name": "F\u00f8royskt"
},
"fr": {
- "articles": 2184872,
+ "articles": 2250872,
"english_name": "French",
"name": "Fran\u00e7ais"
},
"frp": {
- "articles": 3529,
+ "articles": 4080,
"english_name": "Franco-Proven\u00e7al",
"name": "Arpitan"
},
"frr": {
- "articles": 10273,
+ "articles": 11200,
"english_name": "North Frisian",
"name": "Nordfriisk"
},
"fur": {
- "articles": 3338,
+ "articles": 3419,
"english_name": "Friulian",
"name": "Furlan"
},
"fy": {
- "articles": 43513,
+ "articles": 44493,
"english_name": "West Frisian",
"name": "Frysk"
},
"ga": {
- "articles": 52203,
+ "articles": 53585,
"english_name": "Irish",
"name": "Gaeilge"
},
@@ -27193,137 +28272,137 @@
"name": "Gagauz"
},
"gan": {
- "articles": 6430,
+ "articles": 6435,
"english_name": "Gan",
"name": "\u8d1b\u8a9e"
},
"gcr": {
- "articles": 1001,
+ "articles": 1033,
"english_name": "Guianan Creole",
"name": "Kriy\u00f2l Gwiyannen"
},
"gd": {
- "articles": 15062,
+ "articles": 15136,
"english_name": "Scottish Gaelic",
"name": "G\u00e0idhlig"
},
"gl": {
- "articles": 161843,
+ "articles": 166957,
"english_name": "Galician",
"name": "Galego"
},
"glk": {
- "articles": 5931,
+ "articles": 6029,
"english_name": "Gilaki",
"name": "\u06af\u06cc\u0644\u06a9\u06cc"
},
"gn": {
- "articles": 3763,
+ "articles": 3863,
"english_name": "Guarani",
"name": "Ava\u00f1e'\u1ebd"
},
"gom": {
- "articles": 3723,
+ "articles": 3719,
"english_name": "Goan Konkani",
"name": "\u0917\u094b\u0902\u092f\u091a\u0940 \u0915\u094b\u0902\u0915\u0923\u0940 / G\u00f5ychi Konknni"
},
"gor": {
- "articles": 2542,
+ "articles": 9012,
"english_name": "Gorontalo",
"name": "Hulontalo"
},
"got": {
- "articles": 825,
+ "articles": 829,
"english_name": "Gothic",
"name": "\ud800\udf32\ud800\udf3f\ud800\udf44\ud800\udf39\ud800\udf43\ud800\udf3a"
},
"gu": {
- "articles": 28880,
+ "articles": 29088,
"english_name": "Gujarati",
"name": "\u0a97\u0ac1\u0a9c\u0ab0\u0abe\u0aa4\u0ac0"
},
"gv": {
- "articles": 4996,
+ "articles": 5010,
"english_name": "Manx",
"name": "Gaelg"
},
"ha": {
- "articles": 4631,
+ "articles": 5321,
"english_name": "Hausa",
"name": "Hausa / \u0647\u064e\u0648\u064f\u0633\u064e"
},
"hak": {
- "articles": 9277,
+ "articles": 9366,
"english_name": "Hakka",
"name": "Hak-k\u00e2-fa / \u5ba2\u5bb6\u8a71"
},
"haw": {
- "articles": 3862,
+ "articles": 2265,
"english_name": "Hawaiian",
"name": "Hawai`i"
},
"he": {
- "articles": 259224,
+ "articles": 275788,
"english_name": "Hebrew",
"name": "\u05e2\u05d1\u05e8\u05d9\u05ea"
},
"hi": {
- "articles": 136564,
+ "articles": 141828,
"english_name": "Hindi",
"name": "\u0939\u093f\u0928\u094d\u0926\u0940"
},
"hif": {
- "articles": 9774,
+ "articles": 9797,
"english_name": "Fiji Hindi",
"name": "Fiji Hindi"
},
"hr": {
- "articles": 215457,
+ "articles": 222117,
"english_name": "Croatian",
"name": "Hrvatski"
},
"hsb": {
- "articles": 13546,
+ "articles": 13598,
"english_name": "Upper Sorbian",
"name": "Hornjoserbsce"
},
"ht": {
- "articles": 57995,
+ "articles": 59897,
"english_name": "Haitian",
"name": "Kr\u00e8yol ayisyen"
},
"hu": {
- "articles": 465402,
+ "articles": 475889,
"english_name": "Hungarian",
"name": "Magyar"
},
"hy": {
- "articles": 264666,
+ "articles": 275577,
"english_name": "Armenian",
"name": "\u0540\u0561\u0575\u0565\u0580\u0565\u0576"
},
"hyw": {
- "articles": 7830,
+ "articles": 8406,
"english_name": "Western Armenian",
"name": "\u0531\u0580\u0565\u0582\u0574\u057f\u0561\u0570\u0561\u0575\u0565\u0580\u0567\u0576"
},
"ia": {
- "articles": 22095,
+ "articles": 22551,
"english_name": "Interlingua",
"name": "Interlingua"
},
"id": {
- "articles": 521569,
+ "articles": 545873,
"english_name": "Indonesian",
"name": "Bahasa Indonesia"
},
"ie": {
- "articles": 4723,
+ "articles": 5079,
"english_name": "Interlingue",
"name": "Interlingue"
},
"ig": {
- "articles": 1448,
+ "articles": 1533,
"english_name": "Igbo",
"name": "Igbo"
},
@@ -27333,112 +28412,112 @@
"name": "I\u00f1upiak"
},
"ilo": {
- "articles": 14221,
+ "articles": 15249,
"english_name": "Ilokano",
"name": "Ilokano"
},
"inh": {
- "articles": 1225,
+ "articles": 1478,
"english_name": "Ingush",
"name": "\u0413\u04cf\u0430\u043b\u0433\u04cf\u0430\u0439"
},
"io": {
- "articles": 29252,
+ "articles": 29785,
"english_name": "Ido",
"name": "Ido"
},
"is": {
- "articles": 49128,
+ "articles": 50516,
"english_name": "Icelandic",
"name": "\u00cdslenska"
},
"it": {
- "articles": 1585945,
+ "articles": 1636112,
"english_name": "Italian",
"name": "Italiano"
},
"iu": {
- "articles": 403,
+ "articles": 472,
"english_name": "Inuktitut",
"name": "\u1403\u14c4\u1483\u144e\u1450\u1466"
},
"ja": {
- "articles": 1192319,
+ "articles": 1228979,
"english_name": "Japanese",
"name": "\u65e5\u672c\u8a9e"
},
"jam": {
- "articles": 1648,
+ "articles": 1660,
"english_name": "Jamaican Patois",
"name": "Jumiekan Kryuol"
},
"jbo": {
- "articles": 1251,
+ "articles": 1256,
"english_name": "Lojban",
"name": "Lojban"
},
"jv": {
- "articles": 57275,
+ "articles": 58065,
"english_name": "Javanese",
"name": "Basa Jawa"
},
"ka": {
- "articles": 135269,
+ "articles": 140486,
"english_name": "Georgian",
"name": "\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8"
},
"kaa": {
- "articles": 1883,
+ "articles": 1865,
"english_name": "Karakalpak",
"name": "Qaraqalpaqsha"
},
"kab": {
- "articles": 4508,
+ "articles": 4832,
"english_name": "Kabyle",
"name": "Taqbaylit"
},
"kbd": {
- "articles": 1584,
+ "articles": 1586,
"english_name": "Kabardian Circassian",
"name": "\u0410\u0434\u044b\u0433\u044d\u0431\u0437\u044d (Adighabze)"
},
"kbp": {
- "articles": 1603,
+ "articles": 1612,
"english_name": "Kabiye",
"name": "Kab\u0269y\u025b"
},
"kg": {
- "articles": 1197,
+ "articles": 1212,
"english_name": "Kongo",
"name": "KiKongo"
},
"ki": {
- "articles": 1368,
+ "articles": 1366,
"english_name": "Kikuyu",
"name": "G\u0129k\u0169y\u0169"
},
"kk": {
- "articles": 226239,
+ "articles": 227051,
"english_name": "Kazakh",
"name": "\u049a\u0430\u0437\u0430\u049b\u0448\u0430"
},
"kl": {
- "articles": 1669,
+ "articles": 833,
"english_name": "Greenlandic",
"name": "Kalaallisut"
},
"km": {
- "articles": 7994,
+ "articles": 8292,
"english_name": "Khmer",
"name": "\u1797\u17b6\u179f\u17b6\u1781\u17d2\u1798\u17c2\u179a"
},
"kn": {
- "articles": 25796,
+ "articles": 26549,
"english_name": "Kannada",
"name": "\u0c95\u0ca8\u0ccd\u0ca8\u0ca1"
},
"ko": {
- "articles": 485688,
+ "articles": 520943,
"english_name": "Korean",
"name": "\ud55c\uad6d\uc5b4"
},
@@ -27448,482 +28527,487 @@
"name": "\u041f\u0435\u0440\u0435\u043c \u041a\u043e\u043c\u0438 (Perem Komi)"
},
"krc": {
- "articles": 2037,
+ "articles": 2048,
"english_name": "Karachay-Balkar",
"name": "\u041a\u044a\u0430\u0440\u0430\u0447\u0430\u0439-\u041c\u0430\u043b\u043a\u044a\u0430\u0440 (Qarachay-Malqar)"
},
"ks": {
- "articles": 368,
+ "articles": 422,
"english_name": "Kashmiri",
"name": "\u0915\u0936\u094d\u092e\u0940\u0930\u0940 / \u0643\u0634\u0645\u064a\u0631\u064a"
},
"ksh": {
- "articles": 2861,
+ "articles": 2878,
"english_name": "Ripuarian",
"name": "Ripoarisch"
},
"ku": {
- "articles": 26872,
+ "articles": 31973,
"english_name": "Kurdish",
"name": "Kurd\u00ee / \u0643\u0648\u0631\u062f\u06cc"
},
"kv": {
- "articles": 5333,
+ "articles": 5347,
"english_name": "Komi",
"name": "\u041a\u043e\u043c\u0438"
},
"kw": {
- "articles": 3939,
+ "articles": 4196,
"english_name": "Cornish",
"name": "Kernewek/Karnuack"
},
"ky": {
- "articles": 79759,
+ "articles": 80385,
"english_name": "Kirghiz",
"name": "\u041a\u044b\u0440\u0433\u044b\u0437\u0447\u0430"
},
"la": {
- "articles": 132249,
+ "articles": 133666,
"english_name": "Latin",
"name": "Latina"
},
"lad": {
- "articles": 3545,
+ "articles": 3554,
"english_name": "Ladino",
"name": "Dzhudezmo"
},
"lb": {
- "articles": 57817,
+ "articles": 58769,
"english_name": "Luxembourgish",
"name": "L\u00ebtzebuergesch"
},
"lbe": {
- "articles": 1220,
+ "articles": 1223,
"english_name": "Lak",
"name": "\u041b\u0430\u043a\u043a\u0443"
},
"lez": {
- "articles": 4057,
+ "articles": 4109,
"english_name": "Lezgian",
"name": "\u041b\u0435\u0437\u0433\u0438 \u0447\u0406\u0430\u043b (Lezgi \u010d\u2019al)"
},
"lfn": {
- "articles": 3723,
+ "articles": 3983,
"english_name": "Lingua Franca Nova",
"name": "Lingua franca nova"
},
"lg": {
- "articles": 1178,
+ "articles": 1197,
"english_name": "Luganda",
"name": "Luganda"
},
"li": {
- "articles": 12737,
+ "articles": 13098,
"english_name": "Limburgish",
"name": "Limburgs"
},
"lij": {
- "articles": 3682,
+ "articles": 4277,
"english_name": "Ligurian",
"name": "L\u00edguru"
},
+ "lld": {
+ "articles": 916,
+ "english_name": "Ladin",
+ "name": "Ladin"
+ },
"lmo": {
- "articles": 39359,
+ "articles": 42884,
"english_name": "Lombard",
"name": "Lumbaart"
},
"ln": {
- "articles": 3136,
+ "articles": 3175,
"english_name": "Lingala",
"name": "Lingala"
},
"lo": {
- "articles": 3545,
+ "articles": 3566,
"english_name": "Lao",
"name": "\u0ea5\u0eb2\u0ea7"
},
"lrc": {
- "articles": 5360,
+ "articles": 5714,
"english_name": "Northern Luri",
"name": "\u0644\u06ca\u0631\u06cc \u0634\u0648\u0645\u0627\u0644\u06cc"
},
"lt": {
- "articles": 198453,
+ "articles": 200796,
"english_name": "Lithuanian",
"name": "Lietuvi\u0173"
},
"ltg": {
- "articles": 932,
+ "articles": 1001,
"english_name": "Latgalian",
"name": "Latga\u013cu"
},
"lv": {
- "articles": 100417,
+ "articles": 103239,
"english_name": "Latvian",
"name": "Latvie\u0161u"
},
"mai": {
- "articles": 13512,
+ "articles": 13600,
"english_name": "Maithili",
"name": "\u092e\u0948\u0925\u093f\u0932\u0940"
},
"map-bms": {
- "articles": 13344,
+ "articles": 13382,
"english_name": "Banyumasan",
"name": "Basa Banyumasan"
},
"mdf": {
- "articles": 1192,
+ "articles": 1196,
"english_name": "Moksha",
"name": "\u041c\u043e\u043a\u0448\u0435\u043d\u044c (Mokshanj K\u00e4lj)"
},
"mg": {
- "articles": 92711,
+ "articles": 93211,
"english_name": "Malagasy",
"name": "Malagasy"
},
"mhr": {
- "articles": 10108,
+ "articles": 10204,
"english_name": "Meadow Mari",
"name": "\u041e\u043b\u044b\u043a \u041c\u0430\u0440\u0438\u0439 (Olyk Marij)"
},
"mi": {
- "articles": 7159,
+ "articles": 7166,
"english_name": "Maori",
"name": "M\u0101ori"
},
"min": {
- "articles": 223738,
+ "articles": 224076,
"english_name": "Minangkabau",
"name": "Minangkabau"
},
"mk": {
- "articles": 104303,
+ "articles": 108023,
"english_name": "Macedonian",
"name": "\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438"
},
"ml": {
- "articles": 67743,
+ "articles": 70483,
"english_name": "Malayalam",
"name": "\u0d2e\u0d32\u0d2f\u0d3e\u0d33\u0d02"
},
"mn": {
- "articles": 19010,
+ "articles": 19621,
"english_name": "Mongolian",
"name": "\u041c\u043e\u043d\u0433\u043e\u043b"
},
"mnw": {
- "articles": 470,
+ "articles": 642,
"english_name": "Mon",
"name": "\u1019\u1014\u103a"
},
"mr": {
- "articles": 56283,
+ "articles": 61619,
"english_name": "Marathi",
"name": "\u092e\u0930\u093e\u0920\u0940"
},
"mrj": {
- "articles": 10270,
+ "articles": 10275,
"english_name": "Hill Mari",
"name": "\u041a\u044b\u0440\u044b\u043a \u041c\u0430\u0440\u044b (Kyryk Mary)"
},
"ms": {
- "articles": 334930,
+ "articles": 343278,
"english_name": "Malay",
"name": "Bahasa Melayu"
},
"mt": {
- "articles": 3442,
+ "articles": 3614,
"english_name": "Maltese",
"name": "Malti"
},
"mwl": {
- "articles": 3783,
+ "articles": 3828,
"english_name": "Mirandese",
"name": "Mirand\u00e9s"
},
"my": {
- "articles": 45399,
+ "articles": 47334,
"english_name": "Burmese",
"name": "\u1019\u103c\u1014\u103a\u1019\u102c\u1018\u102c\u101e\u102c"
},
"myv": {
- "articles": 5871,
+ "articles": 6196,
"english_name": "Erzya",
"name": "\u042d\u0440\u0437\u044f\u043d\u044c (Erzjanj Kelj)"
},
"mzn": {
- "articles": 13127,
+ "articles": 13157,
"english_name": "Mazandarani",
"name": "\u0645\u064e\u0632\u0650\u0631\u0648\u0646\u064a"
},
"na": {
- "articles": 1309,
+ "articles": 1483,
"english_name": "Nauruan",
"name": "dorerin Naoero"
},
"nah": {
- "articles": 6976,
+ "articles": 7003,
"english_name": "Nahuatl",
"name": "N\u0101huatl"
},
"nap": {
- "articles": 14561,
+ "articles": 14609,
"english_name": "Neapolitan",
"name": "Nnapulitano"
},
"nds": {
- "articles": 61186,
+ "articles": 75834,
"english_name": "Low Saxon",
"name": "Plattd\u00fc\u00fctsch"
},
"nds-nl": {
- "articles": 6935,
+ "articles": 7082,
"english_name": "Dutch Low Saxon",
"name": "Nedersaksisch"
},
"ne": {
- "articles": 34031,
+ "articles": 33466,
"english_name": "Nepali",
"name": "\u0928\u0947\u092a\u093e\u0932\u0940"
},
"new": {
- "articles": 72233,
+ "articles": 72289,
"english_name": "Newar",
"name": "\u0928\u0947\u092a\u093e\u0932 \u092d\u093e\u0937\u093e"
},
"nl": {
- "articles": 1998535,
+ "articles": 2032886,
"english_name": "Dutch",
"name": "Nederlands"
},
"nn": {
- "articles": 151775,
+ "articles": 154370,
"english_name": "Norwegian (Nynorsk)",
"name": "Nynorsk"
},
"no": {
- "articles": 529258,
+ "articles": 546147,
"english_name": "Norwegian (Bokm\u00e5l)",
"name": "Norsk (Bokm\u00e5l)"
},
"nov": {
- "articles": 1671,
+ "articles": 1679,
"english_name": "Novial",
"name": "Novial"
},
"nqo": {
- "articles": 546,
+ "articles": 718,
"english_name": "N\u2019Ko",
"name": "\u07d2\u07de\u07cf"
},
"nrm": {
- "articles": 4332,
+ "articles": 4515,
"english_name": "Norman",
"name": "Nouormand/Normaund"
},
"nso": {
- "articles": 8175,
+ "articles": 8205,
"english_name": "Northern Sotho",
"name": "Sepedi"
},
"nv": {
- "articles": 12264,
+ "articles": 15865,
"english_name": "Navajo",
"name": "Din\u00e9 bizaad"
},
"ny": {
- "articles": 565,
+ "articles": 733,
"english_name": "Chichewa",
"name": "Chichewa"
},
"oc": {
- "articles": 87871,
+ "articles": 86161,
"english_name": "Occitan",
"name": "Occitan"
},
"olo": {
- "articles": 3236,
+ "articles": 3403,
"english_name": "Livvi-Karelian",
"name": "Karjalan"
},
"om": {
- "articles": 786,
+ "articles": 847,
"english_name": "Oromo",
"name": "Oromoo"
},
"or": {
- "articles": 15579,
+ "articles": 15917,
"english_name": "Oriya",
"name": "\u0b13\u0b21\u0b3c\u0b3f\u0b06"
},
"os": {
- "articles": 11884,
+ "articles": 12458,
"english_name": "Ossetian",
"name": "\u0418\u0440\u043e\u043d\u0430\u0443"
},
"pa": {
- "articles": 33858,
+ "articles": 34752,
"english_name": "Punjabi",
"name": "\u0a2a\u0a70\u0a1c\u0a3e\u0a2c\u0a40"
},
"pag": {
- "articles": 2531,
+ "articles": 2663,
"english_name": "Pangasinan",
"name": "Pangasinan"
},
"pam": {
- "articles": 8637,
+ "articles": 8695,
"english_name": "Kapampangan",
"name": "Kapampangan"
},
"pap": {
- "articles": 1941,
+ "articles": 2002,
"english_name": "Papiamentu",
"name": "Papiamentu"
},
"pcd": {
- "articles": 4666,
+ "articles": 4865,
"english_name": "Picard",
"name": "Picard"
},
"pdc": {
- "articles": 1877,
+ "articles": 1894,
"english_name": "Pennsylvania German",
"name": "Deitsch"
},
"pfl": {
- "articles": 2647,
+ "articles": 2678,
"english_name": "Palatinate German",
"name": "P\u00e4lzisch"
},
"pi": {
- "articles": 2540,
+ "articles": 2543,
"english_name": "Pali",
"name": "\u092a\u093e\u0934\u093f"
},
"pih": {
- "articles": 796,
+ "articles": 803,
"english_name": "Norfolk",
"name": "Norfuk"
},
"pl": {
- "articles": 1386843,
+ "articles": 1427892,
"english_name": "Polish",
"name": "Polski"
},
"pms": {
- "articles": 64598,
+ "articles": 64812,
"english_name": "Piedmontese",
"name": "Piemont\u00e8is"
},
"pnb": {
- "articles": 52557,
+ "articles": 53656,
"english_name": "Western Punjabi",
"name": "\u0634\u0627\u06c1 \u0645\u06a9\u06be\u06cc \u067e\u0646\u062c\u0627\u0628\u06cc (Sh\u0101hmukh\u012b Pa\u00f1j\u0101b\u012b)"
},
"pnt": {
- "articles": 467,
+ "articles": 469,
"english_name": "Pontic",
"name": "\u03a0\u03bf\u03bd\u03c4\u03b9\u03b1\u03ba\u03ac"
},
"ps": {
- "articles": 10997,
+ "articles": 11544,
"english_name": "Pashto",
"name": "\u067e\u069a\u062a\u0648"
},
"pt": {
- "articles": 1021915,
+ "articles": 1043641,
"english_name": "Portuguese",
"name": "Portugu\u00eas"
},
"qu": {
- "articles": 21801,
+ "articles": 22691,
"english_name": "Quechua",
"name": "Runa Simi"
},
"rm": {
- "articles": 3646,
+ "articles": 3695,
"english_name": "Romansh",
"name": "Rumantsch"
},
"rmy": {
- "articles": 675,
+ "articles": 676,
"english_name": "Romani",
"name": "romani - \u0930\u094b\u092e\u093e\u0928\u0940"
},
"rn": {
- "articles": 615,
+ "articles": 616,
"english_name": "Kirundi",
"name": "Kirundi"
},
"ro": {
- "articles": 404592,
+ "articles": 412071,
"english_name": "Romanian",
"name": "Rom\u00e2n\u0103"
},
"roa-rup": {
- "articles": 1225,
+ "articles": 1233,
"english_name": "Aromanian",
"name": "Arm\u00e3neashce"
},
"roa-tara": {
- "articles": 9249,
+ "articles": 9302,
"english_name": "Tarantino",
"name": "Tarand\u00edne"
},
"ru": {
- "articles": 1600812,
+ "articles": 1661738,
"english_name": "Russian",
"name": "\u0420\u0443\u0441\u0441\u043a\u0438\u0439"
},
"rue": {
- "articles": 7447,
+ "articles": 7813,
"english_name": "Rusyn",
"name": "\u0420\u0443\u0441\u0438\u043d\u044c\u0441\u043a\u044b\u0439"
},
"rw": {
- "articles": 1820,
+ "articles": 1852,
"english_name": "Kinyarwanda",
"name": "Ikinyarwanda"
},
"sa": {
- "articles": 11456,
+ "articles": 11462,
"english_name": "Sanskrit",
"name": "\u0938\u0902\u0938\u094d\u0915\u0943\u0924\u092e\u094d"
},
"sah": {
- "articles": 12199,
+ "articles": 12759,
"english_name": "Sakha",
"name": "\u0421\u0430\u0445\u0430 \u0442\u044b\u043b\u0430 (Saxa Tyla)"
},
"sat": {
- "articles": 2470,
+ "articles": 4535,
"english_name": "Santali",
"name": "\u1c65\u1c5f\u1c71\u1c5b\u1c5f\u1c72\u1c64"
},
"sc": {
- "articles": 6371,
+ "articles": 6686,
"english_name": "Sardinian",
"name": "Sardu"
},
"scn": {
- "articles": 26074,
+ "articles": 26058,
"english_name": "Sicilian",
"name": "Sicilianu"
},
"sco": {
- "articles": 56578,
+ "articles": 56568,
"english_name": "Scots",
"name": "Scots"
},
"sd": {
- "articles": 12956,
+ "articles": 13687,
"english_name": "Sindhi",
"name": "\u0633\u0646\u068c\u064a\u060c \u0633\u0646\u062f\u06be\u06cc \u060c \u0938\u093f\u0928\u094d\u0927"
},
"se": {
- "articles": 7612,
+ "articles": 7695,
"english_name": "Northern Sami",
"name": "S\u00e1megiella"
},
@@ -27933,297 +29017,297 @@
"name": "S\u00e4ng\u00f6"
},
"sh": {
- "articles": 451031,
+ "articles": 452863,
"english_name": "Serbo-Croatian",
"name": "Srpskohrvatski / \u0421\u0440\u043f\u0441\u043a\u043e\u0445\u0440\u0432\u0430\u0442\u0441\u043a\u0438"
},
"shn": {
- "articles": 6745,
+ "articles": 7293,
"english_name": "Shan",
"name": "\u101c\u102d\u1075\u103a\u1088\u1010\u1086\u1038"
},
"si": {
- "articles": 15543,
+ "articles": 15855,
"english_name": "Sinhalese",
"name": "\u0dc3\u0dd2\u0d82\u0dc4\u0dbd"
},
"simple": {
- "articles": 155539,
+ "articles": 172453,
"english_name": "Simple English",
"name": "Simple English"
},
"sk": {
- "articles": 232538,
+ "articles": 234573,
"english_name": "Slovak",
"name": "Sloven\u010dina"
},
"sl": {
- "articles": 167090,
+ "articles": 169414,
"english_name": "Slovenian",
"name": "Sloven\u0161\u010dina"
},
"sm": {
- "articles": 823,
+ "articles": 855,
"english_name": "Samoan",
"name": "Gagana Samoa"
},
"sn": {
- "articles": 4996,
+ "articles": 5991,
"english_name": "Shona",
"name": "chiShona"
},
"so": {
- "articles": 5752,
+ "articles": 5805,
"english_name": "Somali",
"name": "Soomaali"
},
"sq": {
- "articles": 78674,
+ "articles": 81381,
"english_name": "Albanian",
"name": "Shqip"
},
"sr": {
- "articles": 630590,
+ "articles": 638284,
"english_name": "Serbian",
"name": "\u0421\u0440\u043f\u0441\u043a\u0438 / Srpski"
},
"srn": {
- "articles": 1076,
+ "articles": 1070,
"english_name": "Sranan",
"name": "Sranantongo"
},
"ss": {
- "articles": 504,
+ "articles": 518,
"english_name": "Swati",
"name": "SiSwati"
},
"st": {
- "articles": 656,
+ "articles": 768,
"english_name": "Sesotho",
"name": "Sesotho"
},
"stq": {
- "articles": 4017,
+ "articles": 4025,
"english_name": "Saterland Frisian",
"name": "Seeltersk"
},
"su": {
- "articles": 59863,
+ "articles": 60527,
"english_name": "Sundanese",
"name": "Basa Sunda"
},
"sv": {
- "articles": 3738262,
+ "articles": 3675733,
"english_name": "Swedish",
"name": "Svenska"
},
"sw": {
- "articles": 55888,
+ "articles": 60346,
"english_name": "Swahili",
"name": "Kiswahili"
},
"szl": {
- "articles": 51946,
+ "articles": 52432,
"english_name": "Silesian",
"name": "\u015al\u016fnski"
},
"szy": {
- "articles": 1748,
+ "articles": 1812,
"english_name": "Sakizaya",
"name": "Sakizaya"
},
"ta": {
- "articles": 127284,
+ "articles": 131072,
"english_name": "Tamil",
"name": "\u0ba4\u0bae\u0bbf\u0bb4\u0bcd"
},
"tcy": {
- "articles": 1271,
+ "articles": 1397,
"english_name": "Tulu",
"name": "\u0ca4\u0cc1\u0cb3\u0cc1"
},
"te": {
- "articles": 70262,
+ "articles": 69631,
"english_name": "Telugu",
"name": "\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41"
},
"tet": {
- "articles": 1475,
+ "articles": 1479,
"english_name": "Tetum",
"name": "Tetun"
},
"tg": {
- "articles": 100460,
+ "articles": 101656,
"english_name": "Tajik",
"name": "\u0422\u043e\u04b7\u0438\u043a\u04e3"
},
"th": {
- "articles": 135321,
+ "articles": 139928,
"english_name": "Thai",
"name": "\u0e44\u0e17\u0e22"
},
"ti": {
- "articles": 202,
+ "articles": 195,
"english_name": "Tigrinya",
"name": "\u1275\u130d\u122d\u129b"
},
"tk": {
- "articles": 5656,
+ "articles": 5821,
"english_name": "Turkmen",
"name": "T\u00fcrkmen"
},
"tl": {
- "articles": 75674,
+ "articles": 68568,
"english_name": "Tagalog",
"name": "Tagalog"
},
"tn": {
- "articles": 710,
+ "articles": 712,
"english_name": "Tswana",
"name": "Setswana"
},
"to": {
- "articles": 1738,
+ "articles": 1740,
"english_name": "Tongan",
"name": "faka Tonga"
},
"tpi": {
- "articles": 1608,
+ "articles": 1625,
"english_name": "Tok Pisin",
"name": "Tok Pisin"
},
"tr": {
- "articles": 343072,
+ "articles": 365938,
"english_name": "Turkish",
"name": "T\u00fcrk\u00e7e"
},
"ts": {
- "articles": 683,
+ "articles": 700,
"english_name": "Tsonga",
"name": "Xitsonga"
},
"tt": {
- "articles": 89002,
+ "articles": 136712,
"english_name": "Tatar",
"name": "Tatar\u00e7a / \u0422\u0430\u0442\u0430\u0440\u0447\u0430"
},
"tum": {
- "articles": 586,
+ "articles": 588,
"english_name": "Tumbuka",
"name": "chiTumbuka"
},
"tw": {
- "articles": 705,
+ "articles": 708,
"english_name": "Twi",
"name": "Twi"
},
"ty": {
- "articles": 1204,
+ "articles": 1208,
"english_name": "Tahitian",
"name": "Reo M\u0101`ohi"
},
"tyv": {
- "articles": 1987,
+ "articles": 2704,
"english_name": "Tuvan",
"name": "\u0422\u044b\u0432\u0430"
},
"udm": {
- "articles": 4848,
+ "articles": 4948,
"english_name": "Udmurt",
"name": "\u0423\u0434\u043c\u0443\u0440\u0442 \u043a\u044b\u043b"
},
"ug": {
- "articles": 4244,
+ "articles": 4364,
"english_name": "Uyghur",
"name": "\u0626\u06c7\u064a\u063a\u06c7\u0631 \u062a\u0649\u0644\u0649"
},
"uk": {
- "articles": 993357,
+ "articles": 1044506,
"english_name": "Ukrainian",
"name": "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430"
},
"ur": {
- "articles": 151971,
+ "articles": 157319,
"english_name": "Urdu",
"name": "\u0627\u0631\u062f\u0648"
},
"uz": {
- "articles": 133433,
+ "articles": 136238,
"english_name": "Uzbek",
"name": "O\u2018zbek"
},
"ve": {
- "articles": 368,
+ "articles": 370,
"english_name": "Venda",
"name": "Tshivenda"
},
"vec": {
- "articles": 19577,
+ "articles": 62971,
"english_name": "Venetian",
"name": "V\u00e8neto"
},
"vep": {
- "articles": 6395,
+ "articles": 6601,
"english_name": "Vepsian",
"name": "Veps\u00e4n"
},
"vi": {
- "articles": 1241042,
+ "articles": 1255776,
"english_name": "Vietnamese",
"name": "Ti\u1ebfng Vi\u1ec7t"
},
"vls": {
- "articles": 7079,
+ "articles": 7225,
"english_name": "West Flemish",
"name": "West-Vlams"
},
"vo": {
- "articles": 124156,
+ "articles": 125021,
"english_name": "Volap\u00fck",
"name": "Volap\u00fck"
},
"wa": {
- "articles": 15627,
+ "articles": 14141,
"english_name": "Walloon",
"name": "Walon"
},
"war": {
- "articles": 1263934,
+ "articles": 1264408,
"english_name": "Waray-Waray",
"name": "Winaray"
},
"wo": {
- "articles": 1387,
+ "articles": 1421,
"english_name": "Wolof",
"name": "Wolof"
},
"wuu": {
- "articles": 28716,
+ "articles": 39058,
"english_name": "Wu",
"name": "\u5434\u8bed"
},
"xal": {
- "articles": 2082,
+ "articles": 2085,
"english_name": "Kalmyk",
"name": "\u0425\u0430\u043b\u044c\u043c\u0433"
},
"xh": {
- "articles": 1043,
+ "articles": 1057,
"english_name": "Xhosa",
"name": "isiXhosa"
},
"xmf": {
- "articles": 13670,
+ "articles": 14930,
"english_name": "Mingrelian",
"name": "\u10db\u10d0\u10e0\u10d2\u10d0\u10da\u10e3\u10e0\u10d8 (Margaluri)"
},
"yi": {
- "articles": 14999,
+ "articles": 14885,
"english_name": "Yiddish",
"name": "\u05d9\u05d9\u05b4\u05d3\u05d9\u05e9"
},
"yo": {
- "articles": 32321,
+ "articles": 32714,
"english_name": "Yoruba",
"name": "Yor\u00f9b\u00e1"
},
@@ -28233,32 +29317,32 @@
"name": "Cuengh"
},
"zea": {
- "articles": 4727,
+ "articles": 4741,
"english_name": "Zeelandic",
"name": "Ze\u00eauws"
},
"zh": {
- "articles": 1099382,
+ "articles": 1147282,
"english_name": "Chinese",
"name": "\u4e2d\u6587"
},
"zh-classical": {
- "articles": 10187,
+ "articles": 10450,
"english_name": "Classical Chinese",
"name": "\u53e4\u6587 / \u6587\u8a00\u6587"
},
"zh-min-nan": {
- "articles": 264433,
+ "articles": 405686,
"english_name": "Min Nan",
"name": "B\u00e2n-l\u00e2m-g\u00fa"
},
"zh-yue": {
- "articles": 77673,
+ "articles": 102328,
"english_name": "Cantonese",
"name": "\u7cb5\u8a9e"
},
"zu": {
- "articles": 1382,
+ "articles": 4070,
"english_name": "Zulu",
"name": "isiZulu"
}
diff --git a/searx/data/external_urls.json b/searx/data/external_urls.json
new file mode 100644
index 0000000..75b153a
--- /dev/null
+++ b/searx/data/external_urls.json
@@ -0,0 +1,156 @@
+{
+ "facebook_profile": {
+ "category_name": "Facebook",
+ "url_name": "Facebook profile",
+ "urls": {
+ "default": "https://facebook.com/$1"
+ }
+ },
+ "youtube_channel": {
+ "category_name": "YouTube",
+ "url_name": "YouTube channel",
+ "urls": {
+ "default": "https://www.youtube.com/channel/$1"
+ }
+ },
+ "youtube_video": {
+ "category_name": "YouTube",
+ "url_name": "YouTube video",
+ "urls": {
+ "default": "https://www.youtube.com/watch?v=$1"
+ }
+ },
+ "twitter_profile": {
+ "category_name": "Twitter",
+ "url_name": "Twitter profile",
+ "urls": {
+ "default": "https://twitter.com/$1"
+ }
+ },
+ "instagram_profile": {
+ "category_name": "Instagram",
+ "url_name": "Instagram profile",
+ "urls": {
+ "default": "https://www.instagram.com/$1"
+ }
+ },
+ "imdb_title": {
+ "category_name": "IMDB",
+ "url_name": "IMDB title",
+ "urls": {
+ "default": "https://www.imdb.com/title/$1"
+ }
+ },
+ "imdb_name": {
+ "category_name": "IMDB",
+ "url_name": "IMDB name",
+ "urls": {
+ "default": "https://www.imdb.com/name/$1"
+ }
+ },
+ "imdb_character": {
+ "category_name": "IMDB",
+ "url_name": "IMDB character",
+ "urls": {
+ "default": "https://www.imdb.com/character/$1"
+ }
+ },
+ "imdb_company": {
+ "category_name": "IMDB",
+ "url_name": "IMDB company",
+ "urls": {
+ "default": "https://www.imdb.com/company/$1"
+ }
+ },
+ "imdb_event": {
+ "category_name": "IMDB",
+ "url_name": "IMDB event",
+ "urls": {
+ "default": "https://www.imdb.com/event/$1"
+ }
+ },
+ "rotten_tomatoes": {
+ "category_name": "Rotten tomatoes",
+ "url_name": "Rotten tomatoes title",
+ "urls": {
+ "default": "https://www.rottentomatoes.com/$1"
+ }
+ },
+ "spotify_artist_id": {
+ "category_name": "Spotify",
+ "url_name": "Spotify artist",
+ "urls": {
+ "default": "https://open.spotify.com/artist/$1"
+ }
+ },
+ "itunes_artist_id": {
+ "category_name": "iTunes",
+ "url_name": "iTunes artist",
+ "urls": {
+ "default": "https://music.apple.com/us/artist/$1"
+ }
+ },
+ "soundcloud_id": {
+ "category_name": "Soundcloud",
+ "url_name": "Soundcloud artist",
+ "urls": {
+ "default": "https://soundcloud.com/$1"
+ }
+ },
+ "netflix_id": {
+ "category_name": "Netflix",
+ "url_name": "Netflix movie",
+ "urls": {
+ "default": "https://www.netflix.com/watch/$1"
+ }
+ },
+ "github_profile": {
+ "category_name": "Github",
+ "url_name": "Github profile",
+ "urls": {
+ "default": "https://wwww.github.com/$1"
+ }
+ },
+ "musicbrainz_artist": {
+ "category_name": "Musicbrainz",
+ "url_name": "Musicbrainz artist",
+ "urls": {
+ "default": "http://musicbrainz.org/artist/$1"
+ }
+ },
+ "musicbrainz_work": {
+ "category_name": "Musicbrainz",
+ "url_name": "Musicbrainz work",
+ "urls": {
+ "default": "http://musicbrainz.org/work/$1"
+ }
+ },
+ "musicbrainz_release_group": {
+ "category_name": "Musicbrainz",
+ "url_name": "Musicbrainz release group",
+ "urls": {
+ "default": "http://musicbrainz.org/release-group/$1"
+ }
+ },
+ "musicbrainz_label": {
+ "category_name": "Musicbrainz",
+ "url_name": "Musicbrainz label",
+ "urls": {
+ "default": "http://musicbrainz.org/label/$1"
+ }
+ },
+ "wikimedia_image": {
+ "category_name": "Wikipedia",
+ "url_name": "Wikipedia image",
+ "urls": {
+ "default": "https://commons.wikimedia.org/wiki/Special:FilePath/$1?width=500&height=400"
+ }
+ },
+ "map": {
+ "category_name": "Map",
+ "url_name": "geo map",
+ "urls": {
+ "default": "https://www.openstreetmap.org/?lat=${latitude}&lon=${longitude}&zoom=${zoom}&layers=M"
+ }
+ }
+} \ No newline at end of file
diff --git a/searx/data/wikidata_units.json b/searx/data/wikidata_units.json
new file mode 100644
index 0000000..966e5e8
--- /dev/null
+++ b/searx/data/wikidata_units.json
@@ -0,0 +1,1006 @@
+{
+ "Q199": "1",
+ "Q100149279": "°We",
+ "Q100995": "lb",
+ "Q1022113": "cm³",
+ "Q102573": "Bq",
+ "Q103246": "Sv",
+ "Q103510": "bar",
+ "Q10380431": "TJ",
+ "Q1040401": "das",
+ "Q1040427": "hs",
+ "Q1042866": "Zibit",
+ "Q1050958": "inHg",
+ "Q1051665": "m/s²",
+ "Q1052397": "rad",
+ "Q1054140": "Mm",
+ "Q10543042": "Ym",
+ "Q1057069": "hg",
+ "Q1063756": "rad/s",
+ "Q1063786": "in²",
+ "Q1065153": "mrad",
+ "Q1066138": "Ps",
+ "Q1067722": "Fg",
+ "Q1069725": "p.",
+ "Q1084321": "Tb/s",
+ "Q1086691": "fg",
+ "Q1091257": "tex",
+ "Q1092296": "a",
+ "Q1104069": "CAD$",
+ "Q11061003": "μm²",
+ "Q11061005": "nm²",
+ "Q1131660": "st",
+ "Q1137675": "cr",
+ "Q1140444": "Zbit",
+ "Q1140577": "Ybit",
+ "Q1152074": "Pbit",
+ "Q1152323": "Tbit",
+ "Q1165799": "mil",
+ "Q11776930": "Mg",
+ "Q11830636": "psf",
+ "Q11929860": "kpc",
+ "Q1194225": "lbf",
+ "Q1194580": "Mibit",
+ "Q1195111": "Ebit",
+ "Q1196837": "ω_P",
+ "Q1197459": "Ms",
+ "Q11982285": "Em³",
+ "Q11982288": "Zm³",
+ "Q11982289": "Tm³",
+ "Q12011178": "Zs",
+ "Q1204894": "Gibit",
+ "Q12257695": "Eb/s",
+ "Q12257696": "EB/s",
+ "Q12261466": "kB/s",
+ "Q12265780": "Pb/s",
+ "Q12265783": "PB/s",
+ "Q12269121": "Yb/s",
+ "Q12269122": "YB/s",
+ "Q12269308": "Zb/s",
+ "Q12269309": "ZB/s",
+ "Q1247300": "cm H₂O",
+ "Q12714022": "sh cwt",
+ "Q12789864": "GeV",
+ "Q12874593": "W h",
+ "Q128822": "kn",
+ "Q13035094": "J/mol",
+ "Q130964": "cal",
+ "Q131255": "F",
+ "Q13147228": "g/cm³",
+ "Q1322380": "Ts",
+ "Q1323615": "oz t",
+ "Q132643": "kr",
+ "Q13400897": "g",
+ "Q13479685": "mm wg",
+ "Q1351253": "Eibit",
+ "Q1351334": "Pibit",
+ "Q13542672": "Ry",
+ "Q13548586": "THz",
+ "Q13582667": "kgf/cm²",
+ "Q1361854": "dwt",
+ "Q1363007": "slug",
+ "Q1374438": "ks",
+ "Q13753469": "MB/s",
+ "Q1377051": "Gs",
+ "Q1394540": "bm",
+ "Q1396128": "F",
+ "Q1413142": "Gb",
+ "Q14158377": "A_P",
+ "Q14623803": "MDa",
+ "Q14623804": "kDa",
+ "Q1472674": "Sv",
+ "Q14754979": "Zg",
+ "Q14786969": "MJ",
+ "Q14913554": "Ys",
+ "Q14914907": "th",
+ "Q14916719": "Gpc",
+ "Q14923662": "Pm³",
+ "Q1511773": "LSd",
+ "Q15120301": "l atm",
+ "Q1542309": "xu",
+ "Q1545979": "ft³",
+ "Q1550511": "yd²",
+ "Q15551713": "Sh",
+ "Q1569733": "St",
+ "Q15784325": "apc",
+ "Q160680": "Br",
+ "Q160857": "hp",
+ "Q1628990": "hph",
+ "Q163343": "T",
+ "Q163354": "H",
+ "Q1640501": "hyl",
+ "Q1645498": "μg",
+ "Q16859309": "lb·ft",
+ "Q169893": "S",
+ "Q170804": "Wb",
+ "Q17093295": "m/h",
+ "Q17255465": "v_P",
+ "Q173117": "R$",
+ "Q1741429": "kpm",
+ "Q174467": "Lm",
+ "Q174728": "cm",
+ "Q174789": "mm",
+ "Q175821": "μm",
+ "Q1768929": "p",
+ "Q1770733": "Tg",
+ "Q1772386": "dg",
+ "Q177493": "Gs",
+ "Q177612": "sr",
+ "Q1777507": "fs",
+ "Q177974": "atm",
+ "Q178506": "bbl",
+ "Q178674": "nm",
+ "Q1793863": "sn",
+ "Q179836": "lx",
+ "Q180154": "km/h",
+ "Q180892": "M☉",
+ "Q1815100": "cl",
+ "Q182098": "kWh",
+ "Q1823150": "μW",
+ "Q182429": "m/s",
+ "Q1826195": "dl",
+ "Q18413919": "cm/s",
+ "Q184172": "FF",
+ "Q185078": "a",
+ "Q185153": "erg",
+ "Q185648": "Torr",
+ "Q185759": "span",
+ "Q1872619": "zs",
+ "Q189097": "₧",
+ "Q190095": "Gy",
+ "Q190951": "S$",
+ "Q191118": "t",
+ "Q1913097": "fg",
+ "Q1916026": "μV",
+ "Q192027": "Bd",
+ "Q192274": "pm",
+ "Q193098": "KD",
+ "Q1935515": "mA s",
+ "Q19392152": "TL",
+ "Q193933": "dpt",
+ "Q194339": "B$",
+ "Q1970718": "mam",
+ "Q1972579": "pdl",
+ "Q199462": "LE",
+ "Q199471": "Afs",
+ "Q200323": "dm",
+ "Q200337": "Kz",
+ "Q201880": "LL",
+ "Q201933": "dyn",
+ "Q2029156": "quad",
+ "Q2029519": "hl",
+ "Q203567": "₦",
+ "Q2042279": "m H₂O",
+ "Q204737": "៛",
+ "Q2051195": "GWh",
+ "Q2055118": "ppb",
+ "Q2064166": "fc",
+ "Q206600": "ރ",
+ "Q20706220": "cmm",
+ "Q20706221": "dmm",
+ "Q2080811": "vol%",
+ "Q208526": "NT$",
+ "Q208528": "gon",
+ "Q208634": "kat",
+ "Q208788": "fm",
+ "Q209351": "b",
+ "Q209426": "′",
+ "Q21006887": "ppm",
+ "Q2100949": "P",
+ "Q21014455": "m/min",
+ "Q210472": "B/.",
+ "Q21061369": "g/kg",
+ "Q21062777": "MPa",
+ "Q21064807": "kPa",
+ "Q21064845": "mol/L",
+ "Q21075844": "ml/l",
+ "Q21077820": "mg/m³",
+ "Q21091747": "mg/kg",
+ "Q211256": "mph",
+ "Q211580": "BTU (th)",
+ "Q212120": "A h",
+ "Q2140397": "in³",
+ "Q214377": "ell",
+ "Q2143992": "kHz",
+ "Q21489891": "nm³",
+ "Q21489892": "Gm³",
+ "Q21489893": "Mm³",
+ "Q21489894": "μm³",
+ "Q21500224": "mas",
+ "Q2151240": "mag",
+ "Q215571": "N m",
+ "Q21604951": "g/m³",
+ "Q2165290": "yd³",
+ "Q216880": "kp",
+ "Q217208": "a",
+ "Q2175964": "dm³",
+ "Q218593": "in",
+ "Q2199357": "dec",
+ "Q22137107": "mas/y",
+ "Q2215478": "ppt",
+ "Q2221356": "mW h",
+ "Q22350885": "da",
+ "Q2243141": "Gb/s",
+ "Q2254856": "ca",
+ "Q22673229": "ft/min",
+ "Q2269250": "kb/s",
+ "Q2282891": "μl",
+ "Q2282906": "ng",
+ "Q229354": "Ci",
+ "Q232291": "mi²",
+ "Q2332346": "ml",
+ "Q23823681": "TW",
+ "Q23925410": "gal (UK)",
+ "Q23925413": "gal (US)",
+ "Q23931040": "dam²",
+ "Q23931103": "nmi²",
+ "Q2414435": "$b.",
+ "Q242988": "Lib$",
+ "Q2438073": "ag",
+ "Q2448803": "mV",
+ "Q2451296": "μF",
+ "Q246868": "lot",
+ "Q2474258": "mSv",
+ "Q2483628": "as",
+ "Q2489298": "cm²",
+ "Q249439": "q_P",
+ "Q2518569": "nSv",
+ "Q253276": "mi",
+ "Q25472681": "GB/s",
+ "Q25472693": "TB/s",
+ "Q25499149": "oct",
+ "Q25511288": "mb",
+ "Q2553708": "MV",
+ "Q2554092": "kV",
+ "Q259502": "AU$",
+ "Q260126": "rem",
+ "Q2612219": "Pg",
+ "Q261247": "ct",
+ "Q2619500": "foe",
+ "Q2636421": "nH",
+ "Q2637946": "dal",
+ "Q2642547": "ha",
+ "Q2652700": "Osm",
+ "Q2655272": "Eg",
+ "Q2659078": "TW h",
+ "Q2670039": "₶",
+ "Q26708069": "kcal",
+ "Q267391": "K",
+ "Q2679083": "μH",
+ "Q2682463": "nF",
+ "Q2691798": "cg",
+ "Q271206": "sud£",
+ "Q2737347": "mm²",
+ "Q2739114": "μSv",
+ "Q275112": "Bz$",
+ "Q2756030": "pF",
+ "Q2757753": "PW h",
+ "Q2762458": "ys",
+ "Q27864215": "μW h",
+ "Q2793566": "GV",
+ "Q27949241": "R",
+ "Q2799294": "Gg",
+ "Q281096": "cd/m²",
+ "Q28719934": "keV",
+ "Q28924752": "g/mol",
+ "Q28924753": "kg/mol",
+ "Q2924137": "mH",
+ "Q296936": "toe",
+ "Q29924639": "kVA",
+ "Q30001811": "aBq",
+ "Q30001813": "aC",
+ "Q30001814": "aHz",
+ "Q30001815": "aJ",
+ "Q30001816": "akat",
+ "Q30001818": "aL",
+ "Q30001819": "alm",
+ "Q30001820": "alx",
+ "Q30001822": "aN",
+ "Q30001823": "aΩ",
+ "Q30001825": "aPa",
+ "Q30001826": "arad",
+ "Q30001827": "aS",
+ "Q30001828": "aSv",
+ "Q30001829": "asr",
+ "Q30001830": "aT",
+ "Q30001831": "aV",
+ "Q30001832": "aW",
+ "Q30001833": "aWb",
+ "Q3013059": "kyr",
+ "Q3194304": "kbit",
+ "Q3207456": "mW",
+ "Q321017": "R",
+ "Q3221356": "ym",
+ "Q3239557": "pg",
+ "Q3241121": "mg",
+ "Q324923": "Hart",
+ "Q3249364": "cs",
+ "Q3251645": "ds",
+ "Q3267417": "Tm",
+ "Q3270676": "zm",
+ "Q32750621": "liq pt (US)",
+ "Q32750759": "fl oz (US)",
+ "Q32750816": "bu (US)",
+ "Q32751272": "dry pt (US)",
+ "Q32751296": "bbl (US)",
+ "Q3276763": "GHz",
+ "Q3277907": "Em",
+ "Q3277915": "Zm",
+ "Q3277919": "Pm",
+ "Q3312063": "fL",
+ "Q3320608": "kW",
+ "Q3331719": "dm²",
+ "Q3332689": "ToR",
+ "Q3332814": "Mbit",
+ "Q3396758": "daa",
+ "Q3414243": "rps",
+ "Q3421309": "R_J",
+ "Q3495543": "mbar",
+ "Q355198": "px",
+ "Q3674704": "km/s",
+ "Q3675550": "mm³",
+ "Q3712659": "$",
+ "Q376660": "nat",
+ "Q37732658": "°R",
+ "Q3773454": "Mpc",
+ "Q3815076": "Kibit",
+ "Q3833309": "£",
+ "Q3858002": "mA h",
+ "Q3867152": "ft/s²",
+ "Q389062": "Tibit",
+ "Q3902688": "pl",
+ "Q3902709": "ps",
+ "Q39360235": "US lea",
+ "Q39360471": "nl",
+ "Q39362962": "µin",
+ "Q39363132": "UK lg",
+ "Q39363209": "UK nl",
+ "Q39380159": "US nmi",
+ "Q39462789": "µin²",
+ "Q39467934": "kgf/m²",
+ "Q39469927": "N/m²",
+ "Q39617688": "cwt long",
+ "Q39617818": "t lb",
+ "Q39628023": "y",
+ "Q39699418": "cm/s²",
+ "Q39708248": "S",
+ "Q39709980": "bd",
+ "Q39710113": "bhp EDR",
+ "Q3972226": "kL",
+ "Q4041686": "iwg",
+ "Q4068266": "Ʒ",
+ "Q4176683": "aC",
+ "Q420266": "oz. fl.",
+ "Q42319606": "people/m²",
+ "Q4243638": "km³",
+ "Q4456994": "mF",
+ "Q469356": "tn. sh.",
+ "Q476572": "Ha",
+ "Q482798": "yd",
+ "Q483261": "Da",
+ "Q483725": "A.M.",
+ "Q484092": "lm",
+ "Q4861171": "H",
+ "Q494083": "fur",
+ "Q4989854": "kJ",
+ "Q500515": "Gal",
+ "Q5042194": "£",
+ "Q50808017": "kg m²",
+ "Q5139563": "hPa",
+ "Q514845": "pz",
+ "Q5195628": "hm³",
+ "Q5198770": "dam³",
+ "Q524410": "byr",
+ "Q53393488": "PHz",
+ "Q53393490": "EHz",
+ "Q53393494": "ZHz",
+ "Q53393498": "YHz",
+ "Q53393659": "ML",
+ "Q53393664": "GL",
+ "Q53393674": "ZL",
+ "Q53393678": "YL",
+ "Q53393771": "yL",
+ "Q53393868": "GJ",
+ "Q53393886": "PJ",
+ "Q53393890": "EJ",
+ "Q53448786": "yHz",
+ "Q53448790": "zHz",
+ "Q53448794": "fHz",
+ "Q53448797": "pHz",
+ "Q53448801": "nHz",
+ "Q53448806": "μHz",
+ "Q53448808": "mHz",
+ "Q53448813": "cHz",
+ "Q53448817": "dHz",
+ "Q53448820": "daHz",
+ "Q53448826": "hHz",
+ "Q53448828": "yJ",
+ "Q53448832": "zJ",
+ "Q53448842": "pJ",
+ "Q53448844": "nJ",
+ "Q53448847": "μJ",
+ "Q53448851": "mJ",
+ "Q53448856": "cJ",
+ "Q53448860": "dJ",
+ "Q53448864": "daJ",
+ "Q53448875": "hJ",
+ "Q53448879": "yPa",
+ "Q53448883": "zPa",
+ "Q53448886": "fPa",
+ "Q53448892": "pPa",
+ "Q53448897": "nPa",
+ "Q53448900": "μPa",
+ "Q53448906": "mPa",
+ "Q53448909": "cPa",
+ "Q53448914": "dPa",
+ "Q53448918": "daPa",
+ "Q53448922": "GPa",
+ "Q53448927": "TPa",
+ "Q53448931": "PPa",
+ "Q53448936": "EPa",
+ "Q53448939": "ZPa",
+ "Q53448943": "YPa",
+ "Q53448949": "yV",
+ "Q53448952": "zV",
+ "Q53448957": "fV",
+ "Q53448960": "pV",
+ "Q53448965": "nV",
+ "Q53448969": "cV",
+ "Q53448973": "dV",
+ "Q53448977": "daV",
+ "Q53448981": "hV",
+ "Q53448985": "TV",
+ "Q53448990": "PV",
+ "Q53448994": "EV",
+ "Q53448996": "ZV",
+ "Q53449001": "YV",
+ "Q53449006": "yW",
+ "Q53449008": "zW",
+ "Q53449013": "fW",
+ "Q53449018": "pW",
+ "Q53449021": "nW",
+ "Q53449025": "cW",
+ "Q53449029": "dW",
+ "Q53449033": "daW",
+ "Q53449036": "hW",
+ "Q53449040": "PW",
+ "Q53449045": "EW",
+ "Q53449049": "ZW",
+ "Q53449054": "YW",
+ "Q53561461": "wf",
+ "Q53561822": "wf",
+ "Q53651160": "zm³",
+ "Q53651201": "Ym³",
+ "Q53651356": "ym³",
+ "Q53651512": "pm³",
+ "Q53651713": "fm³",
+ "Q536785": "ρ_P",
+ "Q53951982": "Mt",
+ "Q53952048": "kt",
+ "Q54006645": "ZWb",
+ "Q54081925": "ZSv",
+ "Q54082468": "ZS",
+ "Q54083144": "ZΩ",
+ "Q54083318": "ZN",
+ "Q54083566": "Zlm",
+ "Q54083579": "Zlx",
+ "Q54083712": "ZBq",
+ "Q54083746": "ZC",
+ "Q54083766": "ZF",
+ "Q54083779": "ZGy",
+ "Q54083795": "ZH",
+ "Q54083813": "Zkat",
+ "Q5409016": "MVA",
+ "Q5465723": "ft-pdl",
+ "Q549389": "bit/s",
+ "Q550341": "V A",
+ "Q552299": "ch",
+ "Q55442349": "U/L",
+ "Q55726194": "mg/L",
+ "Q56156859": "mmol",
+ "Q56156949": "μmol",
+ "Q56157046": "nmol",
+ "Q56157048": "pmol",
+ "Q56160603": "fmol",
+ "Q56302633": "UM",
+ "Q56317116": "mgal",
+ "Q56317622": "Q_P",
+ "Q56318907": "kbar",
+ "Q56349362": "Bs.S",
+ "Q56402798": "kN",
+ "Q5711261": "am³",
+ "Q581432": "‴",
+ "Q5879479": "GW",
+ "Q6003257": "am",
+ "Q6009164": "MW h",
+ "Q6014364": "in/s",
+ "Q603071": "E°",
+ "Q605704": "doz",
+ "Q60742631": "AU/yr",
+ "Q608697": "Mx",
+ "Q610135": "G",
+ "Q613726": "Yg",
+ "Q6170164": "yg",
+ "Q6171168": "zg",
+ "Q61756607": "yd",
+ "Q61793198": "rd",
+ "Q61794766": "ch (US survey)",
+ "Q61994988": "Wth",
+ "Q61995006": "KWth",
+ "Q626299": "psi",
+ "Q630369": "var",
+ "Q636200": "U",
+ "Q640907": "sb",
+ "Q6414556": "kip",
+ "Q648908": "bya",
+ "Q64996135": "gal (US)/min",
+ "Q65028392": "mm/yr",
+ "Q651336": "M_J",
+ "Q6517513": "dag",
+ "Q667419": "UK t",
+ "Q681996": "M⊕",
+ "Q685662": "p_P",
+ "Q6859652": "mm Hg",
+ "Q686163": "$",
+ "Q68725821": "°Rø",
+ "Q68726230": "°De",
+ "Q68726625": "°N",
+ "Q69362731": "°C",
+ "Q69363953": "K",
+ "Q693944": "gr",
+ "Q6982035": "MW",
+ "Q69878540": "fl oz (UK)",
+ "Q70378044": "dmol",
+ "Q70378549": "dK",
+ "Q70393458": "kmol",
+ "Q70395375": "Tmol",
+ "Q70395643": "Mmol",
+ "Q70395830": "kK",
+ "Q70396179": "mK",
+ "Q70397275": "μK",
+ "Q70397725": "cmol",
+ "Q70397932": "cK",
+ "Q70398457": "nK",
+ "Q70398619": "MK",
+ "Q70398813": "Gmol",
+ "Q70398991": "GK",
+ "Q70440025": "daK",
+ "Q70440438": "hK",
+ "Q70440620": "damol",
+ "Q70440823": "hmol",
+ "Q70443020": "EK",
+ "Q70443154": "yK",
+ "Q70443282": "zK",
+ "Q70443367": "fK",
+ "Q70443453": "TK",
+ "Q70443757": "pK",
+ "Q70443901": "YK",
+ "Q70444029": "PK",
+ "Q70444141": "Emol",
+ "Q70444284": "ymol",
+ "Q70444386": "zmol",
+ "Q70444514": "Ymol",
+ "Q70444609": "Pmol",
+ "Q712226": "km²",
+ "Q72081071": "MeV",
+ "Q723733": "ms",
+ "Q730251": "ft·lbf",
+ "Q732707": "MHz",
+ "Q73408": "K",
+ "Q7350781": "Mb/s",
+ "Q743895": "bpm",
+ "Q748716": "ft/s",
+ "Q750178": "‱",
+ "Q752197": "kJ/mol",
+ "Q7672057": "TU",
+ "Q777017": "dBm",
+ "Q78754556": "rot",
+ "Q78756901": "rev",
+ "Q78757683": "windings",
+ "Q79726": "kB",
+ "Q79735": "MB",
+ "Q79738": "GB",
+ "Q79741": "TB",
+ "Q79744": "PB",
+ "Q79745": "EB",
+ "Q79747": "ZB",
+ "Q7974920": "W s",
+ "Q79752": "YB",
+ "Q79756": "KiB",
+ "Q79758": "MiB",
+ "Q79765": "GiB",
+ "Q79769": "TiB",
+ "Q79774": "PiB",
+ "Q79777": "EiB",
+ "Q79779": "ZiB",
+ "Q79781": "YiB",
+ "Q80237579": "J/nm",
+ "Q809678": "Ba",
+ "Q81062869": "W/nm",
+ "Q81073100": "W/(sr nm)",
+ "Q81292": "acre",
+ "Q81454": "Å",
+ "Q8229770": "B/s",
+ "Q828224": "km",
+ "Q829073": "\"",
+ "Q83216": "cd",
+ "Q83327": "eV",
+ "Q834105": "g/L",
+ "Q835916": "IU",
+ "Q838801": "ns",
+ "Q842015": "μs",
+ "Q842981": "thm (US)",
+ "Q844211": "kg/m³",
+ "Q844338": "hm",
+ "Q844976": "Oe",
+ "Q845958": "¥",
+ "Q848856": "dam",
+ "Q851872": "o",
+ "Q854546": "Gm",
+ "Q855161": "Yibit",
+ "Q856240": "ft³/min",
+ "Q857027": "ft²",
+ "Q85854198": "MN",
+ "Q864818": "abA",
+ "Q87262709": "kΩ",
+ "Q87416053": "MΩ",
+ "Q88296091": "tsp",
+ "Q9026416": "MWth",
+ "Q9048643": "nl",
+ "Q905912": "L",
+ "Q906223": "Es",
+ "Q909066": "at",
+ "Q911730": "nx",
+ "Q914151": "P_P",
+ "Q915169": "F_P",
+ "Q93318": "nmi",
+ "Q940052": "q",
+ "Q94076025": "dalm",
+ "Q94076717": "dakat",
+ "Q942092": "BWI$",
+ "Q94414053": "Prad",
+ "Q94414499": "PC",
+ "Q94415026": "Grad",
+ "Q94415255": "GC",
+ "Q94415438": "Yrad",
+ "Q94415526": "YC",
+ "Q94415782": "Mrad",
+ "Q94416260": "GN",
+ "Q94416535": "cN",
+ "Q94416879": "YN",
+ "Q94417138": "PN",
+ "Q94417481": "μGy",
+ "Q94417583": "μS",
+ "Q94417598": "μT",
+ "Q94417933": "μlm",
+ "Q94418102": "μN",
+ "Q94418220": "μsr",
+ "Q94418481": "μBq",
+ "Q94479580": "GΩ",
+ "Q94480021": "PΩ",
+ "Q94480081": "YΩ",
+ "Q94480128": "cΩ",
+ "Q94480131": "TΩ",
+ "Q94480136": "pΩ",
+ "Q94480254": "nΩ",
+ "Q94480476": "dΩ",
+ "Q94480633": "EΩ",
+ "Q94480967": "daΩ",
+ "Q94481176": "hΩ",
+ "Q94481339": "fΩ",
+ "Q94481646": "yΩ",
+ "Q94487174": "zΩ",
+ "Q94487366": "mΩ",
+ "Q94487561": "μΩ",
+ "Q94487750": "kGy",
+ "Q94488007": "klx",
+ "Q94488361": "MF",
+ "Q94488759": "GBq",
+ "Q94489041": "PBq",
+ "Q94489223": "YBq",
+ "Q94489429": "MBq",
+ "Q94489465": "kBq",
+ "Q94489476": "TBq",
+ "Q94489494": "kWb",
+ "Q94489520": "kS",
+ "Q94490951": "klm",
+ "Q94491129": "kkat",
+ "Q94634634": "cC",
+ "Q94634655": "MC",
+ "Q94634666": "kC",
+ "Q94634677": "TC",
+ "Q94634684": "μC",
+ "Q94634699": "mC",
+ "Q94693759": "csr",
+ "Q94693773": "msr",
+ "Q94693786": "mWb",
+ "Q94693805": "μWb",
+ "Q94693819": "GS",
+ "Q94693849": "cS",
+ "Q94693918": "MS",
+ "Q94694019": "TS",
+ "Q94694096": "pS",
+ "Q94694154": "nS",
+ "Q94694206": "mS",
+ "Q94731530": "mlm",
+ "Q94731808": "mkat",
+ "Q94731887": "μkat",
+ "Q94732218": "nkat",
+ "Q94732627": "pkat",
+ "Q94733432": "fkat",
+ "Q94733760": "cGy",
+ "Q94734107": "dGy",
+ "Q94734232": "mGy",
+ "Q94734359": "daGy",
+ "Q94734468": "aGy",
+ "Q94734527": "pGy",
+ "Q94734593": "nGy",
+ "Q94734689": "kT",
+ "Q94734788": "mT",
+ "Q94939947": "Gkat",
+ "Q94940018": "Pkat",
+ "Q94940081": "ykat",
+ "Q94940160": "dkat",
+ "Q94940232": "Ekat",
+ "Q94940295": "Ykat",
+ "Q94940582": "Tkat",
+ "Q94940892": "hkat",
+ "Q94941461": "zkat",
+ "Q94942602": "MGy",
+ "Q94942863": "GGy",
+ "Q94986863": "YWb",
+ "Q94986889": "PWb",
+ "Q94986906": "cWb",
+ "Q94986920": "GWb",
+ "Q94986942": "MWb",
+ "Q94986962": "TWb",
+ "Q95178536": "Mlm",
+ "Q95178777": "Tlm",
+ "Q95178881": "clm",
+ "Q95179024": "plm",
+ "Q95179137": "nlm",
+ "Q95179382": "hlm",
+ "Q95179467": "flm",
+ "Q95179608": "zlm",
+ "Q95179695": "Mkat",
+ "Q95179788": "ckat",
+ "Q95179882": "PGy",
+ "Q95377836": "PF",
+ "Q95377853": "YF",
+ "Q95378017": "kF",
+ "Q95378296": "TF",
+ "Q95379145": "cF",
+ "Q95379382": "GF",
+ "Q95379491": "daC",
+ "Q95379580": "hC",
+ "Q95379588": "dC",
+ "Q95379596": "EC",
+ "Q95445986": "nC",
+ "Q95446327": "pC",
+ "Q95446670": "fC",
+ "Q95447079": "zC",
+ "Q95447237": "yC",
+ "Q95447253": "fF",
+ "Q95447263": "zF",
+ "Q95447276": "aF",
+ "Q95447555": "dF",
+ "Q95447863": "EF",
+ "Q95448262": "yF",
+ "Q95448479": "hF",
+ "Q95448689": "daF",
+ "Q95448950": "kSv",
+ "Q95559229": "GSv",
+ "Q95559368": "YSv",
+ "Q95559441": "MSv",
+ "Q95559576": "TSv",
+ "Q95559603": "PSv",
+ "Q95609154": "nWb",
+ "Q95609210": "fWb",
+ "Q95609261": "zWb",
+ "Q95609291": "dWb",
+ "Q95609317": "EWb",
+ "Q95676212": "pWb",
+ "Q95676232": "yWb",
+ "Q95676243": "hWb",
+ "Q95676250": "daWb",
+ "Q95676257": "PS",
+ "Q95676260": "YS",
+ "Q95676273": "zS",
+ "Q95676275": "fS",
+ "Q95676279": "yS",
+ "Q95676287": "hS",
+ "Q95676291": "daS",
+ "Q95676297": "dS",
+ "Q95676298": "ES",
+ "Q95720731": "YGy",
+ "Q95720734": "TGy",
+ "Q95720736": "fGy",
+ "Q95720739": "yGy",
+ "Q95720741": "zGy",
+ "Q95720742": "EGy",
+ "Q95720746": "hGy",
+ "Q95720749": "mlx",
+ "Q95720758": "μlx",
+ "Q95720773": "dalx",
+ "Q95720777": "hlx",
+ "Q95720781": "dlx",
+ "Q95720786": "clx",
+ "Q95857671": "zSv",
+ "Q95859071": "fSv",
+ "Q95860960": "daSv",
+ "Q95861107": "hSv",
+ "Q95861296": "dSv",
+ "Q95862182": "ESv",
+ "Q95863358": "cSv",
+ "Q95863591": "ySv",
+ "Q95863894": "pSv",
+ "Q95864194": "zBq",
+ "Q95864378": "fBq",
+ "Q95864695": "daBq",
+ "Q95864940": "hBq",
+ "Q95865286": "dBq",
+ "Q95865530": "EBq",
+ "Q95865716": "cBq",
+ "Q95865877": "yBq",
+ "Q95866173": "pBq",
+ "Q95866344": "nBq",
+ "Q95866767": "mBq",
+ "Q95867993": "mN",
+ "Q95948345": "crad",
+ "Q95948364": "drad",
+ "Q95948734": "daN",
+ "Q95948739": "hN",
+ "Q95948747": "dN",
+ "Q95976839": "Plm",
+ "Q95976853": "Glm",
+ "Q95976869": "Ylm",
+ "Q95976889": "ylm",
+ "Q95976917": "dlm",
+ "Q95976919": "Elm",
+ "Q95976921": "nT",
+ "Q95993516": "TN",
+ "Q95993522": "nN",
+ "Q95993524": "fN",
+ "Q95993526": "yN",
+ "Q95993528": "zN",
+ "Q95993530": "EN",
+ "Q95993532": "pN",
+ "Q95993537": "μrad",
+ "Q95993542": "nrad",
+ "Q95993547": "frad",
+ "Q95993553": "prad",
+ "Q95993554": "darad",
+ "Q95993557": "hrad",
+ "Q95993619": "pT",
+ "Q96025401": "daT",
+ "Q96025405": "Trad",
+ "Q96025407": "Zrad",
+ "Q96025409": "zrad",
+ "Q96025413": "yrad",
+ "Q96025414": "Erad",
+ "Q96025419": "Ylx",
+ "Q96025422": "Glx",
+ "Q96025427": "Plx",
+ "Q96025431": "Mlx",
+ "Q96025433": "Tlx",
+ "Q96025435": "nlx",
+ "Q96025441": "flx",
+ "Q96050953": "GH",
+ "Q96051010": "PH",
+ "Q96051029": "YH",
+ "Q96051052": "cH",
+ "Q96051074": "TH",
+ "Q96051106": "MH",
+ "Q96051123": "kH",
+ "Q96051126": "fH",
+ "Q96051133": "yH",
+ "Q96051139": "hH",
+ "Q96051142": "dH",
+ "Q96051144": "EH",
+ "Q96051150": "pH",
+ "Q96051160": "daH",
+ "Q96051186": "zH",
+ "Q96051199": "aH",
+ "Q96051245": "ylx",
+ "Q96051267": "Elx",
+ "Q96051282": "plx",
+ "Q96051312": "zlx",
+ "Q96070067": "PT",
+ "Q96070074": "YT",
+ "Q96070076": "GT",
+ "Q96070087": "cT",
+ "Q96070103": "MT",
+ "Q96070125": "hT",
+ "Q96070145": "fT",
+ "Q96070174": "TT",
+ "Q96070195": "zT",
+ "Q96070247": "yT",
+ "Q96070254": "dT",
+ "Q96070264": "ET",
+ "Q96070276": "m°C",
+ "Q96070318": "dsr",
+ "Q96070329": "nsr",
+ "Q96070341": "psr",
+ "Q96095866": "fsr",
+ "Q96095897": "zsr",
+ "Q96095917": "ysr",
+ "Q96095927": "dasr",
+ "Q96095928": "hsr",
+ "Q96095931": "ksr",
+ "Q96095933": "Msr",
+ "Q96095939": "Gsr",
+ "Q96095941": "μ°C",
+ "Q96095955": "n°C",
+ "Q96095960": "k°C",
+ "Q96106290": "Tsr",
+ "Q96106298": "Psr",
+ "Q96106311": "Esr",
+ "Q96106319": "Zsr",
+ "Q96106332": "Ysr",
+ "Q96106346": "c°C",
+ "Q96106360": "d°C",
+ "Q96106368": "da°C",
+ "Q96106385": "h°C",
+ "Q96106393": "M°C",
+ "Q96236286": "G°C",
+ "Q97059641": "p°C",
+ "Q97059652": "T°C",
+ "Q97143826": "P°C",
+ "Q97143831": "y°C",
+ "Q97143835": "f°C",
+ "Q97143838": "Z°C",
+ "Q97143842": "E°C",
+ "Q97143843": "z°C",
+ "Q97143849": "Y°C",
+ "Q97143851": "a°C",
+ "Q98538634": "eV/m²",
+ "Q98635536": "eV/m",
+ "Q98642859": "eV m²/kg",
+ "Q11229": "%",
+ "Q11570": "kg",
+ "Q11573": "m",
+ "Q11574": "s",
+ "Q11579": "K",
+ "Q11582": "L",
+ "Q12129": "pc",
+ "Q12438": "N",
+ "Q16068": "DM",
+ "Q1811": "ua",
+ "Q20764": "Myr",
+ "Q2101": "e",
+ "Q25235": "h",
+ "Q25236": "W",
+ "Q25250": "V",
+ "Q25267": "°C",
+ "Q25269": "J",
+ "Q25272": "A",
+ "Q25343": "m²",
+ "Q25406": "C",
+ "Q25517": "m³",
+ "Q33680": "rad",
+ "Q35852": "ha",
+ "Q36384": "equiv",
+ "Q3710": "ft",
+ "Q39274": "Sv",
+ "Q39369": "Hz",
+ "Q41509": "mol",
+ "Q41803": "g",
+ "Q42289": "°F",
+ "Q4406": "TV$",
+ "Q44395": "Pa",
+ "Q4587": "Le",
+ "Q4588": "WS$",
+ "Q4592": "F$",
+ "Q4596": "Rs",
+ "Q4597": "$",
+ "Q47083": "Ω",
+ "Q48013": "oz",
+ "Q50094": "Np",
+ "Q50098": "B",
+ "Q531": "ly",
+ "Q5329": "dB",
+ "Q573": "d",
+ "Q577": "a",
+ "Q7727": "min",
+ "Q8799": "B"
+} \ No newline at end of file
diff --git a/searx/engines/1337x.py b/searx/engines/1337x.py
index 0de04bd..1847887 100644
--- a/searx/engines/1337x.py
+++ b/searx/engines/1337x.py
@@ -1,7 +1,7 @@
+from urllib.parse import quote, urljoin
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.utils import get_torrent_size
-from searx.url_utils import quote, urljoin
+from searx.utils import extract_text, get_torrent_size, eval_xpath, eval_xpath_list, eval_xpath_getindex
+
url = 'https://1337x.to/'
search_url = url + 'search/{search_term}/{pageno}/'
@@ -20,12 +20,12 @@ def response(resp):
dom = html.fromstring(resp.text)
- for result in dom.xpath('//table[contains(@class, "table-list")]/tbody//tr'):
- href = urljoin(url, result.xpath('./td[contains(@class, "name")]/a[2]/@href')[0])
- title = extract_text(result.xpath('./td[contains(@class, "name")]/a[2]'))
- seed = extract_text(result.xpath('.//td[contains(@class, "seeds")]'))
- leech = extract_text(result.xpath('.//td[contains(@class, "leeches")]'))
- filesize_info = extract_text(result.xpath('.//td[contains(@class, "size")]/text()'))
+ for result in eval_xpath_list(dom, '//table[contains(@class, "table-list")]/tbody//tr'):
+ href = urljoin(url, eval_xpath_getindex(result, './td[contains(@class, "name")]/a[2]/@href', 0))
+ title = extract_text(eval_xpath(result, './td[contains(@class, "name")]/a[2]'))
+ seed = extract_text(eval_xpath(result, './/td[contains(@class, "seeds")]'))
+ leech = extract_text(eval_xpath(result, './/td[contains(@class, "leeches")]'))
+ filesize_info = extract_text(eval_xpath(result, './/td[contains(@class, "size")]/text()'))
filesize, filesize_multiplier = filesize_info.split()
filesize = get_torrent_size(filesize, filesize_multiplier)
diff --git a/searx/engines/__init__.py b/searx/engines/__init__.py
index 48c02e2..b2a9b25 100644
--- a/searx/engines/__init__.py
+++ b/searx/engines/__init__.py
@@ -19,14 +19,14 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
import sys
import threading
from os.path import realpath, dirname
-from io import open
from babel.localedata import locale_identifiers
+from urllib.parse import urlparse
from flask_babel import gettext
from operator import itemgetter
-from json import loads
-from requests import get
from searx import settings
from searx import logger
+from searx.data import ENGINES_LANGUAGES
+from searx.poolrequests import get, get_proxy_cycles
from searx.utils import load_module, match_language, get_engine_from_settings
@@ -38,7 +38,6 @@ engines = {}
categories = {'general': []}
-languages = loads(open(engine_dir + '/../data/engines_languages.json', 'r', encoding='utf-8').read())
babel_langs = [lang_parts[0] + '-' + lang_parts[-1] if len(lang_parts) > 1 else lang_parts[0]
for lang_parts in (lang_code.split('_') for lang_code in locale_identifiers())]
@@ -74,20 +73,25 @@ def load_engine(engine_data):
try:
engine = load_module(engine_module + '.py', engine_dir)
+ except (SyntaxError, KeyboardInterrupt, SystemExit, SystemError, ImportError, RuntimeError):
+ logger.exception('Fatal exception in engine "{}"'.format(engine_module))
+ sys.exit(1)
except:
logger.exception('Cannot load engine "{}"'.format(engine_module))
return None
- for param_name in engine_data:
+ for param_name, param_value in engine_data.items():
if param_name == 'engine':
- continue
- if param_name == 'categories':
- if engine_data['categories'] == 'none':
+ pass
+ elif param_name == 'categories':
+ if param_value == 'none':
engine.categories = []
else:
- engine.categories = list(map(str.strip, engine_data['categories'].split(',')))
- continue
- setattr(engine, param_name, engine_data[param_name])
+ engine.categories = list(map(str.strip, param_value.split(',')))
+ elif param_name == 'proxies':
+ engine.proxies = get_proxy_cycles(param_value)
+ else:
+ setattr(engine, param_name, param_value)
for arg_name, arg_value in engine_default_args.items():
if not hasattr(engine, arg_name):
@@ -105,8 +109,8 @@ def load_engine(engine_data):
sys.exit(1)
# assign supported languages from json file
- if engine_data['name'] in languages:
- setattr(engine, 'supported_languages', languages[engine_data['name']])
+ if engine_data['name'] in ENGINES_LANGUAGES:
+ setattr(engine, 'supported_languages', ENGINES_LANGUAGES[engine_data['name']])
# find custom aliases for non standard language codes
if hasattr(engine, 'supported_languages'):
@@ -129,8 +133,9 @@ def load_engine(engine_data):
lambda: engine._fetch_supported_languages(get(engine.supported_languages_url)))
engine.stats = {
+ 'sent_search_count': 0, # sent search
+ 'search_count': 0, # succesful search
'result_count': 0,
- 'search_count': 0,
'engine_time': 0,
'engine_time_count': 0,
'score_count': 0,
@@ -141,6 +146,17 @@ def load_engine(engine_data):
engine.stats['page_load_time'] = 0
engine.stats['page_load_count'] = 0
+ # tor related settings
+ if settings['outgoing'].get('using_tor_proxy'):
+ # use onion url if using tor.
+ if hasattr(engine, 'onion_url'):
+ engine.search_url = engine.onion_url + getattr(engine, 'search_path', '')
+ elif 'onions' in engine.categories:
+ # exclude onion engines if not using tor.
+ return None
+
+ engine.timeout += settings['outgoing'].get('extra_proxy_timeout', 0)
+
for category_name in engine.categories:
categories.setdefault(category_name, []).append(engine)
@@ -220,7 +236,7 @@ def get_engines_stats(preferences):
results = to_percentage(results, max_results)
scores = to_percentage(scores, max_score)
scores_per_result = to_percentage(scores_per_result, max_score_per_result)
- erros = to_percentage(errors, max_errors)
+ errors = to_percentage(errors, max_errors)
return [
(
@@ -251,8 +267,9 @@ def get_engines_stats(preferences):
def load_engines(engine_list):
- global engines
+ global engines, engine_shortcuts
engines.clear()
+ engine_shortcuts.clear()
for engine_data in engine_list:
engine = load_engine(engine_data)
if engine is not None:
@@ -264,8 +281,12 @@ def initialize_engines(engine_list):
load_engines(engine_list)
def engine_init(engine_name, init_fn):
- init_fn(get_engine_from_settings(engine_name))
- logger.debug('%s engine: Initialized', engine_name)
+ try:
+ init_fn(get_engine_from_settings(engine_name))
+ except Exception:
+ logger.exception('%s engine: Fail to initialize', engine_name)
+ else:
+ logger.debug('%s engine: Initialized', engine_name)
for engine_name, engine in engines.items():
if hasattr(engine, 'init'):
@@ -273,3 +294,34 @@ def initialize_engines(engine_list):
if init_fn:
logger.debug('%s engine: Starting background initialization', engine_name)
threading.Thread(target=engine_init, args=(engine_name, init_fn)).start()
+
+ _set_https_support_for_engine(engine)
+
+
+def _set_https_support_for_engine(engine):
+ # check HTTPS support if it is not disabled
+ if not engine.offline and not hasattr(engine, 'https_support'):
+ params = engine.request('http_test', {
+ 'method': 'GET',
+ 'headers': {},
+ 'data': {},
+ 'url': '',
+ 'cookies': {},
+ 'verify': True,
+ 'auth': None,
+ 'pageno': 1,
+ 'time_range': None,
+ 'language': '',
+ 'safesearch': False,
+ 'is_test': True,
+ 'category': 'files',
+ 'raise_for_status': True,
+ })
+
+ if 'url' not in params:
+ return
+
+ parsed_url = urlparse(params['url'])
+ https_support = parsed_url.scheme == 'https'
+
+ setattr(engine, 'https_support', https_support)
diff --git a/searx/engines/acgsou.py b/searx/engines/acgsou.py
index cca28f0..637443e 100644
--- a/searx/engines/acgsou.py
+++ b/searx/engines/acgsou.py
@@ -9,17 +9,16 @@
@parse url, title, content, seed, leech, torrentfile
"""
+from urllib.parse import urlencode
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
-from searx.utils import get_torrent_size, int_or_zero
+from searx.utils import extract_text, get_torrent_size, eval_xpath_list, eval_xpath_getindex
# engine dependent config
categories = ['files', 'images', 'videos', 'music']
paging = True
# search-url
-base_url = 'http://www.acgsou.com/'
+base_url = 'https://www.acgsou.com/'
search_url = base_url + 'search.php?{query}&page={offset}'
# xpath queries
xpath_results = '//table[contains(@class, "list_style table_fixed")]//tr[not(th)]'
@@ -38,32 +37,28 @@ def request(query, params):
def response(resp):
results = []
dom = html.fromstring(resp.text)
- for result in dom.xpath(xpath_results):
+ for result in eval_xpath_list(dom, xpath_results):
# defaults
filesize = 0
- magnet_link = "magnet:?xt=urn:btih:{}&tr=http://tracker.acgsou.com:2710/announce"
- torrent_link = ""
+ magnet_link = "magnet:?xt=urn:btih:{}&tr=https://tracker.acgsou.com:2710/announce"
- try:
- category = extract_text(result.xpath(xpath_category)[0])
- except:
- pass
-
- page_a = result.xpath(xpath_title)[0]
+ category = extract_text(eval_xpath_getindex(result, xpath_category, 0, default=[]))
+ page_a = eval_xpath_getindex(result, xpath_title, 0)
title = extract_text(page_a)
href = base_url + page_a.attrib.get('href')
magnet_link = magnet_link.format(page_a.attrib.get('href')[5:-5])
- try:
- filesize_info = result.xpath(xpath_filesize)[0]
- filesize = filesize_info[:-2]
- filesize_multiplier = filesize_info[-2:]
- filesize = get_torrent_size(filesize, filesize_multiplier)
- except:
- pass
+ filesize_info = eval_xpath_getindex(result, xpath_filesize, 0, default=None)
+ if filesize_info:
+ try:
+ filesize = filesize_info[:-2]
+ filesize_multiplier = filesize_info[-2:]
+ filesize = get_torrent_size(filesize, filesize_multiplier)
+ except:
+ pass
# I didn't add download/seed/leech count since as I figured out they are generated randomly everytime
- content = u'Category: "{category}".'
+ content = 'Category: "{category}".'
content = content.format(category=category)
results.append({'url': href,
diff --git a/searx/engines/ahmia.py b/searx/engines/ahmia.py
new file mode 100644
index 0000000..7a2ae00
--- /dev/null
+++ b/searx/engines/ahmia.py
@@ -0,0 +1,82 @@
+"""
+ Ahmia (Onions)
+
+ @website http://msydqstlz2kzerdg.onion
+ @provides-api no
+
+ @using-api no
+ @results HTML
+ @stable no
+ @parse url, title, content
+"""
+
+from urllib.parse import urlencode, urlparse, parse_qs
+from lxml.html import fromstring
+from searx.engines.xpath import extract_url, extract_text, eval_xpath_list, eval_xpath
+
+# engine config
+categories = ['onions']
+paging = True
+page_size = 10
+
+# search url
+search_url = 'http://msydqstlz2kzerdg.onion/search/?{query}'
+time_range_support = True
+time_range_dict = {'day': 1,
+ 'week': 7,
+ 'month': 30}
+
+# xpaths
+results_xpath = '//li[@class="result"]'
+url_xpath = './h4/a/@href'
+title_xpath = './h4/a[1]'
+content_xpath = './/p[1]'
+correction_xpath = '//*[@id="didYouMean"]//a'
+number_of_results_xpath = '//*[@id="totalResults"]'
+
+
+def request(query, params):
+ params['url'] = search_url.format(query=urlencode({'q': query}))
+
+ if params['time_range'] in time_range_dict:
+ params['url'] += '&' + urlencode({'d': time_range_dict[params['time_range']]})
+
+ return params
+
+
+def response(resp):
+ results = []
+ dom = fromstring(resp.text)
+
+ # trim results so there's not way too many at once
+ first_result_index = page_size * (resp.search_params.get('pageno', 1) - 1)
+ all_results = eval_xpath_list(dom, results_xpath)
+ trimmed_results = all_results[first_result_index:first_result_index + page_size]
+
+ # get results
+ for result in trimmed_results:
+ # remove ahmia url and extract the actual url for the result
+ raw_url = extract_url(eval_xpath_list(result, url_xpath, min_len=1), search_url)
+ cleaned_url = parse_qs(urlparse(raw_url).query).get('redirect_url', [''])[0]
+
+ title = extract_text(eval_xpath(result, title_xpath))
+ content = extract_text(eval_xpath(result, content_xpath))
+
+ results.append({'url': cleaned_url,
+ 'title': title,
+ 'content': content,
+ 'is_onion': True})
+
+ # get spelling corrections
+ for correction in eval_xpath_list(dom, correction_xpath):
+ results.append({'correction': extract_text(correction)})
+
+ # get number of results
+ number_of_results = eval_xpath(dom, number_of_results_xpath)
+ if number_of_results:
+ try:
+ results.append({'number_of_results': int(extract_text(number_of_results))})
+ except:
+ pass
+
+ return results
diff --git a/searx/engines/apkmirror.py b/searx/engines/apkmirror.py
index f2ee12b..3a948dc 100644
--- a/searx/engines/apkmirror.py
+++ b/searx/engines/apkmirror.py
@@ -9,9 +9,10 @@
@parse url, title, thumbnail_src
"""
+from urllib.parse import urlencode
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
+from searx.utils import extract_text, eval_xpath_list, eval_xpath_getindex
+
# engine dependent config
categories = ['it']
@@ -41,12 +42,13 @@ def response(resp):
dom = html.fromstring(resp.text)
# parse results
- for result in dom.xpath('.//div[@id="content"]/div[@class="listWidget"]/div[@class="appRow"]'):
+ for result in eval_xpath_list(dom, './/div[@id="content"]/div[@class="listWidget"]/div[@class="appRow"]'):
- link = result.xpath('.//h5/a')[0]
+ link = eval_xpath_getindex(result, './/h5/a', 0)
url = base_url + link.attrib.get('href') + '#downloads'
title = extract_text(link)
- thumbnail_src = base_url + result.xpath('.//img')[0].attrib.get('src').replace('&w=32&h=32', '&w=64&h=64')
+ thumbnail_src = base_url\
+ + eval_xpath_getindex(result, './/img', 0).attrib.get('src').replace('&w=32&h=32', '&w=64&h=64')
res = {
'url': url,
diff --git a/searx/engines/archlinux.py b/searx/engines/archlinux.py
index dce862f..04117c0 100644
--- a/searx/engines/archlinux.py
+++ b/searx/engines/archlinux.py
@@ -11,9 +11,9 @@
@parse url, title
"""
+from urllib.parse import urlencode, urljoin
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode, urljoin
+from searx.utils import extract_text, eval_xpath_list, eval_xpath_getindex
# engine dependent config
categories = ['it']
@@ -105,7 +105,7 @@ def request(query, params):
# if our language is hosted on the main site, we need to add its name
# to the query in order to narrow the results to that language
if language in main_langs:
- query += b' (' + main_langs[language] + b')'
+ query += ' (' + main_langs[language] + ')'
# prepare the request parameters
query = urlencode({'search': query})
@@ -131,8 +131,8 @@ def response(resp):
dom = html.fromstring(resp.text)
# parse results
- for result in dom.xpath(xpath_results):
- link = result.xpath(xpath_link)[0]
+ for result in eval_xpath_list(dom, xpath_results):
+ link = eval_xpath_getindex(result, xpath_link, 0)
href = urljoin(base_url, link.attrib.get('href'))
title = extract_text(link)
diff --git a/searx/engines/arxiv.py b/searx/engines/arxiv.py
index e3c871d..1190de3 100644
--- a/searx/engines/arxiv.py
+++ b/searx/engines/arxiv.py
@@ -13,13 +13,13 @@
from lxml import html
from datetime import datetime
-from searx.url_utils import urlencode
+from searx.utils import eval_xpath_list, eval_xpath_getindex
categories = ['science']
paging = True
-base_url = 'http://export.arxiv.org/api/query?search_query=all:'\
+base_url = 'https://export.arxiv.org/api/query?search_query=all:'\
+ '{query}&start={offset}&max_results={number_of_results}'
# engine dependent config
@@ -30,7 +30,7 @@ def request(query, params):
# basic search
offset = (params['pageno'] - 1) * number_of_results
- string_args = dict(query=query.decode('utf-8'),
+ string_args = dict(query=query,
offset=offset,
number_of_results=number_of_results)
@@ -43,29 +43,26 @@ def response(resp):
results = []
dom = html.fromstring(resp.content)
- search_results = dom.xpath('//entry')
- for entry in search_results:
- title = entry.xpath('.//title')[0].text
+ for entry in eval_xpath_list(dom, '//entry'):
+ title = eval_xpath_getindex(entry, './/title', 0).text
- url = entry.xpath('.//id')[0].text
+ url = eval_xpath_getindex(entry, './/id', 0).text
content_string = '{doi_content}{abstract_content}'
- abstract = entry.xpath('.//summary')[0].text
+ abstract = eval_xpath_getindex(entry, './/summary', 0).text
# If a doi is available, add it to the snipppet
- try:
- doi_content = entry.xpath('.//link[@title="doi"]')[0].text
- content = content_string.format(doi_content=doi_content, abstract_content=abstract)
- except:
- content = content_string.format(doi_content="", abstract_content=abstract)
+ doi_element = eval_xpath_getindex(entry, './/link[@title="doi"]', 0, default=None)
+ doi_content = doi_element.text if doi_element is not None else ''
+ content = content_string.format(doi_content=doi_content, abstract_content=abstract)
if len(content) > 300:
- content = content[0:300] + "..."
+ content = content[0:300] + "..."
# TODO: center snippet on query term
- publishedDate = datetime.strptime(entry.xpath('.//published')[0].text, '%Y-%m-%dT%H:%M:%SZ')
+ publishedDate = datetime.strptime(eval_xpath_getindex(entry, './/published', 0).text, '%Y-%m-%dT%H:%M:%SZ')
res_dict = {'url': url,
'title': title,
diff --git a/searx/engines/base.py b/searx/engines/base.py
index f1b1cf6..3648d7e 100755
--- a/searx/engines/base.py
+++ b/searx/engines/base.py
@@ -13,10 +13,10 @@
More info on api: http://base-search.net/about/download/base_interface.pdf
"""
+from urllib.parse import urlencode
from lxml import etree
from datetime import datetime
import re
-from searx.url_utils import urlencode
from searx.utils import searx_useragent
@@ -55,7 +55,7 @@ shorcut_dict = {
def request(query, params):
# replace shortcuts with API advanced search keywords
for key in shorcut_dict.keys():
- query = re.sub(key, shorcut_dict[key], str(query))
+ query = re.sub(key, shorcut_dict[key], query)
# basic search
offset = (params['pageno'] - 1) * number_of_results
@@ -80,10 +80,7 @@ def response(resp):
date = datetime.now() # needed in case no dcdate is available for an item
for item in entry:
- if item.attrib["name"] == "dchdate":
- harvestDate = item.text
-
- elif item.attrib["name"] == "dcdate":
+ if item.attrib["name"] == "dcdate":
date = item.text
elif item.attrib["name"] == "dctitle":
diff --git a/searx/engines/bing.py b/searx/engines/bing.py
index afb776a..f0882fc 100644
--- a/searx/engines/bing.py
+++ b/searx/engines/bing.py
@@ -14,11 +14,10 @@
"""
import re
+from urllib.parse import urlencode
from lxml import html
-from searx import logger, utils
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
-from searx.utils import match_language, gen_useragent, eval_xpath
+from searx import logger
+from searx.utils import eval_xpath, extract_text, match_language
logger = logger.getChild('bing engine')
@@ -47,7 +46,7 @@ def request(query, params):
else:
lang = match_language(params['language'], supported_languages, language_aliases)
- query = u'language:{} {}'.format(lang.split('-')[0].upper(), query.decode('utf-8')).encode('utf-8')
+ query = 'language:{} {}'.format(lang.split('-')[0].upper(), query)
search_path = search_string.format(
query=urlencode({'q': query}),
@@ -99,7 +98,6 @@ def response(resp):
result_len = int(result_len_container)
except Exception as e:
logger.debug('result error :\n%s', e)
- pass
if result_len and _get_offset_from_pageno(resp.search_params.get("pageno", 0)) > result_len:
return []
diff --git a/searx/engines/bing_images.py b/searx/engines/bing_images.py
index 138ed11..2bcf82b 100644
--- a/searx/engines/bing_images.py
+++ b/searx/engines/bing_images.py
@@ -12,13 +12,13 @@
"""
+from urllib.parse import urlencode
from lxml import html
from json import loads
-import re
-from searx.url_utils import urlencode
from searx.utils import match_language
-from searx.engines.bing import _fetch_supported_languages, supported_languages_url, language_aliases
+from searx.engines.bing import language_aliases
+from searx.engines.bing import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
# engine dependent config
categories = ['images']
@@ -80,19 +80,18 @@ def response(resp):
# parse results
for result in dom.xpath('//div[@class="imgpt"]'):
-
- img_format = result.xpath('./div[contains(@class, "img_info")]/span/text()')[0]
- # Microsoft seems to experiment with this code so don't make the path too specific,
- # just catch the text section for the first anchor in img_info assuming this to be
- # the originating site.
- source = result.xpath('./div[contains(@class, "img_info")]//a/text()')[0]
-
try:
+ img_format = result.xpath('./div[contains(@class, "img_info")]/span/text()')[0]
+ # Microsoft seems to experiment with this code so don't make the path too specific,
+ # just catch the text section for the first anchor in img_info assuming this to be
+ # the originating site.
+ source = result.xpath('./div[contains(@class, "img_info")]//a/text()')[0]
+
m = loads(result.xpath('./a/@m')[0])
# strip 'Unicode private use area' highlighting, they render to Tux
# the Linux penguin and a standing diamond on my machine...
- title = m.get('t', '').replace(u'\ue000', '').replace(u'\ue001', '')
+ title = m.get('t', '').replace('\ue000', '').replace('\ue001', '')
results.append({'template': 'images.html',
'url': m['purl'],
'thumbnail_src': m['turl'],
diff --git a/searx/engines/bing_news.py b/searx/engines/bing_news.py
index d13be77..b95def4 100644
--- a/searx/engines/bing_news.py
+++ b/searx/engines/bing_news.py
@@ -13,11 +13,12 @@
from datetime import datetime
from dateutil import parser
+from urllib.parse import urlencode, urlparse, parse_qsl
from lxml import etree
-from searx.utils import list_get, match_language
-from searx.url_utils import urlencode, urlparse, parse_qsl
-
-from searx.engines.bing import _fetch_supported_languages, supported_languages_url, language_aliases
+from lxml.etree import XPath
+from searx.utils import match_language, eval_xpath_getindex
+from searx.engines.bing import language_aliases
+from searx.engines.bing import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
# engine dependent config
categories = ['news']
@@ -94,12 +95,12 @@ def response(resp):
# parse results
for item in rss.xpath('./channel/item'):
# url / title / content
- url = url_cleanup(item.xpath('./link/text()')[0])
- title = list_get(item.xpath('./title/text()'), 0, url)
- content = list_get(item.xpath('./description/text()'), 0, '')
+ url = url_cleanup(eval_xpath_getindex(item, './link/text()', 0, default=None))
+ title = eval_xpath_getindex(item, './title/text()', 0, default=url)
+ content = eval_xpath_getindex(item, './description/text()', 0, default='')
# publishedDate
- publishedDate = list_get(item.xpath('./pubDate/text()'), 0)
+ publishedDate = eval_xpath_getindex(item, './pubDate/text()', 0, default=None)
try:
publishedDate = parser.parse(publishedDate, dayfirst=False)
except TypeError:
@@ -108,7 +109,7 @@ def response(resp):
publishedDate = datetime.now()
# thumbnail
- thumbnail = list_get(item.xpath('./News:Image/text()', namespaces=ns), 0)
+ thumbnail = eval_xpath_getindex(item, XPath('./News:Image/text()', namespaces=ns), 0, default=None)
if thumbnail is not None:
thumbnail = image_url_cleanup(thumbnail)
diff --git a/searx/engines/bing_videos.py b/searx/engines/bing_videos.py
index f048f0d..143c71a 100644
--- a/searx/engines/bing_videos.py
+++ b/searx/engines/bing_videos.py
@@ -12,10 +12,11 @@
from json import loads
from lxml import html
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
from searx.utils import match_language
-from searx.engines.bing import _fetch_supported_languages, supported_languages_url, language_aliases
+from searx.engines.bing import language_aliases
+from searx.engines.bing import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
categories = ['videos']
paging = True
diff --git a/searx/engines/btdigg.py b/searx/engines/btdigg.py
index 82eedc2..72bda8d 100644
--- a/searx/engines/btdigg.py
+++ b/searx/engines/btdigg.py
@@ -11,10 +11,8 @@
"""
from lxml import html
-from operator import itemgetter
-from searx.engines.xpath import extract_text
-from searx.url_utils import quote, urljoin
-from searx.utils import get_torrent_size
+from urllib.parse import quote, urljoin
+from searx.utils import extract_text, get_torrent_size
# engine dependent config
categories = ['videos', 'music', 'files']
diff --git a/searx/engines/command.py b/searx/engines/command.py
new file mode 100644
index 0000000..0268d52
--- /dev/null
+++ b/searx/engines/command.py
@@ -0,0 +1,183 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+'''
+
+
+import re
+from os.path import expanduser, isabs, realpath, commonprefix
+from shlex import split as shlex_split
+from subprocess import Popen, PIPE
+from threading import Thread
+
+from searx import logger
+
+
+offline = True
+paging = True
+command = []
+delimiter = {}
+parse_regex = {}
+query_type = ''
+query_enum = []
+environment_variables = {}
+working_dir = realpath('.')
+result_separator = '\n'
+result_template = 'key-value.html'
+timeout = 4.0
+
+_command_logger = logger.getChild('command')
+_compiled_parse_regex = {}
+
+
+def init(engine_settings):
+ check_parsing_options(engine_settings)
+
+ if 'command' not in engine_settings:
+ raise ValueError('engine command : missing configuration key: command')
+
+ global command, working_dir, result_template, delimiter, parse_regex, timeout, environment_variables
+
+ command = engine_settings['command']
+
+ if 'working_dir' in engine_settings:
+ working_dir = engine_settings['working_dir']
+ if not isabs(engine_settings['working_dir']):
+ working_dir = realpath(working_dir)
+
+ if 'parse_regex' in engine_settings:
+ parse_regex = engine_settings['parse_regex']
+ for result_key, regex in parse_regex.items():
+ _compiled_parse_regex[result_key] = re.compile(regex, flags=re.MULTILINE)
+ if 'delimiter' in engine_settings:
+ delimiter = engine_settings['delimiter']
+
+ if 'environment_variables' in engine_settings:
+ environment_variables = engine_settings['environment_variables']
+
+
+def search(query, params):
+ cmd = _get_command_to_run(query)
+ if not cmd:
+ return []
+
+ results = []
+ reader_thread = Thread(target=_get_results_from_process, args=(results, cmd, params['pageno']))
+ reader_thread.start()
+ reader_thread.join(timeout=timeout)
+
+ return results
+
+
+def _get_command_to_run(query):
+ params = shlex_split(query)
+ __check_query_params(params)
+
+ cmd = []
+ for c in command:
+ if c == '{{QUERY}}':
+ cmd.extend(params)
+ else:
+ cmd.append(c)
+
+ return cmd
+
+
+def _get_results_from_process(results, cmd, pageno):
+ leftover = ''
+ count = 0
+ start, end = __get_results_limits(pageno)
+ with Popen(cmd, stdout=PIPE, stderr=PIPE, env=environment_variables) as process:
+ line = process.stdout.readline()
+ while line:
+ buf = leftover + line.decode('utf-8')
+ raw_results = buf.split(result_separator)
+ if raw_results[-1]:
+ leftover = raw_results[-1]
+ raw_results = raw_results[:-1]
+
+ for raw_result in raw_results:
+ result = __parse_single_result(raw_result)
+ if result is None:
+ _command_logger.debug('skipped result:', raw_result)
+ continue
+
+ if start <= count and count <= end:
+ result['template'] = result_template
+ results.append(result)
+
+ count += 1
+ if end < count:
+ return results
+
+ line = process.stdout.readline()
+
+ return_code = process.wait(timeout=timeout)
+ if return_code != 0:
+ raise RuntimeError('non-zero return code when running command', cmd, return_code)
+
+
+def __get_results_limits(pageno):
+ start = (pageno - 1) * 10
+ end = start + 9
+ return start, end
+
+
+def __check_query_params(params):
+ if not query_type:
+ return
+
+ if query_type == 'path':
+ query_path = params[-1]
+ query_path = expanduser(query_path)
+ if commonprefix([realpath(query_path), working_dir]) != working_dir:
+ raise ValueError('requested path is outside of configured working directory')
+ elif query_type == 'enum' and len(query_enum) > 0:
+ for param in params:
+ if param not in query_enum:
+ raise ValueError('submitted query params is not allowed', param, 'allowed params:', query_enum)
+
+
+def check_parsing_options(engine_settings):
+ """ Checks if delimiter based parsing or regex parsing is configured correctly """
+
+ if 'delimiter' not in engine_settings and 'parse_regex' not in engine_settings:
+ raise ValueError('failed to init settings for parsing lines: missing delimiter or parse_regex')
+ if 'delimiter' in engine_settings and 'parse_regex' in engine_settings:
+ raise ValueError('failed to init settings for parsing lines: too many settings')
+
+ if 'delimiter' in engine_settings:
+ if 'chars' not in engine_settings['delimiter'] or 'keys' not in engine_settings['delimiter']:
+ raise ValueError
+
+
+def __parse_single_result(raw_result):
+ """ Parses command line output based on configuration """
+
+ result = {}
+
+ if delimiter:
+ elements = raw_result.split(delimiter['chars'], maxsplit=len(delimiter['keys']) - 1)
+ if len(elements) != len(delimiter['keys']):
+ return {}
+ for i in range(len(elements)):
+ result[delimiter['keys'][i]] = elements[i]
+
+ if parse_regex:
+ for result_key, regex in _compiled_parse_regex.items():
+ found = regex.search(raw_result)
+ if not found:
+ return {}
+ result[result_key] = raw_result[found.start():found.end()]
+
+ return result
diff --git a/searx/engines/currency_convert.py b/searx/engines/currency_convert.py
index 8eab8f6..87e21d0 100644
--- a/searx/engines/currency_convert.py
+++ b/searx/engines/currency_convert.py
@@ -1,42 +1,35 @@
import json
import re
-import os
-import sys
import unicodedata
+from searx.data import CURRENCIES # NOQA
-from io import open
-from datetime import datetime
-
-if sys.version_info[0] == 3:
- unicode = str
categories = []
url = 'https://duckduckgo.com/js/spice/currency/1/{0}/{1}'
weight = 100
-parser_re = re.compile(b'.*?(\\d+(?:\\.\\d+)?) ([^.0-9]+) (?:in|to) ([^.0-9]+)', re.I)
-
-db = 1
+parser_re = re.compile('.*?(\\d+(?:\\.\\d+)?) ([^.0-9]+) (?:in|to) ([^.0-9]+)', re.I)
+https_support = True
def normalize_name(name):
- name = name.decode('utf-8').lower().replace('-', ' ').rstrip('s')
+ name = name.lower().replace('-', ' ').rstrip('s')
name = re.sub(' +', ' ', name)
return unicodedata.normalize('NFKD', name).lower()
def name_to_iso4217(name):
- global db
+ global CURRENCIES
name = normalize_name(name)
- currencies = db['names'].get(name, [name])
- return currencies[0]
+ currency = CURRENCIES['names'].get(name, [name])
+ return currency[0]
def iso4217_to_name(iso4217, language):
- global db
+ global CURRENCIES
- return db['iso4217'].get(iso4217, {}).get(language, iso4217)
+ return CURRENCIES['iso4217'].get(iso4217, {}).get(language, iso4217)
def request(query, params):
@@ -49,8 +42,6 @@ def request(query, params):
from_currency = name_to_iso4217(from_currency.strip())
to_currency = name_to_iso4217(to_currency.strip())
- q = (from_currency + to_currency).upper()
-
params['url'] = url.format(from_currency, to_currency)
params['amount'] = amount
params['from'] = from_currency
@@ -85,15 +76,3 @@ def response(resp):
results.append({'answer': answer, 'url': url})
return results
-
-
-def load():
- global db
-
- current_dir = os.path.dirname(os.path.realpath(__file__))
- json_data = open(current_dir + "/../data/currencies.json", 'r', encoding='utf-8').read()
-
- db = json.loads(json_data)
-
-
-load()
diff --git a/searx/engines/dailymotion.py b/searx/engines/dailymotion.py
index 1038e64..1e24e41 100644
--- a/searx/engines/dailymotion.py
+++ b/searx/engines/dailymotion.py
@@ -14,7 +14,7 @@
from json import loads
from datetime import datetime
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
from searx.utils import match_language, html_to_text
# engine dependent config
diff --git a/searx/engines/deezer.py b/searx/engines/deezer.py
index af63478..48c0429 100644
--- a/searx/engines/deezer.py
+++ b/searx/engines/deezer.py
@@ -11,7 +11,7 @@
"""
from json import loads
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
# engine dependent config
categories = ['music']
@@ -50,7 +50,7 @@ def response(resp):
if url.startswith('http://'):
url = 'https' + url[4:]
- content = u'{} - {} - {}'.format(
+ content = '{} - {} - {}'.format(
result['artist']['name'],
result['album']['title'],
result['title'])
diff --git a/searx/engines/deviantart.py b/searx/engines/deviantart.py
index a0e27e6..0378929 100644
--- a/searx/engines/deviantart.py
+++ b/searx/engines/deviantart.py
@@ -7,75 +7,70 @@
@using-api no (TODO, rewrite to api)
@results HTML
@stable no (HTML can change)
- @parse url, title, thumbnail_src, img_src
+ @parse url, title, img_src
@todo rewrite to api
"""
+# pylint: disable=missing-function-docstring
+from urllib.parse import urlencode
from lxml import html
-import re
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
# engine dependent config
categories = ['images']
paging = True
time_range_support = True
-# search-url
-base_url = 'https://www.deviantart.com/'
-search_url = base_url + 'search?page={page}&{query}'
-time_range_url = '&order={range}'
-
-time_range_dict = {'day': 11,
- 'week': 14,
- 'month': 15}
+time_range_dict = {
+ 'day': 'popular-24-hours',
+ 'week': 'popular-1-week',
+ 'month': 'popular-1-month',
+ 'year': 'most-recent',
+}
+# search-url
+base_url = 'https://www.deviantart.com'
-# do search-request
def request(query, params):
- if params['time_range'] and params['time_range'] not in time_range_dict:
- return params
- params['url'] = search_url.format(page=params['pageno'],
- query=urlencode({'q': query}))
+ # https://www.deviantart.com/search/deviations?page=5&q=foo
+
+ query = {
+ 'page' : params['pageno'],
+ 'q' : query,
+ }
if params['time_range'] in time_range_dict:
- params['url'] += time_range_url.format(range=time_range_dict[params['time_range']])
+ query['order'] = time_range_dict[params['time_range']]
- return params
+ params['url'] = base_url + '/search/deviations?' + urlencode(query)
+ return params
-# get response from search-request
def response(resp):
- results = []
- # return empty array if a redirection code is returned
- if resp.status_code == 302:
- return []
+ results = []
dom = html.fromstring(resp.text)
- # parse results
for row in dom.xpath('//div[contains(@data-hook, "content_row")]'):
for result in row.xpath('./div'):
- link = result.xpath('.//a[@data-hook="deviation_link"]')[0]
- url = link.attrib.get('href')
- title = link.attrib.get('title')
- thumbnail_src = result.xpath('.//img')[0].attrib.get('src')
- img_src = thumbnail_src
-
- # http to https, remove domain sharding
- thumbnail_src = re.sub(r"https?://(th|fc)\d+.", "https://th01.", thumbnail_src)
- thumbnail_src = re.sub(r"http://", "https://", thumbnail_src)
-
- url = re.sub(r"http://(.*)\.deviantart\.com/", "https://\\1.deviantart.com/", url)
-
- # append result
- results.append({'url': url,
- 'title': title,
- 'img_src': img_src,
- 'thumbnail_src': thumbnail_src,
- 'template': 'images.html'})
-
- # return results
+
+ a_tag = result.xpath('.//a[@data-hook="deviation_link"]')[0]
+ noscript_tag = a_tag.xpath('.//noscript')
+
+ if noscript_tag:
+ img_tag = noscript_tag[0].xpath('.//img')
+ else:
+ img_tag = a_tag.xpath('.//img')
+ if not img_tag:
+ continue
+ img_tag = img_tag[0]
+
+ results.append({
+ 'template': 'images.html',
+ 'url': a_tag.attrib.get('href'),
+ 'img_src': img_tag.attrib.get('src'),
+ 'title': img_tag.attrib.get('alt'),
+ })
+
return results
diff --git a/searx/engines/dictzone.py b/searx/engines/dictzone.py
index 423af09..727eb65 100644
--- a/searx/engines/dictzone.py
+++ b/searx/engines/dictzone.py
@@ -10,16 +10,17 @@
"""
import re
+from urllib.parse import urljoin
from lxml import html
from searx.utils import is_valid_lang, eval_xpath
-from searx.url_utils import urljoin
categories = ['general']
-url = u'https://dictzone.com/{from_lang}-{to_lang}-dictionary/{query}'
+url = 'https://dictzone.com/{from_lang}-{to_lang}-dictionary/{query}'
weight = 100
-parser_re = re.compile(b'.*?([a-z]+)-([a-z]+) ([^ ]+)$', re.I)
+parser_re = re.compile('.*?([a-z]+)-([a-z]+) ([^ ]+)$', re.I)
results_xpath = './/table[@id="r"]/tr'
+https_support = True
def request(query, params):
@@ -37,7 +38,7 @@ def request(query, params):
params['url'] = url.format(from_lang=from_lang[2],
to_lang=to_lang[2],
- query=query.decode('utf-8'))
+ query=query)
return params
diff --git a/searx/engines/digbt.py b/searx/engines/digbt.py
index ff2f945..b1a90fb 100644
--- a/searx/engines/digbt.py
+++ b/searx/engines/digbt.py
@@ -10,14 +10,10 @@
@parse url, title, content, magnetlink
"""
-from sys import version_info
+from urllib.parse import urljoin
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.utils import get_torrent_size
-from searx.url_utils import urljoin
+from searx.utils import extract_text, get_torrent_size
-if version_info[0] == 3:
- unicode = str
categories = ['videos', 'music', 'files']
paging = True
diff --git a/searx/engines/digg.py b/searx/engines/digg.py
index 073410e..85f727f 100644
--- a/searx/engines/digg.py
+++ b/searx/engines/digg.py
@@ -1,7 +1,7 @@
"""
Digg (News, Social media)
- @website https://digg.com/
+ @website https://digg.com
@provide-api no
@using-api no
@@ -9,61 +9,58 @@
@stable no (HTML can change)
@parse url, title, content, publishedDate, thumbnail
"""
+# pylint: disable=missing-function-docstring
-import random
-import string
-from dateutil import parser
from json import loads
-from lxml import html
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
from datetime import datetime
+from lxml import html
+
# engine dependent config
categories = ['news', 'social media']
paging = True
+base_url = 'https://digg.com'
# search-url
-base_url = 'https://digg.com/'
-search_url = base_url + 'api/search/?{query}&from={position}&size=20&format=html'
-
-# specific xpath variables
-results_xpath = '//article'
-link_xpath = './/small[@class="time"]//a'
-title_xpath = './/h2//a//text()'
-content_xpath = './/p//text()'
-pubdate_xpath = './/time'
-
-digg_cookie_chars = string.ascii_uppercase + string.ascii_lowercase +\
- string.digits + "+_"
+search_url = base_url + (
+ '/api/search/'
+ '?{query}'
+ '&from={position}'
+ '&size=20'
+ '&format=html'
+)
-
-# do search-request
def request(query, params):
offset = (params['pageno'] - 1) * 20
- params['url'] = search_url.format(position=offset,
- query=urlencode({'q': query}))
- params['cookies']['frontend.auid'] = ''.join(random.choice(
- digg_cookie_chars) for _ in range(22))
+ params['url'] = search_url.format(
+ query = urlencode({'q': query}),
+ position = offset,
+ )
return params
-
-# get response from search-request
def response(resp):
results = []
- search_result = loads(resp.text)
-
# parse results
- for result in search_result['mapped']:
+ for result in loads(resp.text)['mapped']:
+
+ # strip html tags and superfluous quotation marks from content
+ content = html.document_fromstring(
+ result['excerpt']
+ ).text_content()
- published = datetime.strptime(result['created']['ISO'], "%Y-%m-%d %H:%M:%S")
- # append result
- results.append({'url': result['url'],
- 'title': result['title'],
- 'content': result['excerpt'],
- 'template': 'videos.html',
- 'publishedDate': published,
- 'thumbnail': result['images']['thumbImage']})
+ # 'created': {'ISO': '2020-10-16T14:09:55Z', ...}
+ published = datetime.strptime(
+ result['created']['ISO'], '%Y-%m-%dT%H:%M:%SZ'
+ )
+ results.append({
+ 'url': result['url'],
+ 'title': result['title'],
+ 'content' : content,
+ 'template': 'videos.html',
+ 'publishedDate': published,
+ 'thumbnail': result['images']['thumbImage'],
+ })
- # return results
return results
diff --git a/searx/engines/doku.py b/searx/engines/doku.py
index d20e660..e1b10d6 100644
--- a/searx/engines/doku.py
+++ b/searx/engines/doku.py
@@ -9,10 +9,9 @@
# @stable yes
# @parse (general) url, title, content
+from urllib.parse import urlencode
from lxml.html import fromstring
-from searx.engines.xpath import extract_text
-from searx.utils import eval_xpath
-from searx.url_utils import urlencode
+from searx.utils import extract_text, eval_xpath
# engine dependent config
categories = ['general'] # TODO , 'images', 'music', 'videos', 'files'
diff --git a/searx/engines/duckduckgo.py b/searx/engines/duckduckgo.py
index 6e07b50..c1c9846 100644
--- a/searx/engines/duckduckgo.py
+++ b/searx/engines/duckduckgo.py
@@ -15,14 +15,11 @@
from lxml.html import fromstring
from json import loads
-from searx.engines.xpath import extract_text
-from searx.poolrequests import get
-from searx.url_utils import urlencode
-from searx.utils import match_language, eval_xpath
+from searx.utils import extract_text, match_language, eval_xpath
# engine dependent config
categories = ['general']
-paging = True
+paging = False
language_support = True
supported_languages_url = 'https://duckduckgo.com/util/u172.js'
time_range_support = True
@@ -38,9 +35,7 @@ language_aliases = {
}
# search-url
-url = 'https://duckduckgo.com/html?{query}&s={offset}&dc={dc_param}'
-time_range_url = '&df={range}'
-
+url = 'https://html.duckduckgo.com/html'
time_range_dict = {'day': 'd',
'week': 'w',
'month': 'm'}
@@ -54,11 +49,11 @@ correction_xpath = '//div[@id="did_you_mean"]//a'
# match query's language to a region code that duckduckgo will accept
-def get_region_code(lang, lang_list=[]):
+def get_region_code(lang, lang_list=None):
if lang == 'all':
return None
- lang_code = match_language(lang, lang_list, language_aliases, 'wt-WT')
+ lang_code = match_language(lang, lang_list or [], language_aliases, 'wt-WT')
lang_parts = lang_code.split('-')
# country code goes first
@@ -66,36 +61,21 @@ def get_region_code(lang, lang_list=[]):
def request(query, params):
- if params['time_range'] not in (None, 'None', '') and params['time_range'] not in time_range_dict:
+ if params['time_range'] is not None and params['time_range'] not in time_range_dict:
return params
- offset = (params['pageno'] - 1) * 30
+ params['url'] = url
+ params['method'] = 'POST'
+ params['data']['b'] = ''
+ params['data']['q'] = query
+ params['data']['df'] = ''
region_code = get_region_code(params['language'], supported_languages)
- params['url'] = 'https://duckduckgo.com/html/'
- if params['pageno'] > 1:
- params['method'] = 'POST'
- params['data']['q'] = query
- params['data']['s'] = offset
- params['data']['dc'] = 30
- params['data']['nextParams'] = ''
- params['data']['v'] = 'l'
- params['data']['o'] = 'json'
- params['data']['api'] = '/d.js'
- if params['time_range'] in time_range_dict:
- params['data']['df'] = time_range_dict[params['time_range']]
- if region_code:
- params['data']['kl'] = region_code
- else:
- if region_code:
- params['url'] = url.format(
- query=urlencode({'q': query, 'kl': region_code}), offset=offset, dc_param=offset)
- else:
- params['url'] = url.format(
- query=urlencode({'q': query}), offset=offset, dc_param=offset)
-
- if params['time_range'] in time_range_dict:
- params['url'] += time_range_url.format(range=time_range_dict[params['time_range']])
+ if region_code:
+ params['data']['kl'] = region_code
+ params['cookies']['kl'] = region_code
+ if params['time_range'] in time_range_dict:
+ params['data']['df'] = time_range_dict[params['time_range']]
return params
diff --git a/searx/engines/duckduckgo_definitions.py b/searx/engines/duckduckgo_definitions.py
index 79d10c3..1d1c84b 100644
--- a/searx/engines/duckduckgo_definitions.py
+++ b/searx/engines/duckduckgo_definitions.py
@@ -10,31 +10,55 @@ DuckDuckGo (definitions)
"""
import json
+from urllib.parse import urlencode, urlparse, urljoin
from lxml import html
-from re import compile
-from searx.engines.xpath import extract_text
-from searx.engines.duckduckgo import _fetch_supported_languages, supported_languages_url, language_aliases
-from searx.url_utils import urlencode
-from searx.utils import html_to_text, match_language
-url = 'https://api.duckduckgo.com/'\
+from searx import logger
+from searx.data import WIKIDATA_UNITS
+from searx.engines.duckduckgo import language_aliases
+from searx.engines.duckduckgo import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
+from searx.utils import extract_text, html_to_text, match_language, get_string_replaces_function
+from searx.external_urls import get_external_url, get_earth_coordinates_url, area_to_osm_zoom
+
+logger = logger.getChild('duckduckgo_definitions')
+
+URL = 'https://api.duckduckgo.com/'\
+ '?{query}&format=json&pretty=0&no_redirect=1&d=1'
-http_regex = compile(r'^http:')
+WIKIDATA_PREFIX = [
+ 'http://www.wikidata.org/entity/',
+ 'https://www.wikidata.org/entity/'
+]
+
+replace_http_by_https = get_string_replaces_function({'http:': 'https:'})
+
+
+def is_broken_text(text):
+ """ duckduckgo may return something like "<a href="xxxx">http://somewhere Related website<a/>"
+
+ The href URL is broken, the "Related website" may contains some HTML.
+
+ The best solution seems to ignore these results.
+ """
+ return text.startswith('http') and ' ' in text
-def result_to_text(url, text, htmlResult):
+def result_to_text(text, htmlResult):
# TODO : remove result ending with "Meaning" or "Category"
+ result = None
dom = html.fromstring(htmlResult)
a = dom.xpath('//a')
if len(a) >= 1:
- return extract_text(a[0])
+ result = extract_text(a[0])
else:
- return text
+ result = text
+ if not is_broken_text(result):
+ return result
+ return None
def request(query, params):
- params['url'] = url.format(query=urlencode({'q': query}))
+ params['url'] = URL.format(query=urlencode({'q': query}))
language = match_language(params['language'], supported_languages, language_aliases)
language = language.split('-')[0]
params['headers']['Accept-Language'] = language
@@ -46,6 +70,14 @@ def response(resp):
search_res = json.loads(resp.text)
+ # search_res.get('Entity') possible values (not exhaustive) :
+ # * continent / country / department / location / waterfall
+ # * actor / musician / artist
+ # * book / performing art / film / television / media franchise / concert tour / playwright
+ # * prepared food
+ # * website / software / os / programming language / file format / software engineer
+ # * compagny
+
content = ''
heading = search_res.get('Heading', '')
attributes = []
@@ -56,7 +88,8 @@ def response(resp):
# add answer if there is one
answer = search_res.get('Answer', '')
if answer:
- if search_res.get('AnswerType', '') not in ['calc']:
+ logger.debug('AnswerType="%s" Answer="%s"', search_res.get('AnswerType'), answer)
+ if search_res.get('AnswerType') not in ['calc', 'ip']:
results.append({'answer': html_to_text(answer)})
# add infobox
@@ -67,42 +100,38 @@ def response(resp):
content = content + search_res.get('Abstract', '')
# image
- image = search_res.get('Image', '')
+ image = search_res.get('Image')
image = None if image == '' else image
-
- # attributes
- if 'Infobox' in search_res:
- infobox = search_res.get('Infobox', None)
- if 'content' in infobox:
- for info in infobox.get('content'):
- attributes.append({'label': info.get('label'),
- 'value': info.get('value')})
+ if image is not None and urlparse(image).netloc == '':
+ image = urljoin('https://duckduckgo.com', image)
# urls
+ # Official website, Wikipedia page
for ddg_result in search_res.get('Results', []):
- if 'FirstURL' in ddg_result:
- firstURL = ddg_result.get('FirstURL', '')
- text = ddg_result.get('Text', '')
+ firstURL = ddg_result.get('FirstURL')
+ text = ddg_result.get('Text')
+ if firstURL is not None and text is not None:
urls.append({'title': text, 'url': firstURL})
results.append({'title': heading, 'url': firstURL})
# related topics
for ddg_result in search_res.get('RelatedTopics', []):
if 'FirstURL' in ddg_result:
- suggestion = result_to_text(ddg_result.get('FirstURL', None),
- ddg_result.get('Text', None),
- ddg_result.get('Result', None))
- if suggestion != heading:
- results.append({'suggestion': suggestion})
+ firstURL = ddg_result.get('FirstURL')
+ text = ddg_result.get('Text')
+ if not is_broken_text(text):
+ suggestion = result_to_text(text,
+ ddg_result.get('Result'))
+ if suggestion != heading and suggestion is not None:
+ results.append({'suggestion': suggestion})
elif 'Topics' in ddg_result:
suggestions = []
relatedTopics.append({'name': ddg_result.get('Name', ''),
- 'suggestions': suggestions})
+ 'suggestions': suggestions})
for topic_result in ddg_result.get('Topics', []):
- suggestion = result_to_text(topic_result.get('FirstURL', None),
- topic_result.get('Text', None),
- topic_result.get('Result', None))
- if suggestion != heading:
+ suggestion = result_to_text(topic_result.get('Text'),
+ topic_result.get('Result'))
+ if suggestion != heading and suggestion is not None:
suggestions.append(suggestion)
# abstract
@@ -111,7 +140,10 @@ def response(resp):
# add as result ? problem always in english
infobox_id = abstractURL
urls.append({'title': search_res.get('AbstractSource'),
- 'url': abstractURL})
+ 'url': abstractURL,
+ 'official': True})
+ results.append({'url': abstractURL,
+ 'title': heading})
# definition
definitionURL = search_res.get('DefinitionURL', '')
@@ -119,53 +151,107 @@ def response(resp):
# add as result ? as answer ? problem always in english
infobox_id = definitionURL
urls.append({'title': search_res.get('DefinitionSource'),
- 'url': definitionURL})
+ 'url': definitionURL})
# to merge with wikidata's infobox
if infobox_id:
- infobox_id = http_regex.sub('https:', infobox_id)
-
- # entity
- entity = search_res.get('Entity', None)
- # TODO continent / country / department / location / waterfall /
- # mountain range :
- # link to map search, get weather, near by locations
- # TODO musician : link to music search
- # TODO concert tour : ??
- # TODO film / actor / television / media franchise :
- # links to IMDB / rottentomatoes (or scrap result)
- # TODO music : link tu musicbrainz / last.fm
- # TODO book : ??
- # TODO artist / playwright : ??
- # TODO compagny : ??
- # TODO software / os : ??
- # TODO software engineer : ??
- # TODO prepared food : ??
- # TODO website : ??
- # TODO performing art : ??
- # TODO prepared food : ??
- # TODO programming language : ??
- # TODO file format : ??
+ infobox_id = replace_http_by_https(infobox_id)
+
+ # attributes
+ # some will be converted to urls
+ if 'Infobox' in search_res:
+ infobox = search_res.get('Infobox')
+ if 'content' in infobox:
+ osm_zoom = 17
+ coordinates = None
+ for info in infobox.get('content'):
+ data_type = info.get('data_type')
+ data_label = info.get('label')
+ data_value = info.get('value')
+
+ # Workaround: ddg may return a double quote
+ if data_value == '""':
+ continue
+
+ # Is it an external URL ?
+ # * imdb_id / facebook_profile / youtube_channel / youtube_video / twitter_profile
+ # * instagram_profile / rotten_tomatoes / spotify_artist_id / itunes_artist_id / soundcloud_id
+ # * netflix_id
+ external_url = get_external_url(data_type, data_value)
+ if external_url is not None:
+ urls.append({'title': data_label,
+ 'url': external_url})
+ elif data_type in ['instance', 'wiki_maps_trigger', 'google_play_artist_id']:
+ # ignore instance: Wikidata value from "Instance Of" (Qxxxx)
+ # ignore wiki_maps_trigger: reference to a javascript
+ # ignore google_play_artist_id: service shutdown
+ pass
+ elif data_type == 'string' and data_label == 'Website':
+ # There is already an URL for the website
+ pass
+ elif data_type == 'area':
+ attributes.append({'label': data_label,
+ 'value': area_to_str(data_value),
+ 'entity': 'P2046'})
+ osm_zoom = area_to_osm_zoom(data_value.get('amount'))
+ elif data_type == 'coordinates':
+ if data_value.get('globe') == 'http://www.wikidata.org/entity/Q2':
+ # coordinate on Earth
+ # get the zoom information from the area
+ coordinates = info
+ else:
+ # coordinate NOT on Earth
+ attributes.append({'label': data_label,
+ 'value': data_value,
+ 'entity': 'P625'})
+ elif data_type == 'string':
+ attributes.append({'label': data_label,
+ 'value': data_value})
+
+ if coordinates:
+ data_label = coordinates.get('label')
+ data_value = coordinates.get('value')
+ latitude = data_value.get('latitude')
+ longitude = data_value.get('longitude')
+ url = get_earth_coordinates_url(latitude, longitude, osm_zoom)
+ urls.append({'title': 'OpenStreetMap',
+ 'url': url,
+ 'entity': 'P625'})
if len(heading) > 0:
# TODO get infobox.meta.value where .label='article_title'
if image is None and len(attributes) == 0 and len(urls) == 1 and\
len(relatedTopics) == 0 and len(content) == 0:
- results.append({
- 'url': urls[0]['url'],
- 'title': heading,
- 'content': content
- })
+ results.append({'url': urls[0]['url'],
+ 'title': heading,
+ 'content': content})
else:
- results.append({
- 'infobox': heading,
- 'id': infobox_id,
- 'entity': entity,
- 'content': content,
- 'img_src': image,
- 'attributes': attributes,
- 'urls': urls,
- 'relatedTopics': relatedTopics
- })
+ results.append({'infobox': heading,
+ 'id': infobox_id,
+ 'content': content,
+ 'img_src': image,
+ 'attributes': attributes,
+ 'urls': urls,
+ 'relatedTopics': relatedTopics})
return results
+
+
+def unit_to_str(unit):
+ for prefix in WIKIDATA_PREFIX:
+ if unit.startswith(prefix):
+ wikidata_entity = unit[len(prefix):]
+ return WIKIDATA_UNITS.get(wikidata_entity, unit)
+ return unit
+
+
+def area_to_str(area):
+ """parse {'unit': 'http://www.wikidata.org/entity/Q712226', 'amount': '+20.99'}"""
+ unit = unit_to_str(area.get('unit'))
+ if unit is not None:
+ try:
+ amount = float(area.get('amount'))
+ return '{} {}'.format(amount, unit)
+ except ValueError:
+ pass
+ return '{} {}'.format(area.get('amount', ''), area.get('unit', ''))
diff --git a/searx/engines/duckduckgo_images.py b/searx/engines/duckduckgo_images.py
index 89924b7..009f81c 100644
--- a/searx/engines/duckduckgo_images.py
+++ b/searx/engines/duckduckgo_images.py
@@ -14,13 +14,11 @@
"""
from json import loads
-from searx.engines.xpath import extract_text
-from searx.engines.duckduckgo import (
- _fetch_supported_languages, supported_languages_url,
- get_region_code, language_aliases
-)
+from urllib.parse import urlencode
+from searx.exceptions import SearxEngineAPIException
+from searx.engines.duckduckgo import get_region_code
+from searx.engines.duckduckgo import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
from searx.poolrequests import get
-from searx.url_utils import urlencode
# engine dependent config
categories = ['images']
@@ -40,7 +38,7 @@ def get_vqd(query, headers):
res = get(query_url, headers=headers)
content = res.text
if content.find('vqd=\'') == -1:
- raise Exception('Request failed')
+ raise SearxEngineAPIException('Request failed')
vqd = content[content.find('vqd=\'') + 5:]
vqd = vqd[:vqd.find('\'')]
return vqd
@@ -74,10 +72,7 @@ def response(resp):
results = []
content = resp.text
- try:
- res_json = loads(content)
- except:
- raise Exception('Cannot parse results')
+ res_json = loads(content)
# parse results
for result in res_json['results']:
diff --git a/searx/engines/duden.py b/searx/engines/duden.py
index cf2f1a2..1475fb8 100644
--- a/searx/engines/duden.py
+++ b/searx/engines/duden.py
@@ -8,12 +8,10 @@
@parse url, title, content
"""
-from lxml import html, etree
import re
-from searx.engines.xpath import extract_text
-from searx.utils import eval_xpath
-from searx.url_utils import quote, urljoin
-from searx import logger
+from urllib.parse import quote, urljoin
+from lxml import html
+from searx.utils import extract_text, eval_xpath, eval_xpath_list, eval_xpath_getindex
categories = ['general']
paging = True
@@ -41,6 +39,9 @@ def request(query, params):
params['url'] = search_url_fmt.format(query=quote(query))
else:
params['url'] = search_url.format(offset=offset, query=quote(query))
+ # after the last page of results, spelling corrections are returned after a HTTP redirect
+ # whatever the page number is
+ params['soft_max_redirects'] = 1
return params
@@ -52,29 +53,21 @@ def response(resp):
dom = html.fromstring(resp.text)
- try:
- number_of_results_string =\
- re.sub('[^0-9]', '',
- eval_xpath(dom, '//a[@class="active" and contains(@href,"/suchen/dudenonline")]/span/text()')[0])
-
+ number_of_results_element =\
+ eval_xpath_getindex(dom, '//a[@class="active" and contains(@href,"/suchen/dudenonline")]/span/text()',
+ 0, default=None)
+ if number_of_results_element is not None:
+ number_of_results_string = re.sub('[^0-9]', '', number_of_results_element)
results.append({'number_of_results': int(number_of_results_string)})
- except:
- logger.debug("Couldn't read number of results.")
- pass
-
- for result in eval_xpath(dom, '//section[not(contains(@class, "essay"))]'):
- try:
- url = eval_xpath(result, './/h2/a')[0].get('href')
- url = urljoin(base_url, url)
- title = eval_xpath(result, 'string(.//h2/a)').strip()
- content = extract_text(eval_xpath(result, './/p'))
- # append result
- results.append({'url': url,
- 'title': title,
- 'content': content})
- except:
- logger.debug('result parse error in:\n%s', etree.tostring(result, pretty_print=True))
- continue
+ for result in eval_xpath_list(dom, '//section[not(contains(@class, "essay"))]'):
+ url = eval_xpath_getindex(result, './/h2/a', 0).get('href')
+ url = urljoin(base_url, url)
+ title = eval_xpath(result, 'string(.//h2/a)').strip()
+ content = extract_text(eval_xpath(result, './/p'))
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content})
return results
diff --git a/searx/engines/ebay.py b/searx/engines/ebay.py
new file mode 100644
index 0000000..e2e5ded
--- /dev/null
+++ b/searx/engines/ebay.py
@@ -0,0 +1,68 @@
+# Ebay (Videos, Music, Files)
+#
+# @website https://www.ebay.com
+# @provide-api no (nothing found)
+#
+# @using-api no
+# @results HTML (using search portal)
+# @stable yes (HTML can change)
+# @parse url, title, content, price, shipping, source
+
+from lxml import html
+from searx.engines.xpath import extract_text
+from urllib.parse import quote
+
+categories = ['shopping']
+paging = True
+
+url = 'https://www.ebay.com'
+search_url = url + '/sch/i.html?_nkw={query}&_sacat={pageno}'
+
+results_xpath = '//li[contains(@class, "s-item")]'
+url_xpath = './/a[@class="s-item__link"]/@href'
+title_xpath = './/h3[@class="s-item__title"]'
+content_xpath = './/div[@span="SECONDARY_INFO"]'
+price_xpath = './/div[contains(@class, "s-item__detail")]/span[@class="s-item__price"][1]/text()'
+shipping_xpath = './/span[contains(@class, "s-item__shipping")]/text()'
+source_country_xpath = './/span[contains(@class, "s-item__location")]/text()'
+thumbnail_xpath = './/img[@class="s-item__image-img"]/@src'
+
+
+def request(query, params):
+ params['url'] = search_url.format(query=quote(query), pageno=params['pageno'])
+ return params
+
+
+def response(resp):
+ results = []
+
+ dom = html.fromstring(resp.text)
+ results_dom = dom.xpath(results_xpath)
+ if not results_dom:
+ return []
+
+ for result_dom in results_dom:
+ url = extract_text(result_dom.xpath(url_xpath))
+ title = extract_text(result_dom.xpath(title_xpath))
+ content = extract_text(result_dom.xpath(content_xpath))
+ price = extract_text(result_dom.xpath(price_xpath))
+ shipping = extract_text(result_dom.xpath(shipping_xpath))
+ source_country = extract_text(result_dom.xpath(source_country_xpath))
+ thumbnail = extract_text(result_dom.xpath(thumbnail_xpath))
+
+ if title == "":
+ continue
+
+ results.append({
+ 'url': url,
+ 'title': title,
+ 'content': content,
+ 'price': price,
+ 'shipping': shipping,
+ 'source_country': source_country,
+ 'thumbnail': thumbnail,
+ 'template': 'products.html',
+
+ })
+
+ return results
diff --git a/searx/engines/elasticsearch.py b/searx/engines/elasticsearch.py
new file mode 100644
index 0000000..081736c
--- /dev/null
+++ b/searx/engines/elasticsearch.py
@@ -0,0 +1,140 @@
+from json import loads, dumps
+from requests.auth import HTTPBasicAuth
+from searx.exceptions import SearxEngineAPIException
+
+
+base_url = 'http://localhost:9200'
+username = ''
+password = ''
+index = ''
+search_url = base_url + '/' + index + '/_search'
+query_type = 'match'
+custom_query_json = {}
+show_metadata = False
+categories = ['general']
+
+
+def init(engine_settings):
+ if 'query_type' in engine_settings and engine_settings['query_type'] not in _available_query_types:
+ raise ValueError('unsupported query type', engine_settings['query_type'])
+
+ if index == '':
+ raise ValueError('index cannot be empty')
+
+
+def request(query, params):
+ if query_type not in _available_query_types:
+ return params
+
+ if username and password:
+ params['auth'] = HTTPBasicAuth(username, password)
+
+ params['url'] = search_url
+ params['method'] = 'GET'
+ params['data'] = dumps(_available_query_types[query_type](query))
+ params['headers']['Content-Type'] = 'application/json'
+
+ return params
+
+
+def _match_query(query):
+ """
+ The standard for full text queries.
+ searx format: "key:value" e.g. city:berlin
+ REF: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html
+ """
+
+ try:
+ key, value = query.split(':')
+ except:
+ raise ValueError('query format must be "key:value"')
+
+ return {"query": {"match": {key: {'query': value}}}}
+
+
+def _simple_query_string_query(query):
+ """
+ Accepts query strings, but it is less strict than query_string
+ The field used can be specified in index.query.default_field in Elasticsearch.
+ REF: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-simple-query-string-query.html
+ """
+
+ return {'query': {'simple_query_string': {'query': query}}}
+
+
+def _term_query(query):
+ """
+ Accepts one term and the name of the field.
+ searx format: "key:value" e.g. city:berlin
+ REF: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-term-query.html
+ """
+
+ try:
+ key, value = query.split(':')
+ except:
+ raise ValueError('query format must be key:value')
+
+ return {'query': {'term': {key: value}}}
+
+
+def _terms_query(query):
+ """
+ Accepts multiple terms and the name of the field.
+ searx format: "key:value1,value2" e.g. city:berlin,paris
+ REF: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html
+ """
+
+ try:
+ key, values = query.split(':')
+ except:
+ raise ValueError('query format must be key:value1,value2')
+
+ return {'query': {'terms': {key: values.split(',')}}}
+
+
+def _custom_query(query):
+ key, value = query.split(':')
+ custom_query = custom_query_json
+ for query_key, query_value in custom_query.items():
+ if query_key == '{{KEY}}':
+ custom_query[key] = custom_query.pop(query_key)
+ if query_value == '{{VALUE}}':
+ custom_query[query_key] = value
+ return custom_query
+
+
+def response(resp):
+ results = []
+
+ resp_json = loads(resp.text)
+ if 'error' in resp_json:
+ raise SearxEngineAPIException(resp_json['error'])
+
+ for result in resp_json['hits']['hits']:
+ r = {key: str(value) if not key.startswith('_') else value for key, value in result['_source'].items()}
+ r['template'] = 'key-value.html'
+
+ if show_metadata:
+ r['metadata'] = {'index': result['_index'],
+ 'id': result['_id'],
+ 'score': result['_score']}
+
+ results.append(r)
+
+ return results
+
+
+_available_query_types = {
+ # Full text queries
+ # https://www.elastic.co/guide/en/elasticsearch/reference/current/full-text-queries.html
+ 'match': _match_query,
+ 'simple_query_string': _simple_query_string_query,
+
+ # Term-level queries
+ # https://www.elastic.co/guide/en/elasticsearch/reference/current/term-level-queries.html
+ 'term': _term_query,
+ 'terms': _terms_query,
+
+ # Query JSON defined by the instance administrator.
+ 'custom': _custom_query,
+}
diff --git a/searx/engines/etools.py b/searx/engines/etools.py
index a9eb098..a0762d1 100644
--- a/searx/engines/etools.py
+++ b/searx/engines/etools.py
@@ -10,9 +10,8 @@
"""
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import quote
-from searx.utils import eval_xpath
+from urllib.parse import quote
+from searx.utils import extract_text, eval_xpath
categories = ['general']
paging = False
diff --git a/searx/engines/fdroid.py b/searx/engines/fdroid.py
index 4066dc7..3d37db4 100644
--- a/searx/engines/fdroid.py
+++ b/searx/engines/fdroid.py
@@ -9,9 +9,9 @@
@parse url, title, content
"""
+from urllib.parse import urlencode
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
+from searx.utils import extract_text
# engine dependent config
categories = ['files']
diff --git a/searx/engines/filecrop.py b/searx/engines/filecrop.py
deleted file mode 100644
index ed57a6b..0000000
--- a/searx/engines/filecrop.py
+++ /dev/null
@@ -1,88 +0,0 @@
-from searx.url_utils import urlencode
-
-try:
- from HTMLParser import HTMLParser
-except:
- from html.parser import HTMLParser
-
-url = 'http://www.filecrop.com/'
-search_url = url + '/search.php?{query}&size_i=0&size_f=100000000&engine_r=1&engine_d=1&engine_e=1&engine_4=1&engine_m=1&pos={index}' # noqa
-
-paging = True
-
-
-class FilecropResultParser(HTMLParser):
-
- def __init__(self):
- HTMLParser.__init__(self)
- self.__start_processing = False
-
- self.results = []
- self.result = {}
-
- self.tr_counter = 0
- self.data_counter = 0
-
- def handle_starttag(self, tag, attrs):
-
- if tag == 'tr':
- if ('bgcolor', '#edeff5') in attrs or\
- ('bgcolor', '#ffffff') in attrs:
- self.__start_processing = True
-
- if not self.__start_processing:
- return
-
- if tag == 'label':
- self.result['title'] = [attr[1] for attr in attrs
- if attr[0] == 'title'][0]
- elif tag == 'a' and ('rel', 'nofollow') in attrs\
- and ('class', 'sourcelink') in attrs:
- if 'content' in self.result:
- self.result['content'] += [attr[1] for attr in attrs
- if attr[0] == 'title'][0]
- else:
- self.result['content'] = [attr[1] for attr in attrs
- if attr[0] == 'title'][0]
- self.result['content'] += ' '
- elif tag == 'a':
- self.result['url'] = url + [attr[1] for attr in attrs
- if attr[0] == 'href'][0]
-
- def handle_endtag(self, tag):
- if self.__start_processing is False:
- return
-
- if tag == 'tr':
- self.tr_counter += 1
-
- if self.tr_counter == 2:
- self.__start_processing = False
- self.tr_counter = 0
- self.data_counter = 0
- self.results.append(self.result)
- self.result = {}
-
- def handle_data(self, data):
- if not self.__start_processing:
- return
-
- if 'content' in self.result:
- self.result['content'] += data + ' '
- else:
- self.result['content'] = data + ' '
-
- self.data_counter += 1
-
-
-def request(query, params):
- index = 1 + (params['pageno'] - 1) * 30
- params['url'] = search_url.format(query=urlencode({'w': query}), index=index)
- return params
-
-
-def response(resp):
- parser = FilecropResultParser()
- parser.feed(resp.text)
-
- return parser.results
diff --git a/searx/engines/flickr.py b/searx/engines/flickr.py
index de17693..b23c447 100644
--- a/searx/engines/flickr.py
+++ b/searx/engines/flickr.py
@@ -14,7 +14,7 @@
"""
from json import loads
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
categories = ['images']
diff --git a/searx/engines/flickr_noapi.py b/searx/engines/flickr_noapi.py
index 1cbb3e0..4bcf837 100644
--- a/searx/engines/flickr_noapi.py
+++ b/searx/engines/flickr_noapi.py
@@ -15,8 +15,8 @@
from json import loads
from time import time
import re
+from urllib.parse import urlencode
from searx.engines import logger
-from searx.url_utils import urlencode
from searx.utils import ecma_unescape, html_to_text
logger = logger.getChild('flickr-noapi')
@@ -117,10 +117,10 @@ def response(resp):
'img_format': img_format,
'template': 'images.html'
}
- result['author'] = author.encode('utf-8', 'ignore').decode('utf-8')
- result['source'] = source.encode('utf-8', 'ignore').decode('utf-8')
- result['title'] = title.encode('utf-8', 'ignore').decode('utf-8')
- result['content'] = content.encode('utf-8', 'ignore').decode('utf-8')
+ result['author'] = author.encode(errors='ignore').decode()
+ result['source'] = source.encode(errors='ignore').decode()
+ result['title'] = title.encode(errors='ignore').decode()
+ result['content'] = content.encode(errors='ignore').decode()
results.append(result)
return results
diff --git a/searx/engines/framalibre.py b/searx/engines/framalibre.py
index f3441fa..e3d0564 100644
--- a/searx/engines/framalibre.py
+++ b/searx/engines/framalibre.py
@@ -10,13 +10,10 @@
@parse url, title, content, thumbnail, img_src
"""
-try:
- from cgi import escape
-except:
- from html import escape
+from html import escape
+from urllib.parse import urljoin, urlencode
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urljoin, urlencode
+from searx.utils import extract_text
# engine dependent config
categories = ['it']
diff --git a/searx/engines/frinkiac.py b/searx/engines/frinkiac.py
index a67b42d..5b174a6 100644
--- a/searx/engines/frinkiac.py
+++ b/searx/engines/frinkiac.py
@@ -10,7 +10,7 @@ Frinkiac (Images)
"""
from json import loads
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
categories = ['images']
diff --git a/searx/engines/genius.py b/searx/engines/genius.py
index aa5afad..2bfbfdd 100644
--- a/searx/engines/genius.py
+++ b/searx/engines/genius.py
@@ -11,7 +11,7 @@ Genius
"""
from json import loads
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
from datetime import datetime
# engine dependent config
@@ -36,7 +36,7 @@ def parse_lyric(hit):
try:
content = hit['highlights'][0]['value']
except:
- content = None
+ content = ''
timestamp = hit['result']['lyrics_updated_at']
result = {'url': hit['result']['url'],
'title': hit['result']['full_title'],
@@ -51,7 +51,7 @@ def parse_lyric(hit):
def parse_artist(hit):
result = {'url': hit['result']['url'],
'title': hit['result']['name'],
- 'content': None,
+ 'content': '',
'thumbnail': hit['result']['image_url'],
'template': 'videos.html'}
return result
@@ -61,6 +61,7 @@ def parse_album(hit):
result = {'url': hit['result']['url'],
'title': hit['result']['full_title'],
'thumbnail': hit['result']['cover_art_url'],
+ 'content': '',
# 'thumbnail': hit['result']['cover_art_thumbnail_url'],
'template': 'videos.html'}
try:
@@ -81,9 +82,7 @@ def response(resp):
json = loads(resp.text)
hits = [hit for section in json['response']['sections'] for hit in section['hits']]
for hit in hits:
- try:
- func = parse[hit['type']]
- except KeyError:
- continue
- results.append(func(hit))
+ func = parse.get(hit['type'])
+ if func:
+ results.append(func(hit))
return results
diff --git a/searx/engines/gentoo.py b/searx/engines/gentoo.py
index a7a966c..16b3e69 100644
--- a/searx/engines/gentoo.py
+++ b/searx/engines/gentoo.py
@@ -11,9 +11,9 @@
@parse url, title
"""
+from urllib.parse import urlencode, urljoin
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode, urljoin
+from searx.utils import extract_text
# engine dependent config
categories = ['it']
@@ -90,7 +90,7 @@ def request(query, params):
# if our language is hosted on the main site, we need to add its name
# to the query in order to narrow the results to that language
if language in main_langs:
- query += b' (' + (main_langs[language]).encode('utf-8') + b')'
+ query += ' (' + main_langs[language] + ')'
# prepare the request parameters
query = urlencode({'search': query})
diff --git a/searx/engines/gigablast.py b/searx/engines/gigablast.py
index b139c2a..1d71b18 100644
--- a/searx/engines/gigablast.py
+++ b/searx/engines/gigablast.py
@@ -14,8 +14,8 @@
import re
from json import loads
+from urllib.parse import urlencode
# from searx import logger
-from searx.url_utils import urlencode
from searx.poolrequests import get
# engine dependent config
diff --git a/searx/engines/github.py b/searx/engines/github.py
index eaa00da..80b50ce 100644
--- a/searx/engines/github.py
+++ b/searx/engines/github.py
@@ -11,7 +11,7 @@
"""
from json import loads
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
# engine dependent config
categories = ['it']
diff --git a/searx/engines/google.py b/searx/engines/google.py
index 093ad6b..17ab21f 100644
--- a/searx/engines/google.py
+++ b/searx/engines/google.py
@@ -18,12 +18,12 @@ Definitions`_.
# pylint: disable=invalid-name, missing-function-docstring
+from urllib.parse import urlencode, urlparse
from lxml import html
-from flask_babel import gettext
-from searx.engines.xpath import extract_text
from searx import logger
-from searx.url_utils import urlencode, urlparse
-from searx.utils import match_language, eval_xpath
+from searx.utils import match_language, extract_text, eval_xpath, eval_xpath_list, eval_xpath_getindex
+from searx.exceptions import SearxEngineCaptchaException
+
logger = logger.getChild('google engine')
@@ -116,12 +116,12 @@ g_section_with_header = './g-section-with-header'
# the title is a h3 tag relative to the result group
title_xpath = './/h3[1]'
-# in the result group there is <div class="r" ../> it's first child is a <a
-# href=...> (on some results, the <a> is the first "descendant", not ""child")
-href_xpath = './/div[@class="r"]//a/@href'
+# in the result group there is <div class="yuRUbf" ../> it's first child is a <a
+# href=...>
+href_xpath = './/div[@class="yuRUbf"]//a/@href'
-# in the result group there is <div class="s" ../> containing he *content*
-content_xpath = './/div[@class="s"]'
+# in the result group there is <div class="IsZvec" ../> containing he *content*
+content_xpath = './/div[@class="IsZvec"]'
# Suggestions are links placed in a *card-section*, we extract only the text
# from the links not the links itself.
@@ -132,14 +132,6 @@ suggestion_xpath = '//div[contains(@class, "card-section")]//a'
spelling_suggestion_xpath = '//div[@class="med"]/p/a'
-def extract_text_from_dom(result, xpath):
- """returns extract_text on the first result selected by the xpath or None"""
- r = eval_xpath(result, xpath)
- if len(r) > 0:
- return extract_text(r[0])
- return None
-
-
def get_lang_country(params, lang_list, custom_aliases):
"""Returns a tuple with *langauage* on its first and *country* on its second
position."""
@@ -211,10 +203,10 @@ def response(resp):
# detect google sorry
resp_url = urlparse(resp.url)
if resp_url.netloc == 'sorry.google.com' or resp_url.path == '/sorry/IndexRedirect':
- raise RuntimeWarning('sorry.google.com')
+ raise SearxEngineCaptchaException()
if resp_url.path.startswith('/sorry'):
- raise RuntimeWarning(gettext('CAPTCHA required'))
+ raise SearxEngineCaptchaException()
# which subdomain ?
# subdomain = resp.search_params.get('google_subdomain')
@@ -230,18 +222,17 @@ def response(resp):
logger.debug("did not found 'answer'")
# results --> number_of_results
- try:
- _txt = eval_xpath(dom, '//div[@id="result-stats"]//text()')[0]
- _digit = ''.join([n for n in _txt if n.isdigit()])
- number_of_results = int(_digit)
- results.append({'number_of_results': number_of_results})
-
- except Exception as e: # pylint: disable=broad-except
- logger.debug("did not 'number_of_results'")
- logger.error(e, exc_info=True)
+ try:
+ _txt = eval_xpath_getindex(dom, '//div[@id="result-stats"]//text()', 0)
+ _digit = ''.join([n for n in _txt if n.isdigit()])
+ number_of_results = int(_digit)
+ results.append({'number_of_results': number_of_results})
+ except Exception as e: # pylint: disable=broad-except
+ logger.debug("did not 'number_of_results'")
+ logger.error(e, exc_info=True)
# parse results
- for result in eval_xpath(dom, results_xpath):
+ for result in eval_xpath_list(dom, results_xpath):
# google *sections*
if extract_text(eval_xpath(result, g_section_with_header)):
@@ -249,9 +240,14 @@ def response(resp):
continue
try:
- title = extract_text(eval_xpath(result, title_xpath)[0])
- url = eval_xpath(result, href_xpath)[0]
- content = extract_text_from_dom(result, content_xpath)
+ title_tag = eval_xpath_getindex(result, title_xpath, 0, default=None)
+ if title_tag is None:
+ # this not one of the common google results *section*
+ logger.debug('ingoring <div class="g" ../> section: missing title')
+ continue
+ title = extract_text(title_tag)
+ url = eval_xpath_getindex(result, href_xpath, 0)
+ content = extract_text(eval_xpath_getindex(result, content_xpath, 0, default=None), allow_none=True)
results.append({
'url': url,
'title': title,
@@ -266,11 +262,11 @@ def response(resp):
continue
# parse suggestion
- for suggestion in eval_xpath(dom, suggestion_xpath):
+ for suggestion in eval_xpath_list(dom, suggestion_xpath):
# append suggestion
results.append({'suggestion': extract_text(suggestion)})
- for correction in eval_xpath(dom, spelling_suggestion_xpath):
+ for correction in eval_xpath_list(dom, spelling_suggestion_xpath):
results.append({'correction': extract_text(correction)})
# return results
@@ -282,11 +278,11 @@ def _fetch_supported_languages(resp):
ret_val = {}
dom = html.fromstring(resp.text)
- radio_buttons = eval_xpath(dom, '//*[@id="langSec"]//input[@name="lang"]')
+ radio_buttons = eval_xpath_list(dom, '//*[@id="langSec"]//input[@name="lr"]')
for x in radio_buttons:
name = x.get("data-name")
- code = x.get("value")
+ code = x.get("value").split('_')[-1]
ret_val[code] = {"name": name}
return ret_val
diff --git a/searx/engines/google_images.py b/searx/engines/google_images.py
index 6ec242b..9ef1be7 100644
--- a/searx/engines/google_images.py
+++ b/searx/engines/google_images.py
@@ -24,19 +24,12 @@ Definitions`_.
"""
+from urllib.parse import urlencode, urlparse, unquote
from lxml import html
-from flask_babel import gettext
from searx import logger
-from searx.url_utils import urlencode, urlparse
-from searx.utils import eval_xpath
-from searx.engines.xpath import extract_text
-
-# pylint: disable=unused-import
-from searx.engines.google import (
- supported_languages_url,
- _fetch_supported_languages,
-)
-# pylint: enable=unused-import
+from searx.exceptions import SearxEngineCaptchaException
+from searx.utils import extract_text, eval_xpath
+from searx.engines.google import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
from searx.engines.google import (
get_lang_country,
@@ -77,6 +70,19 @@ def scrap_out_thumbs(dom):
return ret_val
+def scrap_img_by_id(script, data_id):
+ """Get full image URL by data-id in parent element
+ """
+ img_url = ''
+ _script = script.split('\n')
+ for i, line in enumerate(_script):
+ if 'gstatic.com/images' in line and data_id in line:
+ url_line = _script[i + 1]
+ img_url = url_line.split('"')[1]
+ img_url = unquote(img_url.replace(r'\u00', r'%'))
+ return img_url
+
+
def request(query, params):
"""Google-Video search request"""
@@ -122,10 +128,10 @@ def response(resp):
# detect google sorry
resp_url = urlparse(resp.url)
if resp_url.netloc == 'sorry.google.com' or resp_url.path == '/sorry/IndexRedirect':
- raise RuntimeWarning('sorry.google.com')
+ raise SearxEngineCaptchaException()
if resp_url.path.startswith('/sorry'):
- raise RuntimeWarning(gettext('CAPTCHA required'))
+ raise SearxEngineCaptchaException()
# which subdomain ?
# subdomain = resp.search_params.get('google_subdomain')
@@ -133,6 +139,7 @@ def response(resp):
# convert the text to dom
dom = html.fromstring(resp.text)
img_bas64_map = scrap_out_thumbs(dom)
+ img_src_script = eval_xpath(dom, '//script[contains(., "AF_initDataCallback({key: ")]')[1].text
# parse results
#
@@ -142,8 +149,7 @@ def response(resp):
# <div jsmodel="tTXmib"> / <div jsaction="..." data-id="..."
# The data-id matches to a item in a json-data structure in::
# <script nonce="I+vqelcy/01CKiBJi5Z1Ow">AF_initDataCallback({key: 'ds:1', ... data:function(){return [ ...
- # In this structure the ling to the origin PNG, JPG or whatever is given
- # (we do not blow out the link there, you could still implement that)
+ # In this structure the link to the origin PNG, JPG or whatever is given
# first link per image-div contains a <img> with the data-iid for bas64 encoded image data::
# <img class="rg_i Q4LuWd" data-iid="0"
# second link per image-div is the target link::
@@ -186,12 +192,17 @@ def response(resp):
pub_descr = extract_text(pub_nodes[0])
pub_source = extract_text(pub_nodes[1])
+ img_src_id = eval_xpath(img_node, '../../../@data-id')[0]
+ src_url = scrap_img_by_id(img_src_script, img_src_id)
+ if not src_url:
+ src_url = thumbnail_src
+
results.append({
'url': url,
'title': img_alt,
'content': pub_descr,
'source': pub_source,
- 'img_src': url,
+ 'img_src': src_url,
# 'img_format': img_format,
'thumbnail_src': thumbnail_src,
'template': 'images.html'
diff --git a/searx/engines/google_news.py b/searx/engines/google_news.py
index c9cc754..f1b7cfa 100644
--- a/searx/engines/google_news.py
+++ b/searx/engines/google_news.py
@@ -10,10 +10,10 @@
@parse url, title, content, publishedDate
"""
+from urllib.parse import urlencode
from lxml import html
-from searx.engines.google import _fetch_supported_languages, supported_languages_url
-from searx.url_utils import urlencode
from searx.utils import match_language
+from searx.engines.google import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
# search-url
categories = ['news']
diff --git a/searx/engines/google_videos.py b/searx/engines/google_videos.py
index fd6b2e3..eedefbf 100644
--- a/searx/engines/google_videos.py
+++ b/searx/engines/google_videos.py
@@ -11,10 +11,9 @@
"""
from datetime import date, timedelta
-from json import loads
+from urllib.parse import urlencode
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
+from searx.utils import extract_text, eval_xpath, eval_xpath_list, eval_xpath_getindex
import re
# engine dependent config
@@ -67,11 +66,11 @@ def response(resp):
dom = html.fromstring(resp.text)
# parse results
- for result in dom.xpath('//div[@class="g"]'):
+ for result in eval_xpath_list(dom, '//div[@class="g"]'):
- title = extract_text(result.xpath('.//h3'))
- url = result.xpath('.//div[@class="r"]/a/@href')[0]
- content = extract_text(result.xpath('.//span[@class="st"]'))
+ title = extract_text(eval_xpath(result, './/h3'))
+ url = eval_xpath_getindex(result, './/div[@class="r"]/a/@href', 0)
+ content = extract_text(eval_xpath(result, './/span[@class="st"]'))
# get thumbnails
script = str(dom.xpath('//script[contains(., "_setImagesSrc")]')[0].text)
diff --git a/searx/engines/ina.py b/searx/engines/ina.py
index ea50964..52c9394 100644
--- a/searx/engines/ina.py
+++ b/searx/engines/ina.py
@@ -12,15 +12,12 @@
# @todo embedded (needs some md5 from video page)
from json import loads
+from urllib.parse import urlencode
from lxml import html
from dateutil import parser
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
+from html.parser import HTMLParser
+from searx.utils import extract_text
-try:
- from HTMLParser import HTMLParser
-except:
- from html.parser import HTMLParser
# engine dependent config
categories = ['videos']
diff --git a/searx/engines/invidious.py b/searx/engines/invidious.py
index 8d81691..6ea9426 100644
--- a/searx/engines/invidious.py
+++ b/searx/engines/invidious.py
@@ -6,9 +6,9 @@
# @using-api yes
# @results JSON
# @stable yes
-# @parse url, title, content, publishedDate, thumbnail, embedded
+# @parse url, title, content, publishedDate, thumbnail, embedded, author, length
-from searx.url_utils import quote_plus
+from urllib.parse import quote_plus
from dateutil import parser
import time
@@ -84,13 +84,20 @@ def response(resp):
publishedDate = parser.parse(
time.ctime(result.get("published", 0))
)
+ length = time.gmtime(result.get("lengthSeconds"))
+ if length.tm_hour:
+ length = time.strftime("%H:%M:%S", length)
+ else:
+ length = time.strftime("%M:%S", length)
results.append(
{
"url": url,
"title": result.get("title", ""),
"content": result.get("description", ""),
+ 'length': length,
"template": "videos.html",
+ "author": result.get("author"),
"publishedDate": publishedDate,
"embedded": embedded,
"thumbnail": thumbnail,
diff --git a/searx/engines/json_engine.py b/searx/engines/json_engine.py
index 785b0c4..e2aa436 100644
--- a/searx/engines/json_engine.py
+++ b/searx/engines/json_engine.py
@@ -1,11 +1,8 @@
-from collections import Iterable
+from collections.abc import Iterable
from json import loads
-from sys import version_info
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
from searx.utils import to_string
-if version_info[0] == 3:
- unicode = str
search_url = None
url_query = None
@@ -37,8 +34,6 @@ def iterate(iterable):
def is_iterable(obj):
if type(obj) == str:
return False
- if type(obj) == unicode:
- return False
return isinstance(obj, Iterable)
diff --git a/searx/engines/kickass.py b/searx/engines/kickass.py
index 5e897c9..90bd330 100644
--- a/searx/engines/kickass.py
+++ b/searx/engines/kickass.py
@@ -12,9 +12,8 @@
from lxml import html
from operator import itemgetter
-from searx.engines.xpath import extract_text
-from searx.utils import get_torrent_size, convert_str_to_int
-from searx.url_utils import quote, urljoin
+from urllib.parse import quote, urljoin
+from searx.utils import extract_text, get_torrent_size, convert_str_to_int
# engine dependent config
categories = ['videos', 'music', 'files']
diff --git a/searx/engines/mediawiki.py b/searx/engines/mediawiki.py
index 0607ac9..50ba74e 100644
--- a/searx/engines/mediawiki.py
+++ b/searx/engines/mediawiki.py
@@ -14,7 +14,7 @@
from json import loads
from string import Formatter
-from searx.url_utils import urlencode, quote
+from urllib.parse import urlencode, quote
# engine dependent config
categories = ['general']
@@ -79,7 +79,7 @@ def response(resp):
if result.get('snippet', '').startswith('#REDIRECT'):
continue
url = base_url.format(language=resp.search_params['language']) +\
- 'wiki/' + quote(result['title'].replace(' ', '_').encode('utf-8'))
+ 'wiki/' + quote(result['title'].replace(' ', '_').encode())
# append result
results.append({'url': url,
diff --git a/searx/engines/microsoft_academic.py b/searx/engines/microsoft_academic.py
index 9bac006..7426eef 100644
--- a/searx/engines/microsoft_academic.py
+++ b/searx/engines/microsoft_academic.py
@@ -12,8 +12,7 @@ Microsoft Academic (Science)
from datetime import datetime
from json import loads
from uuid import uuid4
-
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
from searx.utils import html_to_text
categories = ['images']
diff --git a/searx/engines/mixcloud.py b/searx/engines/mixcloud.py
index 470c007..0606350 100644
--- a/searx/engines/mixcloud.py
+++ b/searx/engines/mixcloud.py
@@ -12,7 +12,7 @@
from json import loads
from dateutil import parser
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
# engine dependent config
categories = ['music']
diff --git a/searx/engines/not_evil.py b/searx/engines/not_evil.py
new file mode 100644
index 0000000..e84f153
--- /dev/null
+++ b/searx/engines/not_evil.py
@@ -0,0 +1,64 @@
+"""
+ not Evil (Onions)
+
+ @website http://hss3uro2hsxfogfq.onion
+ @provide-api yes (http://hss3uro2hsxfogfq.onion/api.htm)
+
+ @using-api no
+ @results HTML
+ @stable no
+ @parse url, title, content
+"""
+
+from urllib.parse import urlencode
+from lxml import html
+from searx.engines.xpath import extract_text
+
+# engine dependent config
+categories = ['onions']
+paging = True
+page_size = 20
+
+# search-url
+base_url = 'http://hss3uro2hsxfogfq.onion/'
+search_url = 'index.php?{query}&hostLimit=20&start={pageno}&numRows={page_size}'
+
+# specific xpath variables
+results_xpath = '//*[@id="content"]/div/p'
+url_xpath = './span[1]'
+title_xpath = './a[1]'
+content_xpath = './text()'
+
+
+# do search-request
+def request(query, params):
+ offset = (params['pageno'] - 1) * page_size
+
+ params['url'] = base_url + search_url.format(pageno=offset,
+ query=urlencode({'q': query}),
+ page_size=page_size)
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ # needed because otherwise requests guesses wrong encoding
+ resp.encoding = 'utf8'
+ dom = html.fromstring(resp.text)
+
+ # parse results
+ for result in dom.xpath(results_xpath):
+ url = extract_text(result.xpath(url_xpath)[0])
+ title = extract_text(result.xpath(title_xpath)[0])
+ content = extract_text(result.xpath(content_xpath))
+
+ # append result
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'is_onion': True})
+
+ return results
diff --git a/searx/engines/nyaa.py b/searx/engines/nyaa.py
index c57979a..e0a9149 100644
--- a/searx/engines/nyaa.py
+++ b/searx/engines/nyaa.py
@@ -10,9 +10,8 @@
"""
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
-from searx.utils import get_torrent_size, int_or_zero
+from urllib.parse import urlencode
+from searx.utils import extract_text, get_torrent_size, int_or_zero
# engine dependent config
categories = ['files', 'images', 'videos', 'music']
diff --git a/searx/engines/opensemantic.py b/searx/engines/opensemantic.py
new file mode 100644
index 0000000..9364bab
--- /dev/null
+++ b/searx/engines/opensemantic.py
@@ -0,0 +1,42 @@
+"""
+Open Semantic Search
+
+ @website https://www.opensemanticsearch.org/
+ @provide-api yes (https://www.opensemanticsearch.org/dev)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, content, publishedDate
+"""
+from dateutil import parser
+from json import loads
+from urllib.parse import quote
+
+base_url = 'http://localhost:8983/solr/opensemanticsearch/'
+search_string = 'query?q={query}'
+
+
+def request(query, params):
+ search_path = search_string.format(
+ query=quote(query),
+ )
+ params['url'] = base_url + search_path
+ return params
+
+
+def response(resp):
+ results = []
+ data = loads(resp.text)
+ docs = data.get('response', {}).get('docs', [])
+
+ for current in docs:
+ item = {}
+ item['url'] = current['id']
+ item['title'] = current['title_txt_txt_en']
+ if current.get('content_txt'):
+ item['content'] = current['content_txt'][0]
+ item['publishedDate'] = parser.parse(current['file_modified_dt'])
+ results.append(item)
+
+ return results
diff --git a/searx/engines/openstreetmap.py b/searx/engines/openstreetmap.py
index 257b1a1..5475c7a 100644
--- a/searx/engines/openstreetmap.py
+++ b/searx/engines/openstreetmap.py
@@ -30,8 +30,8 @@ route_re = re.compile('(?:from )?(.+) to (.+)')
# do search-request
def request(query, params):
- params['url'] = base_url + search_string.format(query=query.decode('utf-8'))
- params['route'] = route_re.match(query.decode('utf-8'))
+ params['url'] = base_url + search_string.format(query=query)
+ params['route'] = route_re.match(query)
return params
@@ -52,7 +52,7 @@ def response(resp):
if 'display_name' not in r:
continue
- title = r['display_name'] or u''
+ title = r['display_name'] or ''
osm_type = r.get('osm_type', r.get('type'))
url = result_base_url.format(osm_type=osm_type,
osm_id=r['osm_id'])
@@ -64,7 +64,7 @@ def response(resp):
# if no geojson is found and osm_type is a node, add geojson Point
if not geojson and osm_type == 'node':
- geojson = {u'type': u'Point', u'coordinates': [r['lon'], r['lat']]}
+ geojson = {'type': 'Point', 'coordinates': [r['lon'], r['lat']]}
address_raw = r.get('address')
address = {}
diff --git a/searx/engines/peertube.py b/searx/engines/peertube.py
new file mode 100644
index 0000000..e43b2a6
--- /dev/null
+++ b/searx/engines/peertube.py
@@ -0,0 +1,94 @@
+"""
+ peertube (Videos)
+
+ @website https://www.peertube.live
+ @provide-api yes (https://docs.joinpeertube.org/api-rest-reference.html)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, title, thumbnail, publishedDate, embedded
+
+ @todo implement time range support
+"""
+
+from json import loads
+from datetime import datetime
+from urllib.parse import urlencode
+from searx.utils import html_to_text
+
+# engine dependent config
+categories = ["videos"]
+paging = True
+language_support = True
+base_url = "https://peer.tube/"
+supported_languages_url = base_url + "api/v1/videos/languages"
+
+
+# do search-request
+def request(query, params):
+ pageno = (params["pageno"] - 1) * 15
+ search_url = base_url + "api/v1/search/videos/?pageno={pageno}&{query}"
+ query_dict = {"search": query}
+ language = params["language"].split("-")[0]
+ # pylint: disable=undefined-variable
+ if "all" != language and language in supported_languages:
+ query_dict["languageOneOf"] = language
+ params["url"] = search_url.format(
+ query=urlencode(query_dict), pageno=pageno
+ )
+ return params
+
+
+def _get_offset_from_pageno(pageno):
+ return (pageno - 1) * 15 + 1
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ search_res = loads(resp.text)
+
+ embedded_url = (
+ '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts allow-popups" '
+ + 'src="'
+ + base_url
+ + '{embed_path}" frameborder="0" allowfullscreen></iframe>'
+ )
+ # return empty array if there are no results
+ if "data" not in search_res:
+ return []
+
+ # parse results
+ for res in search_res["data"]:
+ title = res["name"]
+ url = base_url + "/videos/watch/" + res["uuid"]
+ description = res["description"]
+ if description:
+ content = html_to_text(res["description"])
+ else:
+ content = None
+ thumbnail = base_url + res["thumbnailPath"]
+ publishedDate = datetime.strptime(res["publishedAt"], "%Y-%m-%dT%H:%M:%S.%fZ")
+ embedded = embedded_url.format(embed_path=res["embedPath"][1:])
+
+ results.append(
+ {
+ "template": "videos.html",
+ "url": url,
+ "title": title,
+ "content": content,
+ "publishedDate": publishedDate,
+ "embedded": embedded,
+ "thumbnail": thumbnail,
+ }
+ )
+
+ # return results
+ return results
+
+
+def _fetch_supported_languages(resp):
+ peertube_languages = list(loads(resp.text).keys())
+ return peertube_languages
diff --git a/searx/engines/photon.py b/searx/engines/photon.py
index 15236f6..7a6fc83 100644
--- a/searx/engines/photon.py
+++ b/searx/engines/photon.py
@@ -11,8 +11,8 @@
"""
from json import loads
+from urllib.parse import urlencode
from searx.utils import searx_useragent
-from searx.url_utils import urlencode
# engine dependent config
categories = ['map']
@@ -21,7 +21,7 @@ language_support = True
number_of_results = 10
# search-url
-base_url = 'https://photon.komoot.de/'
+base_url = 'https://photon.komoot.io/'
search_string = 'api/?{query}&limit={limit}'
result_base_url = 'https://openstreetmap.org/{osm_type}/{osm_id}'
diff --git a/searx/engines/piratebay.py b/searx/engines/piratebay.py
index 2f3f22a..828241e 100644
--- a/searx/engines/piratebay.py
+++ b/searx/engines/piratebay.py
@@ -1,44 +1,51 @@
# Piratebay (Videos, Music, Files)
#
-# @website https://thepiratebay.se
-# @provide-api no (nothing found)
+# @website https://thepiratebay.org
+# @provide-api yes (https://apibay.org/)
#
-# @using-api no
-# @results HTML (using search portal)
-# @stable yes (HTML can change)
-# @parse url, title, content, seed, leech, magnetlink
+# @using-api yes
+# @results JSON
+# @stable no (the API is not documented nor versioned)
+# @parse url, title, seed, leech, magnetlink, filesize, publishedDate
-from lxml import html
+from json import loads
+from datetime import datetime
from operator import itemgetter
-from searx.engines.xpath import extract_text
-from searx.url_utils import quote, urljoin
+
+from urllib.parse import quote
+from searx.utils import get_torrent_size
# engine dependent config
-categories = ['videos', 'music', 'files']
-paging = True
+categories = ["videos", "music", "files"]
# search-url
-url = 'https://thepiratebay.org/'
-search_url = url + 'search/{search_term}/{pageno}/99/{search_type}'
+url = "https://thepiratebay.org/"
+search_url = "https://apibay.org/q.php?q={search_term}&cat={search_type}"
+
+# default trackers provided by thepiratebay
+trackers = [
+ "udp://tracker.coppersurfer.tk:6969/announce",
+ "udp://9.rarbg.to:2920/announce",
+ "udp://tracker.opentrackr.org:1337",
+ "udp://tracker.internetwarriors.net:1337/announce",
+ "udp://tracker.leechers-paradise.org:6969/announce",
+ "udp://tracker.coppersurfer.tk:6969/announce",
+ "udp://tracker.pirateparty.gr:6969/announce",
+ "udp://tracker.cyberia.is:6969/announce",
+]
# piratebay specific type-definitions
-search_types = {'files': '0',
- 'music': '100',
- 'videos': '200'}
-
-# specific xpath variables
-magnet_xpath = './/a[@title="Download this torrent using magnet"]'
-torrent_xpath = './/a[@title="Download this torrent"]'
-content_xpath = './/font[@class="detDesc"]'
+search_types = {"files": "0",
+ "music": "100",
+ "videos": "200"}
# do search-request
def request(query, params):
- search_type = search_types.get(params['category'], '0')
+ search_type = search_types.get(params["category"], "0")
- params['url'] = search_url.format(search_term=quote(query),
- search_type=search_type,
- pageno=params['pageno'] - 1)
+ params["url"] = search_url.format(search_term=quote(query),
+ search_type=search_type)
return params
@@ -47,50 +54,43 @@ def request(query, params):
def response(resp):
results = []
- dom = html.fromstring(resp.text)
-
- search_res = dom.xpath('//table[@id="searchResult"]//tr')
+ search_res = loads(resp.text)
# return empty array if nothing is found
- if not search_res:
+ if search_res[0]["name"] == "No results returned":
return []
# parse results
- for result in search_res[1:]:
- link = result.xpath('.//div[@class="detName"]//a')[0]
- href = urljoin(url, link.attrib.get('href'))
- title = extract_text(link)
- content = extract_text(result.xpath(content_xpath))
- seed, leech = result.xpath('.//td[@align="right"]/text()')[:2]
-
- # convert seed to int if possible
- if seed.isdigit():
- seed = int(seed)
- else:
- seed = 0
-
- # convert leech to int if possible
- if leech.isdigit():
- leech = int(leech)
- else:
- leech = 0
-
- magnetlink = result.xpath(magnet_xpath)[0]
- torrentfile_links = result.xpath(torrent_xpath)
- if torrentfile_links:
- torrentfile_link = torrentfile_links[0].attrib.get('href')
- else:
- torrentfile_link = None
+ for result in search_res:
+ link = url + "description.php?id=" + result["id"]
+ magnetlink = "magnet:?xt=urn:btih:" + result["info_hash"] + "&dn=" + result["name"]\
+ + "&tr=" + "&tr=".join(trackers)
+
+ params = {
+ "url": link,
+ "title": result["name"],
+ "seed": result["seeders"],
+ "leech": result["leechers"],
+ "magnetlink": magnetlink,
+ "template": "torrent.html"
+ }
+
+ # extract and convert creation date
+ try:
+ date = datetime.fromtimestamp(float(result["added"]))
+ params['publishedDate'] = date
+ except:
+ pass
+
+ # let's try to calculate the torrent size
+ try:
+ filesize = get_torrent_size(result["size"], "B")
+ params['filesize'] = filesize
+ except:
+ pass
# append result
- results.append({'url': href,
- 'title': title,
- 'content': content,
- 'seed': seed,
- 'leech': leech,
- 'magnetlink': magnetlink.attrib.get('href'),
- 'torrentfile': torrentfile_link,
- 'template': 'torrent.html'})
+ results.append(params)
# return results sorted by seeder
- return sorted(results, key=itemgetter('seed'), reverse=True)
+ return sorted(results, key=itemgetter("seed"), reverse=True)
diff --git a/searx/engines/pubmed.py b/searx/engines/pubmed.py
index 055f092..07c4570 100644
--- a/searx/engines/pubmed.py
+++ b/searx/engines/pubmed.py
@@ -14,7 +14,7 @@
from flask_babel import gettext
from lxml import etree
from datetime import datetime
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
from searx.poolrequests import get
@@ -81,7 +81,7 @@ def response(resp):
pass
if len(content) > 300:
- content = content[0:300] + "..."
+ content = content[0:300] + "..."
# TODO: center snippet on query term
res_dict = {'url': url,
diff --git a/searx/engines/qwant.py b/searx/engines/qwant.py
index 54e9daf..b785719 100644
--- a/searx/engines/qwant.py
+++ b/searx/engines/qwant.py
@@ -12,20 +12,21 @@
from datetime import datetime
from json import loads
-from searx.utils import html_to_text
-from searx.url_utils import urlencode
-from searx.utils import match_language
+from urllib.parse import urlencode
+from searx.utils import html_to_text, match_language
+from searx.exceptions import SearxEngineAPIException, SearxEngineCaptchaException
+from searx.raise_for_httperror import raise_for_httperror
+
# engine dependent config
-categories = None
+categories = []
paging = True
language_support = True
supported_languages_url = 'https://qwant.com/region'
category_to_keyword = {'general': 'web',
'images': 'images',
- 'news': 'news',
- 'social media': 'social'}
+ 'news': 'news'}
# search-url
url = 'https://api.qwant.com/api/search/{keyword}?count=10&offset={offset}&f=&{query}&t={keyword}&uiv=4'
@@ -51,6 +52,7 @@ def request(query, params):
params['url'] += '&locale=' + language.replace('-', '_').lower()
params['headers']['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:69.0) Gecko/20100101 Firefox/69.0'
+ params['raise_for_httperror'] = False
return params
@@ -58,8 +60,20 @@ def request(query, params):
def response(resp):
results = []
+ # According to https://www.qwant.com/js/app.js
+ if resp.status_code == 429:
+ raise SearxEngineCaptchaException()
+
+ # raise for other errors
+ raise_for_httperror(resp)
+
+ # load JSON result
search_results = loads(resp.text)
+ # check for an API error
+ if search_results.get('status') != 'success':
+ raise SearxEngineAPIException('API error ' + str(search_results.get('error', '')))
+
# return empty array if there are no results
if 'data' not in search_results:
return []
@@ -90,15 +104,6 @@ def response(resp):
'thumbnail_src': thumbnail_src,
'img_src': img_src})
- elif category_to_keyword.get(categories[0], '') == 'social':
- published_date = datetime.fromtimestamp(result['date'], None)
- img_src = result.get('img', None)
- results.append({'url': res_url,
- 'title': title,
- 'publishedDate': published_date,
- 'content': content,
- 'img_src': img_src})
-
elif category_to_keyword.get(categories[0], '') == 'news':
published_date = datetime.fromtimestamp(result['date'], None)
media = result.get('media', [])
@@ -124,11 +129,10 @@ def _fetch_supported_languages(resp):
regions_json = loads(response_text)
- supported_languages = []
+ supported_languages = {}
for lang in regions_json['languages'].values():
- if lang['code'] == 'nb':
- lang['code'] = 'no'
for country in lang['countries']:
- supported_languages.append(lang['code'] + '-' + country)
+ lang_code = "{lang}-{country}".format(lang=lang['code'], country=country)
+ supported_languages[lang_code] = {'name': lang['name']}
return supported_languages
diff --git a/searx/engines/recoll.py b/searx/engines/recoll.py
new file mode 100644
index 0000000..5a956b8
--- /dev/null
+++ b/searx/engines/recoll.py
@@ -0,0 +1,104 @@
+"""
+ Recoll (local search engine)
+
+ @using-api yes
+ @results JSON
+ @stable yes
+ @parse url, content, size, abstract, author, mtype, subtype, time, \
+ filename, label, type, embedded
+"""
+
+from datetime import date, timedelta
+from json import loads
+from urllib.parse import urlencode, quote
+
+# engine dependent config
+time_range_support = True
+
+# parameters from settings.yml
+base_url = None
+search_dir = ''
+mount_prefix = None
+dl_prefix = None
+
+# embedded
+embedded_url = '<{ttype} controls height="166px" ' +\
+ 'src="{url}" type="{mtype}"></{ttype}>'
+
+
+# helper functions
+def get_time_range(time_range):
+ sw = {
+ 'day': 1,
+ 'week': 7,
+ 'month': 30,
+ 'year': 365
+ }
+
+ offset = sw.get(time_range, 0)
+ if not offset:
+ return ''
+
+ return (date.today() - timedelta(days=offset)).isoformat()
+
+
+# do search-request
+def request(query, params):
+ search_after = get_time_range(params['time_range'])
+ search_url = base_url + 'json?{query}&highlight=0'
+ params['url'] = search_url.format(query=urlencode({
+ 'query': query,
+ 'after': search_after,
+ 'dir': search_dir}))
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+
+ response_json = loads(resp.text)
+
+ if not response_json:
+ return []
+
+ for result in response_json.get('results', []):
+ title = result['label']
+ url = result['url'].replace('file://' + mount_prefix, dl_prefix)
+ content = '{}'.format(result['snippet'])
+
+ # append result
+ item = {'url': url,
+ 'title': title,
+ 'content': content,
+ 'template': 'files.html'}
+
+ if result['size']:
+ item['size'] = int(result['size'])
+
+ for parameter in ['filename', 'abstract', 'author', 'mtype', 'time']:
+ if result[parameter]:
+ item[parameter] = result[parameter]
+
+ # facilitate preview support for known mime types
+ if 'mtype' in result and '/' in result['mtype']:
+ (mtype, subtype) = result['mtype'].split('/')
+ item['mtype'] = mtype
+ item['subtype'] = subtype
+
+ if mtype in ['audio', 'video']:
+ item['embedded'] = embedded_url.format(
+ ttype=mtype,
+ url=quote(url.encode('utf8'), '/:'),
+ mtype=result['mtype'])
+
+ if mtype in ['image'] and subtype in ['bmp', 'gif', 'jpeg', 'png']:
+ item['img_src'] = url
+
+ results.append(item)
+
+ if 'nres' in response_json:
+ results.append({'number_of_results': response_json['nres']})
+
+ return results
diff --git a/searx/engines/reddit.py b/searx/engines/reddit.py
index d197249..e732875 100644
--- a/searx/engines/reddit.py
+++ b/searx/engines/reddit.py
@@ -12,7 +12,7 @@
import json
from datetime import datetime
-from searx.url_utils import urlencode, urljoin, urlparse
+from urllib.parse import urlencode, urljoin, urlparse
# engine dependent config
categories = ['general', 'images', 'news', 'social media']
diff --git a/searx/engines/scanr_structures.py b/searx/engines/scanr_structures.py
index 7208dcb..72fd2b3 100644
--- a/searx/engines/scanr_structures.py
+++ b/searx/engines/scanr_structures.py
@@ -29,7 +29,7 @@ def request(query, params):
params['url'] = search_url
params['method'] = 'POST'
params['headers']['Content-type'] = "application/json"
- params['data'] = dumps({"query": query.decode('utf-8'),
+ params['data'] = dumps({"query": query,
"searchField": "ALL",
"sortDirection": "ASC",
"sortOrder": "RELEVANCY",
diff --git a/searx/engines/searchcode_code.py b/searx/engines/searchcode_code.py
index 789e8e7..7062858 100644
--- a/searx/engines/searchcode_code.py
+++ b/searx/engines/searchcode_code.py
@@ -11,7 +11,7 @@
"""
from json import loads
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
# engine dependent config
diff --git a/searx/engines/searchcode_doc.py b/searx/engines/searchcode_doc.py
deleted file mode 100644
index 4b8e9a8..0000000
--- a/searx/engines/searchcode_doc.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""
- Searchcode (It)
-
- @website https://searchcode.com/
- @provide-api yes (https://searchcode.com/api/)
-
- @using-api yes
- @results JSON
- @stable yes
- @parse url, title, content
-"""
-
-from json import loads
-from searx.url_utils import urlencode
-
-# engine dependent config
-categories = ['it']
-paging = True
-
-# search-url
-url = 'https://searchcode.com/'
-search_url = url + 'api/search_IV/?{query}&p={pageno}'
-
-
-# do search-request
-def request(query, params):
- params['url'] = search_url.format(query=urlencode({'q': query}), pageno=params['pageno'] - 1)
-
- return params
-
-
-# get response from search-request
-def response(resp):
- results = []
-
- search_results = loads(resp.text)
-
- # parse results
- for result in search_results.get('results', []):
- href = result['url']
- title = "[{}] {} {}".format(result['type'], result['namespace'], result['name'])
-
- # append result
- results.append({'url': href,
- 'title': title,
- 'content': result['description']})
-
- # return results
- return results
diff --git a/searx/engines/searx_engine.py b/searx/engines/searx_engine.py
index d4c85bd..87e5e05 100644
--- a/searx/engines/searx_engine.py
+++ b/searx/engines/searx_engine.py
@@ -1,8 +1,8 @@
"""
Searx (all)
- @website https://github.com/asciimoo/searx
- @provide-api yes (https://asciimoo.github.io/searx/dev/search_api.html)
+ @website https://github.com/searx/searx
+ @provide-api yes (https://searx.github.io/searx/dev/search_api.html)
@using-api yes
@results JSON
diff --git a/searx/engines/seedpeer.py b/searx/engines/seedpeer.py
deleted file mode 100644
index f9b1f99..0000000
--- a/searx/engines/seedpeer.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Seedpeer (Videos, Music, Files)
-#
-# @website https://seedpeer.me
-# @provide-api no (nothing found)
-#
-# @using-api no
-# @results HTML (using search portal)
-# @stable yes (HTML can change)
-# @parse url, title, content, seed, leech, magnetlink
-
-from lxml import html
-from json import loads
-from operator import itemgetter
-from searx.url_utils import quote, urljoin
-from searx.engines.xpath import extract_text
-
-
-url = 'https://seedpeer.me/'
-search_url = url + 'search/{search_term}?page={page_no}'
-torrent_file_url = url + 'torrent/{torrent_hash}'
-
-# specific xpath variables
-script_xpath = '//script[@type="text/javascript"][not(@src)]'
-torrent_xpath = '(//table)[2]/tbody/tr'
-link_xpath = '(./td)[1]/a/@href'
-age_xpath = '(./td)[2]'
-size_xpath = '(./td)[3]'
-
-
-# do search-request
-def request(query, params):
- params['url'] = search_url.format(search_term=quote(query),
- page_no=params['pageno'])
- return params
-
-
-# get response from search-request
-def response(resp):
- results = []
- dom = html.fromstring(resp.text)
- result_rows = dom.xpath(torrent_xpath)
-
- try:
- script_element = dom.xpath(script_xpath)[0]
- json_string = script_element.text[script_element.text.find('{'):]
- torrents_json = loads(json_string)
- except:
- return []
-
- # parse results
- for torrent_row, torrent_json in zip(result_rows, torrents_json['data']['list']):
- title = torrent_json['name']
- seed = int(torrent_json['seeds'])
- leech = int(torrent_json['peers'])
- size = int(torrent_json['size'])
- torrent_hash = torrent_json['hash']
-
- torrentfile = torrent_file_url.format(torrent_hash=torrent_hash)
- magnetlink = 'magnet:?xt=urn:btih:{}'.format(torrent_hash)
-
- age = extract_text(torrent_row.xpath(age_xpath))
- link = torrent_row.xpath(link_xpath)[0]
-
- href = urljoin(url, link)
-
- # append result
- results.append({'url': href,
- 'title': title,
- 'content': age,
- 'seed': seed,
- 'leech': leech,
- 'filesize': size,
- 'torrentfile': torrentfile,
- 'magnetlink': magnetlink,
- 'template': 'torrent.html'})
-
- # return results sorted by seeder
- return sorted(results, key=itemgetter('seed'), reverse=True)
diff --git a/searx/engines/sepiasearch.py b/searx/engines/sepiasearch.py
new file mode 100644
index 0000000..0b7c1ba
--- /dev/null
+++ b/searx/engines/sepiasearch.py
@@ -0,0 +1,97 @@
+# SepiaSearch (Videos)
+#
+# @website https://sepiasearch.org
+# @provide-api https://framagit.org/framasoft/peertube/search-index/-/tree/master/server/controllers/api
+# @using-api yes
+# @results JSON
+# @stable yes
+# @parse url, title, content, publishedDate, thumbnail
+
+from json import loads
+from dateutil import parser, relativedelta
+from urllib.parse import urlencode
+from datetime import datetime
+
+categories = ['videos']
+paging = True
+language_support = True
+time_range_support = True
+safesearch = True
+supported_languages = [
+ 'en', 'fr', 'ja', 'eu', 'ca', 'cs', 'eo', 'el',
+ 'de', 'it', 'nl', 'es', 'oc', 'gd', 'zh', 'pt',
+ 'sv', 'pl', 'fi', 'ru'
+]
+base_url = 'https://sepiasearch.org/api/v1/search/videos'
+
+safesearch_table = {
+ 0: 'both',
+ 1: 'false',
+ 2: 'false'
+}
+
+time_range_table = {
+ 'day': relativedelta.relativedelta(),
+ 'week': relativedelta.relativedelta(weeks=-1),
+ 'month': relativedelta.relativedelta(months=-1),
+ 'year': relativedelta.relativedelta(years=-1)
+}
+
+
+embedded_url = '<iframe width="540" height="304" src="{url}" frameborder="0" allowfullscreen></iframe>'
+
+
+def minute_to_hm(minute):
+ if isinstance(minute, int):
+ return "%d:%02d" % (divmod(minute, 60))
+ return None
+
+
+def request(query, params):
+ params['url'] = base_url + '?' + urlencode({
+ 'search': query,
+ 'start': (params['pageno'] - 1) * 10,
+ 'count': 10,
+ 'sort': '-match',
+ 'nsfw': safesearch_table[params['safesearch']]
+ })
+
+ language = params['language'].split('-')[0]
+ if language in supported_languages:
+ params['url'] += '&languageOneOf[]=' + language
+ if params['time_range'] in time_range_table:
+ time = datetime.now().date() + time_range_table[params['time_range']]
+ params['url'] += '&startDate=' + time.isoformat()
+
+ return params
+
+
+def response(resp):
+ results = []
+
+ search_results = loads(resp.text)
+
+ if 'data' not in search_results:
+ return []
+
+ for result in search_results['data']:
+ title = result['name']
+ content = result['description']
+ thumbnail = result['thumbnailUrl']
+ publishedDate = parser.parse(result['publishedAt'])
+ embedded = embedded_url.format(url=result.get('embedUrl'))
+ author = result.get('account', {}).get('displayName')
+ length = minute_to_hm(result.get('duration'))
+ url = result['url']
+
+ results.append({'url': url,
+ 'title': title,
+ 'content': content,
+ 'author': author,
+ 'length': length,
+ 'template': 'videos.html',
+ 'publishedDate': publishedDate,
+ 'embedded': embedded,
+ 'thumbnail': thumbnail})
+
+ return results
diff --git a/searx/engines/soundcloud.py b/searx/engines/soundcloud.py
index 284689b..84ff21a 100644
--- a/searx/engines/soundcloud.py
+++ b/searx/engines/soundcloud.py
@@ -14,14 +14,10 @@ import re
from json import loads
from lxml import html
from dateutil import parser
+from urllib.parse import quote_plus, urlencode
from searx import logger
from searx.poolrequests import get as http_get
-from searx.url_utils import quote_plus, urlencode
-try:
- from cStringIO import StringIO
-except:
- from io import StringIO
# engine dependent config
categories = ['music']
@@ -61,7 +57,7 @@ def get_client_id():
# gets app_js and searches for the clientid
response = http_get(app_js_url)
if response.ok:
- cids = cid_re.search(response.content.decode("utf-8"))
+ cids = cid_re.search(response.content.decode())
if cids is not None and len(cids.groups()):
return cids.groups()[0]
logger.warning("Unable to fetch guest client_id from SoundCloud, check parser!")
@@ -95,7 +91,7 @@ def response(resp):
for result in search_res.get('collection', []):
if result['kind'] in ('track', 'playlist'):
title = result['title']
- content = result['description']
+ content = result['description'] or ''
publishedDate = parser.parse(result['last_modified'])
uri = quote_plus(result['uri'])
embedded = embedded_url.format(uri=uri)
diff --git a/searx/engines/spotify.py b/searx/engines/spotify.py
index 00c3957..7494232 100644
--- a/searx/engines/spotify.py
+++ b/searx/engines/spotify.py
@@ -11,7 +11,7 @@
"""
from json import loads
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
import requests
import base64
@@ -39,8 +39,8 @@ def request(query, params):
'https://accounts.spotify.com/api/token',
data={'grant_type': 'client_credentials'},
headers={'Authorization': 'Basic ' + base64.b64encode(
- "{}:{}".format(api_client_id, api_client_secret).encode('utf-8')
- ).decode('utf-8')}
+ "{}:{}".format(api_client_id, api_client_secret).encode()
+ ).decode()}
)
j = loads(r.text)
params['headers'] = {'Authorization': 'Bearer {}'.format(j.get('access_token'))}
@@ -59,7 +59,7 @@ def response(resp):
if result['type'] == 'track':
title = result['name']
url = result['external_urls']['spotify']
- content = u'{} - {} - {}'.format(
+ content = '{} - {} - {}'.format(
result['artists'][0]['name'],
result['album']['name'],
result['name'])
diff --git a/searx/engines/stackoverflow.py b/searx/engines/stackoverflow.py
index 25875aa..f730264 100644
--- a/searx/engines/stackoverflow.py
+++ b/searx/engines/stackoverflow.py
@@ -10,9 +10,10 @@
@parse url, title, content
"""
+from urllib.parse import urlencode, urljoin, urlparse
from lxml import html
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode, urljoin
+from searx.utils import extract_text
+from searx.exceptions import SearxEngineCaptchaException
# engine dependent config
categories = ['it']
@@ -37,6 +38,10 @@ def request(query, params):
# get response from search-request
def response(resp):
+ resp_url = urlparse(resp.url)
+ if resp_url.path.startswith('/nocaptcha'):
+ raise SearxEngineCaptchaException()
+
results = []
dom = html.fromstring(resp.text)
diff --git a/searx/engines/startpage.py b/searx/engines/startpage.py
index 9537349..16e846a 100644
--- a/searx/engines/startpage.py
+++ b/searx/engines/startpage.py
@@ -14,9 +14,10 @@ from lxml import html
from dateutil import parser
from datetime import datetime, timedelta
import re
-from searx.engines.xpath import extract_text
-from searx.languages import language_codes
-from searx.utils import eval_xpath
+from unicodedata import normalize, combining
+from babel import Locale
+from babel.localedata import locale_identifiers
+from searx.utils import extract_text, eval_xpath, match_language
# engine dependent config
categories = ['general']
@@ -26,6 +27,7 @@ categories = ['general']
paging = True
language_support = True
+supported_languages_url = 'https://www.startpage.com/do/settings'
# search-url
base_url = 'https://startpage.com/'
@@ -34,8 +36,8 @@ search_url = base_url + 'do/search'
# specific xpath variables
# ads xpath //div[@id="results"]/div[@id="sponsored"]//div[@class="result"]
# not ads: div[@class="result"] are the direct childs of div[@id="results"]
-results_xpath = '//div[@class="w-gl__result"]'
-link_xpath = './/a[@class="w-gl__result-title"]'
+results_xpath = '//div[@class="w-gl__result__main"]'
+link_xpath = './/a[@class="w-gl__result-url result-link"]'
content_xpath = './/p[@class="w-gl__description"]'
@@ -54,12 +56,11 @@ def request(query, params):
# set language if specified
if params['language'] != 'all':
- language = 'english'
- for lc, _, _, lang in language_codes:
- if lc == params['language']:
- language = lang
- params['data']['language'] = language
- params['data']['lui'] = language
+ lang_code = match_language(params['language'], supported_languages, fallback=None)
+ if lang_code:
+ language_name = supported_languages[lang_code]['alias']
+ params['data']['language'] = language_name
+ params['data']['lui'] = language_name
return params
@@ -132,3 +133,55 @@ def response(resp):
# return results
return results
+
+
+# get supported languages from their site
+def _fetch_supported_languages(resp):
+ # startpage's language selector is a mess
+ # each option has a displayed name and a value, either of which may represent the language name
+ # in the native script, the language name in English, an English transliteration of the native name,
+ # the English name of the writing script used by the language, or occasionally something else entirely.
+
+ # this cases are so special they need to be hardcoded, a couple of them are mispellings
+ language_names = {
+ 'english_uk': 'en-GB',
+ 'fantizhengwen': ['zh-TW', 'zh-HK'],
+ 'hangul': 'ko',
+ 'malayam': 'ml',
+ 'norsk': 'nb',
+ 'sinhalese': 'si',
+ 'sudanese': 'su'
+ }
+
+ # get the English name of every language known by babel
+ language_names.update({name.lower(): lang_code for lang_code, name in Locale('en')._data['languages'].items()})
+
+ # get the native name of every language known by babel
+ for lang_code in filter(lambda lang_code: lang_code.find('_') == -1, locale_identifiers()):
+ native_name = Locale(lang_code).get_language_name().lower()
+ # add native name exactly as it is
+ language_names[native_name] = lang_code
+
+ # add "normalized" language name (i.e. français becomes francais and español becomes espanol)
+ unaccented_name = ''.join(filter(lambda c: not combining(c), normalize('NFKD', native_name)))
+ if len(unaccented_name) == len(unaccented_name.encode()):
+ # add only if result is ascii (otherwise "normalization" didn't work)
+ language_names[unaccented_name] = lang_code
+
+ dom = html.fromstring(resp.text)
+ sp_lang_names = []
+ for option in dom.xpath('//form[@id="settings-form"]//select[@name="language"]/option'):
+ sp_lang_names.append((option.get('value'), extract_text(option).lower()))
+
+ supported_languages = {}
+ for sp_option_value, sp_option_text in sp_lang_names:
+ lang_code = language_names.get(sp_option_value) or language_names.get(sp_option_text)
+ if isinstance(lang_code, str):
+ supported_languages[lang_code] = {'alias': sp_option_value}
+ elif isinstance(lang_code, list):
+ for lc in lang_code:
+ supported_languages[lc] = {'alias': sp_option_value}
+ else:
+ print('Unknown language option in Startpage: {} ({})'.format(sp_option_value, sp_option_text))
+
+ return supported_languages
diff --git a/searx/engines/tokyotoshokan.py b/searx/engines/tokyotoshokan.py
index 7732120..9fffba8 100644
--- a/searx/engines/tokyotoshokan.py
+++ b/searx/engines/tokyotoshokan.py
@@ -11,11 +11,10 @@
"""
import re
+from urllib.parse import urlencode
from lxml import html
-from searx.engines.xpath import extract_text
from datetime import datetime
-from searx.url_utils import urlencode
-from searx.utils import get_torrent_size, int_or_zero
+from searx.utils import extract_text, get_torrent_size, int_or_zero
# engine dependent config
categories = ['files', 'videos', 'music']
diff --git a/searx/engines/torrentz.py b/searx/engines/torrentz.py
index fd4164a..4d3e6fd 100644
--- a/searx/engines/torrentz.py
+++ b/searx/engines/torrentz.py
@@ -1,30 +1,29 @@
"""
- Torrentz2.eu (BitTorrent meta-search engine)
+ Torrentz2.is (BitTorrent meta-search engine)
- @website https://torrentz2.eu/
+ @website https://torrentz2.is/
@provide-api no
@using-api no
@results HTML
@stable no (HTML can change, although unlikely,
- see https://torrentz.eu/torrentz.btsearch)
+ see https://torrentz.is/torrentz.btsearch)
@parse url, title, publishedDate, seed, leech, filesize, magnetlink
"""
import re
+from urllib.parse import urlencode
from lxml import html
from datetime import datetime
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode
-from searx.utils import get_torrent_size
+from searx.utils import extract_text, get_torrent_size
# engine dependent config
categories = ['files', 'videos', 'music']
paging = True
# search-url
-# https://torrentz2.eu/search?f=EXAMPLE&p=6
-base_url = 'https://torrentz2.eu/'
+# https://torrentz2.is/search?f=EXAMPLE&p=6
+base_url = 'https://torrentz2.is/'
search_url = base_url + 'search?{query}'
diff --git a/searx/engines/translated.py b/searx/engines/translated.py
index 5c7b170..75b8b5f 100644
--- a/searx/engines/translated.py
+++ b/searx/engines/translated.py
@@ -9,23 +9,20 @@
@parse url, title, content
"""
import re
-from sys import version_info
from searx.utils import is_valid_lang
-if version_info[0] == 3:
- unicode = str
-
categories = ['general']
-url = u'http://api.mymemory.translated.net/get?q={query}&langpair={from_lang}|{to_lang}{key}'
-web_url = u'http://mymemory.translated.net/en/{from_lang}/{to_lang}/{query}'
+url = 'https://api.mymemory.translated.net/get?q={query}&langpair={from_lang}|{to_lang}{key}'
+web_url = 'https://mymemory.translated.net/en/{from_lang}/{to_lang}/{query}'
weight = 100
+https_support = True
-parser_re = re.compile(u'.*?([a-z]+)-([a-z]+) (.{2,})$', re.I)
+parser_re = re.compile('.*?([a-z]+)-([a-z]+) (.{2,})$', re.I)
api_key = ''
def request(query, params):
- m = parser_re.match(unicode(query, 'utf8'))
+ m = parser_re.match(query)
if not m:
return params
diff --git a/searx/engines/twitter.py b/searx/engines/twitter.py
deleted file mode 100644
index d2a8d20..0000000
--- a/searx/engines/twitter.py
+++ /dev/null
@@ -1,87 +0,0 @@
-"""
- Twitter (Social media)
-
- @website https://twitter.com/
- @provide-api yes (https://dev.twitter.com/docs/using-search)
-
- @using-api no
- @results HTML (using search portal)
- @stable no (HTML can change)
- @parse url, title, content
-
- @todo publishedDate
-"""
-
-from lxml import html
-from datetime import datetime
-from searx.engines.xpath import extract_text
-from searx.url_utils import urlencode, urljoin
-
-# engine dependent config
-categories = ['social media']
-language_support = True
-
-# search-url
-base_url = 'https://twitter.com/'
-search_url = base_url + 'search?'
-
-# specific xpath variables
-results_xpath = '//li[@data-item-type="tweet"]'
-avatar_xpath = './/img[contains(@class, "avatar")]/@src'
-link_xpath = './/small[@class="time"]//a'
-title_xpath = './/span[contains(@class, "username")]'
-content_xpath = './/p[contains(@class, "tweet-text")]'
-timestamp_xpath = './/span[contains(@class,"_timestamp")]'
-
-
-# do search-request
-def request(query, params):
- params['url'] = search_url + urlencode({'q': query})
-
- # set language if specified
- if params['language'] != 'all':
- params['cookies']['lang'] = params['language'].split('-')[0]
- else:
- params['cookies']['lang'] = 'en'
-
- return params
-
-
-# get response from search-request
-def response(resp):
- results = []
-
- dom = html.fromstring(resp.text)
-
- # parse results
- for tweet in dom.xpath(results_xpath):
- try:
- link = tweet.xpath(link_xpath)[0]
- content = extract_text(tweet.xpath(content_xpath)[0])
- img_src = tweet.xpath(avatar_xpath)[0]
- img_src = img_src.replace('_bigger', '_normal')
- except Exception:
- continue
-
- url = urljoin(base_url, link.attrib.get('href'))
- title = extract_text(tweet.xpath(title_xpath))
-
- pubdate = tweet.xpath(timestamp_xpath)
- if len(pubdate) > 0:
- timestamp = float(pubdate[0].attrib.get('data-time'))
- publishedDate = datetime.fromtimestamp(timestamp, None)
- # append result
- results.append({'url': url,
- 'title': title,
- 'content': content,
- 'img_src': img_src,
- 'publishedDate': publishedDate})
- else:
- # append result
- results.append({'url': url,
- 'title': title,
- 'content': content,
- 'img_src': img_src})
-
- # return results
- return results
diff --git a/searx/engines/unsplash.py b/searx/engines/unsplash.py
index 2e8d6fd..45c6b30 100644
--- a/searx/engines/unsplash.py
+++ b/searx/engines/unsplash.py
@@ -10,7 +10,7 @@
@parse url, title, img_src, thumbnail_src
"""
-from searx.url_utils import urlencode, urlparse, urlunparse, parse_qsl
+from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl
from json import loads
url = 'https://unsplash.com/'
diff --git a/searx/engines/vimeo.py b/searx/engines/vimeo.py
index a922710..fd3abc8 100644
--- a/searx/engines/vimeo.py
+++ b/searx/engines/vimeo.py
@@ -12,9 +12,9 @@
# @todo rewrite to api
# @todo set content-parameter with correct data
+from urllib.parse import urlencode
from json import loads
from dateutil import parser
-from searx.url_utils import urlencode
# engine dependent config
categories = ['videos']
diff --git a/searx/engines/wikidata.py b/searx/engines/wikidata.py
index 9d6238d..8d787ca 100644
--- a/searx/engines/wikidata.py
+++ b/searx/engines/wikidata.py
@@ -3,500 +3,690 @@
Wikidata
@website https://wikidata.org
- @provide-api yes (https://wikidata.org/w/api.php)
+ @provide-api yes (https://query.wikidata.org/)
- @using-api partially (most things require scraping)
- @results JSON, HTML
- @stable no (html can change)
+ @using-api yes
+ @results JSON
+ @stable yes
@parse url, infobox
"""
-from searx import logger
-from searx.poolrequests import get
-from searx.engines.xpath import extract_text
-from searx.engines.wikipedia import _fetch_supported_languages, supported_languages_url
-from searx.url_utils import urlencode
-from searx.utils import match_language, eval_xpath
+from urllib.parse import urlencode
from json import loads
-from lxml.html import fromstring
-from lxml import etree
+
+from dateutil.parser import isoparse
+from babel.dates import format_datetime, format_date, format_time, get_datetime_format
+
+from searx import logger
+from searx.data import WIKIDATA_UNITS
+from searx.poolrequests import post, get
+from searx.utils import match_language, searx_useragent, get_string_replaces_function
+from searx.external_urls import get_external_url, get_earth_coordinates_url, area_to_osm_zoom
+from searx.engines.wikipedia import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
logger = logger.getChild('wikidata')
-result_count = 1
-
-# urls
-wikidata_host = 'https://www.wikidata.org'
-url_search = wikidata_host \
- + '/w/index.php?{query}&ns0=1'
-
-wikidata_api = wikidata_host + '/w/api.php'
-url_detail = wikidata_api\
- + '?action=parse&format=json&{query}'\
- + '&redirects=1&prop=text%7Cdisplaytitle%7Cparsewarnings'\
- + '&disableeditsection=1&preview=1&sectionpreview=1&disabletoc=1&utf8=1&formatversion=2'
-
-url_map = 'https://www.openstreetmap.org/'\
- + '?lat={latitude}&lon={longitude}&zoom={zoom}&layers=M'
-url_image = 'https://commons.wikimedia.org/wiki/Special:FilePath/{filename}?width=500&height=400'
-
-# xpaths
-div_ids_xpath = '//div[@id]'
-wikidata_ids_xpath = '//ul[@class="mw-search-results"]/li//a/@href'
-title_xpath = '//*[contains(@class,"wikibase-title-label")]'
-description_xpath = '//div[contains(@class,"wikibase-entitytermsview-heading-description")]'
-label_xpath = './/div[contains(@class,"wikibase-statementgroupview-property-label")]/a'
-url_xpath = './/a[contains(@class,"external free") or contains(@class, "wb-external-id")]'
-wikilink_xpath = './/ul[contains(@class,"wikibase-sitelinklistview-listview")]'\
- + '/li[contains(@data-wb-siteid,"{wikiid}")]//a/@href'
-property_row_xpath = './/div[contains(@class,"wikibase-statementview")]'
-preferred_rank_xpath = './/span[contains(@class,"wikibase-rankselector-preferred")]'
-value_xpath = './/div[contains(@class,"wikibase-statementview-mainsnak")]'\
- + '/*/div[contains(@class,"wikibase-snakview-value")]'
-language_fallback_xpath = '//sup[contains(@class,"wb-language-fallback-indicator")]'
-calendar_name_xpath = './/sup[contains(@class,"wb-calendar-name")]'
-media_xpath = value_xpath + '//div[contains(@class,"commons-media-caption")]//a'
-
-
-def get_id_cache(result):
- id_cache = {}
- for e in eval_xpath(result, div_ids_xpath):
- id = e.get('id')
- if id.startswith('P'):
- id_cache[id] = e
- return id_cache
+
+# SPARQL
+SPARQL_ENDPOINT_URL = 'https://query.wikidata.org/sparql'
+SPARQL_EXPLAIN_URL = 'https://query.wikidata.org/bigdata/namespace/wdq/sparql?explain'
+WIKIDATA_PROPERTIES = {
+ 'P434': 'MusicBrainz',
+ 'P435': 'MusicBrainz',
+ 'P436': 'MusicBrainz',
+ 'P966': 'MusicBrainz',
+ 'P345': 'IMDb',
+ 'P2397': 'YouTube',
+ 'P1651': 'YouTube',
+ 'P2002': 'Twitter',
+ 'P2013': 'Facebook',
+ 'P2003': 'Instagram',
+}
+
+# SERVICE wikibase:mwapi : https://www.mediawiki.org/wiki/Wikidata_Query_Service/User_Manual/MWAPI
+# SERVICE wikibase:label: https://en.wikibooks.org/wiki/SPARQL/SERVICE_-_Label#Manual_Label_SERVICE
+# https://en.wikibooks.org/wiki/SPARQL/WIKIDATA_Precision,_Units_and_Coordinates
+# https://www.mediawiki.org/wiki/Wikibase/Indexing/RDF_Dump_Format#Data_model
+# optmization:
+# * https://www.wikidata.org/wiki/Wikidata:SPARQL_query_service/query_optimization
+# * https://github.com/blazegraph/database/wiki/QueryHints
+QUERY_TEMPLATE = """
+SELECT ?item ?itemLabel ?itemDescription ?lat ?long %SELECT%
+WHERE
+{
+ SERVICE wikibase:mwapi {
+ bd:serviceParam wikibase:endpoint "www.wikidata.org";
+ wikibase:api "EntitySearch";
+ wikibase:limit 1;
+ mwapi:search "%QUERY%";
+ mwapi:language "%LANGUAGE%".
+ ?item wikibase:apiOutputItem mwapi:item.
+ }
+
+ %WHERE%
+
+ SERVICE wikibase:label {
+ bd:serviceParam wikibase:language "%LANGUAGE%,en".
+ ?item rdfs:label ?itemLabel .
+ ?item schema:description ?itemDescription .
+ %WIKIBASE_LABELS%
+ }
+
+}
+GROUP BY ?item ?itemLabel ?itemDescription ?lat ?long %GROUP_BY%
+"""
+
+# Get the calendar names and the property names
+QUERY_PROPERTY_NAMES = """
+SELECT ?item ?name
+WHERE {
+ {
+ SELECT ?item
+ WHERE { ?item wdt:P279* wd:Q12132 }
+ } UNION {
+ VALUES ?item { %ATTRIBUTES% }
+ }
+ OPTIONAL { ?item rdfs:label ?name. }
+}
+"""
+
+
+# https://www.w3.org/TR/sparql11-query/#rSTRING_LITERAL1
+# https://lists.w3.org/Archives/Public/public-rdf-dawg/2011OctDec/0175.html
+sparql_string_escape = get_string_replaces_function({'\t': '\\\t',
+ '\n': '\\\n',
+ '\r': '\\\r',
+ '\b': '\\\b',
+ '\f': '\\\f',
+ '\"': '\\\"',
+ '\'': '\\\'',
+ '\\': '\\\\'})
+
+replace_http_by_https = get_string_replaces_function({'http:': 'https:'})
+
+
+def get_headers():
+ # user agent: https://www.mediawiki.org/wiki/Wikidata_Query_Service/User_Manual#Query_limits
+ return {
+ 'Accept': 'application/sparql-results+json',
+ 'User-Agent': searx_useragent()
+ }
+
+
+def get_label_for_entity(entity_id, language):
+ name = WIKIDATA_PROPERTIES.get(entity_id)
+ if name is None:
+ name = WIKIDATA_PROPERTIES.get((entity_id, language))
+ if name is None:
+ name = WIKIDATA_PROPERTIES.get((entity_id, language.split('-')[0]))
+ if name is None:
+ name = WIKIDATA_PROPERTIES.get((entity_id, 'en'))
+ if name is None:
+ name = entity_id
+ return name
+
+
+def send_wikidata_query(query, method='GET'):
+ if method == 'GET':
+ # query will be cached by wikidata
+ http_response = get(SPARQL_ENDPOINT_URL + '?' + urlencode({'query': query}), headers=get_headers())
+ else:
+ # query won't be cached by wikidata
+ http_response = post(SPARQL_ENDPOINT_URL, data={'query': query}, headers=get_headers())
+ if http_response.status_code != 200:
+ logger.debug('SPARQL endpoint error %s', http_response.content.decode())
+ logger.debug('request time %s', str(http_response.elapsed))
+ http_response.raise_for_status()
+ return loads(http_response.content.decode())
def request(query, params):
- params['url'] = url_search.format(
- query=urlencode({'search': query}))
+ language = params['language'].split('-')[0]
+ if language == 'all':
+ language = 'en'
+ else:
+ language = match_language(params['language'], supported_languages, language_aliases).split('-')[0]
+
+ query, attributes = get_query(query, language)
+
+ params['method'] = 'POST'
+ params['url'] = SPARQL_ENDPOINT_URL
+ params['data'] = {'query': query}
+ params['headers'] = get_headers()
+
+ params['language'] = language
+ params['attributes'] = attributes
return params
def response(resp):
results = []
- htmlparser = etree.HTMLParser()
- html = fromstring(resp.content.decode("utf-8"), parser=htmlparser)
- search_results = eval_xpath(html, wikidata_ids_xpath)
+ jsonresponse = loads(resp.content.decode())
- if resp.search_params['language'].split('-')[0] == 'all':
- language = 'en'
- else:
- language = match_language(resp.search_params['language'], supported_languages, language_aliases).split('-')[0]
+ language = resp.search_params['language'].lower()
+ attributes = resp.search_params['attributes']
+
+ seen_entities = set()
- # TODO: make requests asynchronous to avoid timeout when result_count > 1
- for search_result in search_results[:result_count]:
- wikidata_id = search_result.split('/')[-1]
- url = url_detail.format(query=urlencode({'page': wikidata_id, 'uselang': language}))
- htmlresponse = get(url)
- jsonresponse = loads(htmlresponse.content.decode("utf-8"))
- results += getDetail(jsonresponse, wikidata_id, language, resp.search_params['language'], htmlparser)
+ for result in jsonresponse.get('results', {}).get('bindings', []):
+ attribute_result = {key: value['value'] for key, value in result.items()}
+ entity_url = attribute_result['item']
+ if entity_url not in seen_entities:
+ seen_entities.add(entity_url)
+ results += get_results(attribute_result, attributes, language)
+ else:
+ logger.debug('The SPARQL request returns duplicate entities: %s', str(attribute_result))
return results
-def getDetail(jsonresponse, wikidata_id, language, locale, htmlparser):
+def get_results(attribute_result, attributes, language):
results = []
- urls = []
- attributes = []
+ infobox_title = attribute_result.get('itemLabel')
+ infobox_id = attribute_result['item']
+ infobox_id_lang = None
+ infobox_urls = []
+ infobox_attributes = []
+ infobox_content = attribute_result.get('itemDescription', [])
+ img_src = None
+ img_src_priority = 100
+
+ for attribute in attributes:
+ value = attribute.get_str(attribute_result, language)
+ if value is not None and value != '':
+ attribute_type = type(attribute)
+
+ if attribute_type in (WDURLAttribute, WDArticle):
+ # get_select() method : there is group_concat(distinct ...;separator=", ")
+ # split the value here
+ for url in value.split(', '):
+ infobox_urls.append({'title': attribute.get_label(language), 'url': url, **attribute.kwargs})
+ # "normal" results (not infobox) include official website and Wikipedia links.
+ if attribute.kwargs.get('official') or attribute_type == WDArticle:
+ results.append({'title': infobox_title, 'url': url})
+ # update the infobox_id with the wikipedia URL
+ # first the local wikipedia URL, and as fallback the english wikipedia URL
+ if attribute_type == WDArticle\
+ and ((attribute.language == 'en' and infobox_id_lang is None)
+ or attribute.language != 'en'):
+ infobox_id_lang = attribute.language
+ infobox_id = url
+ elif attribute_type == WDImageAttribute:
+ # this attribute is an image.
+ # replace the current image only the priority is lower
+ # (the infobox contain only one image).
+ if attribute.priority < img_src_priority:
+ img_src = value
+ img_src_priority = attribute.priority
+ elif attribute_type == WDGeoAttribute:
+ # geocoordinate link
+ # use the area to get the OSM zoom
+ # Note: ignre the unit (must be km² otherwise the calculation is wrong)
+ # Should use normalized value p:P2046/psn:P2046/wikibase:quantityAmount
+ area = attribute_result.get('P2046')
+ osm_zoom = area_to_osm_zoom(area) if area else 19
+ url = attribute.get_geo_url(attribute_result, osm_zoom=osm_zoom)
+ if url:
+ infobox_urls.append({'title': attribute.get_label(language),
+ 'url': url,
+ 'entity': attribute.name})
+ else:
+ infobox_attributes.append({'label': attribute.get_label(language),
+ 'value': value,
+ 'entity': attribute.name})
+
+ if infobox_id:
+ infobox_id = replace_http_by_https(infobox_id)
- title = jsonresponse.get('parse', {}).get('displaytitle', {})
- result = jsonresponse.get('parse', {}).get('text', {})
-
- if not title or not result:
- return results
-
- title = fromstring(title, parser=htmlparser)
- for elem in eval_xpath(title, language_fallback_xpath):
- elem.getparent().remove(elem)
- title = extract_text(eval_xpath(title, title_xpath))
-
- result = fromstring(result, parser=htmlparser)
- for elem in eval_xpath(result, language_fallback_xpath):
- elem.getparent().remove(elem)
-
- description = extract_text(eval_xpath(result, description_xpath))
-
- id_cache = get_id_cache(result)
-
- # URLS
-
- # official website
- add_url(urls, result, id_cache, 'P856', results=results)
-
- # wikipedia
- wikipedia_link_count = 0
- wikipedia_link = get_wikilink(result, language + 'wiki')
- if wikipedia_link:
- wikipedia_link_count += 1
- urls.append({'title': 'Wikipedia (' + language + ')',
- 'url': wikipedia_link})
-
- if language != 'en':
- wikipedia_en_link = get_wikilink(result, 'enwiki')
- if wikipedia_en_link:
- wikipedia_link_count += 1
- urls.append({'title': 'Wikipedia (en)',
- 'url': wikipedia_en_link})
-
- # TODO: get_wiki_firstlanguage
- # if wikipedia_link_count == 0:
-
- # more wikis
- add_url(urls, result, id_cache, default_label='Wikivoyage (' + language + ')', link_type=language + 'wikivoyage')
- add_url(urls, result, id_cache, default_label='Wikiquote (' + language + ')', link_type=language + 'wikiquote')
- add_url(urls, result, id_cache, default_label='Wikimedia Commons', link_type='commonswiki')
-
- add_url(urls, result, id_cache, 'P625', 'OpenStreetMap', link_type='geo')
-
- # musicbrainz
- add_url(urls, result, id_cache, 'P434', 'MusicBrainz', 'http://musicbrainz.org/artist/')
- add_url(urls, result, id_cache, 'P435', 'MusicBrainz', 'http://musicbrainz.org/work/')
- add_url(urls, result, id_cache, 'P436', 'MusicBrainz', 'http://musicbrainz.org/release-group/')
- add_url(urls, result, id_cache, 'P966', 'MusicBrainz', 'http://musicbrainz.org/label/')
-
- # IMDb
- add_url(urls, result, id_cache, 'P345', 'IMDb', 'https://www.imdb.com/', link_type='imdb')
- # source code repository
- add_url(urls, result, id_cache, 'P1324')
- # blog
- add_url(urls, result, id_cache, 'P1581')
- # social media links
- add_url(urls, result, id_cache, 'P2397', 'YouTube', 'https://www.youtube.com/channel/')
- add_url(urls, result, id_cache, 'P1651', 'YouTube', 'https://www.youtube.com/watch?v=')
- add_url(urls, result, id_cache, 'P2002', 'Twitter', 'https://twitter.com/')
- add_url(urls, result, id_cache, 'P2013', 'Facebook', 'https://facebook.com/')
- add_url(urls, result, id_cache, 'P2003', 'Instagram', 'https://instagram.com/')
-
- urls.append({'title': 'Wikidata',
- 'url': 'https://www.wikidata.org/wiki/'
- + wikidata_id + '?uselang=' + language})
-
- # INFOBOX ATTRIBUTES (ROWS)
-
- # DATES
- # inception date
- add_attribute(attributes, id_cache, 'P571', date=True)
- # dissolution date
- add_attribute(attributes, id_cache, 'P576', date=True)
- # start date
- add_attribute(attributes, id_cache, 'P580', date=True)
- # end date
- add_attribute(attributes, id_cache, 'P582', date=True)
- # date of birth
- add_attribute(attributes, id_cache, 'P569', date=True)
- # date of death
- add_attribute(attributes, id_cache, 'P570', date=True)
- # date of spacecraft launch
- add_attribute(attributes, id_cache, 'P619', date=True)
- # date of spacecraft landing
- add_attribute(attributes, id_cache, 'P620', date=True)
-
- # nationality
- add_attribute(attributes, id_cache, 'P27')
- # country of origin
- add_attribute(attributes, id_cache, 'P495')
- # country
- add_attribute(attributes, id_cache, 'P17')
- # headquarters
- add_attribute(attributes, id_cache, 'Q180')
-
- # PLACES
- # capital
- add_attribute(attributes, id_cache, 'P36', trim=True)
- # head of state
- add_attribute(attributes, id_cache, 'P35', trim=True)
- # head of government
- add_attribute(attributes, id_cache, 'P6', trim=True)
- # type of government
- add_attribute(attributes, id_cache, 'P122')
- # official language
- add_attribute(attributes, id_cache, 'P37')
- # population
- add_attribute(attributes, id_cache, 'P1082', trim=True)
- # area
- add_attribute(attributes, id_cache, 'P2046')
- # currency
- add_attribute(attributes, id_cache, 'P38', trim=True)
- # heigth (building)
- add_attribute(attributes, id_cache, 'P2048')
-
- # MEDIA
- # platform (videogames)
- add_attribute(attributes, id_cache, 'P400')
- # author
- add_attribute(attributes, id_cache, 'P50')
- # creator
- add_attribute(attributes, id_cache, 'P170')
- # director
- add_attribute(attributes, id_cache, 'P57')
- # performer
- add_attribute(attributes, id_cache, 'P175')
- # developer
- add_attribute(attributes, id_cache, 'P178')
- # producer
- add_attribute(attributes, id_cache, 'P162')
- # manufacturer
- add_attribute(attributes, id_cache, 'P176')
- # screenwriter
- add_attribute(attributes, id_cache, 'P58')
- # production company
- add_attribute(attributes, id_cache, 'P272')
- # record label
- add_attribute(attributes, id_cache, 'P264')
- # publisher
- add_attribute(attributes, id_cache, 'P123')
- # original network
- add_attribute(attributes, id_cache, 'P449')
- # distributor
- add_attribute(attributes, id_cache, 'P750')
- # composer
- add_attribute(attributes, id_cache, 'P86')
- # publication date
- add_attribute(attributes, id_cache, 'P577', date=True)
- # genre
- add_attribute(attributes, id_cache, 'P136')
- # original language
- add_attribute(attributes, id_cache, 'P364')
- # isbn
- add_attribute(attributes, id_cache, 'Q33057')
- # software license
- add_attribute(attributes, id_cache, 'P275')
- # programming language
- add_attribute(attributes, id_cache, 'P277')
- # version
- add_attribute(attributes, id_cache, 'P348', trim=True)
- # narrative location
- add_attribute(attributes, id_cache, 'P840')
-
- # LANGUAGES
- # number of speakers
- add_attribute(attributes, id_cache, 'P1098')
- # writing system
- add_attribute(attributes, id_cache, 'P282')
- # regulatory body
- add_attribute(attributes, id_cache, 'P1018')
- # language code
- add_attribute(attributes, id_cache, 'P218')
-
- # OTHER
- # ceo
- add_attribute(attributes, id_cache, 'P169', trim=True)
- # founder
- add_attribute(attributes, id_cache, 'P112')
- # legal form (company/organization)
- add_attribute(attributes, id_cache, 'P1454')
- # operator
- add_attribute(attributes, id_cache, 'P137')
- # crew members (tripulation)
- add_attribute(attributes, id_cache, 'P1029')
- # taxon
- add_attribute(attributes, id_cache, 'P225')
- # chemical formula
- add_attribute(attributes, id_cache, 'P274')
- # winner (sports/contests)
- add_attribute(attributes, id_cache, 'P1346')
- # number of deaths
- add_attribute(attributes, id_cache, 'P1120')
- # currency code
- add_attribute(attributes, id_cache, 'P498')
-
- image = add_image(id_cache)
-
- if len(attributes) == 0 and len(urls) == 2 and len(description) == 0:
+ # add the wikidata URL at the end
+ infobox_urls.append({'title': 'Wikidata', 'url': attribute_result['item']})
+
+ if img_src is None and len(infobox_attributes) == 0 and len(infobox_urls) == 1 and\
+ len(infobox_content) == 0:
results.append({
- 'url': urls[0]['url'],
- 'title': title,
- 'content': description
- })
+ 'url': infobox_urls[0]['url'],
+ 'title': infobox_title,
+ 'content': infobox_content
+ })
else:
results.append({
- 'infobox': title,
- 'id': wikipedia_link,
- 'content': description,
- 'img_src': image,
- 'attributes': attributes,
- 'urls': urls
- })
-
+ 'infobox': infobox_title,
+ 'id': infobox_id,
+ 'content': infobox_content,
+ 'img_src': img_src,
+ 'urls': infobox_urls,
+ 'attributes': infobox_attributes
+ })
return results
-# only returns first match
-def add_image(id_cache):
- # P15: route map, P242: locator map, P154: logo, P18: image, P242: map, P41: flag, P2716: collage, P2910: icon
- property_ids = ['P15', 'P242', 'P154', 'P18', 'P242', 'P41', 'P2716', 'P2910']
+def get_query(query, language):
+ attributes = get_attributes(language)
+ select = [a.get_select() for a in attributes]
+ where = list(filter(lambda s: len(s) > 0, [a.get_where() for a in attributes]))
+ wikibase_label = list(filter(lambda s: len(s) > 0, [a.get_wikibase_label() for a in attributes]))
+ group_by = list(filter(lambda s: len(s) > 0, [a.get_group_by() for a in attributes]))
+ query = QUERY_TEMPLATE\
+ .replace('%QUERY%', sparql_string_escape(query))\
+ .replace('%SELECT%', ' '.join(select))\
+ .replace('%WHERE%', '\n '.join(where))\
+ .replace('%WIKIBASE_LABELS%', '\n '.join(wikibase_label))\
+ .replace('%GROUP_BY%', ' '.join(group_by))\
+ .replace('%LANGUAGE%', language)
+ return query, attributes
- for property_id in property_ids:
- image = id_cache.get(property_id, None)
- if image is not None:
- image_name = eval_xpath(image, media_xpath)
- image_src = url_image.replace('{filename}', extract_text(image_name[0]))
- return image_src
+def get_attributes(language):
+ attributes = []
-# setting trim will only returned high ranked rows OR the first row
-def add_attribute(attributes, id_cache, property_id, default_label=None, date=False, trim=False):
- attribute = id_cache.get(property_id, None)
- if attribute is not None:
+ def add_value(name):
+ attributes.append(WDAttribute(name))
+
+ def add_amount(name):
+ attributes.append(WDAmountAttribute(name))
+
+ def add_label(name):
+ attributes.append(WDLabelAttribute(name))
+
+ def add_url(name, url_id=None, **kwargs):
+ attributes.append(WDURLAttribute(name, url_id, kwargs))
+
+ def add_image(name, url_id=None, priority=1):
+ attributes.append(WDImageAttribute(name, url_id, priority))
+
+ def add_date(name):
+ attributes.append(WDDateAttribute(name))
+
+ # Dates
+ for p in ['P571', # inception date
+ 'P576', # dissolution date
+ 'P580', # start date
+ 'P582', # end date
+ 'P569', # date of birth
+ 'P570', # date of death
+ 'P619', # date of spacecraft launch
+ 'P620']: # date of spacecraft landing
+ add_date(p)
+
+ for p in ['P27', # country of citizenship
+ 'P495', # country of origin
+ 'P17', # country
+ 'P159']: # headquarters location
+ add_label(p)
+
+ # Places
+ for p in ['P36', # capital
+ 'P35', # head of state
+ 'P6', # head of government
+ 'P122', # basic form of government
+ 'P37']: # official language
+ add_label(p)
+
+ add_value('P1082') # population
+ add_amount('P2046') # area
+ add_amount('P281') # postal code
+ add_label('P38') # currency
+ add_amount('P2048') # heigth (building)
+
+ # Media
+ for p in ['P400', # platform (videogames, computing)
+ 'P50', # author
+ 'P170', # creator
+ 'P57', # director
+ 'P175', # performer
+ 'P178', # developer
+ 'P162', # producer
+ 'P176', # manufacturer
+ 'P58', # screenwriter
+ 'P272', # production company
+ 'P264', # record label
+ 'P123', # publisher
+ 'P449', # original network
+ 'P750', # distributed by
+ 'P86']: # composer
+ add_label(p)
+
+ add_date('P577') # publication date
+ add_label('P136') # genre (music, film, artistic...)
+ add_label('P364') # original language
+ add_value('P212') # ISBN-13
+ add_value('P957') # ISBN-10
+ add_label('P275') # copyright license
+ add_label('P277') # programming language
+ add_value('P348') # version
+ add_label('P840') # narrative location
+
+ # Languages
+ add_value('P1098') # number of speakers
+ add_label('P282') # writing system
+ add_label('P1018') # language regulatory body
+ add_value('P218') # language code (ISO 639-1)
+
+ # Other
+ add_label('P169') # ceo
+ add_label('P112') # founded by
+ add_label('P1454') # legal form (company, organization)
+ add_label('P137') # operator (service, facility, ...)
+ add_label('P1029') # crew members (tripulation)
+ add_label('P225') # taxon name
+ add_value('P274') # chemical formula
+ add_label('P1346') # winner (sports, contests, ...)
+ add_value('P1120') # number of deaths
+ add_value('P498') # currency code (ISO 4217)
+
+ # URL
+ add_url('P856', official=True) # official website
+ attributes.append(WDArticle(language)) # wikipedia (user language)
+ if not language.startswith('en'):
+ attributes.append(WDArticle('en')) # wikipedia (english)
+
+ add_url('P1324') # source code repository
+ add_url('P1581') # blog
+ add_url('P434', url_id='musicbrainz_artist')
+ add_url('P435', url_id='musicbrainz_work')
+ add_url('P436', url_id='musicbrainz_release_group')
+ add_url('P966', url_id='musicbrainz_label')
+ add_url('P345', url_id='imdb_id')
+ add_url('P2397', url_id='youtube_channel')
+ add_url('P1651', url_id='youtube_video')
+ add_url('P2002', url_id='twitter_profile')
+ add_url('P2013', url_id='facebook_profile')
+ add_url('P2003', url_id='instagram_profile')
+
+ # Map
+ attributes.append(WDGeoAttribute('P625'))
+
+ # Image
+ add_image('P15', priority=1, url_id='wikimedia_image') # route map
+ add_image('P242', priority=2, url_id='wikimedia_image') # locator map
+ add_image('P154', priority=3, url_id='wikimedia_image') # logo
+ add_image('P18', priority=4, url_id='wikimedia_image') # image
+ add_image('P41', priority=5, url_id='wikimedia_image') # flag
+ add_image('P2716', priority=6, url_id='wikimedia_image') # collage
+ add_image('P2910', priority=7, url_id='wikimedia_image') # icon
+
+ return attributes
+
+
+class WDAttribute:
+
+ __slots__ = 'name',
+
+ def __init__(self, name):
+ self.name = name
+
+ def get_select(self):
+ return '(group_concat(distinct ?{name};separator=", ") as ?{name}s)'.replace('{name}', self.name)
+
+ def get_label(self, language):
+ return get_label_for_entity(self.name, language)
+
+ def get_where(self):
+ return "OPTIONAL { ?item wdt:{name} ?{name} . }".replace('{name}', self.name)
+
+ def get_wikibase_label(self):
+ return ""
+
+ def get_group_by(self):
+ return ""
+
+ def get_str(self, result, language):
+ return result.get(self.name + 's')
- if default_label:
- label = default_label
- else:
- label = extract_text(eval_xpath(attribute, label_xpath))
- label = label[0].upper() + label[1:]
-
- if date:
- trim = True
- # remove calendar name
- calendar_name = eval_xpath(attribute, calendar_name_xpath)
- for calendar in calendar_name:
- calendar.getparent().remove(calendar)
-
- concat_values = ""
- values = []
- first_value = None
- for row in eval_xpath(attribute, property_row_xpath):
- if not first_value or not trim or eval_xpath(row, preferred_rank_xpath):
- value = eval_xpath(row, value_xpath)
- if not value:
- continue
- value = extract_text(value)
-
- # save first value in case no ranked row is found
- if trim and not first_value:
- first_value = value
- else:
- # to avoid duplicate values
- if value not in values:
- concat_values += value + ", "
- values.append(value)
-
- if trim and not values:
- attributes.append({'label': label,
- 'value': first_value})
- else:
- attributes.append({'label': label,
- 'value': concat_values[:-2]})
+ def __repr__(self):
+ return '<' + str(type(self).__name__) + ':' + self.name + '>'
-# requires property_id unless it's a wiki link (defined in link_type)
-def add_url(urls, result, id_cache, property_id=None, default_label=None, url_prefix=None, results=None,
- link_type=None):
- links = []
+class WDAmountAttribute(WDAttribute):
- # wiki links don't have property in wikidata page
- if link_type and 'wiki' in link_type:
- links.append(get_wikilink(result, link_type))
- else:
- dom_element = id_cache.get(property_id, None)
- if dom_element is not None:
- if not default_label:
- label = extract_text(eval_xpath(dom_element, label_xpath))
- label = label[0].upper() + label[1:]
+ def get_select(self):
+ return '?{name} ?{name}Unit'.replace('{name}', self.name)
- if link_type == 'geo':
- links.append(get_geolink(dom_element))
+ def get_where(self):
+ return """ OPTIONAL { ?item p:{name} ?{name}Node .
+ ?{name}Node rdf:type wikibase:BestRank ; ps:{name} ?{name} .
+ OPTIONAL { ?{name}Node psv:{name}/wikibase:quantityUnit ?{name}Unit. } }""".replace('{name}', self.name)
- elif link_type == 'imdb':
- links.append(get_imdblink(dom_element, url_prefix))
+ def get_group_by(self):
+ return self.get_select()
- else:
- url_results = eval_xpath(dom_element, url_xpath)
- for link in url_results:
- if link is not None:
- if url_prefix:
- link = url_prefix + extract_text(link)
- else:
- link = extract_text(link)
- links.append(link)
-
- # append urls
- for url in links:
- if url is not None:
- u = {'title': default_label or label, 'url': url}
- if property_id == 'P856':
- u['official'] = True
- u['domain'] = url.split('/')[2]
- urls.append(u)
- if results is not None:
- results.append(u)
-
-
-def get_imdblink(result, url_prefix):
- imdb_id = eval_xpath(result, value_xpath)
- if imdb_id:
- imdb_id = extract_text(imdb_id)
- id_prefix = imdb_id[:2]
- if id_prefix == 'tt':
- url = url_prefix + 'title/' + imdb_id
- elif id_prefix == 'nm':
- url = url_prefix + 'name/' + imdb_id
- elif id_prefix == 'ch':
- url = url_prefix + 'character/' + imdb_id
- elif id_prefix == 'co':
- url = url_prefix + 'company/' + imdb_id
- elif id_prefix == 'ev':
- url = url_prefix + 'event/' + imdb_id
- else:
- url = None
- return url
+ def get_str(self, result, language):
+ value = result.get(self.name)
+ unit = result.get(self.name + "Unit")
+ if unit is not None:
+ unit = unit.replace('http://www.wikidata.org/entity/', '')
+ return value + " " + get_label_for_entity(unit, language)
+ return value
-def get_geolink(result):
- coordinates = eval_xpath(result, value_xpath)
- if not coordinates:
- return None
- coordinates = extract_text(coordinates[0])
- latitude, longitude = coordinates.split(',')
-
- # convert to decimal
- lat = int(latitude[:latitude.find(u'°')])
- if latitude.find('\'') >= 0:
- lat += int(latitude[latitude.find(u'°') + 1:latitude.find('\'')] or 0) / 60.0
- if latitude.find('"') >= 0:
- lat += float(latitude[latitude.find('\'') + 1:latitude.find('"')] or 0) / 3600.0
- if latitude.find('S') >= 0:
- lat *= -1
- lon = int(longitude[:longitude.find(u'°')])
- if longitude.find('\'') >= 0:
- lon += int(longitude[longitude.find(u'°') + 1:longitude.find('\'')] or 0) / 60.0
- if longitude.find('"') >= 0:
- lon += float(longitude[longitude.find('\'') + 1:longitude.find('"')] or 0) / 3600.0
- if longitude.find('W') >= 0:
- lon *= -1
-
- # TODO: get precision
- precision = 0.0002
- # there is no zoom information, deduce from precision (error prone)
- # samples :
- # 13 --> 5
- # 1 --> 6
- # 0.016666666666667 --> 9
- # 0.00027777777777778 --> 19
- # wolframalpha :
- # quadratic fit { {13, 5}, {1, 6}, {0.0166666, 9}, {0.0002777777,19}}
- # 14.1186-8.8322 x+0.625447 x^2
- if precision < 0.0003:
- zoom = 19
- else:
- zoom = int(15 - precision * 8.8322 + precision * precision * 0.625447)
+class WDArticle(WDAttribute):
+
+ __slots__ = 'language', 'kwargs'
+
+ def __init__(self, language, kwargs=None):
+ super().__init__('wikipedia')
+ self.language = language
+ self.kwargs = kwargs or {}
+
+ def get_label(self, language):
+ # language parameter is ignored
+ return "Wikipedia ({language})".replace('{language}', self.language)
+
+ def get_select(self):
+ return "?article{language} ?articleName{language}".replace('{language}', self.language)
+
+ def get_where(self):
+ return """OPTIONAL { ?article{language} schema:about ?item ;
+ schema:inLanguage "{language}" ;
+ schema:isPartOf <https://{language}.wikipedia.org/> ;
+ schema:name ?articleName{language} . }""".replace('{language}', self.language)
+
+ def get_group_by(self):
+ return self.get_select()
+
+ def get_str(self, result, language):
+ key = 'article{language}'.replace('{language}', self.language)
+ return result.get(key)
+
+
+class WDLabelAttribute(WDAttribute):
+
+ def get_select(self):
+ return '(group_concat(distinct ?{name}Label;separator=", ") as ?{name}Labels)'.replace('{name}', self.name)
+
+ def get_where(self):
+ return "OPTIONAL { ?item wdt:{name} ?{name} . }".replace('{name}', self.name)
+
+ def get_wikibase_label(self):
+ return "?{name} rdfs:label ?{name}Label .".replace('{name}', self.name)
- url = url_map\
- .replace('{latitude}', str(lat))\
- .replace('{longitude}', str(lon))\
- .replace('{zoom}', str(zoom))
+ def get_str(self, result, language):
+ return result.get(self.name + 'Labels')
- return url
+class WDURLAttribute(WDAttribute):
-def get_wikilink(result, wikiid):
- url = eval_xpath(result, wikilink_xpath.replace('{wikiid}', wikiid))
- if not url:
+ HTTP_WIKIMEDIA_IMAGE = 'http://commons.wikimedia.org/wiki/Special:FilePath/'
+
+ __slots__ = 'url_id', 'kwargs'
+
+ def __init__(self, name, url_id=None, kwargs=None):
+ super().__init__(name)
+ self.url_id = url_id
+ self.kwargs = kwargs
+
+ def get_str(self, result, language):
+ value = result.get(self.name + 's')
+ if self.url_id and value is not None and value != '':
+ value = value.split(',')[0]
+ url_id = self.url_id
+ if value.startswith(WDURLAttribute.HTTP_WIKIMEDIA_IMAGE):
+ value = value[len(WDURLAttribute.HTTP_WIKIMEDIA_IMAGE):]
+ url_id = 'wikimedia_image'
+ return get_external_url(url_id, value)
+ return value
+
+
+class WDGeoAttribute(WDAttribute):
+
+ def get_label(self, language):
+ return "OpenStreetMap"
+
+ def get_select(self):
+ return "?{name}Lat ?{name}Long".replace('{name}', self.name)
+
+ def get_where(self):
+ return """OPTIONAL { ?item p:{name}/psv:{name} [
+ wikibase:geoLatitude ?{name}Lat ;
+ wikibase:geoLongitude ?{name}Long ] }""".replace('{name}', self.name)
+
+ def get_group_by(self):
+ return self.get_select()
+
+ def get_str(self, result, language):
+ latitude = result.get(self.name + 'Lat')
+ longitude = result.get(self.name + 'Long')
+ if latitude and longitude:
+ return latitude + ' ' + longitude
+ return None
+
+ def get_geo_url(self, result, osm_zoom=19):
+ latitude = result.get(self.name + 'Lat')
+ longitude = result.get(self.name + 'Long')
+ if latitude and longitude:
+ return get_earth_coordinates_url(latitude, longitude, osm_zoom)
return None
- url = url[0]
- if url.startswith('http://'):
- url = url.replace('http://', 'https://')
- elif url.startswith('//'):
- url = 'https:' + url
- return url
+
+
+class WDImageAttribute(WDURLAttribute):
+
+ __slots__ = 'priority',
+
+ def __init__(self, name, url_id=None, priority=100):
+ super().__init__(name, url_id)
+ self.priority = priority
+
+
+class WDDateAttribute(WDAttribute):
+
+ def get_select(self):
+ return '?{name} ?{name}timePrecision ?{name}timeZone ?{name}timeCalendar'.replace('{name}', self.name)
+
+ def get_where(self):
+ # To remove duplicate, add
+ # FILTER NOT EXISTS { ?item p:{name}/psv:{name}/wikibase:timeValue ?{name}bis FILTER (?{name}bis < ?{name}) }
+ # this filter is too slow, so the response function ignore duplicate results
+ # (see the seen_entities variable)
+ return """OPTIONAL { ?item p:{name}/psv:{name} [
+ wikibase:timeValue ?{name} ;
+ wikibase:timePrecision ?{name}timePrecision ;
+ wikibase:timeTimezone ?{name}timeZone ;
+ wikibase:timeCalendarModel ?{name}timeCalendar ] . }
+ hint:Prior hint:rangeSafe true;""".replace('{name}', self.name)
+
+ def get_group_by(self):
+ return self.get_select()
+
+ def format_8(self, value, locale):
+ # precision: less than a year
+ return value
+
+ def format_9(self, value, locale):
+ year = int(value)
+ # precision: year
+ if year < 1584:
+ if year < 0:
+ return str(year - 1)
+ return str(year)
+ timestamp = isoparse(value)
+ return format_date(timestamp, format='yyyy', locale=locale)
+
+ def format_10(self, value, locale):
+ # precision: month
+ timestamp = isoparse(value)
+ return format_date(timestamp, format='MMMM y', locale=locale)
+
+ def format_11(self, value, locale):
+ # precision: day
+ timestamp = isoparse(value)
+ return format_date(timestamp, format='full', locale=locale)
+
+ def format_13(self, value, locale):
+ timestamp = isoparse(value)
+ # precision: minute
+ return get_datetime_format(format, locale=locale) \
+ .replace("'", "") \
+ .replace('{0}', format_time(timestamp, 'full', tzinfo=None,
+ locale=locale)) \
+ .replace('{1}', format_date(timestamp, 'short', locale=locale))
+
+ def format_14(self, value, locale):
+ # precision: second.
+ return format_datetime(isoparse(value), format='full', locale=locale)
+
+ DATE_FORMAT = {
+ '0': ('format_8', 1000000000),
+ '1': ('format_8', 100000000),
+ '2': ('format_8', 10000000),
+ '3': ('format_8', 1000000),
+ '4': ('format_8', 100000),
+ '5': ('format_8', 10000),
+ '6': ('format_8', 1000),
+ '7': ('format_8', 100),
+ '8': ('format_8', 10),
+ '9': ('format_9', 1), # year
+ '10': ('format_10', 1), # month
+ '11': ('format_11', 0), # day
+ '12': ('format_13', 0), # hour (not supported by babel, display minute)
+ '13': ('format_13', 0), # minute
+ '14': ('format_14', 0) # second
+ }
+
+ def get_str(self, result, language):
+ value = result.get(self.name)
+ if value == '' or value is None:
+ return None
+ precision = result.get(self.name + 'timePrecision')
+ date_format = WDDateAttribute.DATE_FORMAT.get(precision)
+ if date_format is not None:
+ format_method = getattr(self, date_format[0])
+ precision = date_format[1]
+ try:
+ if precision >= 1:
+ t = value.split('-')
+ if value.startswith('-'):
+ value = '-' + t[1]
+ else:
+ value = t[0]
+ return format_method(value, language)
+ except Exception:
+ return value
+ return value
+
+
+def debug_explain_wikidata_query(query, method='GET'):
+ if method == 'GET':
+ http_response = get(SPARQL_EXPLAIN_URL + '&' + urlencode({'query': query}), headers=get_headers())
+ else:
+ http_response = post(SPARQL_EXPLAIN_URL, data={'query': query}, headers=get_headers())
+ http_response.raise_for_status()
+ return http_response.content
+
+
+def init(engine_settings=None):
+ # WIKIDATA_PROPERTIES : add unit symbols
+ WIKIDATA_PROPERTIES.update(WIKIDATA_UNITS)
+
+ # WIKIDATA_PROPERTIES : add property labels
+ wikidata_property_names = []
+ for attribute in get_attributes('en'):
+ if type(attribute) in (WDAttribute, WDAmountAttribute, WDURLAttribute, WDDateAttribute, WDLabelAttribute):
+ if attribute.name not in WIKIDATA_PROPERTIES:
+ wikidata_property_names.append("wd:" + attribute.name)
+ query = QUERY_PROPERTY_NAMES.replace('%ATTRIBUTES%', " ".join(wikidata_property_names))
+ jsonresponse = send_wikidata_query(query)
+ for result in jsonresponse.get('results', {}).get('bindings', {}):
+ name = result['name']['value']
+ lang = result['name']['xml:lang']
+ entity_id = result['item']['value'].replace('http://www.wikidata.org/entity/', '')
+ WIKIDATA_PROPERTIES[(entity_id, lang)] = name.capitalize()
diff --git a/searx/engines/wikipedia.py b/searx/engines/wikipedia.py
index a216ba8..54d7510 100644
--- a/searx/engines/wikipedia.py
+++ b/searx/engines/wikipedia.py
@@ -1,7 +1,7 @@
"""
Wikipedia (Web)
- @website https://{language}.wikipedia.org
+ @website https://en.wikipedia.org/api/rest_v1/
@provide-api yes
@using-api yes
@@ -10,23 +10,14 @@
@parse url, infobox
"""
+from urllib.parse import quote
from json import loads
from lxml.html import fromstring
-from searx.url_utils import quote, urlencode
-from searx.utils import match_language
+from searx.utils import match_language, searx_useragent
+from searx.raise_for_httperror import raise_for_httperror
# search-url
-base_url = u'https://{language}.wikipedia.org/'
-search_url = base_url + u'w/api.php?'\
- 'action=query'\
- '&format=json'\
- '&{query}'\
- '&prop=extracts|pageimages|pageprops'\
- '&ppprop=disambiguation'\
- '&exintro'\
- '&explaintext'\
- '&pithumbsize=300'\
- '&redirects'
+search_url = 'https://{language}.wikipedia.org/api/rest_v1/page/summary/{title}'
supported_languages_url = 'https://meta.wikimedia.org/wiki/List_of_Wikipedias'
@@ -41,77 +32,40 @@ def url_lang(lang):
# do search-request
def request(query, params):
if query.islower():
- query = u'{0}|{1}'.format(query.decode('utf-8'), query.decode('utf-8').title()).encode('utf-8')
+ query = query.title()
- params['url'] = search_url.format(query=urlencode({'titles': query}),
+ params['url'] = search_url.format(title=quote(query),
language=url_lang(params['language']))
- return params
-
-
-# get first meaningful paragraph
-# this should filter out disambiguation pages and notes above first paragraph
-# "magic numbers" were obtained by fine tuning
-def extract_first_paragraph(content, title, image):
- first_paragraph = None
-
- failed_attempts = 0
- for paragraph in content.split('\n'):
-
- starts_with_title = paragraph.lower().find(title.lower(), 0, len(title) + 35)
- length = len(paragraph)
+ params['headers']['User-Agent'] = searx_useragent()
+ params['raise_for_httperror'] = False
+ params['soft_max_redirects'] = 2
- if length >= 200 or (starts_with_title >= 0 and (image or length >= 150)):
- first_paragraph = paragraph
- break
-
- failed_attempts += 1
- if failed_attempts > 3:
- return None
-
- return first_paragraph
+ return params
# get response from search-request
def response(resp):
- results = []
-
- search_result = loads(resp.text)
-
- # wikipedia article's unique id
- # first valid id is assumed to be the requested article
- if 'pages' not in search_result['query']:
- return results
-
- for article_id in search_result['query']['pages']:
- page = search_result['query']['pages'][article_id]
- if int(article_id) > 0:
- break
-
- if int(article_id) < 0 or 'disambiguation' in page.get('pageprops', {}):
+ if resp.status_code == 404:
return []
+ raise_for_httperror(resp)
- title = page.get('title')
-
- image = page.get('thumbnail')
- if image:
- image = image.get('source')
-
- extract = page.get('extract')
+ results = []
+ api_result = loads(resp.text)
- summary = extract_first_paragraph(extract, title, image)
- summary = summary.replace('() ', '')
+ # skip disambiguation pages
+ if api_result.get('type') != 'standard':
+ return []
- # link to wikipedia article
- wikipedia_link = base_url.format(language=url_lang(resp.search_params['language'])) \
- + 'wiki/' + quote(title.replace(' ', '_').encode('utf8'))
+ title = api_result['title']
+ wikipedia_link = api_result['content_urls']['desktop']['page']
results.append({'url': wikipedia_link, 'title': title})
results.append({'infobox': title,
'id': wikipedia_link,
- 'content': summary,
- 'img_src': image,
+ 'content': api_result.get('extract', ''),
+ 'img_src': api_result.get('thumbnail', {}).get('source'),
'urls': [{'title': 'Wikipedia', 'url': wikipedia_link}]})
return results
diff --git a/searx/engines/wolframalpha_api.py b/searx/engines/wolframalpha_api.py
index 1c58c4a..520eaa2 100644
--- a/searx/engines/wolframalpha_api.py
+++ b/searx/engines/wolframalpha_api.py
@@ -9,7 +9,7 @@
# @parse url, infobox
from lxml import etree
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
# search-url
search_url = 'https://api.wolframalpha.com/v2/query?appid={api_key}&{query}'
@@ -45,15 +45,15 @@ def request(query, params):
# replace private user area characters to make text legible
def replace_pua_chars(text):
- pua_chars = {u'\uf522': u'\u2192', # rigth arrow
- u'\uf7b1': u'\u2115', # set of natural numbers
- u'\uf7b4': u'\u211a', # set of rational numbers
- u'\uf7b5': u'\u211d', # set of real numbers
- u'\uf7bd': u'\u2124', # set of integer numbers
- u'\uf74c': 'd', # differential
- u'\uf74d': u'\u212f', # euler's number
- u'\uf74e': 'i', # imaginary number
- u'\uf7d9': '='} # equals sign
+ pua_chars = {'\uf522': '\u2192', # rigth arrow
+ '\uf7b1': '\u2115', # set of natural numbers
+ '\uf7b4': '\u211a', # set of rational numbers
+ '\uf7b5': '\u211d', # set of real numbers
+ '\uf7bd': '\u2124', # set of integer numbers
+ '\uf74c': 'd', # differential
+ '\uf74d': '\u212f', # euler's number
+ '\uf74e': 'i', # imaginary number
+ '\uf7d9': '='} # equals sign
for k, v in pua_chars.items():
text = text.replace(k, v)
diff --git a/searx/engines/wolframalpha_noapi.py b/searx/engines/wolframalpha_noapi.py
index 387c9fa..943d4f3 100644
--- a/searx/engines/wolframalpha_noapi.py
+++ b/searx/engines/wolframalpha_noapi.py
@@ -10,9 +10,9 @@
from json import loads
from time import time
+from urllib.parse import urlencode
from searx.poolrequests import get as http_get
-from searx.url_utils import urlencode
# search-url
url = 'https://www.wolframalpha.com/'
diff --git a/searx/engines/www1x.py b/searx/engines/www1x.py
index f1154b1..b8f111a 100644
--- a/searx/engines/www1x.py
+++ b/searx/engines/www1x.py
@@ -7,12 +7,12 @@
@using-api no
@results HTML
@stable no (HTML can change)
- @parse url, title, thumbnail, img_src, content
+ @parse url, title, thumbnail
"""
-from lxml import html
-from searx.url_utils import urlencode, urljoin
-from searx.engines.xpath import extract_text
+from lxml import html, etree
+from urllib.parse import urlencode, urljoin
+from searx.utils import extract_text, eval_xpath_list, eval_xpath_getindex
# engine dependent config
categories = ['images']
@@ -21,6 +21,7 @@ paging = False
# search-url
base_url = 'https://1x.com'
search_url = base_url + '/backend/search.php?{query}'
+gallery_url = 'https://gallery.1x.com/'
# do search-request
@@ -33,23 +34,18 @@ def request(query, params):
# get response from search-request
def response(resp):
results = []
-
- dom = html.fromstring(resp.text)
- for res in dom.xpath('//div[@class="List-item MainListing"]'):
- # processed start and end of link
- link = res.xpath('//a')[0]
-
+ xmldom = etree.fromstring(resp.content)
+ xmlsearchresult = eval_xpath_getindex(xmldom, '//searchresult', 0)
+ dom = html.fragment_fromstring(xmlsearchresult.text, create_parent='div')
+ for link in eval_xpath_list(dom, '/div/table/tr/td/div[2]//a'):
url = urljoin(base_url, link.attrib.get('href'))
title = extract_text(link)
-
- thumbnail_src = urljoin(base_url, res.xpath('.//img')[0].attrib['src'])
- # TODO: get image with higher resolution
- img_src = thumbnail_src
+ thumbnail_src = urljoin(gallery_url, eval_xpath_getindex(link, './/img', 0).attrib['src'])
# append result
results.append({'url': url,
'title': title,
- 'img_src': img_src,
+ 'img_src': thumbnail_src,
'content': '',
'thumbnail_src': thumbnail_src,
'template': 'images.html'})
diff --git a/searx/engines/xpath.py b/searx/engines/xpath.py
index b75896c..1507176 100644
--- a/searx/engines/xpath.py
+++ b/searx/engines/xpath.py
@@ -1,7 +1,6 @@
from lxml import html
-from lxml.etree import _ElementStringResult, _ElementUnicodeResult
-from searx.utils import html_to_text, eval_xpath
-from searx.url_utils import unquote, urlencode, urljoin, urlparse
+from urllib.parse import urlencode
+from searx.utils import extract_text, extract_url, eval_xpath, eval_xpath_list
search_url = None
url_xpath = None
@@ -11,6 +10,8 @@ thumbnail_xpath = False
paging = False
suggestion_xpath = ''
results_xpath = ''
+cached_xpath = ''
+cached_url = ''
# parameters for engines with paging support
#
@@ -21,72 +22,6 @@ page_size = 1
first_page_num = 1
-'''
-if xpath_results is list, extract the text from each result and concat the list
-if xpath_results is a xml element, extract all the text node from it
- ( text_content() method from lxml )
-if xpath_results is a string element, then it's already done
-'''
-
-
-def extract_text(xpath_results):
- if type(xpath_results) == list:
- # it's list of result : concat everything using recursive call
- result = ''
- for e in xpath_results:
- result = result + extract_text(e)
- return result.strip()
- elif type(xpath_results) in [_ElementStringResult, _ElementUnicodeResult]:
- # it's a string
- return ''.join(xpath_results)
- else:
- # it's a element
- text = html.tostring(
- xpath_results, encoding='unicode', method='text', with_tail=False
- )
- text = text.strip().replace('\n', ' ')
- return ' '.join(text.split())
-
-
-def extract_url(xpath_results, search_url):
- if xpath_results == []:
- raise Exception('Empty url resultset')
- url = extract_text(xpath_results)
-
- if url.startswith('//'):
- # add http or https to this kind of url //example.com/
- parsed_search_url = urlparse(search_url)
- url = u'{0}:{1}'.format(parsed_search_url.scheme or 'http', url)
- elif url.startswith('/'):
- # fix relative url to the search engine
- url = urljoin(search_url, url)
-
- # normalize url
- url = normalize_url(url)
-
- return url
-
-
-def normalize_url(url):
- parsed_url = urlparse(url)
-
- # add a / at this end of the url if there is no path
- if not parsed_url.netloc:
- raise Exception('Cannot parse url')
- if not parsed_url.path:
- url += '/'
-
- # FIXME : hack for yahoo
- if parsed_url.hostname == 'search.yahoo.com'\
- and parsed_url.path.startswith('/r'):
- p = parsed_url.path
- mark = p.find('/**')
- if mark != -1:
- return unquote(p[mark + 3:]).decode('utf-8')
-
- return url
-
-
def request(query, params):
query = urlencode({'q': query})[2:]
@@ -103,28 +38,49 @@ def request(query, params):
def response(resp):
results = []
dom = html.fromstring(resp.text)
+ is_onion = True if 'onions' in categories else False # pylint: disable=undefined-variable
+
if results_xpath:
- for result in eval_xpath(dom, results_xpath):
- url = extract_url(eval_xpath(result, url_xpath), search_url)
- title = extract_text(eval_xpath(result, title_xpath))
- content = extract_text(eval_xpath(result, content_xpath))
+ for result in eval_xpath_list(dom, results_xpath):
+ url = extract_url(eval_xpath_list(result, url_xpath, min_len=1), search_url)
+ title = extract_text(eval_xpath_list(result, title_xpath, min_len=1))
+ content = extract_text(eval_xpath_list(result, content_xpath, min_len=1))
tmp_result = {'url': url, 'title': title, 'content': content}
# add thumbnail if available
if thumbnail_xpath:
- thumbnail_xpath_result = eval_xpath(result, thumbnail_xpath)
+ thumbnail_xpath_result = eval_xpath_list(result, thumbnail_xpath)
if len(thumbnail_xpath_result) > 0:
tmp_result['img_src'] = extract_url(thumbnail_xpath_result, search_url)
+ # add alternative cached url if available
+ if cached_xpath:
+ tmp_result['cached_url'] = cached_url\
+ + extract_text(eval_xpath_list(result, cached_xpath, min_len=1))
+
+ if is_onion:
+ tmp_result['is_onion'] = True
+
results.append(tmp_result)
else:
- for url, title, content in zip(
- (extract_url(x, search_url) for
- x in eval_xpath(dom, url_xpath)),
- map(extract_text, eval_xpath(dom, title_xpath)),
- map(extract_text, eval_xpath(dom, content_xpath))
- ):
- results.append({'url': url, 'title': title, 'content': content})
+ if cached_xpath:
+ for url, title, content, cached in zip(
+ (extract_url(x, search_url) for
+ x in eval_xpath_list(dom, url_xpath)),
+ map(extract_text, eval_xpath_list(dom, title_xpath)),
+ map(extract_text, eval_xpath_list(dom, content_xpath)),
+ map(extract_text, eval_xpath_list(dom, cached_xpath))
+ ):
+ results.append({'url': url, 'title': title, 'content': content,
+ 'cached_url': cached_url + cached, 'is_onion': is_onion})
+ else:
+ for url, title, content in zip(
+ (extract_url(x, search_url) for
+ x in eval_xpath_list(dom, url_xpath)),
+ map(extract_text, eval_xpath_list(dom, title_xpath)),
+ map(extract_text, eval_xpath_list(dom, content_xpath))
+ ):
+ results.append({'url': url, 'title': title, 'content': content, 'is_onion': is_onion})
if not suggestion_xpath:
return results
diff --git a/searx/engines/yacy.py b/searx/engines/yacy.py
index f1d4c6a..6f7ab75 100644
--- a/searx/engines/yacy.py
+++ b/searx/engines/yacy.py
@@ -14,7 +14,9 @@
from json import loads
from dateutil import parser
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
+
+from requests.auth import HTTPDigestAuth
from searx.utils import html_to_text
@@ -23,6 +25,8 @@ categories = ['general', 'images'] # TODO , 'music', 'videos', 'files'
paging = True
language_support = True
number_of_results = 5
+http_digest_auth_user = ""
+http_digest_auth_pass = ""
# search-url
base_url = 'http://localhost:8090'
@@ -51,6 +55,9 @@ def request(query, params):
limit=number_of_results,
search_type=search_type)
+ if http_digest_auth_user and http_digest_auth_pass:
+ params['auth'] = HTTPDigestAuth(http_digest_auth_user, http_digest_auth_pass)
+
# add language tag if specified
if params['language'] != 'all':
params['url'] += '&lr=lang_' + params['language'].split('-')[0]
@@ -75,7 +82,7 @@ def response(resp):
for result in search_results[0].get('items', []):
# parse image results
- if result.get('image') and result.get('width') and result.get('height'):
+ if resp.search_params.get('category') == 'images':
result_url = ''
if 'url' in result:
@@ -104,5 +111,4 @@ def response(resp):
# TODO parse video, audio and file results
- # return results
return results
diff --git a/searx/engines/yahoo.py b/searx/engines/yahoo.py
index a6b4aeb..3420aa6 100644
--- a/searx/engines/yahoo.py
+++ b/searx/engines/yahoo.py
@@ -11,10 +11,9 @@
@parse url, title, content, suggestion
"""
+from urllib.parse import unquote, urlencode
from lxml import html
-from searx.engines.xpath import extract_text, extract_url
-from searx.url_utils import unquote, urlencode
-from searx.utils import match_language, eval_xpath
+from searx.utils import extract_text, extract_url, match_language, eval_xpath
# engine dependent config
categories = ['general']
diff --git a/searx/engines/yahoo_news.py b/searx/engines/yahoo_news.py
index 9f6a415..793d110 100644
--- a/searx/engines/yahoo_news.py
+++ b/searx/engines/yahoo_news.py
@@ -11,14 +11,12 @@
import re
from datetime import datetime, timedelta
+from urllib.parse import urlencode
from lxml import html
-from searx.engines.xpath import extract_text, extract_url
-from searx.engines.yahoo import (
- parse_url, _fetch_supported_languages, supported_languages_url, language_aliases
-)
+from searx.engines.yahoo import parse_url, language_aliases
+from searx.engines.yahoo import _fetch_supported_languages, supported_languages_url # NOQA # pylint: disable=unused-import
from dateutil import parser
-from searx.url_utils import urlencode
-from searx.utils import match_language
+from searx.utils import extract_text, extract_url, match_language
# engine dependent config
categories = ['news']
@@ -58,7 +56,7 @@ def request(query, params):
def sanitize_url(url):
if ".yahoo.com/" in url:
- return re.sub(u"\\;\\_ylt\\=.+$", "", url)
+ return re.sub("\\;\\_ylt\\=.+$", "", url)
else:
return url
diff --git a/searx/engines/yandex.py b/searx/engines/yandex.py
index 1c789f6..b4a6a54 100644
--- a/searx/engines/yandex.py
+++ b/searx/engines/yandex.py
@@ -9,9 +9,10 @@
@parse url, title, content
"""
+from urllib.parse import urlencode, urlparse
from lxml import html
from searx import logger
-from searx.url_utils import urlencode
+from searx.exceptions import SearxEngineCaptchaException
logger = logger.getChild('yandex engine')
@@ -47,6 +48,10 @@ def request(query, params):
# get response from search-request
def response(resp):
+ resp_url = urlparse(resp.url)
+ if resp_url.path.startswith('/showcaptcha'):
+ raise SearxEngineCaptchaException()
+
dom = html.fromstring(resp.text)
results = []
diff --git a/searx/engines/yggtorrent.py b/searx/engines/yggtorrent.py
new file mode 100644
index 0000000..ec84d2c
--- /dev/null
+++ b/searx/engines/yggtorrent.py
@@ -0,0 +1,123 @@
+# Yggtorrent (Videos, Music, Files)
+#
+# @website https://www2.yggtorrent.si
+# @provide-api no (nothing found)
+#
+# @using-api no
+# @results HTML (using search portal)
+# @stable no (HTML can change)
+# @parse url, title, seed, leech, publishedDate, filesize
+
+from lxml import html
+from operator import itemgetter
+from datetime import datetime
+from urllib.parse import quote
+from searx.utils import extract_text, get_torrent_size
+from searx.poolrequests import get as http_get
+
+# engine dependent config
+categories = ['videos', 'music', 'files']
+paging = True
+
+# search-url
+url = 'https://www2.yggtorrent.si/'
+search_url = url + 'engine/search?name={search_term}&do=search&page={pageno}&category={search_type}'
+
+# yggtorrent specific type-definitions
+search_types = {'files': 'all',
+ 'music': '2139',
+ 'videos': '2145'}
+
+cookies = dict()
+
+
+def init(engine_settings=None):
+ global cookies
+ # initial cookies
+ resp = http_get(url, allow_redirects=False)
+ if resp.ok:
+ for r in resp.history:
+ cookies.update(r.cookies)
+ cookies.update(resp.cookies)
+
+
+# do search-request
+def request(query, params):
+ search_type = search_types.get(params['category'], 'all')
+ pageno = (params['pageno'] - 1) * 50
+
+ params['url'] = search_url.format(search_term=quote(query),
+ search_type=search_type,
+ pageno=pageno)
+
+ params['cookies'] = cookies
+
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+ dom = html.fromstring(resp.text)
+
+ search_res = dom.xpath('//section[@id="#torrents"]/div/table/tbody/tr')
+
+ # return empty array if nothing is found
+ if not search_res:
+ return []
+
+ # parse results
+ for result in search_res:
+ link = result.xpath('.//a[@id="torrent_name"]')[0]
+ href = link.attrib.get('href')
+ title = extract_text(link)
+ seed = result.xpath('.//td[8]/text()')[0]
+ leech = result.xpath('.//td[9]/text()')[0]
+
+ # convert seed to int if possible
+ if seed.isdigit():
+ seed = int(seed)
+ else:
+ seed = 0
+
+ # convert leech to int if possible
+ if leech.isdigit():
+ leech = int(leech)
+ else:
+ leech = 0
+
+ params = {'url': href,
+ 'title': title,
+ 'seed': seed,
+ 'leech': leech,
+ 'template': 'torrent.html'}
+
+ # let's try to calculate the torrent size
+ try:
+ filesize_info = result.xpath('.//td[6]/text()')[0]
+ filesize = filesize_info[:-2]
+ filesize_multiplier = filesize_info[-2:].lower()
+ multiplier_french_to_english = {
+ 'to': 'TiB',
+ 'go': 'GiB',
+ 'mo': 'MiB',
+ 'ko': 'KiB'
+ }
+ filesize = get_torrent_size(filesize, multiplier_french_to_english[filesize_multiplier])
+ params['filesize'] = filesize
+ except:
+ pass
+
+ # extract and convert creation date
+ try:
+ date_ts = result.xpath('.//td[5]/div/text()')[0]
+ date = datetime.fromtimestamp(float(date_ts))
+ params['publishedDate'] = date
+ except:
+ pass
+
+ # append result
+ results.append(params)
+
+ # return results sorted by seeder
+ return sorted(results, key=itemgetter('seed'), reverse=True)
diff --git a/searx/engines/youtube_api.py b/searx/engines/youtube_api.py
index bc4c0d5..8c12ac4 100644
--- a/searx/engines/youtube_api.py
+++ b/searx/engines/youtube_api.py
@@ -10,7 +10,8 @@
from json import loads
from dateutil import parser
-from searx.url_utils import urlencode
+from urllib.parse import urlencode
+from searx.exceptions import SearxEngineAPIException
# engine dependent config
categories = ['videos', 'music']
@@ -47,6 +48,9 @@ def response(resp):
search_results = loads(resp.text)
+ if 'error' in search_results and 'message' in search_results['error']:
+ raise SearxEngineAPIException(search_results['error']['message'])
+
# return empty array if there are no results
if 'items' not in search_results:
return []
diff --git a/searx/engines/youtube_noapi.py b/searx/engines/youtube_noapi.py
index 68a3739..36fc72e 100644
--- a/searx/engines/youtube_noapi.py
+++ b/searx/engines/youtube_noapi.py
@@ -10,9 +10,7 @@
from functools import reduce
from json import loads
-from searx.engines.xpath import extract_text
-from searx.utils import list_get
-from searx.url_utils import quote_plus
+from urllib.parse import quote_plus
# engine dependent config
categories = ['videos', 'music']
@@ -51,7 +49,7 @@ def response(resp):
results = []
results_data = resp.text[resp.text.find('ytInitialData'):]
- results_data = results_data[results_data.find('{'):results_data.find(';\n')]
+ results_data = results_data[results_data.find('{'):results_data.find(';</script>')]
results_json = loads(results_data) if results_data else {}
sections = results_json.get('contents', {})\
diff --git a/searx/exceptions.py b/searx/exceptions.py
index 0175acf..67a282d 100644
--- a/searx/exceptions.py
+++ b/searx/exceptions.py
@@ -27,7 +27,77 @@ class SearxParameterException(SearxException):
message = 'Empty ' + name + ' parameter'
else:
message = 'Invalid value "' + value + '" for parameter ' + name
- super(SearxParameterException, self).__init__(message)
+ super().__init__(message)
self.message = message
self.parameter_name = name
self.parameter_value = value
+
+
+class SearxSettingsException(SearxException):
+ """Error while loading the settings"""
+
+ def __init__(self, message, filename):
+ super().__init__(message)
+ self.message = message
+ self.filename = filename
+
+
+class SearxEngineException(SearxException):
+ """Error inside an engine"""
+
+
+class SearxXPathSyntaxException(SearxEngineException):
+ """Syntax error in a XPATH"""
+
+ def __init__(self, xpath_spec, message):
+ super().__init__(str(xpath_spec) + " " + message)
+ self.message = message
+ # str(xpath_spec) to deal with str and XPath instance
+ self.xpath_str = str(xpath_spec)
+
+
+class SearxEngineResponseException(SearxEngineException):
+ """Impossible to parse the result of an engine"""
+
+
+class SearxEngineAPIException(SearxEngineResponseException):
+ """The website has returned an application error"""
+
+
+class SearxEngineAccessDeniedException(SearxEngineResponseException):
+ """The website is blocking the access"""
+
+ def __init__(self, suspended_time=24 * 3600, message='Access denied'):
+ super().__init__(message + ', suspended_time=' + str(suspended_time))
+ self.suspended_time = suspended_time
+ self.message = message
+
+
+class SearxEngineCaptchaException(SearxEngineAccessDeniedException):
+ """The website has returned a CAPTCHA
+
+ By default, searx stops sending requests to this engine for 1 day.
+ """
+
+ def __init__(self, suspended_time=24 * 3600, message='CAPTCHA'):
+ super().__init__(message=message, suspended_time=suspended_time)
+
+
+class SearxEngineTooManyRequestsException(SearxEngineAccessDeniedException):
+ """The website has returned a Too Many Request status code
+
+ By default, searx stops sending requests to this engine for 1 hour.
+ """
+
+ def __init__(self, suspended_time=3600, message='Too many request'):
+ super().__init__(message=message, suspended_time=suspended_time)
+
+
+class SearxEngineXPathException(SearxEngineResponseException):
+ """Error while getting the result of an XPath expression"""
+
+ def __init__(self, xpath_spec, message):
+ super().__init__(str(xpath_spec) + " " + message)
+ self.message = message
+ # str(xpath_spec) to deal with str and XPath instance
+ self.xpath_str = str(xpath_spec)
diff --git a/searx/external_bang.py b/searx/external_bang.py
index f8b9c44..104f859 100644
--- a/searx/external_bang.py
+++ b/searx/external_bang.py
@@ -1,7 +1,4 @@
-import json
-from os.path import join
-
-from searx import searx_dir
+from searx.data import bangs_loader
# bangs data coming from the following url convert to json with
# https://raw.githubusercontent.com/jivesearch/jivesearch/master/bangs/bangs.toml
@@ -9,10 +6,9 @@ from searx import searx_dir
# NOTE only use the get_bang_url
bangs_data = {}
-with open(join(searx_dir, 'data/bangs.json')) as json_file:
- for bang in json.load(json_file)['bang']:
- for trigger in bang["triggers"]:
- bangs_data[trigger] = {x: y for x, y in bang.items() if x != "triggers"}
+for bang in bangs_loader()['bang']:
+ for trigger in bang["triggers"]:
+ bangs_data[trigger] = {x: y for x, y in bang.items() if x != "triggers"}
def get_bang_url(search_query):
@@ -23,7 +19,7 @@ def get_bang_url(search_query):
"""
if search_query.external_bang:
- query = search_query.query.decode('utf-8', 'ignore')
+ query = search_query.query
bang = _get_bang(search_query.external_bang)
if bang and query:
diff --git a/searx/external_urls.py b/searx/external_urls.py
new file mode 100644
index 0000000..da58b8f
--- /dev/null
+++ b/searx/external_urls.py
@@ -0,0 +1,77 @@
+import math
+
+from searx.data import EXTERNAL_URLS
+
+
+IMDB_PREFIX_TO_URL_ID = {
+ 'tt': 'imdb_title',
+ 'mn': 'imdb_name',
+ 'ch': 'imdb_character',
+ 'co': 'imdb_company',
+ 'ev': 'imdb_event'
+}
+
+
+def get_imdb_url_id(imdb_item_id):
+ id_prefix = imdb_item_id[:2]
+ return IMDB_PREFIX_TO_URL_ID.get(id_prefix)
+
+
+def get_external_url(url_id, item_id, alternative="default"):
+ """Return an external URL or None if url_id is not found.
+
+ url_id can take value from data/external_urls.json
+ The "imdb_id" value is automaticaly converted according to the item_id value.
+
+ If item_id is None, the raw URL with the $1 is returned.
+ """
+ if url_id == 'imdb_id' and item_id is not None:
+ url_id = get_imdb_url_id(item_id)
+
+ url_description = EXTERNAL_URLS.get(url_id)
+ if url_description:
+ url_template = url_description["urls"].get(alternative)
+ if url_template is not None:
+ if item_id is not None:
+ return url_template.replace('$1', item_id)
+ else:
+ return url_template
+ return None
+
+
+def get_earth_coordinates_url(latitude, longitude, osm_zoom, alternative='default'):
+ url = get_external_url('map', None, alternative)\
+ .replace('${latitude}', str(latitude))\
+ .replace('${longitude}', str(longitude))\
+ .replace('${zoom}', str(osm_zoom))
+ return url
+
+
+def area_to_osm_zoom(area):
+ """Convert an area in km² into an OSM zoom. Less reliable if the shape is not round.
+
+ logarithm regression using these data:
+ * 9596961 -> 4 (China)
+ * 3287263 -> 5 (India)
+ * 643801 -> 6 (France)
+ * 6028 -> 9
+ * 1214 -> 10
+ * 891 -> 12
+ * 12 -> 13
+
+ In WolframAlpha:
+ >>> log fit {9596961,15},{3287263, 14},{643801,13},{6028,10},{1214,9},{891,7},{12,6}
+
+ with 15 = 19-4 (China); 14 = 19-5 (India) and so on
+
+ Args:
+ area (int,float,str): area in km²
+
+ Returns:
+ int: OSM zoom or 19 in area is not a number
+ """
+ try:
+ amount = float(area)
+ return max(0, min(19, round(19 - 0.688297 * math.log(226.878 * amount))))
+ except ValueError:
+ return 19
diff --git a/searx/languages.py b/searx/languages.py
index 72e1a73..20f72cf 100644
--- a/searx/languages.py
+++ b/searx/languages.py
@@ -1,75 +1,73 @@
# -*- coding: utf-8 -*-
# list of language codes
-# this file is generated automatically by utils/update_search_languages.py
-
-language_codes = (
- (u"af-NA", u"Afrikaans", u"", u"Afrikaans"),
- (u"ar-SA", u"العربية", u"", u"Arabic"),
- (u"be-BY", u"Беларуская", u"", u"Belarusian"),
- (u"bg-BG", u"Български", u"", u"Bulgarian"),
- (u"ca-AD", u"Català", u"", u"Catalan"),
- (u"cs-CZ", u"Čeština", u"", u"Czech"),
- (u"da-DK", u"Dansk", u"", u"Danish"),
- (u"de", u"Deutsch", u"", u"German"),
- (u"de-AT", u"Deutsch", u"Österreich", u"German"),
- (u"de-CH", u"Deutsch", u"Schweiz", u"German"),
- (u"de-DE", u"Deutsch", u"Deutschland", u"German"),
- (u"el-GR", u"Ελληνικά", u"", u"Greek"),
- (u"en", u"English", u"", u"English"),
- (u"en-AU", u"English", u"Australia", u"English"),
- (u"en-CA", u"English", u"Canada", u"English"),
- (u"en-GB", u"English", u"United Kingdom", u"English"),
- (u"en-IE", u"English", u"Ireland", u"English"),
- (u"en-IN", u"English", u"India", u"English"),
- (u"en-NZ", u"English", u"New Zealand", u"English"),
- (u"en-PH", u"English", u"Philippines", u"English"),
- (u"en-SG", u"English", u"Singapore", u"English"),
- (u"en-US", u"English", u"United States", u"English"),
- (u"es", u"Español", u"", u"Spanish"),
- (u"es-AR", u"Español", u"Argentina", u"Spanish"),
- (u"es-CL", u"Español", u"Chile", u"Spanish"),
- (u"es-ES", u"Español", u"España", u"Spanish"),
- (u"es-MX", u"Español", u"México", u"Spanish"),
- (u"et-EE", u"Eesti", u"", u"Estonian"),
- (u"fa-IR", u"فارسی", u"", u"Persian"),
- (u"fi-FI", u"Suomi", u"", u"Finnish"),
- (u"fr", u"Français", u"", u"French"),
- (u"fr-BE", u"Français", u"Belgique", u"French"),
- (u"fr-CA", u"Français", u"Canada", u"French"),
- (u"fr-CH", u"Français", u"Suisse", u"French"),
- (u"fr-FR", u"Français", u"France", u"French"),
- (u"he-IL", u"עברית", u"", u"Hebrew"),
- (u"hr-HR", u"Hrvatski", u"", u"Croatian"),
- (u"hu-HU", u"Magyar", u"", u"Hungarian"),
- (u"hy-AM", u"Հայերեն", u"", u"Armenian"),
- (u"id-ID", u"Indonesia", u"", u"Indonesian"),
- (u"is-IS", u"Íslenska", u"", u"Icelandic"),
- (u"it-IT", u"Italiano", u"", u"Italian"),
- (u"ja-JP", u"日本語", u"", u"Japanese"),
- (u"ko-KR", u"한국어", u"", u"Korean"),
- (u"lt-LT", u"Lietuvių", u"", u"Lithuanian"),
- (u"lv-LV", u"Latviešu", u"", u"Latvian"),
- (u"ms-MY", u"Melayu", u"", u"Malay"),
- (u"nb-NO", u"Norsk Bokmål", u"", u"Norwegian Bokmål"),
- (u"nl", u"Nederlands", u"", u"Dutch"),
- (u"nl-BE", u"Nederlands", u"België", u"Dutch"),
- (u"nl-NL", u"Nederlands", u"Nederland", u"Dutch"),
- (u"pl-PL", u"Polski", u"", u"Polish"),
- (u"pt", u"Português", u"", u"Portuguese"),
- (u"pt-BR", u"Português", u"Brasil", u"Portuguese"),
- (u"pt-PT", u"Português", u"Portugal", u"Portuguese"),
- (u"ro-RO", u"Română", u"", u"Romanian"),
- (u"ru-RU", u"Русский", u"", u"Russian"),
- (u"sk-SK", u"Slovenčina", u"", u"Slovak"),
- (u"sl-SI", u"Slovenščina", u"", u"Slovenian"),
- (u"sr-RS", u"Srpski", u"", u"Serbian"),
- (u"sv-SE", u"Svenska", u"", u"Swedish"),
- (u"sw-KE", u"Kiswahili", u"", u"Swahili"),
- (u"th-TH", u"ไทย", u"", u"Thai"),
- (u"tr-TR", u"Türkçe", u"", u"Turkish"),
- (u"uk-UA", u"Українська", u"", u"Ukrainian"),
- (u"vi-VN", u"Tiếng Việt", u"", u"Vietnamese"),
- (u"zh", u"中文", u"", u"Chinese"),
- (u"zh-CN", u"中文", u"中国", u"Chinese"),
- (u"zh-TW", u"中文", u"台灣", u"Chinese")
-)
+# this file is generated automatically by utils/fetch_languages.py
+language_codes = \
+( ('af-ZA', 'Afrikaans', '', 'Afrikaans'),
+ ('ar-EG', 'العربية', '', 'Arabic'),
+ ('be-BY', 'Беларуская', '', 'Belarusian'),
+ ('bg-BG', 'Български', '', 'Bulgarian'),
+ ('ca-ES', 'Català', '', 'Catalan'),
+ ('cs-CZ', 'Čeština', '', 'Czech'),
+ ('da-DK', 'Dansk', '', 'Danish'),
+ ('de', 'Deutsch', '', 'German'),
+ ('de-AT', 'Deutsch', 'Österreich', 'German'),
+ ('de-CH', 'Deutsch', 'Schweiz', 'German'),
+ ('de-DE', 'Deutsch', 'Deutschland', 'German'),
+ ('el-GR', 'Ελληνικά', '', 'Greek'),
+ ('en', 'English', '', 'English'),
+ ('en-AU', 'English', 'Australia', 'English'),
+ ('en-CA', 'English', 'Canada', 'English'),
+ ('en-GB', 'English', 'United Kingdom', 'English'),
+ ('en-IE', 'English', 'Ireland', 'English'),
+ ('en-IN', 'English', 'India', 'English'),
+ ('en-NZ', 'English', 'New Zealand', 'English'),
+ ('en-PH', 'English', 'Philippines', 'English'),
+ ('en-SG', 'English', 'Singapore', 'English'),
+ ('en-US', 'English', 'United States', 'English'),
+ ('es', 'Español', '', 'Spanish'),
+ ('es-AR', 'Español', 'Argentina', 'Spanish'),
+ ('es-CL', 'Español', 'Chile', 'Spanish'),
+ ('es-ES', 'Español', 'España', 'Spanish'),
+ ('es-MX', 'Español', 'México', 'Spanish'),
+ ('et-EE', 'Eesti', '', 'Estonian'),
+ ('fa-IR', 'فارسی', '', 'Persian'),
+ ('fi-FI', 'Suomi', '', 'Finnish'),
+ ('fr', 'Français', '', 'French'),
+ ('fr-BE', 'Français', 'Belgique', 'French'),
+ ('fr-CA', 'Français', 'Canada', 'French'),
+ ('fr-CH', 'Français', 'Suisse', 'French'),
+ ('fr-FR', 'Français', 'France', 'French'),
+ ('he-IL', 'עברית', '', 'Hebrew'),
+ ('hr-HR', 'Hrvatski', '', 'Croatian'),
+ ('hu-HU', 'Magyar', '', 'Hungarian'),
+ ('hy-AM', 'Հայերեն', '', 'Armenian'),
+ ('id-ID', 'Indonesia', '', 'Indonesian'),
+ ('is-IS', 'Íslenska', '', 'Icelandic'),
+ ('it-IT', 'Italiano', '', 'Italian'),
+ ('ja-JP', '日本語', '', 'Japanese'),
+ ('ko-KR', '한국어', '', 'Korean'),
+ ('lt-LT', 'Lietuvių', '', 'Lithuanian'),
+ ('lv-LV', 'Latviešu', '', 'Latvian'),
+ ('ms-MY', 'Melayu', '', 'Malay'),
+ ('nb-NO', 'Norsk Bokmål', '', 'Norwegian Bokmål'),
+ ('nl', 'Nederlands', '', 'Dutch'),
+ ('nl-BE', 'Nederlands', 'België', 'Dutch'),
+ ('nl-NL', 'Nederlands', 'Nederland', 'Dutch'),
+ ('pl-PL', 'Polski', '', 'Polish'),
+ ('pt', 'Português', '', 'Portuguese'),
+ ('pt-BR', 'Português', 'Brasil', 'Portuguese'),
+ ('pt-PT', 'Português', 'Portugal', 'Portuguese'),
+ ('ro-RO', 'Română', '', 'Romanian'),
+ ('ru-RU', 'Русский', '', 'Russian'),
+ ('sk-SK', 'Slovenčina', '', 'Slovak'),
+ ('sl-SI', 'Slovenščina', '', 'Slovenian'),
+ ('sr-RS', 'Srpski', '', 'Serbian'),
+ ('sv-SE', 'Svenska', '', 'Swedish'),
+ ('sw-TZ', 'Kiswahili', '', 'Swahili'),
+ ('th-TH', 'ไทย', '', 'Thai'),
+ ('tr-TR', 'Türkçe', '', 'Turkish'),
+ ('uk-UA', 'Українська', '', 'Ukrainian'),
+ ('vi-VN', 'Tiếng Việt', '', 'Vietnamese'),
+ ('zh', '中文', '', 'Chinese'),
+ ('zh-CN', '中文', '中国', 'Chinese'),
+ ('zh-TW', '中文', '台灣', 'Chinese')) \ No newline at end of file
diff --git a/searx/metrology/__init__.py b/searx/metrology/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/searx/metrology/__init__.py
diff --git a/searx/metrology/error_recorder.py b/searx/metrology/error_recorder.py
new file mode 100644
index 0000000..fee1ef7
--- /dev/null
+++ b/searx/metrology/error_recorder.py
@@ -0,0 +1,147 @@
+import typing
+import inspect
+import logging
+from json import JSONDecodeError
+from urllib.parse import urlparse
+from requests.exceptions import RequestException
+from searx.exceptions import (SearxXPathSyntaxException, SearxEngineXPathException, SearxEngineAPIException,
+ SearxEngineAccessDeniedException)
+from searx import logger
+
+
+logging.basicConfig(level=logging.INFO)
+
+errors_per_engines = {}
+
+
+class ErrorContext:
+
+ __slots__ = 'filename', 'function', 'line_no', 'code', 'exception_classname', 'log_message', 'log_parameters'
+
+ def __init__(self, filename, function, line_no, code, exception_classname, log_message, log_parameters):
+ self.filename = filename
+ self.function = function
+ self.line_no = line_no
+ self.code = code
+ self.exception_classname = exception_classname
+ self.log_message = log_message
+ self.log_parameters = log_parameters
+
+ def __eq__(self, o) -> bool:
+ if not isinstance(o, ErrorContext):
+ return False
+ return self.filename == o.filename and self.function == o.function and self.line_no == o.line_no\
+ and self.code == o.code and self.exception_classname == o.exception_classname\
+ and self.log_message == o.log_message and self.log_parameters == o.log_parameters
+
+ def __hash__(self):
+ return hash((self.filename, self.function, self.line_no, self.code, self.exception_classname, self.log_message,
+ self.log_parameters))
+
+ def __repr__(self):
+ return "ErrorContext({!r}, {!r}, {!r}, {!r}, {!r}, {!r})".\
+ format(self.filename, self.line_no, self.code, self.exception_classname, self.log_message,
+ self.log_parameters)
+
+
+def add_error_context(engine_name: str, error_context: ErrorContext) -> None:
+ errors_for_engine = errors_per_engines.setdefault(engine_name, {})
+ errors_for_engine[error_context] = errors_for_engine.get(error_context, 0) + 1
+ logger.debug('⚠️ %s: %s', engine_name, str(error_context))
+
+
+def get_trace(traces):
+ previous_trace = traces[-1]
+ for trace in reversed(traces):
+ if trace.filename.endswith('searx/search.py'):
+ if previous_trace.filename.endswith('searx/poolrequests.py'):
+ return trace
+ if previous_trace.filename.endswith('requests/models.py'):
+ return trace
+ return previous_trace
+ previous_trace = trace
+ return traces[-1]
+
+
+def get_hostname(exc: RequestException) -> typing.Optional[None]:
+ url = exc.request.url
+ if url is None and exc.response is not None:
+ url = exc.response.url
+ return urlparse(url).netloc
+
+
+def get_request_exception_messages(exc: RequestException)\
+ -> typing.Tuple[typing.Optional[str], typing.Optional[str], typing.Optional[str]]:
+ url = None
+ status_code = None
+ reason = None
+ hostname = None
+ if exc.request is not None:
+ url = exc.request.url
+ if url is None and exc.response is not None:
+ url = exc.response.url
+ if url is not None:
+ hostname = str(urlparse(url).netloc)
+ if exc.response is not None:
+ status_code = str(exc.response.status_code)
+ reason = exc.response.reason
+ return (status_code, reason, hostname)
+
+
+def get_messages(exc, filename) -> typing.Tuple:
+ if isinstance(exc, JSONDecodeError):
+ return (exc.msg, )
+ if isinstance(exc, TypeError):
+ return (str(exc), )
+ if isinstance(exc, ValueError) and 'lxml' in filename:
+ return (str(exc), )
+ if isinstance(exc, RequestException):
+ return get_request_exception_messages(exc)
+ if isinstance(exc, SearxXPathSyntaxException):
+ return (exc.xpath_str, exc.message)
+ if isinstance(exc, SearxEngineXPathException):
+ return (exc.xpath_str, exc.message)
+ if isinstance(exc, SearxEngineAPIException):
+ return (str(exc.args[0]), )
+ if isinstance(exc, SearxEngineAccessDeniedException):
+ return (exc.message, )
+ return ()
+
+
+def get_exception_classname(exc: Exception) -> str:
+ exc_class = exc.__class__
+ exc_name = exc_class.__qualname__
+ exc_module = exc_class.__module__
+ if exc_module is None or exc_module == str.__class__.__module__:
+ return exc_name
+ return exc_module + '.' + exc_name
+
+
+def get_error_context(framerecords, exception_classname, log_message, log_parameters) -> ErrorContext:
+ searx_frame = get_trace(framerecords)
+ filename = searx_frame.filename
+ function = searx_frame.function
+ line_no = searx_frame.lineno
+ code = searx_frame.code_context[0].strip()
+ del framerecords
+ return ErrorContext(filename, function, line_no, code, exception_classname, log_message, log_parameters)
+
+
+def record_exception(engine_name: str, exc: Exception) -> None:
+ framerecords = inspect.trace()
+ try:
+ exception_classname = get_exception_classname(exc)
+ log_parameters = get_messages(exc, framerecords[-1][1])
+ error_context = get_error_context(framerecords, exception_classname, None, log_parameters)
+ add_error_context(engine_name, error_context)
+ finally:
+ del framerecords
+
+
+def record_error(engine_name: str, log_message: str, log_parameters: typing.Optional[typing.Tuple] = None) -> None:
+ framerecords = list(reversed(inspect.stack()[1:]))
+ try:
+ error_context = get_error_context(framerecords, None, log_message, log_parameters or ())
+ add_error_context(engine_name, error_context)
+ finally:
+ del framerecords
diff --git a/searx/plugins/__init__.py b/searx/plugins/__init__.py
index 4dbcbbd..661b4f6 100644
--- a/searx/plugins/__init__.py
+++ b/searx/plugins/__init__.py
@@ -14,25 +14,30 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2015 by Adam Tauber, <asciimoo@gmail.com>
'''
-from sys import exit, version_info
-from searx import logger
-if version_info[0] == 3:
- unicode = str
+from hashlib import sha256
+from importlib import import_module
+from os import listdir, makedirs, remove, stat, utime
+from os.path import abspath, basename, dirname, exists, join
+from shutil import copyfile
+
+from searx import logger, settings, static_path
+
logger = logger.getChild('plugins')
from searx.plugins import (oa_doi_rewrite,
+ ahmia_filter,
+ hash_plugin,
https_rewrite,
infinite_scroll,
- open_results_on_new_tab,
self_info,
search_on_category_select,
tracker_url_remover,
vim_hotkeys)
-required_attrs = (('name', (str, unicode)),
- ('description', (str, unicode)),
+required_attrs = (('name', str),
+ ('description', str),
('default_on', bool))
optional_attrs = (('js_dependencies', tuple),
@@ -54,7 +59,9 @@ class PluginStore():
for plugin in self.plugins:
yield plugin
- def register(self, *plugins):
+ def register(self, *plugins, external=False):
+ if external:
+ plugins = load_external_plugins(plugins)
for plugin in plugins:
for plugin_attr, plugin_attr_type in required_attrs:
if not hasattr(plugin, plugin_attr) or not isinstance(getattr(plugin, plugin_attr), plugin_attr_type):
@@ -77,12 +84,104 @@ class PluginStore():
return ret
+def load_external_plugins(plugin_names):
+ plugins = []
+ for name in plugin_names:
+ logger.debug('loading plugin: {0}'.format(name))
+ try:
+ pkg = import_module(name)
+ except Exception as e:
+ logger.critical('failed to load plugin module {0}: {1}'.format(name, e))
+ exit(3)
+
+ pkg.__base_path = dirname(abspath(pkg.__file__))
+
+ prepare_package_resources(pkg, name)
+
+ plugins.append(pkg)
+ logger.debug('plugin "{0}" loaded'.format(name))
+ return plugins
+
+
+def sync_resource(base_path, resource_path, name, target_dir, plugin_dir):
+ dep_path = join(base_path, resource_path)
+ file_name = basename(dep_path)
+ resource_path = join(target_dir, file_name)
+ if not exists(resource_path) or sha_sum(dep_path) != sha_sum(resource_path):
+ try:
+ copyfile(dep_path, resource_path)
+ # copy atime_ns and mtime_ns, so the weak ETags (generated by
+ # the HTTP server) do not change
+ dep_stat = stat(dep_path)
+ utime(resource_path, ns=(dep_stat.st_atime_ns, dep_stat.st_mtime_ns))
+ except:
+ logger.critical('failed to copy plugin resource {0} for plugin {1}'.format(file_name, name))
+ exit(3)
+
+ # returning with the web path of the resource
+ return join('plugins/external_plugins', plugin_dir, file_name)
+
+
+def prepare_package_resources(pkg, name):
+ plugin_dir = 'plugin_' + name
+ target_dir = join(static_path, 'plugins/external_plugins', plugin_dir)
+ try:
+ makedirs(target_dir, exist_ok=True)
+ except:
+ logger.critical('failed to create resource directory {0} for plugin {1}'.format(target_dir, name))
+ exit(3)
+
+ resources = []
+
+ if hasattr(pkg, 'js_dependencies'):
+ resources.extend(map(basename, pkg.js_dependencies))
+ pkg.js_dependencies = tuple([
+ sync_resource(pkg.__base_path, x, name, target_dir, plugin_dir)
+ for x in pkg.js_dependencies
+ ])
+ if hasattr(pkg, 'css_dependencies'):
+ resources.extend(map(basename, pkg.css_dependencies))
+ pkg.css_dependencies = tuple([
+ sync_resource(pkg.__base_path, x, name, target_dir, plugin_dir)
+ for x in pkg.css_dependencies
+ ])
+
+ for f in listdir(target_dir):
+ if basename(f) not in resources:
+ resource_path = join(target_dir, basename(f))
+ try:
+ remove(resource_path)
+ except:
+ logger.critical('failed to remove unused resource file {0} for plugin {1}'.format(resource_path, name))
+ exit(3)
+
+
+def sha_sum(filename):
+ with open(filename, "rb") as f:
+ file_content_bytes = f.read()
+ return sha256(file_content_bytes).hexdigest()
+
+
plugins = PluginStore()
plugins.register(oa_doi_rewrite)
+plugins.register(hash_plugin)
plugins.register(https_rewrite)
plugins.register(infinite_scroll)
-plugins.register(open_results_on_new_tab)
plugins.register(self_info)
plugins.register(search_on_category_select)
plugins.register(tracker_url_remover)
plugins.register(vim_hotkeys)
+# load external plugins
+if 'plugins' in settings:
+ plugins.register(*settings['plugins'], external=True)
+
+if 'enabled_plugins' in settings:
+ for plugin in plugins:
+ if plugin.name in settings['enabled_plugins']:
+ plugin.default_on = True
+ else:
+ plugin.default_on = False
+
+# load tor specific plugins
+if settings['outgoing'].get('using_tor_proxy'):
+ plugins.register(ahmia_filter)
diff --git a/searx/plugins/ahmia_filter.py b/searx/plugins/ahmia_filter.py
new file mode 100644
index 0000000..83b05e4
--- /dev/null
+++ b/searx/plugins/ahmia_filter.py
@@ -0,0 +1,33 @@
+'''
+ SPDX-License-Identifier: AGPL-3.0-or-later
+'''
+
+from hashlib import md5
+from searx.data import ahmia_blacklist_loader
+
+name = "Ahmia blacklist"
+description = "Filter out onion results that appear in Ahmia's blacklist. (See https://ahmia.fi/blacklist)"
+default_on = True
+preference_section = 'onions'
+
+ahmia_blacklist = None
+
+
+def get_ahmia_blacklist():
+ global ahmia_blacklist
+ if not ahmia_blacklist:
+ ahmia_blacklist = ahmia_blacklist_loader()
+ return ahmia_blacklist
+
+
+def not_blacklisted(result):
+ if not result.get('is_onion') or not result.get('parsed_url'):
+ return True
+ result_hash = md5(result['parsed_url'].hostname.encode()).hexdigest()
+ return result_hash not in get_ahmia_blacklist()
+
+
+def post_search(request, search):
+ filtered_results = list(filter(not_blacklisted, search.result_container._merged_results))
+ search.result_container._merged_results = filtered_results
+ return True
diff --git a/searx/plugins/hash_plugin.py b/searx/plugins/hash_plugin.py
new file mode 100644
index 0000000..1d3baae
--- /dev/null
+++ b/searx/plugins/hash_plugin.py
@@ -0,0 +1,54 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+(C) 2015 by Adam Tauber, <asciimoo@gmail.com>
+(C) 2018, 2020 by Vaclav Zouzalik
+'''
+
+from flask_babel import gettext
+import hashlib
+import re
+
+name = "Hash plugin"
+description = gettext("Converts strings to different hash digests.")
+default_on = True
+
+parser_re = re.compile('(md5|sha1|sha224|sha256|sha384|sha512) (.*)', re.I)
+
+
+def post_search(request, search):
+ # process only on first page
+ if search.search_query.pageno > 1:
+ return True
+ m = parser_re.match(search.search_query.query)
+ if not m:
+ # wrong query
+ return True
+
+ function, string = m.groups()
+ if string.strip().__len__() == 0:
+ # end if the string is empty
+ return True
+
+ # select hash function
+ f = hashlib.new(function.lower())
+
+ # make digest from the given string
+ f.update(string.encode('utf-8').strip())
+ answer = function + " " + gettext('hash digest') + ": " + f.hexdigest()
+
+ # print result
+ search.result_container.answers.clear()
+ search.result_container.answers['hash'] = {'answer': answer}
+ return True
diff --git a/searx/plugins/https_rewrite.py b/searx/plugins/https_rewrite.py
index 8255601..aeb4249 100644
--- a/searx/plugins/https_rewrite.py
+++ b/searx/plugins/https_rewrite.py
@@ -16,17 +16,14 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
'''
import re
-import sys
+from urllib.parse import urlparse
from lxml import etree
from os import listdir, environ
from os.path import isfile, isdir, join
from searx.plugins import logger
from flask_babel import gettext
from searx import searx_dir
-from searx.url_utils import urlparse
-if sys.version_info[0] == 3:
- unicode = str
name = "HTTPS rewrite"
description = gettext('Rewrite HTTP links to HTTPS if possible')
diff --git a/searx/plugins/oa_doi_rewrite.py b/searx/plugins/oa_doi_rewrite.py
index be80beb..eef29f1 100644
--- a/searx/plugins/oa_doi_rewrite.py
+++ b/searx/plugins/oa_doi_rewrite.py
@@ -1,6 +1,6 @@
+from urllib.parse import urlparse, parse_qsl
from flask_babel import gettext
import re
-from searx.url_utils import urlparse, parse_qsl
from searx import settings
diff --git a/searx/plugins/open_results_on_new_tab.py b/searx/plugins/open_results_on_new_tab.py
deleted file mode 100644
index 0f06f5a..0000000
--- a/searx/plugins/open_results_on_new_tab.py
+++ /dev/null
@@ -1,25 +0,0 @@
-'''
-searx is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-searx is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with searx. If not, see < http://www.gnu.org/licenses/ >.
-
-(C) 2016 by Adam Tauber, <asciimoo@gmail.com>
-'''
-from flask_babel import gettext
-name = gettext('Open result links on new browser tabs')
-description = gettext('Results are opened in the same window by default. '
- 'This plugin overwrites the default behaviour to open links on new tabs/windows. '
- '(JavaScript required)')
-default_on = False
-preference_section = 'ui'
-
-js_dependencies = ('plugins/js/open_results_on_new_tab.js',)
diff --git a/searx/plugins/self_info.py b/searx/plugins/self_info.py
index 67e5e0e..4fdfb42 100644
--- a/searx/plugins/self_info.py
+++ b/searx/plugins/self_info.py
@@ -16,13 +16,13 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
'''
from flask_babel import gettext
import re
-name = "Self Informations"
+name = gettext('Self Informations')
description = gettext('Displays your IP if the query is "ip" and your user agent if the query contains "user agent".')
default_on = True
# Self User Agent regex
-p = re.compile(b'.*user[ -]agent.*', re.IGNORECASE)
+p = re.compile('.*user[ -]agent.*', re.IGNORECASE)
# attach callback to the post search hook
@@ -31,7 +31,7 @@ p = re.compile(b'.*user[ -]agent.*', re.IGNORECASE)
def post_search(request, search):
if search.search_query.pageno > 1:
return True
- if search.search_query.query == b'ip':
+ if search.search_query.query == 'ip':
x_forwarded_for = request.headers.getlist("X-Forwarded-For")
if x_forwarded_for:
ip = x_forwarded_for[0]
diff --git a/searx/plugins/tracker_url_remover.py b/searx/plugins/tracker_url_remover.py
index 33dd621..742f390 100644
--- a/searx/plugins/tracker_url_remover.py
+++ b/searx/plugins/tracker_url_remover.py
@@ -17,7 +17,7 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
from flask_babel import gettext
import re
-from searx.url_utils import urlunparse, parse_qsl, urlencode
+from urllib.parse import urlunparse, parse_qsl, urlencode
regexes = {re.compile(r'utm_[^&]+'),
re.compile(r'(wkey|wemail)[^&]*'),
diff --git a/searx/poolrequests.py b/searx/poolrequests.py
index f9a9d77..25a6bae 100644
--- a/searx/poolrequests.py
+++ b/searx/poolrequests.py
@@ -1,9 +1,34 @@
-import requests
-
+import sys
+from time import time
from itertools import cycle
from threading import RLock, local
+
+import requests
+
from searx import settings
-from time import time
+from searx import logger
+from searx.raise_for_httperror import raise_for_httperror
+
+
+logger = logger.getChild('poolrequests')
+
+
+try:
+ import ssl
+ if ssl.OPENSSL_VERSION_INFO[0:3] < (1, 0, 2):
+ # https://github.com/certifi/python-certifi#1024-bit-root-certificates
+ logger.critical('You are using an old openssl version({0}), please upgrade above 1.0.2!'
+ .format(ssl.OPENSSL_VERSION))
+ sys.exit(1)
+except ImportError:
+ ssl = None
+if not getattr(ssl, "HAS_SNI", False):
+ try:
+ import OpenSSL # pylint: disable=unused-import
+ except ImportError:
+ logger.critical("ssl doesn't support SNI and the pyopenssl module is not installed.\n"
+ "Some HTTPS connections will fail")
+ sys.exit(1)
class HTTPAdapterWithConnParams(requests.adapters.HTTPAdapter):
@@ -20,7 +45,7 @@ class HTTPAdapterWithConnParams(requests.adapters.HTTPAdapter):
self.config = {}
self.proxy_manager = {}
- super(requests.adapters.HTTPAdapter, self).__init__()
+ super().__init__()
self._pool_connections = pool_connections
self._pool_maxsize = pool_maxsize
@@ -60,7 +85,7 @@ else:
class SessionSinglePool(requests.Session):
def __init__(self):
- super(SessionSinglePool, self).__init__()
+ super().__init__()
# reuse the same adapters
with RLock():
@@ -71,7 +96,7 @@ class SessionSinglePool(requests.Session):
def close(self):
"""Call super, but clear adapters since there are managed globaly"""
self.adapters.clear()
- super(SessionSinglePool, self).close()
+ super().close()
def set_timeout_for_thread(timeout, start_time=None):
@@ -87,6 +112,32 @@ def get_time_for_thread():
return threadLocal.total_time
+def get_proxy_cycles(proxy_settings):
+ if not proxy_settings:
+ return None
+ # Backwards compatibility for single proxy in settings.yml
+ for protocol, proxy in proxy_settings.items():
+ if isinstance(proxy, str):
+ proxy_settings[protocol] = [proxy]
+
+ for protocol in proxy_settings:
+ proxy_settings[protocol] = cycle(proxy_settings[protocol])
+ return proxy_settings
+
+
+GLOBAL_PROXY_CYCLES = get_proxy_cycles(settings['outgoing'].get('proxies'))
+
+
+def get_proxies(proxy_cycles):
+ if proxy_cycles:
+ return {protocol: next(proxy_cycle) for protocol, proxy_cycle in proxy_cycles.items()}
+ return None
+
+
+def get_global_proxies():
+ return get_proxies(GLOBAL_PROXY_CYCLES)
+
+
def request(method, url, **kwargs):
"""same as requests/requests/api.py request(...)"""
time_before_request = time()
@@ -95,7 +146,8 @@ def request(method, url, **kwargs):
session = SessionSinglePool()
# proxies
- kwargs['proxies'] = settings['outgoing'].get('proxies') or None
+ if not kwargs.get('proxies'):
+ kwargs['proxies'] = get_global_proxies()
# timeout
if 'timeout' in kwargs:
@@ -105,6 +157,12 @@ def request(method, url, **kwargs):
if timeout is not None:
kwargs['timeout'] = timeout
+ # raise_for_error
+ check_for_httperror = True
+ if 'raise_for_httperror' in kwargs:
+ check_for_httperror = kwargs['raise_for_httperror']
+ del kwargs['raise_for_httperror']
+
# do request
response = session.request(method=method, url=url, **kwargs)
@@ -125,6 +183,10 @@ def request(method, url, **kwargs):
if hasattr(threadLocal, 'total_time'):
threadLocal.total_time += time_after_request - time_before_request
+ # raise an exception
+ if check_for_httperror:
+ raise_for_httperror(response)
+
return response
diff --git a/searx/preferences.py b/searx/preferences.py
index f70aee3..a9f16ff 100644
--- a/searx/preferences.py
+++ b/searx/preferences.py
@@ -6,16 +6,11 @@
from base64 import urlsafe_b64encode, urlsafe_b64decode
from zlib import compress, decompress
-from sys import version
+from urllib.parse import parse_qs, urlencode
from searx import settings, autocomplete
from searx.languages import language_codes as languages
-from searx.utils import match_language
-from searx.url_utils import parse_qs, urlencode
-
-if version[0] == '3':
- # pylint: disable=invalid-name
- unicode = str
+from searx.webutils import VALID_LANGUAGE_CODE
COOKIE_MAX_AGE = 60 * 60 * 24 * 365 * 5 # 5 years
@@ -37,12 +32,13 @@ class ValidationException(Exception):
"""
-class Setting(object):
+class Setting:
"""Base class of user settings"""
- def __init__(self, default_value, **kwargs):
- super(Setting, self).__init__()
+ def __init__(self, default_value, locked=False, **kwargs):
+ super().__init__()
self.value = default_value
+ self.locked = locked
for key, value in kwargs.items():
setattr(self, key, value)
@@ -120,6 +116,9 @@ class MultipleChoiceSetting(EnumStringSetting):
self.value = elements
def parse_form(self, data): # pylint: disable=missing-function-docstring
+ if self.locked:
+ return
+
self.value = []
for choice in data:
if choice in self.choices and choice not in self.value: # pylint: disable=no-member
@@ -154,6 +153,9 @@ class SetSetting(Setting):
self.values.add(element)
def parse_form(self, data): # pylint: disable=missing-function-docstring
+ if self.locked:
+ return
+
elements = data.split(',')
self.values = set(elements) # pylint: disable=attribute-defined-outside-init
@@ -167,9 +169,7 @@ class SearchLanguageSetting(EnumStringSetting):
"""Available choices may change, so user's value may not be in choices anymore"""
def _validate_selection(self, selection):
- if selection != "" and not match_language(
- # pylint: disable=no-member
- selection, self.choices, fallback=None):
+ if selection != '' and not VALID_LANGUAGE_CODE.match(selection):
raise ValidationException('Invalid language code: "{0}"'.format(selection))
def parse(self, data):
@@ -186,6 +186,7 @@ class SearchLanguageSetting(EnumStringSetting):
data = lang
else:
data = self.value
+ self._validate_selection(data)
self.value = data
@@ -239,6 +240,9 @@ class SwitchableSetting(Setting):
self.enabled = set(data[ENABLED].split(','))
def parse_form(self, items): # pylint: disable=missing-function-docstring
+ if self.locked:
+ return
+
items = self.transform_form_items(items)
self.disabled = set() # pylint: disable=attribute-defined-outside-init
self.enabled = set() # pylint: disable=attribute-defined-outside-init
@@ -275,7 +279,7 @@ class EnginesSetting(SwitchableSetting):
"""Engine settings"""
def _post_init(self):
- super(EnginesSetting, self)._post_init()
+ super()._post_init()
transformed_choices = []
for engine_name, engine in self.choices.items(): # pylint: disable=no-member,access-member-before-definition
for category in engine.categories:
@@ -302,7 +306,7 @@ class PluginsSetting(SwitchableSetting):
"""Plugin settings"""
def _post_init(self):
- super(PluginsSetting, self)._post_init()
+ super()._post_init()
transformed_choices = []
for plugin in self.choices: # pylint: disable=access-member-before-definition
transformed_choice = dict()
@@ -315,30 +319,36 @@ class PluginsSetting(SwitchableSetting):
return [item[len('plugin_'):] for item in items]
-class Preferences(object):
+class Preferences:
"""Validates and saves preferences to cookies"""
def __init__(self, themes, categories, engines, plugins):
- super(Preferences, self).__init__()
+ super().__init__()
self.key_value_settings = {
'categories': MultipleChoiceSetting(
- ['general'], choices=categories + ['none']
+ ['general'],
+ is_locked('categories'),
+ choices=categories + ['none']
),
'language': SearchLanguageSetting(
settings['search'].get('default_lang', ''),
+ is_locked('language'),
choices=list(LANGUAGE_CODES) + ['']
),
'locale': EnumStringSetting(
settings['ui'].get('default_locale', ''),
+ is_locked('locale'),
choices=list(settings['locales'].keys()) + ['']
),
'autocomplete': EnumStringSetting(
settings['search'].get('autocomplete', ''),
+ is_locked('autocomplete'),
choices=list(autocomplete.backends.keys()) + ['']
),
'image_proxy': MapSetting(
settings['server'].get('image_proxy', False),
+ is_locked('image_proxy'),
map={
'': settings['server'].get('image_proxy', 0),
'0': False,
@@ -348,11 +358,13 @@ class Preferences(object):
}
),
'method': EnumStringSetting(
- 'POST',
+ settings['server'].get('method', 'POST'),
+ is_locked('method'),
choices=('GET', 'POST')
),
'safesearch': MapSetting(
settings['search'].get('safe_search', 0),
+ is_locked('safesearch'),
map={
'0': 0,
'1': 1,
@@ -361,10 +373,12 @@ class Preferences(object):
),
'theme': EnumStringSetting(
settings['ui'].get('default_theme', 'oscar'),
+ is_locked('theme'),
choices=themes
),
'results_on_new_tab': MapSetting(
- False,
+ settings['ui'].get('results_on_new_tab', False),
+ is_locked('results_on_new_tab'),
map={
'0': False,
'1': True,
@@ -373,11 +387,25 @@ class Preferences(object):
}
),
'doi_resolver': MultipleChoiceSetting(
- ['oadoi.org'], choices=DOI_RESOLVERS
+ ['oadoi.org'],
+ is_locked('doi_resolver'),
+ choices=DOI_RESOLVERS
),
'oscar-style': EnumStringSetting(
settings['ui'].get('theme_args', {}).get('oscar_style', 'logicodev'),
+ is_locked('oscar-style'),
choices=['', 'logicodev', 'logicodev-dark', 'pointhi']),
+ 'advanced_search': MapSetting(
+ settings['ui'].get('advanced_search', False),
+ is_locked('advanced_search'),
+ map={
+ '0': False,
+ '1': True,
+ 'False': False,
+ 'True': True,
+ 'on': True,
+ }
+ ),
}
self.engines = EnginesSetting('engines', choices=engines)
@@ -389,6 +417,8 @@ class Preferences(object):
"""Return preferences as URL parameters"""
settings_kv = {}
for k, v in self.key_value_settings.items():
+ if v.locked:
+ continue
if isinstance(v, MultipleChoiceSetting):
settings_kv[k] = ','.join(v.get_value())
else:
@@ -402,20 +432,22 @@ class Preferences(object):
settings_kv['tokens'] = ','.join(self.tokens.values)
- return urlsafe_b64encode(compress(urlencode(settings_kv).encode('utf-8'))).decode('utf-8')
+ return urlsafe_b64encode(compress(urlencode(settings_kv).encode())).decode()
def parse_encoded_data(self, input_data):
"""parse (base64) preferences from request (``flask.request.form['preferences']``)"""
- decoded_data = decompress(urlsafe_b64decode(input_data.encode('utf-8')))
+ decoded_data = decompress(urlsafe_b64decode(input_data.encode()))
dict_data = {}
for x, y in parse_qs(decoded_data).items():
- dict_data[x.decode('utf8')] = y[0].decode('utf8')
+ dict_data[x.decode()] = y[0].decode()
self.parse_dict(dict_data)
def parse_dict(self, input_data):
"""parse preferences from request (``flask.request.form``)"""
for user_setting_name, user_setting in input_data.items():
if user_setting_name in self.key_value_settings:
+ if self.key_value_settings[user_setting_name].locked:
+ continue
self.key_value_settings[user_setting_name].parse(user_setting)
elif user_setting_name == 'disabled_engines':
self.engines.parse_cookie((input_data.get('disabled_engines', ''),
@@ -470,6 +502,8 @@ class Preferences(object):
"""Save cookie in the HTTP reponse obect
"""
for user_setting_name, user_setting in self.key_value_settings.items():
+ if self.key_value_settings[user_setting_name].locked:
+ continue
user_setting.save(user_setting_name, resp)
self.engines.save(resp)
self.plugins.save(resp)
@@ -488,3 +522,13 @@ class Preferences(object):
break
return valid
+
+
+def is_locked(setting_name):
+ """Checks if a given setting name is locked by settings.yml
+ """
+ if 'preferences' not in settings:
+ return False
+ if 'lock' not in settings['preferences']:
+ return False
+ return setting_name in settings['preferences']['lock']
diff --git a/searx/query.py b/searx/query.py
index e8b57d4..422cd57 100644
--- a/searx/query.py
+++ b/searx/query.py
@@ -17,23 +17,19 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2014 by Thomas Pointhuber, <thomas.pointhuber@gmx.at>
'''
-from searx.languages import language_codes
-from searx.engines import (
- categories, engines, engine_shortcuts
-)
import re
-import sys
-
-if sys.version_info[0] == 3:
- unicode = str
-VALID_LANGUAGE_CODE = re.compile(r'^[a-z]{2,3}(-[a-zA-Z]{2})?$')
+from searx.languages import language_codes
+from searx.engines import categories, engines, engine_shortcuts
+from searx.search import EngineRef
+from searx.webutils import VALID_LANGUAGE_CODE
-class RawTextQuery(object):
+class RawTextQuery:
"""parse raw text query (the value from the html input)"""
def __init__(self, query, disabled_engines):
+ assert isinstance(query, str)
self.query = query
self.disabled_engines = []
@@ -41,34 +37,28 @@ class RawTextQuery(object):
self.disabled_engines = disabled_engines
self.query_parts = []
- self.engines = []
+ self.user_query_parts = []
+ self.enginerefs = []
self.languages = []
self.timeout_limit = None
self.external_bang = None
self.specific = False
+ self._parse_query()
# parse query, if tags are set, which
# change the search engine or search-language
- def parse_query(self):
+ def _parse_query(self):
self.query_parts = []
# split query, including whitespaces
- raw_query_parts = re.split(r'(\s+)' if isinstance(self.query, str) else b'(\s+)', self.query)
-
- parse_next = True
+ raw_query_parts = re.split(r'(\s+)', self.query)
for query_part in raw_query_parts:
- if not parse_next:
- self.query_parts[-1] += query_part
- continue
-
- parse_next = False
+ searx_query_part = False
# part does only contain spaces, skip
if query_part.isspace()\
or query_part == '':
- parse_next = True
- self.query_parts.append(query_part)
continue
# this force the timeout
@@ -81,7 +71,7 @@ class RawTextQuery(object):
else:
# 100 or above, the unit is the millisecond ( <850 = 850 milliseconds timeout )
self.timeout_limit = raw_timeout_limit / 1000.0
- parse_next = True
+ searx_query_part = True
except ValueError:
# error not reported to the user
pass
@@ -93,7 +83,7 @@ class RawTextQuery(object):
# check if any language-code is equal with
# declared language-codes
for lc in language_codes:
- lang_id, lang_name, country, english_name = map(unicode.lower, lc)
+ lang_id, lang_name, country, english_name = map(str.lower, lc)
# if correct language-code is found
# set it as new search-language
@@ -102,15 +92,15 @@ class RawTextQuery(object):
or lang == english_name
or lang.replace('-', ' ') == country)\
and lang not in self.languages:
- parse_next = True
- lang_parts = lang_id.split('-')
- if len(lang_parts) == 2:
- self.languages.append(lang_parts[0] + '-' + lang_parts[1].upper())
- else:
- self.languages.append(lang_id)
- # to ensure best match (first match is not necessarily the best one)
- if lang == lang_id:
- break
+ searx_query_part = True
+ lang_parts = lang_id.split('-')
+ if len(lang_parts) == 2:
+ self.languages.append(lang_parts[0] + '-' + lang_parts[1].upper())
+ else:
+ self.languages.append(lang_id)
+ # to ensure best match (first match is not necessarily the best one)
+ if lang == lang_id:
+ break
# user may set a valid, yet not selectable language
if VALID_LANGUAGE_CODE.match(lang):
@@ -119,12 +109,12 @@ class RawTextQuery(object):
lang = lang_parts[0].lower() + '-' + lang_parts[1].upper()
if lang not in self.languages:
self.languages.append(lang)
- parse_next = True
+ searx_query_part = True
# external bang
if query_part[0:2] == "!!":
self.external_bang = query_part[2:]
- parse_next = True
+ searx_query_part = True
continue
# this force a engine or category
if query_part[0] == '!' or query_part[0] == '?':
@@ -132,69 +122,41 @@ class RawTextQuery(object):
# check if prefix is equal with engine shortcut
if prefix in engine_shortcuts:
- parse_next = True
+ searx_query_part = True
engine_name = engine_shortcuts[prefix]
if engine_name in engines:
- self.engines.append({'category': 'none',
- 'name': engine_name,
- 'from_bang': True})
+ self.enginerefs.append(EngineRef(engine_name, 'none', True))
# check if prefix is equal with engine name
elif prefix in engines:
- parse_next = True
- self.engines.append({'category': 'none',
- 'name': prefix,
- 'from_bang': True})
+ searx_query_part = True
+ self.enginerefs.append(EngineRef(prefix, 'none', True))
# check if prefix is equal with categorie name
elif prefix in categories:
# using all engines for that search, which
# are declared under that categorie name
- parse_next = True
- self.engines.extend({'category': prefix,
- 'name': engine.name}
- for engine in categories[prefix]
- if (engine.name, prefix) not in self.disabled_engines)
+ searx_query_part = True
+ self.enginerefs.extend(EngineRef(engine.name, prefix)
+ for engine in categories[prefix]
+ if (engine.name, prefix) not in self.disabled_engines)
if query_part[0] == '!':
self.specific = True
# append query part to query_part list
- self.query_parts.append(query_part)
+ if searx_query_part:
+ self.query_parts.append(query_part)
+ else:
+ self.user_query_parts.append(query_part)
- def changeSearchQuery(self, search_query):
- if len(self.query_parts):
- self.query_parts[-1] = search_query
- else:
- self.query_parts.append(search_query)
+ def changeQuery(self, query):
+ self.user_query_parts = query.strip().split()
return self
- def getSearchQuery(self):
- if len(self.query_parts):
- return self.query_parts[-1]
- else:
- return ''
+ def getQuery(self):
+ return ' '.join(self.user_query_parts)
def getFullQuery(self):
# get full querry including whitespaces
- return u''.join(self.query_parts)
-
-
-class SearchQuery(object):
- """container for all the search parameters (query, language, etc...)"""
-
- def __init__(self, query, engines, categories, lang, safesearch, pageno, time_range,
- timeout_limit=None, preferences=None, external_bang=None):
- self.query = query.encode('utf-8')
- self.engines = engines
- self.categories = categories
- self.lang = lang
- self.safesearch = safesearch
- self.pageno = pageno
- self.time_range = None if time_range in ('', 'None', None) else time_range
- self.timeout_limit = timeout_limit
- self.preferences = preferences
- self.external_bang = external_bang
-
- def __str__(self):
- return str(self.query) + ";" + str(self.engines)
+ return '{0} {1}'.format(''.join(self.query_parts), self.getQuery()).strip()
diff --git a/searx/raise_for_httperror.py b/searx/raise_for_httperror.py
new file mode 100644
index 0000000..bd12df9
--- /dev/null
+++ b/searx/raise_for_httperror.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: AGPL-3.0-or-later
+"""
+Raise exception for an HTTP response is an error.
+"""
+from searx.exceptions import (SearxEngineCaptchaException, SearxEngineTooManyRequestsException,
+ SearxEngineAccessDeniedException)
+
+
+def is_cloudflare_challenge(resp):
+ if resp.status_code in [429, 503]:
+ if ('__cf_chl_jschl_tk__=' in resp.text)\
+ or ('/cdn-cgi/challenge-platform/' in resp.text
+ and 'orchestrate/jsch/v1' in resp.text
+ and 'window._cf_chl_enter(' in resp.text):
+ return True
+ if resp.status_code == 403 and '__cf_chl_captcha_tk__=' in resp.text:
+ return True
+ return False
+
+
+def is_cloudflare_firewall(resp):
+ return resp.status_code == 403 and '<span class="cf-error-code">1020</span>' in resp.text
+
+
+def raise_for_cloudflare_captcha(resp):
+ if resp.headers.get('Server', '').startswith('cloudflare'):
+ if is_cloudflare_challenge(resp):
+ # https://support.cloudflare.com/hc/en-us/articles/200170136-Understanding-Cloudflare-Challenge-Passage-Captcha-
+ # suspend for 2 weeks
+ raise SearxEngineCaptchaException(message='Cloudflare CAPTCHA', suspended_time=3600 * 24 * 15)
+
+ if is_cloudflare_firewall(resp):
+ raise SearxEngineAccessDeniedException(message='Cloudflare Firewall', suspended_time=3600 * 24)
+
+
+def raise_for_recaptcha(resp):
+ if resp.status_code == 503 \
+ and '"https://www.google.com/recaptcha/' in resp.text:
+ raise SearxEngineCaptchaException(message='ReCAPTCHA', suspended_time=3600 * 24 * 7)
+
+
+def raise_for_captcha(resp):
+ raise_for_cloudflare_captcha(resp)
+ raise_for_recaptcha(resp)
+
+
+def raise_for_httperror(resp):
+ """Raise exception for an HTTP response is an error.
+
+ Args:
+ resp (requests.Response): Response to check
+
+ Raises:
+ requests.HTTPError: raise by resp.raise_for_status()
+ searx.exceptions.SearxEngineAccessDeniedException: raise when the HTTP status code is 402 or 403.
+ searx.exceptions.SearxEngineTooManyRequestsException: raise when the HTTP status code is 429.
+ searx.exceptions.SearxEngineCaptchaException: raise when if CATPCHA challenge is detected.
+ """
+ if resp.status_code and resp.status_code >= 400:
+ raise_for_captcha(resp)
+ if resp.status_code in (402, 403):
+ raise SearxEngineAccessDeniedException(message='HTTP error ' + str(resp.status_code),
+ suspended_time=3600 * 24)
+ if resp.status_code == 429:
+ raise SearxEngineTooManyRequestsException()
+ resp.raise_for_status()
diff --git a/searx/results.py b/searx/results.py
index 4fed58e..fb7e816 100644
--- a/searx/results.py
+++ b/searx/results.py
@@ -1,13 +1,11 @@
import re
-import sys
-from collections import defaultdict
from operator import itemgetter
from threading import RLock
+from urllib.parse import urlparse, unquote
+from searx import logger
from searx.engines import engines
-from searx.url_utils import urlparse, unquote
+from searx.metrology.error_recorder import record_error
-if sys.version_info[0] == 3:
- basestring = str
CONTENT_LEN_IGNORED_CHARS_REGEX = re.compile(r'[,;:!?\./\\\\ ()-_]', re.M | re.U)
WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U)
@@ -15,13 +13,25 @@ WHITESPACE_REGEX = re.compile('( |\t|\n)+', re.M | re.U)
# return the meaningful length of the content for a result
def result_content_len(content):
- if isinstance(content, basestring):
+ if isinstance(content, str):
return len(CONTENT_LEN_IGNORED_CHARS_REGEX.sub('', content))
else:
return 0
def compare_urls(url_a, url_b):
+ """Lazy compare between two URL.
+ "www.example.com" and "example.com" are equals.
+ "www.example.com/path/" and "www.example.com/path" are equals.
+ "https://www.example.com/" and "http://www.example.com/" are equals.
+
+ Args:
+ url_a (ParseResult): first URL
+ url_b (ParseResult): second URL
+
+ Returns:
+ bool: True if url_a and url_b are equals
+ """
# ignore www. in comparison
if url_a.netloc.startswith('www.'):
host_a = url_a.netloc.replace('www.', '', 1)
@@ -60,6 +70,8 @@ def merge_two_infoboxes(infobox1, infobox2):
if weight2 > weight1:
infobox1['engine'] = infobox2['engine']
+ infobox1['engines'] |= infobox2['engines']
+
if 'urls' in infobox2:
urls1 = infobox1.get('urls', None)
if urls1 is None:
@@ -68,8 +80,10 @@ def merge_two_infoboxes(infobox1, infobox2):
for url2 in infobox2.get('urls', []):
unique_url = True
parsed_url2 = urlparse(url2.get('url', ''))
+ entity_url2 = url2.get('entity')
for url1 in urls1:
- if compare_urls(urlparse(url1.get('url', '')), parsed_url2):
+ if (entity_url2 is not None and url1.get('entity') == entity_url2)\
+ or compare_urls(urlparse(url1.get('url', '')), parsed_url2):
unique_url = False
break
if unique_url:
@@ -86,18 +100,22 @@ def merge_two_infoboxes(infobox1, infobox2):
infobox1['img_src'] = img2
if 'attributes' in infobox2:
- attributes1 = infobox1.get('attributes', None)
+ attributes1 = infobox1.get('attributes')
if attributes1 is None:
- attributes1 = []
- infobox1['attributes'] = attributes1
+ infobox1['attributes'] = attributes1 = []
attributeSet = set()
- for attribute in infobox1.get('attributes', []):
- if attribute.get('label', None) not in attributeSet:
- attributeSet.add(attribute.get('label', None))
+ for attribute in attributes1:
+ label = attribute.get('label')
+ if label not in attributeSet:
+ attributeSet.add(label)
+ entity = attribute.get('entity')
+ if entity not in attributeSet:
+ attributeSet.add(entity)
for attribute in infobox2.get('attributes', []):
- if attribute.get('label', None) not in attributeSet:
+ if attribute.get('label') not in attributeSet\
+ and attribute.get('entity') not in attributeSet:
attributes1.append(attribute)
if 'content' in infobox2:
@@ -122,12 +140,14 @@ def result_score(result):
return sum((occurences * weight) / position for position in result['positions'])
-class ResultContainer(object):
+class ResultContainer:
"""docstring for ResultContainer"""
+ __slots__ = '_merged_results', 'infoboxes', 'suggestions', 'answers', 'corrections', '_number_of_results',\
+ '_ordered', 'paging', 'unresponsive_engines', 'timings', 'redirect_url'
+
def __init__(self):
- super(ResultContainer, self).__init__()
- self.results = defaultdict(list)
+ super().__init__()
self._merged_results = []
self.infoboxes = []
self.suggestions = set()
@@ -141,54 +161,52 @@ class ResultContainer(object):
self.redirect_url = None
def extend(self, engine_name, results):
+ standard_result_count = 0
+ error_msgs = set()
for result in list(results):
result['engine'] = engine_name
if 'suggestion' in result:
self.suggestions.add(result['suggestion'])
- results.remove(result)
elif 'answer' in result:
self.answers[result['answer']] = result
- results.remove(result)
elif 'correction' in result:
self.corrections.add(result['correction'])
- results.remove(result)
elif 'infobox' in result:
self._merge_infobox(result)
- results.remove(result)
elif 'number_of_results' in result:
self._number_of_results.append(result['number_of_results'])
- results.remove(result)
+ else:
+ # standard result (url, title, content)
+ if 'url' in result and not isinstance(result['url'], str):
+ logger.debug('result: invalid URL: %s', str(result))
+ error_msgs.add('invalid URL')
+ elif 'title' in result and not isinstance(result['title'], str):
+ logger.debug('result: invalid title: %s', str(result))
+ error_msgs.add('invalid title')
+ elif 'content' in result and not isinstance(result['content'], str):
+ logger.debug('result: invalid content: %s', str(result))
+ error_msgs.add('invalid content')
+ else:
+ self._merge_result(result, standard_result_count + 1)
+ standard_result_count += 1
+
+ if len(error_msgs) > 0:
+ for msg in error_msgs:
+ record_error(engine_name, 'some results are invalids: ' + msg)
if engine_name in engines:
with RLock():
engines[engine_name].stats['search_count'] += 1
- engines[engine_name].stats['result_count'] += len(results)
-
- if not results:
- return
-
- self.results[engine_name].extend(results)
+ engines[engine_name].stats['result_count'] += standard_result_count
- if not self.paging and engine_name in engines and engines[engine_name].paging:
+ if not self.paging and standard_result_count > 0 and engine_name in engines\
+ and engines[engine_name].paging:
self.paging = True
- for i, result in enumerate(results):
- if 'url' in result and not isinstance(result['url'], basestring):
- continue
- try:
- result['url'] = result['url'].decode('utf-8')
- except:
- pass
- if 'title' in result and not isinstance(result['title'], basestring):
- continue
- if 'content' in result and not isinstance(result['content'], basestring):
- continue
- position = i + 1
- self._merge_result(result, position)
-
def _merge_infobox(self, infobox):
add_infobox = True
infobox_id = infobox.get('id', None)
+ infobox['engines'] = set([infobox['engine']])
if infobox_id is not None:
parsed_url_infobox_id = urlparse(infobox_id)
for existingIndex in self.infoboxes:
@@ -289,12 +307,13 @@ class ResultContainer(object):
gresults = []
categoryPositions = {}
- for i, res in enumerate(results):
+ for res in results:
# FIXME : handle more than one category per engine
- res['category'] = engines[res['engine']].categories[0]
+ engine = engines[res['engine']]
+ res['category'] = engine.categories[0] if len(engine.categories) > 0 else ''
# FIXME : handle more than one category per engine
- category = engines[res['engine']].categories[0]\
+ category = res['category']\
+ ':' + res.get('template', '')\
+ ':' + ('img_src' if 'img_src' in res or 'thumbnail' in res else '')
diff --git a/searx/search.py b/searx/search.py
index 2469508..2209508 100644
--- a/searx/search.py
+++ b/searx/search.py
@@ -15,39 +15,30 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
'''
+import typing
import gc
-import sys
import threading
from time import time
from uuid import uuid4
+from urllib.parse import urlparse
+from _thread import start_new_thread
-import six
-from flask_babel import gettext
import requests.exceptions
import searx.poolrequests as requests_lib
-from searx.engines import (
- categories, engines, settings
-)
+from searx.engines import engines, settings
from searx.answerers import ask
from searx.external_bang import get_bang_url
from searx.utils import gen_useragent
-from searx.query import RawTextQuery, SearchQuery, VALID_LANGUAGE_CODE
from searx.results import ResultContainer
from searx import logger
from searx.plugins import plugins
-from searx.exceptions import SearxParameterException
+from searx.exceptions import (SearxEngineAccessDeniedException, SearxEngineCaptchaException,
+ SearxEngineTooManyRequestsException,)
+from searx.metrology.error_recorder import record_exception, record_error
-try:
- from thread import start_new_thread
-except:
- from _thread import start_new_thread
-
-if sys.version_info[0] == 3:
- unicode = str
logger = logger.getChild('search')
-number_of_searches = 0
max_request_timeout = settings.get('outgoing', {}).get('max_request_timeout' or None)
if max_request_timeout is None:
logger.info('max_request_timeout={0}'.format(max_request_timeout))
@@ -56,9 +47,67 @@ else:
logger.info('max_request_timeout={0} second(s)'.format(max_request_timeout))
else:
logger.critical('outgoing.max_request_timeout if defined has to be float')
- from sys import exit
-
- exit(1)
+ import sys
+ sys.exit(1)
+
+
+class EngineRef:
+
+ __slots__ = 'name', 'category', 'from_bang'
+
+ def __init__(self, name: str, category: str, from_bang: bool=False):
+ self.name = name
+ self.category = category
+ self.from_bang = from_bang
+
+ def __repr__(self):
+ return "EngineRef({!r}, {!r}, {!r})".format(self.name, self.category, self.from_bang)
+
+ def __eq__(self, other):
+ return self.name == other.name and self.category == other.category and self.from_bang == other.from_bang
+
+
+class SearchQuery:
+ """container for all the search parameters (query, language, etc...)"""
+
+ __slots__ = 'query', 'engineref_list', 'categories', 'lang', 'safesearch', 'pageno', 'time_range',\
+ 'timeout_limit', 'external_bang'
+
+ def __init__(self,
+ query: str,
+ engineref_list: typing.List[EngineRef],
+ categories: typing.List[str],
+ lang: str,
+ safesearch: int,
+ pageno: int,
+ time_range: typing.Optional[str],
+ timeout_limit: typing.Optional[float]=None,
+ external_bang: typing.Optional[str]=None):
+ self.query = query
+ self.engineref_list = engineref_list
+ self.categories = categories
+ self.lang = lang
+ self.safesearch = safesearch
+ self.pageno = pageno
+ self.time_range = time_range
+ self.timeout_limit = timeout_limit
+ self.external_bang = external_bang
+
+ def __repr__(self):
+ return "SearchQuery({!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r})".\
+ format(self.query, self.engineref_list, self.categories, self.lang, self.safesearch,
+ self.pageno, self.time_range, self.timeout_limit, self.external_bang)
+
+ def __eq__(self, other):
+ return self.query == other.query\
+ and self.engineref_list == other.engineref_list\
+ and self.categories == self.categories\
+ and self.lang == other.lang\
+ and self.safesearch == other.safesearch\
+ and self.pageno == other.pageno\
+ and self.time_range == other.time_range\
+ and self.timeout_limit == other.timeout_limit\
+ and self.external_bang == other.external_bang
def send_http_request(engine, request_params):
@@ -67,18 +116,48 @@ def send_http_request(engine, request_params):
request_args = dict(
headers=request_params['headers'],
cookies=request_params['cookies'],
- verify=request_params['verify']
+ verify=request_params['verify'],
+ auth=request_params['auth']
)
+ # setting engine based proxies
+ if hasattr(engine, 'proxies'):
+ request_args['proxies'] = requests_lib.get_proxies(engine.proxies)
+
+ # max_redirects
+ max_redirects = request_params.get('max_redirects')
+ if max_redirects:
+ request_args['max_redirects'] = max_redirects
+
+ # soft_max_redirects
+ soft_max_redirects = request_params.get('soft_max_redirects', max_redirects or 0)
+
+ # raise_for_status
+ request_args['raise_for_httperror'] = request_params.get('raise_for_httperror', False)
+
# specific type of request (GET or POST)
if request_params['method'] == 'GET':
req = requests_lib.get
else:
req = requests_lib.post
- request_args['data'] = request_params['data']
+
+ request_args['data'] = request_params['data']
# send the request
- return req(request_params['url'], **request_args)
+ response = req(request_params['url'], **request_args)
+
+ # check soft limit of the redirect count
+ if len(response.history) > soft_max_redirects:
+ # unexpected redirect : record an error
+ # but the engine might still return valid results.
+ status_code = str(response.status_code or '')
+ reason = response.reason or ''
+ hostname = str(urlparse(response.url or '').netloc)
+ record_error(engine.name,
+ '{} redirects, maximum: {}'.format(len(response.history), soft_max_redirects),
+ (status_code, reason, hostname))
+
+ return response
def search_one_http_request(engine, query, request_params):
@@ -101,48 +180,6 @@ def search_one_http_request(engine, query, request_params):
return engine.response(response)
-def search_one_offline_request(engine, query, request_params):
- return engine.search(query, request_params)
-
-
-def search_one_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit):
- if engines[engine_name].offline:
- return search_one_offline_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit) # noqa
- return search_one_http_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit)
-
-
-def search_one_offline_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit):
- engine = engines[engine_name]
-
- try:
- search_results = search_one_offline_request(engine, query, request_params)
-
- if search_results:
- result_container.extend(engine_name, search_results)
-
- engine_time = time() - start_time
- result_container.add_timing(engine_name, engine_time, engine_time)
- with threading.RLock():
- engine.stats['engine_time'] += engine_time
- engine.stats['engine_time_count'] += 1
-
- except ValueError as e:
- record_offline_engine_stats_on_error(engine, result_container, start_time)
- logger.exception('engine {0} : invalid input : {1}'.format(engine_name, e))
- except Exception as e:
- record_offline_engine_stats_on_error(engine, result_container, start_time)
- result_container.add_unresponsive_engine(engine_name, 'unexpected crash', str(e))
- logger.exception('engine {0} : exception : {1}'.format(engine_name, e))
-
-
-def record_offline_engine_stats_on_error(engine, result_container, start_time):
- engine_time = time() - start_time
- result_container.add_timing(engine.name, engine_time, engine_time)
-
- with threading.RLock():
- engine.stats['errors'] += 1
-
-
def search_one_http_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit):
# set timeout for all HTTP requests
requests_lib.set_timeout_for_thread(timeout_limit, start_time=start_time)
@@ -154,6 +191,7 @@ def search_one_http_request_safe(engine_name, query, request_params, result_cont
# suppose everything will be alright
requests_exception = False
+ suspended_time = None
try:
# send requests and parse the results
@@ -174,8 +212,9 @@ def search_one_http_request_safe(engine_name, query, request_params, result_cont
# update stats with the total HTTP time
engine.stats['page_load_time'] += page_load_time
engine.stats['page_load_count'] += 1
-
except Exception as e:
+ record_exception(engine_name, e)
+
# Timing
engine_time = time() - start_time
page_load_time = requests_lib.get_time_for_thread()
@@ -186,38 +225,102 @@ def search_one_http_request_safe(engine_name, query, request_params, result_cont
engine.stats['errors'] += 1
if (issubclass(e.__class__, requests.exceptions.Timeout)):
- result_container.add_unresponsive_engine(engine_name, 'timeout')
+ result_container.add_unresponsive_engine(engine_name, 'HTTP timeout')
# requests timeout (connect or read)
logger.error("engine {0} : HTTP requests timeout"
"(search duration : {1} s, timeout: {2} s) : {3}"
.format(engine_name, engine_time, timeout_limit, e.__class__.__name__))
requests_exception = True
elif (issubclass(e.__class__, requests.exceptions.RequestException)):
- result_container.add_unresponsive_engine(engine_name, 'request exception')
+ result_container.add_unresponsive_engine(engine_name, 'HTTP error')
# other requests exception
logger.exception("engine {0} : requests exception"
"(search duration : {1} s, timeout: {2} s) : {3}"
.format(engine_name, engine_time, timeout_limit, e))
requests_exception = True
+ elif (issubclass(e.__class__, SearxEngineCaptchaException)):
+ result_container.add_unresponsive_engine(engine_name, 'CAPTCHA required')
+ logger.exception('engine {0} : CAPTCHA')
+ suspended_time = e.suspended_time # pylint: disable=no-member
+ elif (issubclass(e.__class__, SearxEngineTooManyRequestsException)):
+ result_container.add_unresponsive_engine(engine_name, 'too many requests')
+ logger.exception('engine {0} : Too many requests')
+ suspended_time = e.suspended_time # pylint: disable=no-member
+ elif (issubclass(e.__class__, SearxEngineAccessDeniedException)):
+ result_container.add_unresponsive_engine(engine_name, 'blocked')
+ logger.exception('engine {0} : Searx is blocked')
+ suspended_time = e.suspended_time # pylint: disable=no-member
else:
- result_container.add_unresponsive_engine(engine_name, 'unexpected crash', str(e))
+ result_container.add_unresponsive_engine(engine_name, 'unexpected crash')
# others errors
logger.exception('engine {0} : exception : {1}'.format(engine_name, e))
+ else:
+ if getattr(threading.current_thread(), '_timeout', False):
+ record_error(engine_name, 'Timeout')
- # suspend or not the engine if there are HTTP errors
+ # suspend the engine if there is an HTTP error
+ # or suspended_time is defined
with threading.RLock():
- if requests_exception:
+ if requests_exception or suspended_time:
# update continuous_errors / suspend_end_time
engine.continuous_errors += 1
- engine.suspend_end_time = time() + min(settings['search']['max_ban_time_on_fail'],
- engine.continuous_errors * settings['search']['ban_time_on_fail'])
+ if suspended_time is None:
+ suspended_time = min(settings['search']['max_ban_time_on_fail'],
+ engine.continuous_errors * settings['search']['ban_time_on_fail'])
+ engine.suspend_end_time = time() + suspended_time
else:
- # no HTTP error (perhaps an engine error)
- # anyway, reset the suspend variables
+ # reset the suspend variables
engine.continuous_errors = 0
engine.suspend_end_time = 0
+def record_offline_engine_stats_on_error(engine, result_container, start_time):
+ engine_time = time() - start_time
+ result_container.add_timing(engine.name, engine_time, engine_time)
+
+ with threading.RLock():
+ engine.stats['errors'] += 1
+
+
+def search_one_offline_request(engine, query, request_params):
+ return engine.search(query, request_params)
+
+
+def search_one_offline_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit):
+ engine = engines[engine_name]
+
+ try:
+ search_results = search_one_offline_request(engine, query, request_params)
+
+ if search_results:
+ result_container.extend(engine_name, search_results)
+
+ engine_time = time() - start_time
+ result_container.add_timing(engine_name, engine_time, engine_time)
+ with threading.RLock():
+ engine.stats['engine_time'] += engine_time
+ engine.stats['engine_time_count'] += 1
+
+ except ValueError as e:
+ record_exception(engine_name, e)
+ record_offline_engine_stats_on_error(engine, result_container, start_time)
+ logger.exception('engine {0} : invalid input : {1}'.format(engine_name, e))
+ except Exception as e:
+ record_exception(engine_name, e)
+ record_offline_engine_stats_on_error(engine, result_container, start_time)
+ result_container.add_unresponsive_engine(engine_name, 'unexpected crash', str(e))
+ logger.exception('engine {0} : exception : {1}'.format(engine_name, e))
+ else:
+ if getattr(threading.current_thread(), '_timeout', False):
+ record_error(engine_name, 'Timeout')
+
+
+def search_one_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit):
+ if engines[engine_name].offline:
+ return search_one_offline_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit) # noqa
+ return search_one_http_request_safe(engine_name, query, request_params, result_container, start_time, timeout_limit)
+
+
def search_multiple_requests(requests, result_container, start_time, timeout_limit):
search_id = uuid4().__str__()
@@ -227,6 +330,7 @@ def search_multiple_requests(requests, result_container, start_time, timeout_lim
args=(engine_name, query, request_params, result_container, start_time, timeout_limit),
name=search_id,
)
+ th._timeout = False
th._engine_name = engine_name
th.start()
@@ -234,7 +338,8 @@ def search_multiple_requests(requests, result_container, start_time, timeout_lim
if th.name == search_id:
remaining_time = max(0.0, timeout_limit - (time() - start_time))
th.join(remaining_time)
- if th.isAlive():
+ if th.is_alive():
+ th._timeout = True
result_container.add_unresponsive_engine(th._engine_name, 'timeout')
logger.warning('engine timeout: {0}'.format(th._engine_name))
@@ -247,265 +352,126 @@ def default_request_params():
'data': {},
'url': '',
'cookies': {},
- 'verify': True
+ 'verify': True,
+ 'auth': None,
+ 'raise_for_httperror': True
}
-# remove duplicate queries.
-# FIXME: does not fix "!music !soundcloud", because the categories are 'none' and 'music'
-def deduplicate_query_engines(query_engines):
- uniq_query_engines = {q["category"] + '|' + q["name"]: q for q in query_engines}
- return uniq_query_engines.values()
-
-
-def get_search_query_from_webapp(preferences, form):
- # no text for the query ?
- if not form.get('q'):
- raise SearxParameterException('q', '')
-
- # set blocked engines
- disabled_engines = preferences.engines.get_disabled()
-
- # parse query, if tags are set, which change
- # the serch engine or search-language
- raw_text_query = RawTextQuery(form['q'], disabled_engines)
- raw_text_query.parse_query()
-
- # set query
- query = raw_text_query.getSearchQuery()
-
- # get and check page number
- pageno_param = form.get('pageno', '1')
- if not pageno_param.isdigit() or int(pageno_param) < 1:
- raise SearxParameterException('pageno', pageno_param)
- query_pageno = int(pageno_param)
-
- # get language
- # set specific language if set on request, query or preferences
- # TODO support search with multible languages
- if len(raw_text_query.languages):
- query_lang = raw_text_query.languages[-1]
- elif 'language' in form:
- query_lang = form.get('language')
- else:
- query_lang = preferences.get_value('language')
-
- # check language
- if not VALID_LANGUAGE_CODE.match(query_lang):
- raise SearxParameterException('language', query_lang)
-
- # get safesearch
- if 'safesearch' in form:
- query_safesearch = form.get('safesearch')
- # first check safesearch
- if not query_safesearch.isdigit():
- raise SearxParameterException('safesearch', query_safesearch)
- query_safesearch = int(query_safesearch)
- else:
- query_safesearch = preferences.get_value('safesearch')
-
- # safesearch : second check
- if query_safesearch < 0 or query_safesearch > 2:
- raise SearxParameterException('safesearch', query_safesearch)
-
- # get time_range
- query_time_range = form.get('time_range')
-
- # check time_range
- if query_time_range not in ('None', None, '', 'day', 'week', 'month', 'year'):
- raise SearxParameterException('time_range', query_time_range)
-
- # query_engines
- query_engines = raw_text_query.engines
-
- # timeout_limit
- query_timeout = raw_text_query.timeout_limit
- if query_timeout is None and 'timeout_limit' in form:
- raw_time_limit = form.get('timeout_limit')
- if raw_time_limit in ['None', '']:
- raw_time_limit = None
- else:
- try:
- query_timeout = float(raw_time_limit)
- except ValueError:
- raise SearxParameterException('timeout_limit', raw_time_limit)
-
- # query_categories
- query_categories = []
-
- # if engines are calculated from query,
- # set categories by using that informations
- if query_engines and raw_text_query.specific:
- additional_categories = set()
- for engine in query_engines:
- if 'from_bang' in engine and engine['from_bang']:
- additional_categories.add('none')
- else:
- additional_categories.add(engine['category'])
- query_categories = list(additional_categories)
-
- # otherwise, using defined categories to
- # calculate which engines should be used
- else:
- # set categories/engines
- load_default_categories = True
- for pd_name, pd in form.items():
- if pd_name == 'categories':
- query_categories.extend(categ for categ in map(unicode.strip, pd.split(',')) if categ in categories)
- elif pd_name == 'engines':
- pd_engines = [{'category': engines[engine].categories[0],
- 'name': engine}
- for engine in map(unicode.strip, pd.split(',')) if engine in engines]
- if pd_engines:
- query_engines.extend(pd_engines)
- load_default_categories = False
- elif pd_name.startswith('category_'):
- category = pd_name[9:]
-
- # if category is not found in list, skip
- if category not in categories:
- continue
-
- if pd != 'off':
- # add category to list
- query_categories.append(category)
- elif category in query_categories:
- # remove category from list if property is set to 'off'
- query_categories.remove(category)
-
- if not load_default_categories:
- if not query_categories:
- query_categories = list(set(engine['category']
- for engine in query_engines))
- else:
- # if no category is specified for this search,
- # using user-defined default-configuration which
- # (is stored in cookie)
- if not query_categories:
- cookie_categories = preferences.get_value('categories')
- for ccateg in cookie_categories:
- if ccateg in categories:
- query_categories.append(ccateg)
-
- # if still no category is specified, using general
- # as default-category
- if not query_categories:
- query_categories = ['general']
-
- # using all engines for that search, which are
- # declared under the specific categories
- for categ in query_categories:
- query_engines.extend({'category': categ,
- 'name': engine.name}
- for engine in categories[categ]
- if (engine.name, categ) not in disabled_engines)
-
- query_engines = deduplicate_query_engines(query_engines)
- external_bang = raw_text_query.external_bang
-
- return (SearchQuery(query, query_engines, query_categories,
- query_lang, query_safesearch, query_pageno,
- query_time_range, query_timeout, preferences,
- external_bang=external_bang),
- raw_text_query)
-
-
-class Search(object):
+class Search:
"""Search information container"""
+ __slots__ = "search_query", "result_container", "start_time", "actual_timeout"
+
def __init__(self, search_query):
# init vars
- super(Search, self).__init__()
+ super().__init__()
self.search_query = search_query
self.result_container = ResultContainer()
+ self.start_time = None
self.actual_timeout = None
- # do search-request
- def search(self):
- global number_of_searches
-
- # Check if there is a external bang. After that we can stop because the search will terminate.
+ def search_external_bang(self):
+ """
+ Check if there is a external bang.
+ If yes, update self.result_container and return True
+ """
if self.search_query.external_bang:
self.result_container.redirect_url = get_bang_url(self.search_query)
# This means there was a valid bang and the
# rest of the search does not need to be continued
- if isinstance(self.result_container.redirect_url, six.string_types):
- return self.result_container
- # start time
- start_time = time()
-
- # answeres ?
+ if isinstance(self.result_container.redirect_url, str):
+ return True
+ return False
+
+ def search_answerers(self):
+ """
+ Check if an answer return a result.
+ If yes, update self.result_container and return True
+ """
answerers_results = ask(self.search_query)
if answerers_results:
for results in answerers_results:
self.result_container.extend('answer', results)
- return self.result_container
+ return True
+ return False
- # init vars
- requests = []
+ def _is_accepted(self, engine_name, engine):
+ # skip suspended engines
+ if engine.suspend_end_time >= time():
+ logger.debug('Engine currently suspended: %s', engine_name)
+ return False
- # increase number of searches
- number_of_searches += 1
+ # if paging is not supported, skip
+ if self.search_query.pageno > 1 and not engine.paging:
+ return False
- # set default useragent
- # user_agent = request.headers.get('User-Agent', '')
- user_agent = gen_useragent()
+ # if time_range is not supported, skip
+ if self.search_query.time_range and not engine.time_range_support:
+ return False
- search_query = self.search_query
+ return True
- # max of all selected engine timeout
- default_timeout = 0
+ def _get_params(self, engineref, user_agent):
+ if engineref.name not in engines:
+ return None, None
- # start search-reqest for all selected engines
- for selected_engine in search_query.engines:
- if selected_engine['name'] not in engines:
- continue
+ engine = engines[engineref.name]
- engine = engines[selected_engine['name']]
+ if not self._is_accepted(engineref.name, engine):
+ return None, None
- if not search_query.preferences.validate_token(engine):
- continue
+ # set default request parameters
+ request_params = {}
+ if not engine.offline:
+ request_params = default_request_params()
+ request_params['headers']['User-Agent'] = user_agent
- # skip suspended engines
- if engine.suspend_end_time >= time():
- logger.debug('Engine currently suspended: %s', selected_engine['name'])
- continue
+ if hasattr(engine, 'language') and engine.language:
+ request_params['language'] = engine.language
+ else:
+ request_params['language'] = self.search_query.lang
- # if paging is not supported, skip
- if search_query.pageno > 1 and not engine.paging:
- continue
+ request_params['safesearch'] = self.search_query.safesearch
+ request_params['time_range'] = self.search_query.time_range
- # if time_range is not supported, skip
- if search_query.time_range and not engine.time_range_support:
- continue
+ request_params['category'] = engineref.category
+ request_params['pageno'] = self.search_query.pageno
- # set default request parameters
- request_params = {}
- if not engine.offline:
- request_params = default_request_params()
- request_params['headers']['User-Agent'] = user_agent
+ with threading.RLock():
+ engine.stats['sent_search_count'] += 1
- if hasattr(engine, 'language') and engine.language:
- request_params['language'] = engine.language
- else:
- request_params['language'] = search_query.lang
+ return request_params, engine.timeout
- request_params['safesearch'] = search_query.safesearch
- request_params['time_range'] = search_query.time_range
+ # do search-request
+ def _get_requests(self):
+ # init vars
+ requests = []
+
+ # set default useragent
+ # user_agent = request.headers.get('User-Agent', '')
+ user_agent = gen_useragent()
- request_params['category'] = selected_engine['category']
- request_params['pageno'] = search_query.pageno
+ # max of all selected engine timeout
+ default_timeout = 0
+
+ # start search-reqest for all selected engines
+ for engineref in self.search_query.engineref_list:
+ # set default request parameters
+ request_params, engine_timeout = self._get_params(engineref, user_agent)
+ if request_params is None:
+ continue
# append request to list
- requests.append((selected_engine['name'], search_query.query, request_params))
+ requests.append((engineref.name, self.search_query.query, request_params))
# update default_timeout
- default_timeout = max(default_timeout, engine.timeout)
+ default_timeout = max(default_timeout, engine_timeout)
# adjust timeout
- self.actual_timeout = default_timeout
+ actual_timeout = default_timeout
query_timeout = self.search_query.timeout_limit
if max_request_timeout is None and query_timeout is None:
@@ -513,37 +479,57 @@ class Search(object):
pass
elif max_request_timeout is None and query_timeout is not None:
# No max, but user query: From user query except if above default
- self.actual_timeout = min(default_timeout, query_timeout)
+ actual_timeout = min(default_timeout, query_timeout)
elif max_request_timeout is not None and query_timeout is None:
# Max, no user query: Default except if above max
- self.actual_timeout = min(default_timeout, max_request_timeout)
+ actual_timeout = min(default_timeout, max_request_timeout)
elif max_request_timeout is not None and query_timeout is not None:
# Max & user query: From user query except if above max
- self.actual_timeout = min(query_timeout, max_request_timeout)
+ actual_timeout = min(query_timeout, max_request_timeout)
logger.debug("actual_timeout={0} (default_timeout={1}, ?timeout_limit={2}, max_request_timeout={3})"
- .format(self.actual_timeout, default_timeout, query_timeout, max_request_timeout))
+ .format(actual_timeout, default_timeout, query_timeout, max_request_timeout))
+
+ return requests, actual_timeout
+
+ def search_standard(self):
+ """
+ Update self.result_container, self.actual_timeout
+ """
+ requests, self.actual_timeout = self._get_requests()
# send all search-request
if requests:
- search_multiple_requests(requests, self.result_container, start_time, self.actual_timeout)
+ search_multiple_requests(requests, self.result_container, self.start_time, self.actual_timeout)
start_new_thread(gc.collect, tuple())
# return results, suggestions, answers and infoboxes
+ return True
+
+ # do search-request
+ def search(self):
+ self.start_time = time()
+
+ if not self.search_external_bang():
+ if not self.search_answerers():
+ self.search_standard()
+
return self.result_container
class SearchWithPlugins(Search):
"""Similar to the Search class but call the plugins."""
+ __slots__ = 'ordered_plugin_list', 'request'
+
def __init__(self, search_query, ordered_plugin_list, request):
- super(SearchWithPlugins, self).__init__(search_query)
+ super().__init__(search_query)
self.ordered_plugin_list = ordered_plugin_list
self.request = request
def search(self):
if plugins.call(self.ordered_plugin_list, 'pre_search', self.request, self):
- super(SearchWithPlugins, self).search()
+ super().search()
plugins.call(self.ordered_plugin_list, 'post_search', self.request, self)
diff --git a/searx/settings.yml b/searx/settings.yml
index 8df151b..e263e3a 100644
--- a/searx/settings.yml
+++ b/searx/settings.yml
@@ -16,6 +16,13 @@ server:
base_url : False # Set custom base_url. Possible values: False or "https://your.custom.host/location/"
image_proxy : False # Proxying image results through searx
http_protocol_version : "1.0" # 1.0 and 1.1 are supported
+ method: "POST" # POST queries are more secure as they don't show up in history but may cause problems when using Firefox containers
+ default_http_headers:
+ X-Content-Type-Options : nosniff
+ X-XSS-Protection : 1; mode=block
+ X-Download-Options : noopen
+ X-Robots-Tag : noindex, nofollow
+ Referrer-Policy : no-referrer
ui:
static_path : "" # Custom static path - leave it blank if you didn't change
@@ -24,6 +31,7 @@ ui:
default_locale : "" # Default interface locale - leave blank to detect from browser information or use codes from the 'locales' config section
theme_args :
oscar_style : logicodev # default style of oscar
+# results_on_new_tab: False # Open result links in a new tab by default
# categories_order :
# - general
# - files
@@ -31,6 +39,15 @@ ui:
# - it
# - science
+# Lock arbitrary settings on the preferences page.
+# To find the ID of the user setting you want to lock, check
+# the ID of the form on the page "preferences".
+#preferences:
+# lock:
+# - language
+# - autocomplete
+# - method
+
# searx supports result proxification using an external service: https://github.com/asciimoo/morty
# uncomment below section if you have running morty proxy
# the key is base64 encoded (keep the !!binary notation)
@@ -46,17 +63,35 @@ outgoing: # communication with search engines
pool_connections : 100 # Number of different hosts
pool_maxsize : 10 # Number of simultaneous requests by host
# uncomment below section if you want to use a proxy
-# see http://docs.python-requests.org/en/latest/user/advanced/#proxies
-# SOCKS proxies are also supported: see http://requests.readthedocs.io/en/master/user/advanced/#socks
-# proxies :
-# http : http://127.0.0.1:8080
-# https: http://127.0.0.1:8080
+# see https://2.python-requests.org/en/latest/user/advanced/#proxies
+# SOCKS proxies are also supported: see https://2.python-requests.org/en/latest/user/advanced/#socks
+# proxies:
+# http:
+# - http://proxy1:8080
+# - http://proxy2:8080
+# https:
+# - http://proxy1:8080
+# - http://proxy2:8080
# uncomment below section only if you have more than one network interface
# which can be the source of outgoing search requests
# source_ips:
# - 1.1.1.1
# - 1.1.1.2
+# External plugin configuration
+# See http://searx.github.io/searx/dev/plugins.html for more details
+#
+# plugins:
+# - plugin1
+# - plugin2
+# - ...
+
+# uncomment below section if you want to configure which plugin is enabled by default
+#
+# enabled_plugins:
+# - "HTTPS rewrite"
+# - ...
+
engines:
- name: apk mirror
engine: apkmirror
@@ -64,6 +99,12 @@ engines:
shortcut: apkm
disabled: True
+# Requires Tor
+ - name : ahmia
+ engine : ahmia
+ categories : onions
+ shortcut : ah
+
- name : arch linux wiki
engine : archlinux
shortcut : al
@@ -160,7 +201,7 @@ engines:
- name : deviantart
engine : deviantart
shortcut : da
- timeout: 3.0
+ timeout : 3.0
- name : ddg definitions
engine : duckduckgo_definitions
@@ -192,6 +233,20 @@ engines:
shortcut : ew
disabled : True
+# - name : elasticsearch
+# shortcut : es
+# engine : elasticsearch
+# base_url : http://localhost:9200
+# username : elastic
+# password : changeme
+# index : my-index
+# # available options: match, simple_query_string, term, terms, custom
+# query_type : match
+# # if query_type is set to custom, provide your query here
+# #custom_query_json: {"query":{"match_all": {}}}
+# #show_metadata: False
+# disabled : True
+
- name : wikidata
engine : wikidata
shortcut : wd
@@ -225,9 +280,16 @@ engines:
shortcut : et
disabled : True
+# - name : ebay
+# engine : ebay
+# shortcut : eb
+# disabled : True
+# timeout: 5
+
- name : 1x
engine : www1x
shortcut : 1x
+ timeout : 3.0
disabled : True
- name : fdroid
@@ -293,6 +355,19 @@ engines:
engine : github
shortcut : gh
+ # This a Gitea service. If you would like to use a different instance,
+ # change codeberg.org to URL of the desired Gitea host. Or you can create
+ # a new engine by copying this and changing the name, shortcut and search_url.
+ - name : codeberg
+ engine : json_engine
+ search_url : https://codeberg.org/api/v1/repos/search?q={query}&limit=10
+ url_query : html_url
+ title_query : name
+ content_query : description
+ categories : it
+ shortcut : cb
+ disabled : True
+
- name : google
engine : google
shortcut : go
@@ -406,6 +481,7 @@ engines:
base_url : 'https://invidio.us/'
shortcut: iv
timeout : 5.0
+ disabled : True
- name: kickass
engine : kickass
@@ -415,7 +491,7 @@ engines:
- name : library genesis
engine : xpath
- search_url : https://libgen.is/search.php?req={query}
+ search_url : http://libgen.rs/search.php?req={query}
url_xpath : //a[contains(@href,"bookfi.net")]/@href
title_xpath : //a[contains(@href,"book/")]/text()[1]
content_xpath : //td/a[1][contains(@href,"=author")]/text()
@@ -428,11 +504,24 @@ engines:
engine : xpath
search_url : https://lobste.rs/search?utf8=%E2%9C%93&q={query}&what=stories&order=relevance
results_xpath : //li[contains(@class, "story")]
- url_xpath : .//span[@class="link"]/a/@href
- title_xpath : .//span[@class="link"]/a
+ url_xpath : .//a[@class="u-url"]/@href
+ title_xpath : .//a[@class="u-url"]
content_xpath : .//a[@class="domain"]
categories : it
shortcut : lo
+ timeout : 3.0
+ disabled: True
+
+ - name : metager
+ engine : xpath
+ paging : False
+ search_url : https://metager.org/meta/meta.ger3?eingabe={query}
+ url_xpath : //div[@class="result-subheadline"]/a/@href
+ title_xpath : //div[@class="result-headline"]/h2/@title
+ content_xpath : //div[@class="result-description"]/text()
+ categories : general
+ shortcut : mg
+ disabled : True
- name : microsoft academic
engine : microsoft_academic
@@ -457,6 +546,11 @@ engines:
timeout: 5.0
shortcut : npm
+# Requires Tor
+ - name : not evil
+ engine : not_evil
+ shortcut : ne
+
- name : nyaa
engine : nyaa
shortcut : nt
@@ -492,6 +586,11 @@ engines:
shortcut : oap
timeout: 5.0
+# - name : opensemanticsearch
+# engine : opensemantic
+# shortcut : oss
+# base_url : 'http://localhost:8983/solr/opensemanticsearch/'
+
- name : openstreetmap
engine : openstreetmap
shortcut : osm
@@ -522,7 +621,8 @@ engines:
- name : piratebay
engine : piratebay
shortcut : tpb
- url: https://pirateproxy.red/
+ # You may need to change this URL to a proxy if piratebay is blocked in your country
+ url: https://thepiratebay.org/
timeout : 3.0
- name : pubmed
@@ -547,10 +647,27 @@ engines:
shortcut : qwn
categories : news
- - name : qwant social
- engine : qwant
- shortcut : qws
- categories : social media
+# - name: library
+# engine: recoll
+# shortcut: lib
+# base_url: 'https://recoll.example.org/'
+# search_dir: ''
+# mount_prefix: /export
+# dl_prefix: 'https://download.example.org'
+# timeout: 30.0
+# categories: files
+# disabled: True
+
+# - name: recoll library reference
+# engine: recoll
+# base_url: 'https://recoll.example.org/'
+# search_dir: reference
+# mount_prefix: /export
+# dl_prefix: 'https://download.example.org'
+# shortcut: libr
+# timeout: 30.0
+# categories: files
+# disabled: True
- name : reddit
engine : reddit
@@ -565,6 +682,10 @@ engines:
# engine : scanr_structures
# disabled : True
+ - name: sepiasearch
+ engine: sepiasearch
+ shortcut: sep
+
- name : soundcloud
engine : soundcloud
shortcut : sc
@@ -573,10 +694,6 @@ engines:
engine : stackoverflow
shortcut : st
- - name : searchcode doc
- engine : searchcode_doc
- shortcut : scd
-
- name : searchcode code
engine : searchcode_code
shortcut : scc
@@ -631,9 +748,17 @@ engines:
url: https://torrentz2.eu/
timeout : 3.0
- - name : twitter
- engine : twitter
- shortcut : tw
+# Requires Tor
+ - name : torch
+ engine : xpath
+ paging : True
+ search_url : http://xmh57jrknzkhv6y3ls3ubitzfqnkrwxhopf5aygthi7d6rplyvk3noyd.onion/cgi-bin/omega/omega?P={query}&DEFAULTOP=and
+ results_xpath : //table//tr
+ url_xpath : ./td[2]/a
+ title_xpath : ./td[2]/b
+ content_xpath : ./td[2]/small
+ categories : onions
+ shortcut : tch
# maybe in a fun category
# - name : uncyclopedia
@@ -679,6 +804,14 @@ engines:
# Or you can use the html non-stable engine, activated by default
engine : youtube_noapi
+ # tmp suspended: Cloudflare CAPTCHA
+ #- name : yggtorrent
+ # engine : yggtorrent
+ # shortcut : ygg
+ # url: https://www2.yggtorrent.si/
+ # disabled : True
+ # timeout : 4.0
+
- name : dailymotion
engine : dailymotion
shortcut : dm
@@ -822,10 +955,19 @@ engines:
page_size : 10
disabled : True
- - name : seedpeer
- shortcut : speu
- engine : seedpeer
- categories: files, music, videos
+ - name : naver
+ shortcut: nvr
+ engine: xpath
+ paging : True
+ search_url : https://search.naver.com/search.naver?where=webkr&sm=osp_hty&ie=UTF-8&query={query}&start={pageno}
+ results_xpath: /html/body//ul[@id="elThumbnailResultArea"]/li
+ url_xpath : ./dl/dt/a[@class="title_link"]/@href
+ title_xpath : ./dl/dt/a[@class="title_link"]
+ content_xpath : ./dl/dd[@class="sh_web_passage"]
+ suggestion_xpath : /html/body//div[@class="sp_keyword section"]//a
+ first_page_num : 1
+ page_size : 10
+ disabled : True
- name : rubygems
shortcut: rbg
@@ -841,6 +983,14 @@ engines:
categories: it
disabled : True
+ - name : peertube
+ engine: peertube
+ shortcut: ptb
+ paging : True
+ base_url : https://peer.tube/
+ categories: videos
+ disabled : True
+
# - name : yacy
# engine : yacy
# shortcut : ya
@@ -855,6 +1005,77 @@ engines:
# shortcut : uw
# base_url : 'http://doc.ubuntu-fr.org'
+# Be careful when enabling this engine if you are
+# running a public instance. Do not expose any sensitive
+# information. You can restrict access by configuring a list
+# of access tokens under tokens.
+# - name: git grep
+# engine: command
+# command: ['git', 'grep', '{{QUERY}}']
+# shortcut: gg
+# tokens: []
+# disabled: True
+# delimiter:
+# chars: ':'
+# keys: ['filepath', 'code']
+
+# Be careful when enabling this engine if you are
+# running a public instance. Do not expose any sensitive
+# information. You can restrict access by configuring a list
+# of access tokens under tokens.
+# - name: locate
+# engine: command
+# command: ['locate', '{{QUERY}}']
+# shortcut: loc
+# tokens: []
+# disabled: True
+# delimiter:
+# chars: ' '
+# keys: ['line']
+
+# Be careful when enabling this engine if you are
+# running a public instance. Do not expose any sensitive
+# information. You can restrict access by configuring a list
+# of access tokens under tokens.
+# - name: find
+# engine: command
+# command: ['find', '.', '-name', '{{QUERY}}']
+# query_type: path
+# shortcut: fnd
+# tokens: []
+# disabled: True
+# delimiter:
+# chars: ' '
+# keys: ['line']
+
+# Be careful when enabling this engine if you are
+# running a public instance. Do not expose any sensitive
+# information. You can restrict access by configuring a list
+# of access tokens under tokens.
+# - name: pattern search in files
+# engine: command
+# command: ['fgrep', '{{QUERY}}']
+# shortcut: fgr
+# tokens: []
+# disabled: True
+# delimiter:
+# chars: ' '
+# keys: ['line']
+
+# Be careful when enabling this engine if you are
+# running a public instance. Do not expose any sensitive
+# information. You can restrict access by configuring a list
+# of access tokens under tokens.
+# - name: regex search in files
+# engine: command
+# command: ['grep', '{{QUERY}}']
+# shortcut: gr
+# tokens: []
+# disabled: True
+# delimiter:
+# chars: ' '
+# keys: ['line']
+
locales:
en : English
ar : العَرَبِيَّة (Arabic)
@@ -905,7 +1126,7 @@ locales:
doi_resolvers :
oadoi.org : 'https://oadoi.org/'
doi.org : 'https://doi.org/'
- doai.io : 'https://doai.io/'
+ doai.io : 'https://dissem.in/'
sci-hub.tw : 'https://sci-hub.tw/'
-default_doi_resolver : 'oadoi.org'
+default_doi_resolver : 'sci-hub.tw'
diff --git a/searx/settings_loader.py b/searx/settings_loader.py
new file mode 100644
index 0000000..5dbeb4a
--- /dev/null
+++ b/searx/settings_loader.py
@@ -0,0 +1,132 @@
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+from os import environ
+from os.path import dirname, join, abspath, isfile
+from collections.abc import Mapping
+from itertools import filterfalse
+
+import yaml
+
+from searx.exceptions import SearxSettingsException
+
+
+searx_dir = abspath(dirname(__file__))
+
+
+def check_settings_yml(file_name):
+ if isfile(file_name):
+ return file_name
+ return None
+
+
+def load_yaml(file_name):
+ try:
+ with open(file_name, 'r', encoding='utf-8') as settings_yaml:
+ return yaml.safe_load(settings_yaml)
+ except IOError as e:
+ raise SearxSettingsException(e, file_name)
+ except yaml.YAMLError as e:
+ raise SearxSettingsException(e, file_name)
+
+
+def get_default_settings_path():
+ return check_settings_yml(join(searx_dir, 'settings.yml'))
+
+
+def get_user_settings_path():
+ # find location of settings.yml
+ if 'SEARX_SETTINGS_PATH' in environ:
+ # if possible set path to settings using the
+ # enviroment variable SEARX_SETTINGS_PATH
+ return check_settings_yml(environ['SEARX_SETTINGS_PATH'])
+
+ # if not, get it from searx code base or last solution from /etc/searx
+ return check_settings_yml('/etc/searx/settings.yml')
+
+
+def update_dict(default_dict, user_dict):
+ for k, v in user_dict.items():
+ if isinstance(v, Mapping):
+ default_dict[k] = update_dict(default_dict.get(k, {}), v)
+ else:
+ default_dict[k] = v
+ return default_dict
+
+
+def update_settings(default_settings, user_settings):
+ # merge everything except the engines
+ for k, v in user_settings.items():
+ if k not in ('use_default_settings', 'engines'):
+ if k in default_settings:
+ update_dict(default_settings[k], v)
+ else:
+ default_settings[k] = v
+
+ # parse the engines
+ remove_engines = None
+ keep_only_engines = None
+ use_default_settings = user_settings.get('use_default_settings')
+ if isinstance(use_default_settings, dict):
+ remove_engines = use_default_settings.get('engines', {}).get('remove')
+ keep_only_engines = use_default_settings.get('engines', {}).get('keep_only')
+
+ if 'engines' in user_settings or remove_engines is not None or keep_only_engines is not None:
+ engines = default_settings['engines']
+
+ # parse "use_default_settings.engines.remove"
+ if remove_engines is not None:
+ engines = list(filterfalse(lambda engine: (engine.get('name')) in remove_engines, engines))
+
+ # parse "use_default_settings.engines.keep_only"
+ if keep_only_engines is not None:
+ engines = list(filter(lambda engine: (engine.get('name')) in keep_only_engines, engines))
+
+ # parse "engines"
+ user_engines = user_settings.get('engines')
+ if user_engines:
+ engines_dict = dict((definition['name'], definition) for definition in engines)
+ for user_engine in user_engines:
+ default_engine = engines_dict.get(user_engine['name'])
+ if default_engine:
+ update_dict(default_engine, user_engine)
+ else:
+ engines.append(user_engine)
+
+ # store the result
+ default_settings['engines'] = engines
+
+ return default_settings
+
+
+def is_use_default_settings(user_settings):
+ use_default_settings = user_settings.get('use_default_settings')
+ if use_default_settings is True:
+ return True
+ if isinstance(use_default_settings, dict):
+ return True
+ if use_default_settings is False or use_default_settings is None:
+ return False
+ raise ValueError('Invalid value for use_default_settings')
+
+
+def load_settings(load_user_setttings=True):
+ default_settings_path = get_default_settings_path()
+ user_settings_path = get_user_settings_path()
+ if user_settings_path is None or not load_user_setttings:
+ # no user settings
+ return (load_yaml(default_settings_path),
+ 'load the default settings from {}'.format(default_settings_path))
+
+ # user settings
+ user_settings = load_yaml(user_settings_path)
+ if is_use_default_settings(user_settings):
+ # the user settings are merged with the default configuration
+ default_settings = load_yaml(default_settings_path)
+ update_settings(default_settings, user_settings)
+ return (default_settings,
+ 'merge the default settings ( {} ) and the user setttings ( {} )'
+ .format(default_settings_path, user_settings_path))
+
+ # the user settings, fully replace the default configuration
+ return (user_settings,
+ 'load the user settings from {}'.format(user_settings_path))
diff --git a/searx/settings_robot.yml b/searx/settings_robot.yml
index 33e7626..05c8eeb 100644
--- a/searx/settings_robot.yml
+++ b/searx/settings_robot.yml
@@ -8,7 +8,7 @@ search:
server:
port : 11111
bind_address : 127.0.0.1
- secret_key : "ultrasecretkey" # change this!
+ secret_key : "changedultrasecretkey"
base_url : False
http_protocol_version : "1.0"
@@ -17,6 +17,9 @@ ui:
templates_path : ""
default_theme : oscar
+preferences:
+ lock: []
+
outgoing:
request_timeout : 1.0 # seconds
useragent_suffix : ""
@@ -39,7 +42,7 @@ locales:
doi_resolvers :
oadoi.org : 'https://oadoi.org/'
doi.org : 'https://doi.org/'
- doai.io : 'https://doai.io/'
+ doai.io : 'https://dissem.in/'
sci-hub.tw : 'https://sci-hub.tw/'
-default_doi_resolver : 'oadoi.org'
+default_doi_resolver : 'sci-hub.tw'
diff --git a/searx/static/plugins/external_plugins/.gitignore b/searx/static/plugins/external_plugins/.gitignore
new file mode 100644
index 0000000..94548af
--- /dev/null
+++ b/searx/static/plugins/external_plugins/.gitignore
@@ -0,0 +1,3 @@
+*
+*/
+!.gitignore
diff --git a/searx/static/plugins/js/infinite_scroll.js b/searx/static/plugins/js/infinite_scroll.js
index 9930880..cd80965 100644
--- a/searx/static/plugins/js/infinite_scroll.js
+++ b/searx/static/plugins/js/infinite_scroll.js
@@ -9,7 +9,7 @@ function loadNextPage() {
$('#pagination').html('<div class="loading-spinner"></div>');
$.ajax({
type: "POST",
- url: './',
+ url: $('#search_form').prop('action'),
data: formData,
dataType: 'html',
success: function(data) {
diff --git a/searx/static/plugins/js/open_results_on_new_tab.js b/searx/static/plugins/js/open_results_on_new_tab.js
deleted file mode 100644
index 99ef382..0000000
--- a/searx/static/plugins/js/open_results_on_new_tab.js
+++ /dev/null
@@ -1,3 +0,0 @@
-$(document).ready(function() {
- $('.result_header > a').attr('target', '_blank');
-});
diff --git a/searx/static/themes/oscar/gruntfile.js b/searx/static/themes/oscar/gruntfile.js
index 606b6bc..b18c0bc 100644
--- a/searx/static/themes/oscar/gruntfile.js
+++ b/searx/static/themes/oscar/gruntfile.js
@@ -13,6 +13,7 @@ module.exports = function(grunt) {
},
uglify: {
options: {
+ sourceMap: true,
banner: '/*! oscar/searx.min.js | <%= grunt.template.today("dd-mm-yyyy") %> | <%= process.env.GIT_URL %> */\n'
},
dist: {
diff --git a/searx/static/themes/oscar/js/searx.js b/searx/static/themes/oscar/js/searx.js
index 040d57b..8208ce4 100644
--- a/searx/static/themes/oscar/js/searx.js
+++ b/searx/static/themes/oscar/js/searx.js
@@ -124,6 +124,13 @@ $(document).ready(function(){
$('#q.autofocus').focus();
/**
+ * Empty search bar when click on reset button
+ */
+ $("#clear_search").click(function () {
+ document.getElementById("q").value = "";
+ });
+
+ /**
* select full content on click if class="select-all-on-click"
*/
$(".select-all-on-click").click(function () {
@@ -204,6 +211,17 @@ $(document).ready(function(){
$(a.target).parent().attr("aria-selected", "true");
});
});
+;window.addEventListener('load', function() {
+ // Hide infobox toggle if shrunk size already fits all content.
+ $('.infobox').each(function() {
+ var infobox_body = $(this).find('.infobox_body');
+ var total_height = infobox_body.prop('scrollHeight') + infobox_body.find('img.infobox_part').height();
+ var max_height = infobox_body.css('max-height').replace('px', '');
+ if (total_height <= max_height) {
+ $(this).find('.infobox_toggle').hide();
+ }
+ });
+});
;/**
* searx is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
diff --git a/searx/static/themes/oscar/js/searx.min.js.map b/searx/static/themes/oscar/js/searx.min.js.map
new file mode 100644
index 0000000..4dd22d6
--- /dev/null
+++ b/searx/static/themes/oscar/js/searx.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["searx.js"],"names":["requirejs","config","baseUrl","paths","app","window","searx","d","script","currentScript","scripts","getElementsByTagName","length","autocompleter","getAttribute","method","document","searchResults","Bloodhound","datumTokenizer","tokenizers","obj","whitespace","queryTokenizer","remote","initialize","$","ready","original_search_value","on","e","which","val","typeahead","name","displayKey","result","source","ttAdapter","bind","ev","suggestion","submit","focus","click","getElementById","value","this","select","btnTextCollapsed","data","btnTextNotCollapsed","hasClass","new_html","html","replace","btnClass","btnLabelDefault","btnLabelToggled","toggleClass","target","iframe_load","srctest","attr","undefined","dblclick","prop","addClass","removeClass","removeAttr","checked","a","parents","children","parent","addEventListener","each","infobox_body","find","height","css","hide","event","overpass_url","query_start","query_end","osm_id","osm_type","result_table","result_table_loadicon","osm_ignore_tags","query","ajax","done","elements","element","newHtml","row","tags","indexOf","substring","fail","could_not_load","off","leaflet_target","map_lon","map_lat","map_zoom","map_boundingbox","map_geojson","require","leaflet","southWest","L","latLng","northEast","map_bounds","latLngBounds","Icon","Default","imagePath","map","osmMapnikUrl","osmMapnikAttrib","osmMapnik","TileLayer","minZoom","maxZoom","attribution","osmWikimediaUrl","osmWikimediaAttrib","setTimeout","fitBounds","setView","LatLng","addLayer","baseLayers","OSM Mapnik","control","layers","addTo","geoJson"],"mappings":";;AAiBAA,UAAUC,QACNC,QAAS,2BACTC,OACIC,IAAK,YAmBbC,OAAOC,MAAQ,SAAUC,GACrB,YAGA,IAAIC,GAASD,EAAEE,eAAkB,WAC7B,GAAIC,GAAUH,EAAEI,qBAAqB,SACrC,OAAOD,GAAQA,EAAQE,OAAS,KAGpC,QACIC,cAA6D,SAA9CL,EAAOM,aAAa,sBACnCC,OAAQP,EAAOM,aAAa,iBAEjCE,UAkBAV,MAAMO,gBACLP,MAAMW,cAAgB,GAAIC,aACtBC,eAAgBD,WAAWE,WAAWC,IAAIC,WAAW,SACrDC,eAAgBL,WAAWE,WAAWE,WACtCE,OAAQ,6BAEZlB,MAAMW,cAAcQ,cAGxBC,EAAEV,UAAUW,MAAM,WACd,GAAIC,GAAwB,EACzBtB,OAAMO,gBACXa,EAAE,MAAMG,GAAG,UAAW,SAASC,GAChB,IAAXA,EAAEC,QACQH,EAAwBF,EAAE,MAAMM,SAGxCN,EAAE,MAAMO,UAAU,MACdC,KAAM,iBACNC,WAAY,SAASC,GACjB,MAAOA,IAEXC,OAAQ/B,MAAMW,cAAcqB,cAEhCZ,EAAE,MAAMa,KAAK,qBAAsB,SAASC,EAAIC,GACzCb,GACCF,EAAE,MAAMM,IAAIJ,GAEhBF,EAAE,gBAAgBgB,cAqB9BhB,EAAEV,UAAUW,MAAM,WAIdD,EAAE,gBAAgBiB,QAKlBjB,EAAE,iBAAiBkB,MAAM,WAC5B5B,SAAS6B,eAAe,KAAKC,MAAQ,KAMlCpB,EAAE,wBAAwBkB,MAAM,WAC5BlB,EAAEqB,MAAMC,WAMZtB,EAAE,iBAAiBkB,MAAM,WACrB,GAAIK,GAAmBvB,EAAEqB,MAAMG,KAAK,sBAChCC,EAAsBzB,EAAEqB,MAAMG,KAAK,yBAEf,MAArBD,GAAmD,KAAxBE,IACvBzB,EAAEqB,MAAMK,SAAS,aAChBC,SAAW3B,EAAEqB,MAAMO,OAAOC,QAAQN,EAAkBE,GAEpDE,SAAW3B,EAAEqB,MAAMO,OAAOC,QAAQJ,EAAqBF,GAE3DvB,EAAEqB,MAAMO,KAAKD,aAOrB3B,EAAE,oBAAoBkB,MAAM,WACxB,GAAIY,GAAW,OAAS9B,EAAEqB,MAAMG,KAAK,aACjCO,EAAkB/B,EAAEqB,MAAMG,KAAK,qBAC/BQ,EAAkBhC,EAAEqB,MAAMG,KAAK,oBACZ,MAApBQ,IACIhC,EAAEqB,MAAMK,SAAS,eAChBC,SAAW3B,EAAEqB,MAAMO,OAAOC,QAAQE,EAAiBC,GAEnDL,SAAW3B,EAAEqB,MAAMO,OAAOC,QAAQG,EAAiBD,GAEvD/B,EAAEqB,MAAMO,KAAKD,WAEjB3B,EAAEqB,MAAMY,YAAYH,GACpB9B,EAAEqB,MAAMY,YAAY,iBAMxBjC,EAAE,iBAAiBkB,MAAM,WACrB,GAAIgB,GAASlC,EAAEqB,MAAMG,KAAK,UACtBW,EAAcnC,EAAEkC,EAAS,aACzBE,EAAUD,EAAYE,KAAK,WAChBC,KAAZF,IAAqC,IAAZA,GACxBD,EAAYE,KAAK,MAAOF,EAAYX,KAAK,UAOjDxB,EAAE,WAAWuC,SAAS,WACtB,GAAIT,GAAW,OAAS9B,EAAEqB,MAAMG,KAAK,YAC9BxB,GAAEqB,MAAMK,SAAS,gBAChB1B,EAAE,mBAAmBqC,KAAK,UAAW,WACrCrC,EAAE,mBAAmBwC,KAAK,WAAW,GACrCxC,EAAE,WAAWyC,SAASX,GACtB9B,EAAE,WAAWyC,SAAS,UACtBzC,EAAE,WAAW0C,YAAY,iBAEzB1C,EAAE,mBAAmBqC,KAAK,UAAW,IACrCrC,EAAE,mBAAmB2C,WAAW,WAChC3C,EAAE,mBAAmB4C,SAAU,EAC/B5C,EAAE,WAAW0C,YAAYZ,GACzB9B,EAAE,WAAW0C,YAAY,UACzB1C,EAAE,WAAWyC,SAAS,kBAG9BzC,EAAE,aAAakB,MAAM,SAAS2B,GACf7C,EAAE6C,EAAEX,QAAQY,QAAQ,MAC1BC,WAAWV,KAAK,gBAAiB,SACtCrC,EAAE6C,EAAEX,QAAQc,SAASX,KAAK,gBAAiB,YAGlD1D,OAAOsE,iBAAiB,OAAQ,WAE7BjD,EAAE,YAAYkD,KAAK,WACf,GAAIC,GAAenD,EAAEqB,MAAM+B,KAAK,gBACbD,GAAaX,KAAK,gBAAkBW,EAAaC,KAAK,oBAAoBC,UAC5EF,EAAaG,IAAI,cAAczB,QAAQ,KAAM,KAE1D7B,EAAEqB,MAAM+B,KAAK,mBAAmBG,WAqB5CvD,EAAEV,UAAUW,MAAM,WACdD,EAAE,2BAA2BG,GAAI,QAAS,SAAUqD,GAChD,GAAIC,GAAe,gDACfC,EAAcD,EAAe,2BAC7BE,EAAY,cAEZC,EAAS5D,EAAEqB,MAAMG,KAAK,UACtBqC,EAAW7D,EAAEqB,MAAMG,KAAK,YACxBsC,EAAe9D,EAAEqB,MAAMG,KAAK,gBAC5BuC,EAAwB,IAAM/D,EAAEqB,MAAMG,KAAK,yBAG3CwC,GAAoB,YAAa,eAAgB,mBAAoB,gBAAiB,cAE1F,IAAGJ,GAAUC,GAAYC,EAAc,CACnCA,EAAe,IAAMA,CACrB,IAAIG,GAAQ,IACZ,QAAOJ,GACH,IAAK,OACDI,EAAQP,EAAc,QAAUE,EAAS,KAAOD,CAChD,MACJ,KAAK,MACDM,EAAQP,EAAc,OAASE,EAAS,KAAOD,CAC/C,MACJ,KAAK,WACDM,EAAQP,EAAc,YAAcE,EAAS,KAAOD,EAK5D,GAAGM,EAEC,CAAkBjE,EAAEkE,KAAMD,GACzBE,KAAK,SAAUvC,GACZ,GAAGA,GAAQA,EAAKwC,UAAYxC,EAAKwC,SAAS,GAAI,CAC1C,GAAIC,GAAUzC,EAAKwC,SAAS,GACxBE,EAAUtE,EAAE8D,GAAclC,MAC9B,KAAK,GAAI2C,KAAOF,GAAQG,KACpB,GAAyB,OAAtBH,EAAQG,KAAKhE,OAAkD,GAAjCwD,EAAgBS,QAAQF,GAAY,CAEjE,OADAD,GAAW,WAAaC,EAAM,YACvBA,GACH,IAAK,QACL,IAAK,MACDD,GAAW,gBAAmBD,EAAQG,KAAKD,GAAK1C,QAAQ,KAAK,IAAM,KAAQwC,EAAQG,KAAKD,GAAO,MAC/F,MACJ,KAAK,QACDD,GAAW,mBAAsBD,EAAQG,KAAKD,GAAO,KAAQF,EAAQG,KAAKD,GAAO,MACjF,MACJ,KAAK,UACL,IAAK,MACDD,GAAW,YAAeD,EAAQG,KAAKD,GAAO,KAAQF,EAAQG,KAAKD,GAAO,MAC1E,MACJ,KAAK,WACDD,GAAW,0CAA6CD,EAAQG,KAAKD,GAAO,KAAQF,EAAQG,KAAKD,GAAO,MACxG,MACJ,KAAK,YACD,IAAsC,GAAnCF,EAAQG,KAAKD,GAAKE,QAAQ,KAAY,CACrCH,GAAW,oBAAuBD,EAAQG,KAAKD,GAAKG,UAAU,EAAEL,EAAQG,KAAKD,GAAKE,QAAQ,MAAQ,uBAAyBJ,EAAQG,KAAKD,GAAKG,UAAUL,EAAQG,KAAKD,GAAKE,QAAQ,KAAK,GAAK,KAAQJ,EAAQG,KAAKD,GAAO,MACvN,OAGR,QAEID,GAAWD,EAAQG,KAAKD,GAGhCD,GAAW,aAGnBtE,EAAE8D,GAAclC,KAAK0C,GACrBtE,EAAE8D,GAAcpB,YAAY,UAC5B1C,EAAE+D,GAAuBtB,SAAS,aAGzCkC,KAAK,WACF3E,EAAE+D,GAAuBnC,KAAK5B,EAAE+D,GAAuBnC,OAAS,yBAA2BgD,eAAe,WAMtH5E,EAAGqB,MAAOwD,IAAKrB,KAGnBxD,EAAE,mBAAmBG,GAAI,QAAS,SAAUqD,GACxC,GAAIsB,GAAiB9E,EAAEqB,MAAMG,KAAK,kBAC9BuD,EAAU/E,EAAEqB,MAAMG,KAAK,WACvBwD,EAAUhF,EAAEqB,MAAMG,KAAK,WACvByD,EAAWjF,EAAEqB,MAAMG,KAAK,YACxB0D,EAAkBlF,EAAEqB,MAAMG,KAAK,mBAC/B2D,EAAcnF,EAAEqB,MAAMG,KAAK,cAE/B4D,UAAS,qBAAsB,SAASC,GACjCH,IACCI,UAAYC,EAAEC,OAAON,EAAgB,GAAIA,EAAgB,IACzDO,UAAYF,EAAEC,OAAON,EAAgB,GAAIA,EAAgB,IACzDQ,WAAaH,EAAEI,aAAaL,UAAWG,YAK3CF,EAAEK,KAAKC,QAAQC,UAAa,+BAG5B,IAAIC,GAAMR,EAAEQ,IAAIjB,GAGZkB,EAAa,qDACbC,EAAgB,gFAChBC,EAAY,GAAIX,GAAEY,UAAUH,GAAeI,QAAS,EAAGC,QAAS,GAAIC,YAAaL,IAEjFM,EAAgB,sDAChBC,EAAqB,sGACN,IAAIjB,GAAEY,UAAUI,GAAkBH,QAAS,EAAGC,QAAS,GAAIC,YAAaE,GAGxFd,YAECe,WAAW,WACPV,EAAIW,UAAUhB,YACVW,QAAQ,MAEb,GACItB,GAAWC,IACfC,EACCc,EAAIY,QAAQ,GAAIpB,GAAEqB,OAAO5B,EAASD,GAASE,GAE3Cc,EAAIY,QAAQ,GAAIpB,GAAEqB,OAAO5B,EAASD,GAAS,IAG/CgB,EAAIc,SAASX,EAEb,IAAIY,IACPC,aAAcb,EAIfX,GAAEyB,QAAQC,OAAOH,GAAYI,MAAMnB,GAGhCZ,GACCI,EAAE4B,QAAQhC,GAAa+B,MAAMnB,KAMrC/F,EAAGqB,MAAOwD,IAAKrB,OAGtBxD,EAAEV,UAAUW,MAAM,WACfD,EAAE,sBAAsBkB,MAAM,WAC1BlB,EAAE,yBAAyBkD,KAAK,WAAa7B,KAAKuB,SAAU,MAGhE5C,EAAE,wBAAwBkB,MAAM,WAC5BlB,EAAE,yBAAyBkD,KAAK,WAAa7B,KAAKuB,SAAU","file":"searx.min.js"} \ No newline at end of file
diff --git a/searx/static/themes/oscar/js/searx_src/element_modifiers.js b/searx/static/themes/oscar/js/searx_src/element_modifiers.js
index 31fe760..a113df9 100644
--- a/searx/static/themes/oscar/js/searx_src/element_modifiers.js
+++ b/searx/static/themes/oscar/js/searx_src/element_modifiers.js
@@ -22,6 +22,13 @@ $(document).ready(function(){
$('#q.autofocus').focus();
/**
+ * Empty search bar when click on reset button
+ */
+ $("#clear_search").click(function () {
+ document.getElementById("q").value = "";
+ });
+
+ /**
* select full content on click if class="select-all-on-click"
*/
$(".select-all-on-click").click(function () {
diff --git a/searx/static/themes/oscar/js/searx_src/infobox.js b/searx/static/themes/oscar/js/searx_src/infobox.js
new file mode 100644
index 0000000..cb7f1ee
--- /dev/null
+++ b/searx/static/themes/oscar/js/searx_src/infobox.js
@@ -0,0 +1,11 @@
+window.addEventListener('load', function() {
+ // Hide infobox toggle if shrunk size already fits all content.
+ $('.infobox').each(function() {
+ var infobox_body = $(this).find('.infobox_body');
+ var total_height = infobox_body.prop('scrollHeight') + infobox_body.find('img.infobox_part').height();
+ var max_height = infobox_body.css('max-height').replace('px', '');
+ if (total_height <= max_height) {
+ $(this).find('.infobox_toggle').hide();
+ }
+ });
+});
diff --git a/searx/static/themes/oscar/less/logicodev/infobox.less b/searx/static/themes/oscar/less/logicodev/infobox.less
index 86b8afb..e3582df 100644
--- a/searx/static/themes/oscar/less/logicodev/infobox.less
+++ b/searx/static/themes/oscar/less/logicodev/infobox.less
@@ -8,12 +8,23 @@
}
}
+ .header_url {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: block;
+ }
+
p{
font-family: "DejaVu Serif", Georgia, Cambria, "Times New Roman", Times, serif !important;
font-style: italic;
}
+ img{
+ max-height: "250px";
+ }
+
.btn{
background-color: @dark-blue;
border: none;
@@ -34,4 +45,43 @@
.infobox_part:last-child {
margin-bottom: 0;
}
+
+ .infobox_toggle {
+ width: 100%;
+ text-align: center;
+ margin-bottom: 0px;
+ }
+
+ // Shrink infobox size when toggle is off
+ .infobox_checkbox ~ .infobox_body {
+ max-height: 300px;
+ overflow: hidden;
+ }
+ .infobox_checkbox:checked ~ .infobox_body {
+ max-height: none;
+ }
+
+ // Show toggle button as down when infobox is shrunk
+ .infobox_checkbox ~ .infobox_toggle .infobox_label_down {
+ display: block;
+ }
+ .infobox_checkbox ~ .infobox_toggle .infobox_label_up {
+ display: none;
+ }
+
+ // Show toggle button as up when infobox is expanded
+ .infobox_checkbox:checked ~ .infobox_toggle .infobox_label_up {
+ display: block;
+ }
+ .infobox_checkbox:checked ~ .infobox_toggle .infobox_label_down {
+ display: none;
+ }
+
+ // Hide main image when toggle is off
+ .infobox_checkbox ~ .infobox_body img.infobox_part {
+ display: none;
+ }
+ .infobox_checkbox:checked ~ .infobox_body img.infobox_part {
+ display: block;
+ }
}
diff --git a/searx/static/themes/oscar/less/logicodev/results.less b/searx/static/themes/oscar/less/logicodev/results.less
index 9926d6e..33965fb 100644
--- a/searx/static/themes/oscar/less/logicodev/results.less
+++ b/searx/static/themes/oscar/less/logicodev/results.less
@@ -51,6 +51,11 @@
float: right;
}
+.result-abstract {
+ margin-top: 0.5em;
+ margin-bottom: 0.8em;
+}
+
.external-link {
color: @dark-green;
font-size: 12px;
@@ -124,6 +129,20 @@
}
}
+.result-metadata {
+ clear: both;
+ margin: 1em;
+
+ td {
+ padding-right: 1em;
+ color: @gray;
+ }
+
+ td:first-of-type {
+ color: @dark-gray;
+ }
+}
+
// map formating of results
.result-map {
clear: both;
diff --git a/searx/static/themes/oscar/less/logicodev/search.less b/searx/static/themes/oscar/less/logicodev/search.less
index a0cbe4d..0142287 100644
--- a/searx/static/themes/oscar/less/logicodev/search.less
+++ b/searx/static/themes/oscar/less/logicodev/search.less
@@ -56,7 +56,7 @@
color: white;
}
-.custom-select {
+.custom-select, .custom-select-rtl {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
@@ -75,6 +75,10 @@ Ny0yNFQxMToxNTowMCswMjowMP7RDgQAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb
7jwaAAAAAElFTkSuQmCC) 96% no-repeat;
}
+.custom-select-rtl {
+ background-position-x: 4%;
+}
+
.search-margin {
margin-bottom: 0.6em;
}
diff --git a/searx/static/themes/oscar/less/pointhi/infobox.less b/searx/static/themes/oscar/less/pointhi/infobox.less
index df51b00..e6a55e9 100644
--- a/searx/static/themes/oscar/less/pointhi/infobox.less
+++ b/searx/static/themes/oscar/less/pointhi/infobox.less
@@ -1,4 +1,9 @@
.infobox {
+
+ img {
+ max-height: 250px;
+ }
+
.infobox_part {
margin-bottom: 20px;
word-wrap: break-word;
@@ -8,4 +13,50 @@
.infobox_part:last-child {
margin-bottom: 0;
}
+
+ .header_url {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: block;
+ }
+
+ .infobox_toggle {
+ width: 100%;
+ text-align: center;
+ margin-bottom: 0px;
+ }
+
+ // Shrink infobox size when toggle is off
+ .infobox_checkbox ~ .infobox_body {
+ max-height: 300px;
+ overflow: hidden;
+ }
+ .infobox_checkbox:checked ~ .infobox_body {
+ max-height: none;
+ }
+
+ // Show toggle button as down when infobox is shrunk
+ .infobox_checkbox ~ .infobox_toggle .infobox_label_down {
+ display: block;
+ }
+ .infobox_checkbox ~ .infobox_toggle .infobox_label_up {
+ display: none;
+ }
+
+ // Show toggle button as up when infobox is expanded
+ .infobox_checkbox:checked ~ .infobox_toggle .infobox_label_up {
+ display: block;
+ }
+ .infobox_checkbox:checked ~ .infobox_toggle .infobox_label_down {
+ display: none;
+ }
+
+ // Hide main image when toggle is off
+ .infobox_checkbox ~ .infobox_body img.infobox_part {
+ display: none;
+ }
+ .infobox_checkbox:checked ~ .infobox_body img.infobox_part {
+ display: block;
+ }
}
diff --git a/searx/static/themes/oscar/less/pointhi/search.less b/searx/static/themes/oscar/less/pointhi/search.less
index 5ff7336..ac0a7a0 100644
--- a/searx/static/themes/oscar/less/pointhi/search.less
+++ b/searx/static/themes/oscar/less/pointhi/search.less
@@ -31,3 +31,13 @@
background-color: #EEE;
}
}
+
+.visually-hidden {
+ position: absolute !important;
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+ clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ white-space: nowrap; /* added line */
+}
diff --git a/searx/static/themes/simple/js/searx_src/searx_search.js b/searx/static/themes/simple/js/searx_src/searx_search.js
index 580d98d..7b652fa 100644
--- a/searx/static/themes/simple/js/searx_src/searx_search.js
+++ b/searx/static/themes/simple/js/searx_src/searx_search.js
@@ -75,6 +75,10 @@
Url: "./autocompleter",
EmptyMessage: searx.no_item_found,
HttpMethod: searx.method,
+ HttpHeaders: {
+ "Content-type": "application/x-www-form-urlencoded",
+ "X-Requested-With": "XMLHttpRequest"
+ },
MinChars: 4,
Delay: 300,
}, "#" + qinput_id);
diff --git a/searx/templates/__common__/about.html b/searx/templates/__common__/about.html
index 9741b51..649e661 100644
--- a/searx/templates/__common__/about.html
+++ b/searx/templates/__common__/about.html
@@ -7,10 +7,10 @@
while not storing information about its users.
</p>
- <p>More about searx ...</p>
+ <p>More about searx...</p>
<ul>
- <li><a href="https://github.com/asciimoo/searx">github</a></li>
+ <li><a href="https://github.com/searx/searx">github</a></li>
<li><a href="https://twitter.com/Searx_engine">twitter</a></li>
<li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
<li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
@@ -31,7 +31,7 @@
</li>
<li>
Searx is free software, the code is 100% open and you can help to make it
- better. See more on <a href="https://github.com/asciimoo/searx">github</a>.
+ better. See more on <a href="https://github.com/searx/searx">github</a>.
</li>
</ul>
@@ -49,10 +49,11 @@
It provides basic privacy by mixing your queries with searches on other
platforms without storing search data. Queries are made using a POST request
- on every browser (except chrome*). Therefore they show up in neither our
- logs, nor your url history. In case of Chrome* users there is an exception,
- searx uses the search bar to perform GET requests.
-
+ on every browser (except Chromium-based browsers*). Therefore they show up
+ in neither our logs, nor your url history. In the case of Chromium-based
+ browser users there is an exception: searx uses the search bar to perform GET
+ requests.
+
Searx can be added to your browser's search bar; moreover, it can be set as
the default search engine.
</p>
@@ -66,8 +67,8 @@
<ul>
<li><a href="https://support.mozilla.org/en-US/kb/add-or-remove-search-engine-firefox">Firefox</a></li>
- <li><a href="https://support.microsoft.com/en-us/help/4028574/microsoft-edge-change-the-default-search-engine" >Microsoft Egde</a></li>
- <li>Chrome based browsers <a href="https://www.chromium.org/tab-to-search">only add websites that the user navigates to without a path.</a>
+ <li><a href="https://support.microsoft.com/en-us/help/4028574/microsoft-edge-change-the-default-search-engine">Microsoft Edge</a></li>
+ <li>Chromium-based browsers <a href="https://www.chromium.org/tab-to-search">only add websites that the user navigates to without a path.</a>
</ul>
<h2>Where to find anonymous usage statistics of this instance ?</h2>
@@ -80,13 +81,13 @@
<p>
Searx appreciates your concern regarding logs, so take the
- code from the <a href="https://github.com/asciimoo/searx">original searx project</a> and
+ code from the <a href="https://github.com/searx/searx">original searx project</a> and
run it yourself!
</p>
<p>
Add your searx instance to this <a href="{{ brand.PUBLIC_INSTANCES }}"> list
of public searx instances</a> to help other people reclaim their privacy and
- make the Internet freer! The more decentralized the Internet is, the more
+ make the Internet freer! The more decentralized the Internet is, the more
freedom we have!
</p>
diff --git a/searx/templates/__common__/opensearch.xml b/searx/templates/__common__/opensearch.xml
index 2763424..2476258 100644
--- a/searx/templates/__common__/opensearch.xml
+++ b/searx/templates/__common__/opensearch.xml
@@ -6,13 +6,17 @@
<Image>{{ urljoin(host, url_for('static', filename='img/favicon.png')) }}</Image>
<LongName>searx metasearch</LongName>
{% if opensearch_method == 'get' %}
- <Url rel="results" type="text/html" method="get" template="{{ host }}search?q={searchTerms}"/>
+ <Url rel="results" type="text/html" method="get" template="{{ url_for('search', _external=True) }}?q={searchTerms}"/>
{% else %}
- <Url rel="results" type="text/html" method="post" template="{{ host }}">
+ <Url rel="results" type="text/html" method="post" template="{{ url_for('search', _external=True) }}">
<Param name="q" value="{searchTerms}" />
</Url>
{% endif %}
{% if autocomplete %}
- <Url rel="suggestions" type="application/json" template="{{ host }}autocompleter"/>
+ <Url rel="suggestions" type="application/x-suggestions+json" template="{{ host }}autocompleter?q={searchTerms}"/>
{% endif %}
+
+ <Url type="application/opensearchdescription+xml"
+ rel="self"
+ template="{{ opensearch_url }}" />
</OpenSearchDescription>
diff --git a/searx/templates/__common__/opensearch_response_rss.xml b/searx/templates/__common__/opensearch_response_rss.xml
index 3781dd8..82d3f7c 100644
--- a/searx/templates/__common__/opensearch_response_rss.xml
+++ b/searx/templates/__common__/opensearch_response_rss.xml
@@ -4,12 +4,12 @@
xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Searx search: {{ q|e }}</title>
- <link>{{ base_url }}?q={{ q|e }}</link>
+ <link>{{ url_for('search', _external=True) }}?q={{ q|e }}</link>
<description>Search results for "{{ q|e }}" - searx</description>
<opensearch:totalResults>{{ number_of_results }}</opensearch:totalResults>
<opensearch:startIndex>1</opensearch:startIndex>
<opensearch:itemsPerPage>{{ number_of_results }}</opensearch:itemsPerPage>
- <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ base_url }}opensearch.xml"/>
+ <atom:link rel="search" type="application/opensearchdescription+xml" href="{{ opensearch_url }}"/>
<opensearch:Query role="request" searchTerms="{{ q|e }}" startPage="1" />
{% if error_message %}
<item>
diff --git a/searx/templates/courgette/404.html b/searx/templates/courgette/404.html
index 9e3b8ac..7a317f0 100644
--- a/searx/templates/courgette/404.html
+++ b/searx/templates/courgette/404.html
@@ -3,7 +3,7 @@
<div class="center">
<h1>{{ _('Page not found') }}</h1>
{% autoescape false %}
- <p>{{ _('Go to %(search_page)s.', search_page=unicode('<a href="{}">{}</a>').format(url_for('index'), _('search page'))) }}</p>
+ <p>{{ _('Go to %(search_page)s.', search_page='<a href="{}">{}</a>'.format(url_for('index'), _('search page'))) }}</p>
{% endautoescape %}
</div>
{% endblock %}
diff --git a/searx/templates/courgette/base.html b/searx/templates/courgette/base.html
index f4c61da..468b817 100644
--- a/searx/templates/courgette/base.html
+++ b/searx/templates/courgette/base.html
@@ -22,7 +22,7 @@
{% endblock %}
{% block meta %}{% endblock %}
{% block head %}
- <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ opensearch_url }}"/>
{% endblock %}
<script type="text/javascript">
searx = {};
diff --git a/searx/templates/courgette/github_ribbon.html b/searx/templates/courgette/github_ribbon.html
index bdd9cf1..fb38a20 100644
--- a/searx/templates/courgette/github_ribbon.html
+++ b/searx/templates/courgette/github_ribbon.html
@@ -1,3 +1,3 @@
-<a href="https://github.com/asciimoo/searx" class="github">
+<a href="https://github.com/searx/searx" class="github">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="{{ url_for('static', filename='img/github_ribbon.png') }}" alt="Fork me on GitHub" class="github"/>
</a>
diff --git a/searx/templates/courgette/preferences.html b/searx/templates/courgette/preferences.html
index c67f766..6480694 100644
--- a/searx/templates/courgette/preferences.html
+++ b/searx/templates/courgette/preferences.html
@@ -5,10 +5,13 @@
<h2>{{ _('Preferences') }}</h2>
<form method="post" action="{{ url_for('preferences') }}" id="search_form">
+ {% if 'categories' not in locked_preferences %}
<fieldset>
<legend>{{ _('Default categories') }}</legend>
{% include 'courgette/categories.html' %}
</fieldset>
+ {% endif %}
+ {% if 'language' not in locked_preferences %}
<fieldset>
<legend>{{ _('Search language') }}</legend>
<p>
@@ -20,6 +23,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'locale' not in locked_preferences %}
<fieldset>
<legend>{{ _('Interface language') }}</legend>
<p>
@@ -30,6 +35,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'autocomplete' not in locked_preferences %}
<fieldset>
<legend>{{ _('Autocomplete') }}</legend>
<p>
@@ -41,6 +48,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'image_proxy' not in locked_preferences %}
<fieldset>
<legend>{{ _('Image proxy') }}</legend>
<p>
@@ -50,6 +59,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'method' not in locked_preferences %}
<fieldset>
<legend>{{ _('Method') }}</legend>
<p>
@@ -59,6 +70,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'safesearch' not in locked_preferences %}
<fieldset>
<legend>{{ _('SafeSearch') }}</legend>
<p>
@@ -69,6 +82,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'theme' not in locked_preferences %}
<fieldset>
<legend>{{ _('Themes') }}</legend>
<p>
@@ -92,6 +107,7 @@
</select>
</p>
</fieldset>
+ {% endif %}
<fieldset>
<legend>{{ _('Currently used search engines') }}</legend>
diff --git a/searx/templates/courgette/results.html b/searx/templates/courgette/results.html
index aa983e6..716ea4d 100644
--- a/searx/templates/courgette/results.html
+++ b/searx/templates/courgette/results.html
@@ -1,6 +1,6 @@
{% extends "courgette/base.html" %}
{% block title %}{{ q|e }} - {% endblock %}
-{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&amp;format=rss&amp;{% for category in selected_categories %}category_{{ category }}=1&amp;{% endfor %}pageno={{ pageno }}">{% endblock %}
+{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('search', _external=True) }}?q={{ q|urlencode }}&amp;format=rss&amp;{% for category in selected_categories %}category_{{ category }}=1&amp;{% endfor %}pageno={{ pageno }}">{% endblock %}
{% block content %}
<div class="right"><a href="{{ url_for('preferences') }}" id="preferences"><span>{{ _('preferences') }}</span></a></div>
<div class="small search center">
@@ -10,12 +10,12 @@
<div id="sidebar">
<div id="search_url">
{{ _('Search URL') }}:
- <input type="text" value="{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}" readonly />
+ <input type="text" value="{{ url_for('search', _external=True) }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}" readonly />
</div>
<div id="apis">
{{ _('Download results') }}<br />
{% for output_type in ('csv', 'json', 'rss') %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div class="left">
<input type="hidden" name="q" value="{{ q|e }}" />
<input type="hidden" name="format" value="{{ output_type }}" />
@@ -41,7 +41,7 @@
{% if suggestions %}
<div id="suggestions"><span>{{ _('Suggestions') }}</span>
{% for suggestion in suggestions %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<input type="hidden" name="q" value="{{ suggestion.url }}">
<input type="submit" value="{{ suggestion.title }}" />
</form>
@@ -60,7 +60,7 @@
{% if paging %}
<div id="pagination">
{% if pageno > 1 %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div class="left">
<input type="hidden" name="q" value="{{ q|e }}" />
{% for category in selected_categories %}
@@ -71,7 +71,7 @@
</div>
</form>
{% endif %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div class="left">
{% for category in selected_categories %}
<input type="hidden" name="category_{{ category }}" value="1"/>
diff --git a/searx/templates/courgette/search.html b/searx/templates/courgette/search.html
index fe70fde..89daead 100644
--- a/searx/templates/courgette/search.html
+++ b/searx/templates/courgette/search.html
@@ -1,7 +1,7 @@
-<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form">
+<form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" id="search_form">
<div id="search_wrapper">
<input type="text" autofocus placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" autocomplete="off" {% if q %}value="{{ q }}"{% endif %}/>
<input type="submit" value="search" id="search_submit" />
</div>
{% include 'courgette/categories.html' %}
-</form> \ No newline at end of file
+</form>
diff --git a/searx/templates/legacy/404.html b/searx/templates/legacy/404.html
index 3e889dd..c0fa62b 100644
--- a/searx/templates/legacy/404.html
+++ b/searx/templates/legacy/404.html
@@ -3,7 +3,7 @@
<div class="center">
<h1>{{ _('Page not found') }}</h1>
{% autoescape false %}
- <p>{{ _('Go to %(search_page)s.', search_page=unicode('<a href="{}">{}</a>').format(url_for('index'), _('search page'))) }}</p>
+ <p>{{ _('Go to %(search_page)s.', search_page='<a href="{}">{}</a>'.format(url_for('index'), _('search page'))) }}</p>
{% endautoescape %}
</div>
{% endblock %}
diff --git a/searx/templates/legacy/base.html b/searx/templates/legacy/base.html
index 21fe42e..1e52322 100644
--- a/searx/templates/legacy/base.html
+++ b/searx/templates/legacy/base.html
@@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}>
<head>
<meta charset="UTF-8" />
- <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
+ <meta name="description" content="searx — a privacy-respecting, hackable metasearch engine" />
<meta name="keywords" content="searx, search, search engine, metasearch, meta search" />
<meta name="generator" content="searx/{{ searx_version }}">
<meta name="referrer" content="no-referrer">
@@ -17,7 +17,7 @@
{% endblock %}
{% block meta %}{% endblock %}
{% block head %}
- <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ opensearch_url }}"/>
{% endblock %}
</head>
<body class="{{ endpoint }}_endpoint" >
diff --git a/searx/templates/legacy/github_ribbon.html b/searx/templates/legacy/github_ribbon.html
index bdd9cf1..fb38a20 100644
--- a/searx/templates/legacy/github_ribbon.html
+++ b/searx/templates/legacy/github_ribbon.html
@@ -1,3 +1,3 @@
-<a href="https://github.com/asciimoo/searx" class="github">
+<a href="https://github.com/searx/searx" class="github">
<img style="position: absolute; top: 0; right: 0; border: 0;" src="{{ url_for('static', filename='img/github_ribbon.png') }}" alt="Fork me on GitHub" class="github"/>
</a>
diff --git a/searx/templates/legacy/infobox.html b/searx/templates/legacy/infobox.html
index 4dd25fa..70f3b12 100644
--- a/searx/templates/legacy/infobox.html
+++ b/searx/templates/legacy/infobox.html
@@ -36,7 +36,7 @@
<div>
<h3><bdi>{{ topic.name }}</bdi></h3>
{% for suggestion in topic.suggestions %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<input type="hidden" name="q" value="{{ suggestion }}">
<input type="submit" value="{{ suggestion }}" />
</form>
diff --git a/searx/templates/legacy/preferences.html b/searx/templates/legacy/preferences.html
index 414b3f6..23b3875 100644
--- a/searx/templates/legacy/preferences.html
+++ b/searx/templates/legacy/preferences.html
@@ -10,6 +10,7 @@
{% set display_tooltip = false %}
{% include 'legacy/categories.html' %}
</fieldset>
+ {% if 'language' not in locked_preferences %}
<fieldset>
<legend>{{ _('Search language') }}</legend>
<p>
@@ -21,6 +22,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'locale' not in locked_preferences %}
<fieldset>
<legend>{{ _('Interface language') }}</legend>
<p>
@@ -31,6 +34,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'autocomplete' not in locked_preferences %}
<fieldset>
<legend>{{ _('Autocomplete') }}</legend>
<p>
@@ -42,6 +47,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'image_proxy' not in locked_preferences %}
<fieldset>
<legend>{{ _('Image proxy') }}</legend>
<p>
@@ -51,6 +58,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'method' not in locked_preferences %}
<fieldset>
<legend>{{ _('Method') }}</legend>
<p>
@@ -60,6 +69,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'safesearch' not in locked_preferences %}
<fieldset>
<legend>{{ _('SafeSearch') }}</legend>
<p>
@@ -70,6 +81,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'theme' not in locked_preferences %}
<fieldset>
<legend>{{ _('Themes') }}</legend>
<p>
@@ -80,6 +93,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'results_on_new_tab' not in locked_preferences %}
<fieldset>
<legend>{{ _('Results on new tabs') }}</legend>
<p>
@@ -89,6 +104,7 @@
</select>
</p>
</fieldset>
+ {% endif %}
<fieldset>
<legend>{{ _('Currently used search engines') }}</legend>
diff --git a/searx/templates/legacy/result_templates/default.html b/searx/templates/legacy/result_templates/default.html
index 13e2d29..78bf031 100644
--- a/searx/templates/legacy/result_templates/default.html
+++ b/searx/templates/legacy/result_templates/default.html
@@ -1,6 +1,11 @@
<div class="result {{ result.class }}{% for e in result.engines %} {{ e }}{% endfor %}">
<h3 class="result_title">{% if "icon_"~result.engine~".ico" in favicons %}<img width="14" height="14" class="favicon" src="{{ url_for('static', filename='img/icons/icon_'+result.engine+'.ico') }}" alt="{{result.engine}}" />{% endif %}<a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ result.title|safe }}</a></h3>
- <p class="url">{{ result.pretty_url }}&lrm; <a class="cache_link" href="https://web.archive.org/web/{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('cached') }}</a>
+ <p class="url">{{ result.pretty_url }}&lrm;
+ {% if result.cached_url %}
+ <a class="cache_link" href="{{ result.cached_url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('cached') }}</a>
+ {% elif not result.is_onion %}
+ <a class="cache_link" href="https://web.archive.org/web/{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ _('cached') }}</a>
+ {% endif %}
{% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}</p>
<p class="content">{% if result.img_src %}<img src="{{ image_proxify(result.img_src) }}" class="image" />{% endif %}{% if result.content %}{{ result.content|safe }}<br class="last"/>{% endif %}</p>
</div>
diff --git a/searx/templates/legacy/results.html b/searx/templates/legacy/results.html
index fd95657..efff066 100644
--- a/searx/templates/legacy/results.html
+++ b/searx/templates/legacy/results.html
@@ -1,6 +1,6 @@
{% extends "legacy/base.html" %}
{% block title %}{{ q|e }} - {% endblock %}
-{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&amp;format=rss&amp;{% for category in selected_categories %}category_{{ category }}=1&amp;{% endfor %}pageno={{ pageno }}">{% endblock %}
+{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('search', _external=True) }}?q={{ q|urlencode }}&amp;format=rss&amp;{% for category in selected_categories %}category_{{ category }}=1&amp;{% endfor %}pageno={{ pageno }}">{% endblock %}
{% block content %}
<div class="preferences_container right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div>
<div class="small search center">
@@ -11,12 +11,12 @@
<div id="search_url">
{{ _('Search URL') }}:
- <input type="text" value="{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}" readonly />
+ <input type="text" value="{{ url_for('search', _external=True) }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}" readonly />
</div>
<div id="apis">
{{ _('Download results') }}
{% for output_type in ('csv', 'json', 'rss') %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div class="left">
<input type="hidden" name="q" value="{{ q|e }}" />
<input type="hidden" name="format" value="{{ output_type }}" />
@@ -47,7 +47,7 @@
<div id="suggestions"><span id="suggestions-title">{{ _('Suggestions') }} : </span>
{% set first = true %}
{% for suggestion in suggestions %}
- {% if not first %} &bull; {% endif %}<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ {% if not first %} &bull; {% endif %}<form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<input type="hidden" name="q" value="{{ suggestion.url }}">
<input type="submit" class="suggestion" value="{{ suggestion.title }}" />
</form>
@@ -75,7 +75,7 @@
{% if paging %}
<div id="pagination">
{% if pageno > 1 %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div class="{% if rtl %}right{% else %}left{% endif %}">
<input type="hidden" name="q" value="{{ q|e }}" />
{% for category in selected_categories %}
@@ -86,7 +86,7 @@
</div>
</form>
{% endif %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div class="{% if rtl %}left{% else %}right{% endif %}">
{% for category in selected_categories %}
<input type="hidden" name="category_{{ category }}" value="1"/>
diff --git a/searx/templates/legacy/search.html b/searx/templates/legacy/search.html
index fcd08d6..88cf3d3 100644
--- a/searx/templates/legacy/search.html
+++ b/searx/templates/legacy/search.html
@@ -1,4 +1,4 @@
-<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form">
+<form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" id="search_form">
<div id="search_wrapper">
<input type="text" autofocus placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" autocomplete="off" size="100" {% if q %}value="{{ q }}"{% endif %}/>
<input type="submit" value="search" id="search_submit" />
diff --git a/searx/templates/oscar/404.html b/searx/templates/oscar/404.html
index 5a50880..cdb31db 100644
--- a/searx/templates/oscar/404.html
+++ b/searx/templates/oscar/404.html
@@ -3,7 +3,7 @@
<div class="text-center">
<h1>{{ _('Page not found') }}</h1>
{% autoescape false %}
- <p>{{ _('Go to %(search_page)s.', search_page=unicode('<a href="{}">{}</a>').format(url_for('index'), _('search page'))) }}</p>
+ <p>{{ _('Go to %(search_page)s.', search_page='<a href="{}">{}</a>'.format(url_for('index'), _('search page'))) }}</p>
{% endautoescape %}
</div>
{% endblock %}
diff --git a/searx/templates/oscar/base.html b/searx/templates/oscar/base.html
index a3bfa52..7b3d33f 100644
--- a/searx/templates/oscar/base.html
+++ b/searx/templates/oscar/base.html
@@ -37,7 +37,7 @@
{% block head %}
{% endblock %}
- <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ opensearch_url }}"/>
<noscript>
<style type="text/css">
.tab-content > .active_if_nojs, .active_if_nojs {display: block !important; visibility: visible !important;}
diff --git a/searx/templates/oscar/infobox.html b/searx/templates/oscar/infobox.html
index 6ae7965..8a12b80 100644
--- a/searx/templates/oscar/infobox.html
+++ b/searx/templates/oscar/infobox.html
@@ -1,10 +1,18 @@
{% from 'oscar/macros.html' import result_link with context %}
<div class="panel panel-default infobox">
<div class="panel-heading">{{- "" -}}
- <h4 class="panel-title infobox_part"><bdi>{{ infobox.infobox }}</bdi></h4>{{- "" -}}
- {% for u in infobox.urls %}{% if u.official %} <a href="{{ u.url }}">{{ u.domain }}</a>{% endif %}{% endfor %}
+ <div class="infobox_part">
+ <div class="{% if not rtl %}pull-right{% endif %}">
+ {% for engine in infobox.engines %}
+ <span class="label label-default">{{ engine }}</span>
+ {% endfor %}
+ </div>
+ <h4 class="panel-title"><bdi>{{ infobox.infobox }}</bdi></h4>{{- "" -}}
+ {% for u in infobox.urls %}{% if u.official %} <a class="header_url" href="{{ u.url }}">{{ u.url }}</a>{% endif %}{% endfor %}
+ </div>
</div>
- <div class="panel-body">
+ <input type="checkbox" class="infobox_checkbox" id="expand_infobox_{{ infobox.engine }}" hidden>
+ <div class="panel-body infobox_body">
{% if infobox.img_src %}<img class="img-responsive center-block infobox_part" src="{{ image_proxify(infobox.img_src) }}" />{% endif %}
{% if infobox.content %}<bdi><p class="infobox_part">{{ infobox.content | safe }}</p></bdi>{% endif %}
@@ -17,11 +25,7 @@
{%- if attribute.image -%}
<td><img class="img-responsive" src="{{ image_proxify(attribute.image.src) }}" alt="{{ attribute.image.alt }}" /></td>
{%- else -%}
- {% if attribute.label == 'Instance of' %}
- <td><bdi><a href="https://wikidata.org/wiki/{{ attribute.value.id }}">{{ attribute.value.id }}</a></bdi></td>
- {% else %}
- <td><bdi>{{ attribute.value }}</bdi></td>
- {%- endif -%}
+ <td><bdi>{{ attribute.value }}</bdi></td>
{%- endif -%}
</tr>
{% endfor -%}
@@ -38,4 +42,8 @@
</div>
{% endif %}
</div>
+ <label for="expand_infobox_{{ infobox.engine }}" class="infobox_toggle panel-footer">
+ <span class="infobox_label_down glyphicon glyphicon-chevron-down"></span>
+ <span class="infobox_label_up glyphicon glyphicon-chevron-up"></span>
+ </label>
</div>
diff --git a/searx/templates/oscar/languages.html b/searx/templates/oscar/languages.html
index 9c00c9c..0846caa 100644
--- a/searx/templates/oscar/languages.html
+++ b/searx/templates/oscar/languages.html
@@ -1,5 +1,6 @@
+{% from 'oscar/macros.html' import custom_select_class %}
<label class="visually-hidden" for="language">{{ _('Language') }}</label>
-<select class="language custom-select form-control" id="language" name="language" accesskey="l">
+<select class="language form-control {{ custom_select_class(rtl) }}" id="language" name="language" accesskey="l">
<option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Default language') }}</option>
{%- for lang_id,lang_name,country_name,english_name in language_codes | sort(attribute=1) -%}
<option value="{{ lang_id }}" {% if lang_id == current_language %}selected="selected"{% endif %}>
diff --git a/searx/templates/oscar/macros.html b/searx/templates/oscar/macros.html
index 58f9669..f40eebd 100644
--- a/searx/templates/oscar/macros.html
+++ b/searx/templates/oscar/macros.html
@@ -1,6 +1,6 @@
<!-- Draw glyphicon icon from bootstrap-theme -->
-{% macro icon(action) -%}
- <span class="glyphicon glyphicon-{{ action }}"></span>
+{% macro icon(action, alt) -%}
+ <span title="{{ alt }}" class="glyphicon glyphicon-{{ action }}"></span>
{%- endmacro %}
<!-- Draw favicon -->
@@ -32,7 +32,11 @@
<span class="label label-default">{{ engine }}</span>
{%- endfor -%}
{%- if result.url -%}
- <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info", id) }}</small>
+ {% if result.cached_url %}
+ <small>{{ result_link(result.cached_url, icon('link') + _('cached'), "text-info", id) }}</small>
+ {% elif not result.is_onion %}
+ <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info", id) }}</small>
+ {% endif %}
{%- endif -%}
{%- if proxify -%}
<small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info", id) }}</small>
@@ -43,6 +47,20 @@
{%- endif -%}
{%- endmacro %}
+<!-- Draw result footer without cache link -->
+{% macro result_footer_nocache(result) -%}
+ <div class="clearfix"></div>
+ <div class="pull-right">
+ {% for engine in result.engines %}
+ <span class="label label-default">{{ engine }}</span>
+ {% endfor %}
+ {% if proxify %}
+ <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
+ {% endif %}
+</div>
+<div class="external-link">{{ result.pretty_url }}</div>
+{%- endmacro %}
+
<!-- Draw result footer -->
{% macro result_footer_rtl(result, id) -%}
<div class="clearfix"></div>{{- "" -}}
@@ -50,7 +68,11 @@
<span class="label label-default">{{ engine }}</span>
{%- endfor %}
{%- if result.url -%}
- <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info", id) }}</small>
+ {% if result.cached_url %}
+ <small>{{ result_link(result.cached_url, icon('link') + _('cached'), "text-info", id) }}</small>
+ {% elif not result.is_onion %}
+ <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info", id) }}</small>
+ {% endif %}
{%- endif -%}
{% if proxify -%}
<small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info", id) }}</small>
@@ -60,6 +82,18 @@
{%- endif %}
{%- endmacro %}
+<!-- Draw result footer without cache link -->
+{% macro result_footer_nocache_rtl(result) -%}
+ <div class="clearfix"></div>
+ {% for engine in result.engines %}
+ <span class="label label-default">{{ engine }}</span>
+ {% endfor %}
+ {% if proxify %}
+ <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
+ {% endif %}
+ <div class="external-link">{{ result.pretty_url }}</div>
+{%- endmacro %}
+
{% macro preferences_item_header(info, label, rtl, id) -%}
{% if rtl %}
<div class="row form-group">
@@ -84,6 +118,10 @@
{% endif %}
{%- endmacro %}
+{% macro custom_select_class(rtl) -%}
+custom-select{% if rtl %}-rtl{% endif %}
+{%- endmacro %}
+
{% macro checkbox_toggle(id, blocked) -%}
<div class="onoffswitch">
<input type="checkbox" id="{{ id }}" name="{{ id }}"{% if blocked %} checked="checked"{% endif %} class="onoffswitch-checkbox">
diff --git a/searx/templates/oscar/preferences.html b/searx/templates/oscar/preferences.html
index ab71b06..fc20b8c 100644
--- a/searx/templates/oscar/preferences.html
+++ b/searx/templates/oscar/preferences.html
@@ -1,4 +1,4 @@
-{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle, support_toggle %}
+{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle, support_toggle, custom_select_class %}
{% extends "oscar/base.html" %}
{% block title %}{{ _('preferences') }} - {% endblock %}
{% block content %}
@@ -25,6 +25,7 @@
<div class="tab-pane active" id="tab_general">
<fieldset>
<div class="container-fluid">
+ {% if 'categories' not in locked_preferences %}
<div class="row form-group">
{% if rtl %}
<div class="col-sm-11 col-md-10">
@@ -38,92 +39,121 @@
</div>
{% endif %}
</div>
+ {% endif %}
+ {% if 'language' not in locked_preferences %}
{% set language_label = _('Search language') %}
{% set language_info = _('What language do you prefer for search?') %}
{{ preferences_item_header(language_info, language_label, rtl, 'language') }}
{% include 'oscar/languages.html' %}
{{ preferences_item_footer(language_info, language_label, rtl) }}
+ {% endif %}
+ {% if 'locale' not in locked_preferences %}
{% set locale_label = _('Interface language') %}
{% set locale_info = _('Change the language of the layout') %}
{{ preferences_item_header(locale_info, locale_label, rtl, 'locale') }}
- <select class="form-control" name="locale" id="locale">
+ <select class="form-control {{ custom_select_class(rtl)}}" name="locale" id="locale">
{% for locale_id,locale_name in locales.items() | sort %}
<option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
{% endfor %}
</select>
{{ preferences_item_footer(locale_info, locale_label, rtl) }}
+ {% endif %}
+ {% if 'autocomplete' not in locked_preferences %}
{% set autocomplete_label = _('Autocomplete') %}
{% set autocomplete_info = _('Find stuff as you type') %}
{{ preferences_item_header(autocomplete_info, autocomplete_label, rtl, 'autocomplete') }}
- <select class="form-control" name="autocomplete" id="autocomplete">
+ <select class="form-control {{ custom_select_class(rtl) }}" name="autocomplete" id="autocomplete">
<option value=""> - </option>
{% for backend in autocomplete_backends %}
<option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
{% endfor %}
</select>
{{ preferences_item_footer(autocomplete_info, autocomplete_label, rtl) }}
+ {% endif %}
+ {% if 'image_proxy' not in locked_preferences %}
{% set image_proxy_label = _('Image proxy') %}
{% set image_proxy_info = _('Proxying image results through searx') %}
{{ preferences_item_header(image_proxy_info, image_proxy_label, rtl, 'image_proxy') }}
- <select class="form-control" name="image_proxy" id="image_proxy">
+ <select class="form-control {{ custom_select_class(rtl) }}" name="image_proxy" id="image_proxy">
<option value="1" {% if image_proxy %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
<option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled')}}</option>
</select>
{{ preferences_item_footer(image_proxy_info, image_proxy_label, rtl) }}
+ {% endif %}
+ {% if 'method' not in locked_preferences %}
{% set method_label = _('Method') %}
{% set method_info = _('Change how forms are submited, <a href="http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods" rel="external">learn more about request methods</a>') %}
{{ preferences_item_header(method_info, method_label, rtl, 'method') }}
- <select class="form-control" name="method" id="method">
+ <select class="form-control {{ custom_select_class(rtl) }}" name="method" id="method">
<option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
<option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
</select>
{{ preferences_item_footer(method_info, method_label, rtl) }}
+ {% endif %}
+ {% if 'safesearch' not in locked_preferences %}
{% set safesearch_label = _('SafeSearch') %}
{% set safesearch_info = _('Filter content') %}
{{ preferences_item_header(safesearch_info, safesearch_label, rtl, 'safesearch') }}
- <select class="form-control" name="safesearch" id="safesearch">
+ <select class="form-control {{ custom_select_class(rtl) }}" name="safesearch" id="safesearch">
<option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
<option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
<option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
</select>
{{ preferences_item_footer(safesearch_info, safesearch_label, rtl) }}
+ {% endif %}
+ {% if 'theme' not in locked_preferences %}
{% set theme_label = _('Themes') %}
{% set theme_info = _('Change searx layout') %}
{{ preferences_item_header(theme_info, theme_label, rtl, 'theme') }}
- <select class="form-control" name="theme" id="theme">
+ <select class="form-control {{ custom_select_class(rtl) }}" name="theme" id="theme">
{% for name in themes %}
<option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
{% endfor %}
</select>
{{ preferences_item_footer(theme_info, theme_label, rtl) }}
+ {% endif %}
+ {% if 'oscar-style' not in locked_preferences %}
{{ preferences_item_header(_('Choose style for this theme'), _('Style'), rtl, 'oscar_style') }}
- <select class="form-control" name="oscar-style" id="oscar_style">
+ <select class="form-control {{ custom_select_class(rtl) }}" name="oscar-style" id="oscar_style">
<option value="logicodev" >Logicodev</option>
<option value="pointhi" {% if preferences.get_value('oscar-style') == 'pointhi' %}selected="selected"{% endif %}>Pointhi</option>
<option value="logicodev-dark" {% if preferences.get_value('oscar-style') == 'logicodev-dark' %}selected="selected"{% endif %}>Logicodev dark</option>
</select>
{{ preferences_item_footer(_('Choose style for this theme'), _('Style'), rtl) }}
+ {% endif %}
+ {% if 'results_on_new_tab' not in locked_preferences %}
{% set label = _('Results on new tabs') %}
{% set info = _('Open result links on new browser tabs') %}
{{ preferences_item_header(info, label, rtl, 'results_on_new_tab') }}
- <select class="form-control" name="results_on_new_tab" id="results_on_new_tab">
+ <select class="form-control {{ custom_select_class(rtl) }}" name="results_on_new_tab" id="results_on_new_tab">
<option value="1" {% if results_on_new_tab %}selected="selected"{% endif %}>{{ _('On') }}</option>
<option value="0" {% if not results_on_new_tab %}selected="selected"{% endif %}>{{ _('Off')}}</option>
</select>
{{ preferences_item_footer(info, label, rtl) }}
+ {% endif %}
+ {% set label = _('Show advanced settings') %}
+ {% set info = _('Show advanced settings panel in the home page by default') %}
+ {{ preferences_item_header(info, label, rtl, 'advanced_search') }}
+ <select class="form-control {{ custom_select_class(rtl) }}" name="advanced_search" id="advanced_search">
+ <option value="1" {% if preferences.get_value('advanced_search')%}selected="selected"{% endif %}>{{ _('On') }}</option>
+ <option value="0" {% if not preferences.get_value('advanced_search')%}selected="selected"{% endif %}>{{ _('Off')}}</option>
+ </select>
+ {{ preferences_item_footer(info, label, rtl) }}
+
+ {% if 'doi_resolver' not in locked_preferences %}
{% set label = _('Open Access DOI resolver') %}
{% set info = _('Redirect to open-access versions of publications when available (plugin required)') %}
{{ preferences_item_header(info, label, rtl, 'doi_resolver') }}
- <select class="form-control" name="doi_resolver" id="doi_resolver">
+ <select class="form-control {{ custom_select_class(rtl) }}" name="doi_resolver" id="doi_resolver">
{% for doi_resolver_name,doi_resolver_url in doi_resolvers.items() %}
<option value="{{ doi_resolver_name }}" {% if doi_resolver_name == current_doi_resolver %}selected="selected"{% endif %}>
{{ doi_resolver_name }} - {{ doi_resolver_url }}
@@ -131,6 +161,7 @@
{% endfor %}
</select>
{{ preferences_item_footer(info, label, rtl) }}
+ {% endif %}
{% set label = _('Engine tokens') %}
{% set info = _('Access tokens for private engines') %}
@@ -199,8 +230,8 @@
<td class="onoff-checkbox">
{{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
</td>
- <th scope="row">{{ search_engine.name }}</th>
- <td class="name">{{ shortcuts[search_engine.name] }}</td>
+ <th scope="row">{% if not search_engine.https_support %}{{ icon('exclamation-sign', 'No HTTPS') }}{% endif %} {{ search_engine.name }}</td></th>
+ <td class="name">{{ shortcuts[search_engine.name] }}
<td>{{ support_toggle(stats[search_engine.name].supports_selected_language) }}</td>
<td>{{ support_toggle(search_engine.safesearch==True) }}</td>
<td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
@@ -236,6 +267,7 @@
<fieldset>
<div class="container-fluid">
{% for plugin in plugins %}
+ {% if plugin.preference_section != 'onions' %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ _(plugin.name) }}</h3>
@@ -249,6 +281,7 @@
</div>
</div>
</div>
+ {% endif %}
{% endfor %}
</div>
</fieldset>
@@ -321,7 +354,7 @@
</p>
<div class="tab-pane">
- <input readonly="" class="form-control select-all-on-click cursor-text" type="url" value="{{ url_for('index', _external=True) }}?preferences={{ preferences_url_params|e }}{% raw %}&amp;q=%s{% endraw %}">
+ <input readonly="" class="form-control select-all-on-click cursor-text" type="url" value="{{ base_url }}?preferences={{ preferences_url_params|e }}{% raw %}&amp;q=%s{% endraw %}">
<input type="submit" class="btn btn-primary" value="{{ _('save') }}" />
<a href="{{ url_for('index') }}"><div class="btn btn-default">{{ _('back') }}</div></a>
<a href="{{ url_for('clear_cookies') }}"><div class="btn btn-default">{{ _('Reset defaults') }}</div></a>
diff --git a/searx/templates/oscar/result_templates/files.html b/searx/templates/oscar/result_templates/files.html
new file mode 100644
index 0000000..5e3894e
--- /dev/null
+++ b/searx/templates/oscar/result_templates/files.html
@@ -0,0 +1,55 @@
+{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer_nocache, result_footer_nocache_rtl, icon with context %}
+
+{{ result_header(result, favicons) }}
+{{ result_sub_header(result) }}
+
+{% if result.embedded %}
+ <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader disabled_if_nojs" data-toggle="collapse" data-target="#result-media-{{ index }}" data-btn-text-collapsed="{{ _('show media') }}" data-btn-text-not-collapsed="{{ _('hide media') }}">
+ {% if result.mtype == 'audio' %}{{ icon('music') }}
+ {% elif result.mtype == 'video' %} {{ icon('film') }}
+ {% endif %} {{ _('show media') }}</a></small>
+{% endif %}
+
+{% if result.embedded %}
+<div id="result-media-{{ index }}" class="collapse">
+ {{ result.embedded|safe }}
+</div>
+{% endif %}
+
+{% if result.abstract %}<p class="result-content result-abstract">{{ result.abstract|safe }}</p>{% endif %}
+
+{% if result.img_src %}
+<div class="container-fluid">
+ <div class="row">
+<img src="{{ image_proxify(result.img_src) }}" alt="{{ result.title|striptags }}" title="{{ result.title|striptags }}" style="width: auto; max-height: 60px; min-height: 60px;" class="col-xs-2 col-sm-4 col-md-4 result-content">
+{% if result.content %}<p class="result-content col-xs-8 col-sm-8 col-md-8">{{ result.content|safe }}</p>{% endif %}
+ </div>
+</div>
+{% else %}
+{% if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif %}
+{% endif %}
+
+<table class="result-metadata result-content">
+{% if result.author %}<tr><td>{{ _('Author') }}</td><td>{{ result.author|safe }}</td></tr>{% endif %}
+
+{% if result.filename %}<tr><td>{{ _('Filename') }}</td><td>{{ result.filename|safe }}</td></tr>{% endif %}
+
+{% if result.size %}<tr><td>{{ _('Filesize') }}</td><td>
+ {% if result.size < 1024 %}{{ result.size }} {{ _('Bytes') }}
+ {% elif result.size < 1024*1024 %}{{ '{0:0.2f}'.format(result.size/1024) }} {{ _('kiB') }}
+ {% elif result.size < 1024*1024*1024 %}{{ '{0:0.2f}'.format(result.size/1024/1024) }} {{ _('MiB') }}
+ {% elif result.size < 1024*1024*1024*1024 %}{{ '{0:0.2f}'.format(result.size/1024/1024/1024) }} {{ _('GiB') }}
+ {% else %}{{ '{0:0.2f}'.format(result.size/1024/1024/1024/1024) }} {{ _('TiB') }}{% endif %}
+ </td></tr>
+{% endif %}
+
+{% if result.time %}<tr><td>{{ _('Date') }}</td><td>{{ result.time|safe }}</td></tr>{% endif %}
+
+{% if result.mtype %}<tr><td>{{ _('Type') }}</td><td>{{ result.mtype|safe }}/{{ result.subtype|safe }}</td></tr>{% endif %}
+</table>
+
+{% if rtl %}
+{{ result_footer_nocache_rtl(result) }}
+{% else %}
+{{ result_footer_nocache(result) }}
+{% endif %}
diff --git a/searx/templates/oscar/result_templates/key-value.html b/searx/templates/oscar/result_templates/key-value.html
index 67c748e..d5c56a1 100644
--- a/searx/templates/oscar/result_templates/key-value.html
+++ b/searx/templates/oscar/result_templates/key-value.html
@@ -6,7 +6,7 @@
{% continue %}
{% endif %}
<tr>
- <td><b>{{ key|upper }}</b>: {{ value }}</td>
+ <td><b>{{ key|upper }}</b>: {{ value|truncate }}</td>
</tr>
{% endfor %}
</table>
diff --git a/searx/templates/oscar/result_templates/products.html b/searx/templates/oscar/result_templates/products.html
new file mode 100644
index 0000000..590db0e
--- /dev/null
+++ b/searx/templates/oscar/result_templates/products.html
@@ -0,0 +1,22 @@
+{% from 'oscar/macros.html' import draw_favicon, result_header, result_sub_header, result_footer_rtl, result_footer %}
+
+{{ result_header(result, favicons) }}
+{{ result_sub_header(result) }}
+
+<div class="container-fluid">
+ <div class="row">
+ <a href="{{ result.url }}" {% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}><img class="thumbnail col-xs-6 col-sm-3 col-md-3 result-content" src="{{ image_proxify(result.thumbnail) }}" alt="{{ result.title|striptags }} {{ result.engine }}" /></a>
+ <p class="col-xs-12 col-sm-9 col-md-9 result-content">
+ {% if result.price %}<big>{{ result.price|safe }}</big></br>{% endif %}
+ {% if result.shipping %}<small>{{ result.shipping|safe }}</small></br>{% endif %}
+ {% if result.source_country %}<small>{{ result.source_country|safe }}</small></br>{% endif %}
+ {% if result.content %}{{ result.content|safe }}{% endif %}
+ </p>
+ </div>
+</div>
+
+{% if rtl %}
+{{ result_footer_rtl(result) }}
+{% else %}
+{{ result_footer(result) }}
+{% endif %}
diff --git a/searx/templates/oscar/results.html b/searx/templates/oscar/results.html
index 7a444d1..7f60713 100644
--- a/searx/templates/oscar/results.html
+++ b/searx/templates/oscar/results.html
@@ -7,7 +7,7 @@
<input type="hidden" name="language" value="{{ current_language }}" />{{- "" -}}
{% if timeout_limit %}<input type="hidden" name="timeout_limit" value="{{ timeout_limit|e }}" />{% endif -%}
{%- endmacro %}
-{%- macro search_url() %}{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}{% if time_range %}&amp;time_range={{ time_range }}{% endif %}{% if current_language != 'all' %}&amp;language={{ current_language }}{% endif %}{% endmacro -%}
+{%- macro search_url() %}{{ url_for('search', _external=True) }}?q={{ q|urlencode }}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}{% if time_range %}&amp;time_range={{ time_range }}{% endif %}{% if current_language != 'all' %}&amp;language={{ current_language }}{% endif %}{% endmacro -%}
{% block title %}{{ q|e }} - {% endblock %}
{% block meta %}{{" "}}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ search_url() }}&amp;format=rss">{% endblock %}
@@ -42,7 +42,13 @@
</div>
<div class="panel-body">
{% for suggestion in suggestions %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" role="navigation" class="form-inline pull-{% if rtl %}right{% else %}left{% endif %} suggestion_item">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" role="navigation" class="form-inline pull-{% if rtl %}right{% else %}left{% endif %} suggestion_item">
+ {% if current_language != 'all' %}
+ <input type="hidden" name="language" value="{{ current_language }}">
+ {% endif %}
+ {% if time_range %}
+ <input type="hidden" name="time_range" value="{{ time_range }}">
+ {% endif %}
<input type="hidden" name="q" value="{{ suggestion.url }}">
<button type="submit" class="btn btn-default btn-xs">{{ suggestion.title }}</button>
</form>
@@ -65,7 +71,7 @@
<label>{{ _('Download results') }}</label>
<div class="clearfix"></div>
{% for output_type in ('csv', 'json', 'rss') %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="form-inline pull-{% if rtl %}right{% else %}left{% endif %} result_download">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" class="form-inline pull-{% if rtl %}right{% else %}left{% endif %} result_download">
{{- search_form_attrs(pageno) -}}
<input type="hidden" name="format" value="{{ output_type }}">{{- "" -}}
<button type="submit" class="btn btn-default">{{ output_type }}</button>{{- "" -}}
@@ -83,13 +89,21 @@
{% if corrections -%}
<div class="result">
- <span class="result_header text-muted form-inline pull-left suggestion_item">{{ _('Try searching for:') }}</span>
- {% for correction in corrections -%}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" role="navigation" class="form-inline pull-left suggestion_item">{{- "" -}}
- <input type="hidden" name="q" value="{{ correction.url }}">{{- "" -}}
- <button type="submit" class="btn btn-default btn-xs">{{ correction.title }}</button>{{- "" -}}
- </form>
- {% endfor %}
+ <div class="clearfix">
+ <span class="result_header text-muted form-inline pull-left suggestion_item">{{ _('Try searching for:') }}</span>
+ {% for correction in corrections -%}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" role="navigation" class="form-inline pull-left suggestion_item">{{- "" -}}
+ {% if current_language != 'all' %}
+ <input type="hidden" name="language" value="{{ current_language }}">
+ {% endif %}
+ {% if time_range %}
+ <input type="hidden" name="time_range" value="{{ time_range }}">
+ {% endif %}
+ <input type="hidden" name="q" value="{{ correction.url }}">{{- "" -}}
+ <button type="submit" class="btn btn-default btn-xs">{{ correction.title }}</button>{{- "" -}}
+ </form>
+ {% endfor %}
+ </div>
</div>
{%- endif %}
@@ -126,13 +140,13 @@
{% if rtl %}
<div id="pagination">
<div class="pull-left">{{- "" -}}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" class="pull-left">
{{- search_form_attrs(pageno+1) -}}
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-backward"></span> {{ _('next page') }}</button>{{- "" -}}
</form>{{- "" -}}
</div>
<div class="pull-right">{{- "" -}}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" class="pull-left">
{{- search_form_attrs(pageno-1) -}}
<button type="submit" class="btn btn-default" {% if pageno == 1 %}disabled{% endif %}><span class="glyphicon glyphicon-forward"></span> {{ _('previous page') }}</button>{{- "" -}}
</form>{{- "" -}}
@@ -142,13 +156,13 @@
{% else %}
<div id="pagination">
<div class="pull-left">{{- "" -}}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" class="pull-left">
{{- search_form_attrs(pageno-1) -}}
<button type="submit" class="btn btn-default" {% if pageno == 1 %}disabled{% endif %}><span class="glyphicon glyphicon-backward"></span> {{ _('previous page') }}</button>{{- "" -}}
</form>{{- "" -}}
</div>
<div class="pull-right">{{- "" -}}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" class="pull-left">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" class="pull-left">
{{- search_form_attrs(pageno+1) -}}
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-forward"></span> {{ _('next page') }}</button>{{- "" -}}
</form>{{- "" -}}
diff --git a/searx/templates/oscar/search.html b/searx/templates/oscar/search.html
index 666a4df..2b3758e 100644
--- a/searx/templates/oscar/search.html
+++ b/searx/templates/oscar/search.html
@@ -1,12 +1,12 @@
{% from 'oscar/macros.html' import icon %}
-<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form" role="search">
+<form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" id="search_form" role="search">
<div class="row">
<div class="col-xs-12 col-md-8">
<div class="input-group search-margin">
<input type="search" autofocus name="q" class="form-control" id="q" placeholder="{{ _('Search for...') }}" aria-label="{{ _('Search for...') }}" autocomplete="off" value="{{ q }}" accesskey="s">
<span class="input-group-btn">
<button type="submit" class="btn btn-default" aria-label="{{ _('Start search') }}"><span class="hide_if_nojs">{{ icon('search') }}</span><span class="hidden active_if_nojs">{{ _('Start search') }}</span></button>
- <button type="reset" class="btn btn-default" aria-label="{{ _('Clear search') }}"><span class="hide_if_nojs">{{ icon('remove') }}</span><span class="hidden active_if_nojs">{{ _('Clear') }}</span></button>
+ <button type="button" id="clear_search" class="btn btn-default hide_if_nojs" aria-label="{{ _('Clear search') }}">{{ icon('remove') }}</button>
</span>
</div>
</div>
diff --git a/searx/templates/oscar/search_full.html b/searx/templates/oscar/search_full.html
index 1f1c50e..d398230 100644
--- a/searx/templates/oscar/search_full.html
+++ b/searx/templates/oscar/search_full.html
@@ -1,6 +1,6 @@
{% from 'oscar/macros.html' import icon %}
-<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form" role="search">
+<form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" id="search_form" role="search">
{% if rtl %}
<div class="input-group">
{% else %}
diff --git a/searx/templates/oscar/time-range.html b/searx/templates/oscar/time-range.html
index 181b576..6087dd4 100644
--- a/searx/templates/oscar/time-range.html
+++ b/searx/templates/oscar/time-range.html
@@ -1,5 +1,6 @@
+{% from 'oscar/macros.html' import custom_select_class %}
<label class="visually-hidden" for="time-range">{{ _('Time range') }}</label>
-<select name="time_range" id="time-range" class="custom-select form-control" accesskey="t">{{- "" -}}
+<select name="time_range" id="time-range" class="{{ custom_select_class(rtl) }} form-control" accesskey="t">{{- "" -}}
<option id="time-range-anytime" value="" {{ "selected" if time_range=="" or not time_range else ""}}>
{{- _('Anytime') -}}
</option>{{- "" -}}
diff --git a/searx/templates/pix-art/preferences.html b/searx/templates/pix-art/preferences.html
index 05876de..ee41543 100644
--- a/searx/templates/pix-art/preferences.html
+++ b/searx/templates/pix-art/preferences.html
@@ -5,6 +5,7 @@
<h2>{{ _('Preferences') }}</h2>
<form method="post" action="{{ url_for('preferences') }}" id="search_form">
+ {% if 'language' not in locked_preferences %}
<fieldset>
<legend>{{ _('Search language') }}</legend>
<p>
@@ -16,6 +17,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'locale' not in locked_preferences %}
<fieldset>
<legend>{{ _('Interface language') }}</legend>
<p>
@@ -26,6 +29,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'method' not in locked_preferences %}
<fieldset>
<legend>{{ _('Method') }}</legend>
<p>
@@ -35,6 +40,8 @@
</select>
</p>
</fieldset>
+ {% endif %}
+ {% if 'theme' not in locked_preferences %}
<fieldset>
<legend>{{ _('Themes') }}</legend>
<p>
@@ -45,6 +52,7 @@
</select>
</p>
</fieldset>
+ {% endif %}
<fieldset>
<legend>{{ _('Currently used search engines') }}</legend>
diff --git a/searx/templates/pix-art/search.html b/searx/templates/pix-art/search.html
index bb40559..210913e 100644
--- a/searx/templates/pix-art/search.html
+++ b/searx/templates/pix-art/search.html
@@ -1,4 +1,4 @@
-<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form">
+<form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" id="search_form">
<div id="search_wrapper">
<input type="text" autofocus placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" size="100" {% if q %}value="{{ q }}"{% endif %}/>
<input type="submit" value="" id="search_submit" />
diff --git a/searx/templates/simple/404.html b/searx/templates/simple/404.html
index 11d6043..1a10514 100644
--- a/searx/templates/simple/404.html
+++ b/searx/templates/simple/404.html
@@ -3,7 +3,7 @@
<div class="center">
<h1>{{ _('Page not found') }}</h1>
{% autoescape false %}
- <p>{{ _('Go to %(search_page)s.', search_page=unicode('<a href="{}">{}</a>').format(url_for('index'), _('search page'))) }}</p>
+ <p>{{ _('Go to %(search_page)s.', search_page='<a href="{}">{}</a>'.format(url_for('index'), _('search page'))) }}</p>
{% endautoescape %}
</div>
{% endblock %}
diff --git a/searx/templates/simple/base.html b/searx/templates/simple/base.html
index 5cb1e17..10fb424 100644
--- a/searx/templates/simple/base.html
+++ b/searx/templates/simple/base.html
@@ -2,7 +2,7 @@
<html class="no-js" lang="en" {% if rtl %} dir="rtl"{% endif %}>
<head>
<meta charset="UTF-8" />
- <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine">
+ <meta name="description" content="searx — a privacy-respecting, hackable metasearch engine">
<meta name="keywords" content="searx, search, search engine, metasearch, meta search">
<meta name="generator" content="searx/{{ searx_version }}">
<meta name="referrer" content="no-referrer">
@@ -29,7 +29,7 @@
data-no-item-found="{{ _('No item found') }}"></script>
<!--<![endif]-->
{% block head %}
- <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ opensearch_url }}"/>
{% endblock %}
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}" />
</head>
@@ -51,7 +51,7 @@
</main>
<footer>
<p>
- {{ _('Powered by') }} <a href="{{ url_for('about') }}">searx</a> - {{ searx_version }} - {{ _('a privacy-respecting, hackable metasearch engine') }}<br/>
+ {{ _('Powered by') }} <a href="{{ url_for('about') }}">searx</a> - {{ searx_version }} — {{ _('a privacy-respecting, hackable metasearch engine') }}<br/>
<a href="{{ brand.GIT_URL }}">{{ _('Source code') }}</a> |
<a href="{{ brand.ISSUE_URL }}">{{ _('Issue tracker') }}</a> |
<a href="{{ brand.PUBLIC_INSTANCES }}">{{ _('Public instances') }}</a>
diff --git a/searx/templates/simple/infobox.html b/searx/templates/simple/infobox.html
index 50b5689..56c51af 100644
--- a/searx/templates/simple/infobox.html
+++ b/searx/templates/simple/infobox.html
@@ -1,7 +1,6 @@
<aside class="infobox">
<h2><bdi>{{ infobox.infobox }}</bdi></h2>
{% if infobox.img_src %}<img src="{{ image_proxify(infobox.img_src) }}" title="{{ infobox.infobox|striptags }}" alt="{{ infobox.infobox|striptags }}" />{% endif %}
- <p><bdi>{{ infobox.entity }}</bdi></p>
<p><bdi>{{ infobox.content | safe }}</bdi></p>
{% if infobox.attributes %}
<div class="attributes">
@@ -34,7 +33,7 @@
<div>
<h3><bdi>{{ topic.name }}</bdi></h3>
{% for suggestion in topic.suggestions %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<input type="hidden" name="q" value="{{ suggestion }}">
<input type="hidden" name="time_range" value="{{ time_range }}">
<input type="hidden" name="language" value="{{ current_language }}">
diff --git a/searx/templates/simple/macros.html b/searx/templates/simple/macros.html
index cacbbec..1eb4266 100644
--- a/searx/templates/simple/macros.html
+++ b/searx/templates/simple/macros.html
@@ -1,6 +1,6 @@
<!-- Draw glyphicon icon from bootstrap-theme -->
-{% macro icon(action) -%}
- <span class="ion-icon-big ion-{{ action }}"></span>
+{% macro icon(action, alt) -%}
+ <span title="{{ alt }}" class="ion-icon-big ion-{{ action }}"></span>
{%- endmacro %}
{% macro icon_small(action) -%}
diff --git a/searx/templates/simple/preferences.html b/searx/templates/simple/preferences.html
index 7437ed4..f091a97 100644
--- a/searx/templates/simple/preferences.html
+++ b/searx/templates/simple/preferences.html
@@ -1,4 +1,4 @@
-{% from 'simple/macros.html' import tabs_open, tabs_close, tab_header, tab_footer, checkbox_onoff, checkbox %}
+{% from 'simple/macros.html' import icon, tabs_open, tabs_close, tab_header, tab_footer, checkbox_onoff, checkbox %}
{% extends "simple/base.html" %}
@@ -30,11 +30,14 @@
{{ tabs_open() }}
{{ tab_header('maintab', 'general', _('General')) }}
+ {% if 'categories' not in locked_preferences %}
<fieldset>
<legend>{{ _('Default categories') }}</legend>
{% set display_tooltip = false %}
{% include 'simple/categories.html' %}
</fieldset>
+ {% endif %}
+ {% if 'language' not in locked_preferences %}
<fieldset>
<legend>{{ _('Search language') }}</legend>
<p class="value">{{- '' -}}
@@ -47,6 +50,8 @@
</p>
<div class="description">{{ _('What language do you prefer for search?') }}</div>
</fieldset>
+ {% endif %}
+ {% if 'autocomplete' not in locked_preferences %}
<fieldset>
<legend>{{ _('Autocomplete') }}</legend>
<p class="value">
@@ -59,6 +64,8 @@
</p>
<div class="description">{{ _('Find stuff as you type') }}</div>
</fieldset>
+ {% endif %}
+ {% if 'safesearch' not in locked_preferences %}
<fieldset>
<legend>{{ _('SafeSearch') }}</legend>
<p class="value">
@@ -70,7 +77,9 @@
</p>
<p class="description">{{ _('Filter content') }}</p>
</fieldset>
+ {% endif %}
{{ plugin_preferences('general') }}
+ {% if 'doi_resolver' not in locked_preferences %}
<fieldset>
<legend>{{ _('Open Access DOI resolver') }}</legend>
<p class="value">
@@ -84,6 +93,7 @@
</p>
<div class="description"><!-- {{ _('Redirect to open-access versions of publications when available (plugin required)') }} --></div>
</fieldset>
+ {% endif %}
{{ tab_footer() }}
{{ tab_header('maintab', 'engines', _('Engines')) }}
@@ -111,7 +121,7 @@
{% set engine_id = 'engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_') %}
<tr>
<td class="engine_checkbox">{{ checkbox_onoff(engine_id, (search_engine.name, categ) in disabled_engines) }}</td>
- <th class="name">{{ search_engine.name }}</th>
+ <th class="name">{% if not search_engine.https_support %}{{ icon('warning', 'No HTTPS') }}{% endif %} {{ search_engine.name }}</th>
<td class="shortcut">{{ shortcuts[search_engine.name] }}</td>
<td>{{ checkbox(engine_id + '_supported_languages', current_language == 'all' or current_language in search_engine.supported_languages or current_language.split('-')[0] in search_engine.supported_languages, true, true) }}</td>
<td>{{ checkbox(engine_id + '_safesearch', search_engine.safesearch==True, true, true) }}</td>
@@ -129,6 +139,7 @@
{{ tab_footer() }}
{{ tab_header('maintab', 'ui', _('User interface')) }}
+ {% if 'locale' not in locked_preferences %}
<fieldset>
<legend>{{ _('Interface language') }}</legend>
<p class="value">
@@ -140,6 +151,8 @@
</p>
<div class="description">{{ _('Change the language of the layout') }}</div>
</fieldset>
+ {% endif %}
+ {% if 'theme' not in locked_preferences %}
<fieldset>
<legend>{{ _('Themes') }}</legend>
<p class="value">
@@ -151,6 +164,8 @@
</p>
<div class="description">{{ _('Change searx layout') }}</div>
</fieldset>
+ {% endif %}
+ {% if 'results_on_new_tab' not in locked_preferences %}
<fieldset>
<legend>{{ _('Results on new tabs') }}</legend>
<p class="value">
@@ -161,6 +176,7 @@
</p>
<div class="description">{{_('Open result links on new browser tabs') }}</div>
</fieldset>
+ {% endif %}
{{ plugin_preferences('ui') }}
{{ tab_footer() }}
@@ -197,6 +213,7 @@
{{ tab_footer() }}
{{ tab_header('maintab', 'privacy', _('Privacy')) }}
+ {% if 'method' not in locked_preferences %}
<fieldset>
<legend>{{ _('Method') }}</legend>
<p class="value">
@@ -207,6 +224,8 @@
</p>
<div class="description">{{ _('Search language') }}</div>
</fieldset>
+ {% endif %}
+ {% if 'image_proxy' not in locked_preferences %}
<fieldset>
<legend>{{ _('Image proxy') }}</legend>
<p class="value">
@@ -217,6 +236,7 @@
</p>
<div class="description">{{ _('Proxying image results through searx') }}</div>
</fieldset>
+ {% endif %}
{{ plugin_preferences('privacy') }}
{{ tab_footer() }}
diff --git a/searx/templates/simple/results.html b/searx/templates/simple/results.html
index 2e39319..936de88 100644
--- a/searx/templates/simple/results.html
+++ b/searx/templates/simple/results.html
@@ -1,7 +1,7 @@
{% extends "simple/base.html" %}
{% from 'simple/macros.html' import icon, icon_small %}
{% block title %}{% if method == 'GET' %}{{- q|e -}} -{% endif %}{% endblock %}
-{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}&amp;pageno={{ pageno }}&amp;time_range={{ time_range }}&amp;language={{ current_language }}&amp;safesearch={{ safesearch }}&amp;format=rss">{% endblock %}
+{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q|e }}" href="{{ url_for('search', _external=True) }}?q={{ q|urlencode }}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}&amp;pageno={{ pageno }}&amp;time_range={{ time_range }}&amp;language={{ current_language }}&amp;safesearch={{ safesearch }}&amp;format=rss">{% endblock %}
{% block content %}
<nav id="linkto_preferences"><a href="{{ url_for('preferences') }}">{{ icon('navicon-round') }}</a></nav>
{% include 'simple/search.html' %}
@@ -55,7 +55,7 @@
<h4 class="title">{{ _('Suggestions') }} : </h4>
<div class="wrapper">
{% for suggestion in suggestions %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<input type="hidden" name="q" value="{{ suggestion.url }}">
<input type="hidden" name="time_range" value="{{ time_range }}">
<input type="hidden" name="language" value="{{ current_language }}">
@@ -71,13 +71,13 @@
<div id="search_url">
<h4 class="title">{{ _('Search URL') }} :</h4>
- <div class="selectable_url"><pre>{{ base_url }}?q={{ q|urlencode }}&amp;language={{ current_language }}&amp;time_range={{ time_range }}&amp;safesearch={{ safesearch }}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if timeout_limit %}&amp;timeout_limit={{ timeout_limit|urlencode }}{% endif %}</pre></div>
+ <div class="selectable_url"><pre>{{ url_for('search', _external=True) }}?q={{ q|urlencode }}&amp;language={{ current_language }}&amp;time_range={{ time_range }}&amp;safesearch={{ safesearch }}{% if pageno > 1 %}&amp;pageno={{ pageno }}{% endif %}{% if selected_categories %}&amp;categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if timeout_limit %}&amp;timeout_limit={{ timeout_limit|urlencode }}{% endif %}</pre></div>
</div>
<div id="apis">
<h4 class="title">{{ _('Download results') }}</h4>
{% for output_type in ('csv', 'json', 'rss') %}
<div class="left">
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<input type="hidden" name="q" value="{{ q|e }}">
{% for category in selected_categories %}
<input type="hidden" name="category_{{ category }}" value="1">
@@ -100,7 +100,7 @@
<h4>{{ _('Try searching for:') }}</h4>
{% for correction in corrections %}
<div class="left">
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" role="navigation">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}" role="navigation">
<input type="hidden" name="q" value="{{ correction.url }}">
<input type="hidden" name="time_range" value="{{ time_range }}">
<input type="hidden" name="language" value="{{ current_language }}">
@@ -133,7 +133,7 @@
{% if paging %}
<nav id="pagination">
{% if pageno > 1 %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div class="{% if rtl %}right{% else %}left{% endif %}">
<input type="hidden" name="q" value="{{ q|e }}" >
{% for category in selected_categories %}
@@ -149,7 +149,7 @@
</div>
</form>
{% endif %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <form method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div class="{% if rtl %}left{% else %}right{% endif %}">
<input type="hidden" name="q" value="{{ q|e }}" >
{% for category in selected_categories %}
diff --git a/searx/templates/simple/search.html b/searx/templates/simple/search.html
index 61d52db..176e790 100644
--- a/searx/templates/simple/search.html
+++ b/searx/templates/simple/search.html
@@ -1,4 +1,4 @@
-<form id="search" method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+<form id="search" method="{{ method or 'POST' }}" action="{{ url_for('search') }}">
<div id="search_wrapper">
<div class="search_box">
<input id="q" autofocus name="q" type="text" placeholder="{{ _('Search for...') }}" tabindex="1" autocomplete="off" spellcheck="false" dir="auto" {% if q %}value="{{ q }}"{% endif %} >
diff --git a/searx/testing.py b/searx/testing.py
index f0e303e..c529749 100644
--- a/searx/testing.py
+++ b/searx/testing.py
@@ -17,7 +17,7 @@ from unittest2 import TestCase
class SearxTestLayer:
"""Base layer for non-robot tests."""
- __name__ = u'SearxTestLayer'
+ __name__ = 'SearxTestLayer'
@classmethod
def setUp(cls):
@@ -66,7 +66,7 @@ class SearxRobotLayer():
stderr=subprocess.STDOUT
)
if hasattr(self.server.stdout, 'read1'):
- print(self.server.stdout.read1(1024).decode('utf-8'))
+ print(self.server.stdout.read1(1024).decode())
def tearDown(self):
os.kill(self.server.pid, 9)
diff --git a/searx/url_utils.py b/searx/url_utils.py
deleted file mode 100644
index dcafc3b..0000000
--- a/searx/url_utils.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from sys import version_info
-
-if version_info[0] == 2:
- from urllib import quote, quote_plus, unquote, urlencode
- from urlparse import parse_qs, parse_qsl, urljoin, urlparse, urlunparse, ParseResult
-else:
- from urllib.parse import (
- parse_qs,
- parse_qsl,
- quote,
- quote_plus,
- unquote,
- urlencode,
- urljoin,
- urlparse,
- urlunparse,
- ParseResult
- )
-
-
-__export__ = (parse_qs,
- parse_qsl,
- quote,
- quote_plus,
- unquote,
- urlencode,
- urljoin,
- urlparse,
- urlunparse,
- ParseResult)
diff --git a/searx/utils.py b/searx/utils.py
index 5ea9dc8..057e9d0 100644
--- a/searx/utils.py
+++ b/searx/utils.py
@@ -1,45 +1,26 @@
# -*- coding: utf-8 -*-
-import csv
-import hashlib
-import hmac
-import os
+import sys
import re
+import importlib
-from babel.core import get_global
-from babel.dates import format_date
-from codecs import getincrementalencoder
-from imp import load_source
from numbers import Number
from os.path import splitext, join
-from io import open
from random import choice
-from lxml.etree import XPath
-import sys
-import json
+from html.parser import HTMLParser
+from urllib.parse import urljoin, urlparse
+
+from lxml import html
+from lxml.etree import ElementBase, XPath, XPathError, XPathSyntaxError, _ElementStringResult, _ElementUnicodeResult
+from babel.core import get_global
+
from searx import settings
+from searx.data import USER_AGENTS
from searx.version import VERSION_STRING
from searx.languages import language_codes
-from searx import settings
+from searx.exceptions import SearxXPathSyntaxException, SearxEngineXPathException
from searx import logger
-try:
- from cStringIO import StringIO
-except:
- from io import StringIO
-
-try:
- from HTMLParser import HTMLParser
-except:
- from html.parser import HTMLParser
-
-if sys.version_info[0] == 3:
- unichr = chr
- unicode = str
- IS_PY2 = False
- basestring = str
-else:
- IS_PY2 = True
logger = logger.getChild('utils')
@@ -49,52 +30,37 @@ blocked_tags = ('script',
ecma_unescape4_re = re.compile(r'%u([0-9a-fA-F]{4})', re.UNICODE)
ecma_unescape2_re = re.compile(r'%([0-9a-fA-F]{2})', re.UNICODE)
-useragents = json.loads(open(os.path.dirname(os.path.realpath(__file__))
- + "/data/useragents.json", 'r', encoding='utf-8').read())
-
xpath_cache = dict()
lang_to_lc_cache = dict()
+class NotSetClass:
+ pass
+
+
+NOTSET = NotSetClass()
+
+
def searx_useragent():
+ """Return the searx User Agent"""
return 'searx/{searx_version} {suffix}'.format(
searx_version=VERSION_STRING,
suffix=settings['outgoing'].get('useragent_suffix', ''))
def gen_useragent(os=None):
- return str(useragents['ua'].format(os=os or choice(useragents['os']), version=choice(useragents['versions'])))
-
+ """Return a random browser User Agent
-def highlight_content(content, query):
+ See searx/data/useragents.json
+ """
+ return str(USER_AGENTS['ua'].format(os=os or choice(USER_AGENTS['os']), version=choice(USER_AGENTS['versions'])))
- if not content:
- return None
- # ignoring html contents
- # TODO better html content detection
- if content.find('<') != -1:
- return content
-
- query = query.decode('utf-8')
- if content.lower().find(query.lower()) > -1:
- query_regex = u'({0})'.format(re.escape(query))
- content = re.sub(query_regex, '<span class="highlight">\\1</span>',
- content, flags=re.I | re.U)
- else:
- regex_parts = []
- for chunk in query.split():
- if len(chunk) == 1:
- regex_parts.append(u'\\W+{0}\\W+'.format(re.escape(chunk)))
- else:
- regex_parts.append(u'{0}'.format(re.escape(chunk)))
- query_regex = u'({0})'.format('|'.join(regex_parts))
- content = re.sub(query_regex, '<span class="highlight">\\1</span>',
- content, flags=re.I | re.U)
- return content
+class HTMLTextExtractorException(Exception):
+ pass
-class HTMLTextExtractor(HTMLParser):
+class HTMLTextExtractor(HTMLParser): # pylint: disable=W0223 # (see https://bugs.python.org/issue31844)
def __init__(self):
HTMLParser.__init__(self)
@@ -109,140 +75,193 @@ class HTMLTextExtractor(HTMLParser):
return
if tag != self.tags[-1]:
- raise Exception("invalid html")
+ raise HTMLTextExtractorException()
self.tags.pop()
def is_valid_tag(self):
return not self.tags or self.tags[-1] not in blocked_tags
- def handle_data(self, d):
+ def handle_data(self, data):
if not self.is_valid_tag():
return
- self.result.append(d)
+ self.result.append(data)
- def handle_charref(self, number):
+ def handle_charref(self, name):
if not self.is_valid_tag():
return
- if number[0] in (u'x', u'X'):
- codepoint = int(number[1:], 16)
+ if name[0] in ('x', 'X'):
+ codepoint = int(name[1:], 16)
else:
- codepoint = int(number)
- self.result.append(unichr(codepoint))
+ codepoint = int(name)
+ self.result.append(chr(codepoint))
def handle_entityref(self, name):
if not self.is_valid_tag():
return
# codepoint = htmlentitydefs.name2codepoint[name]
- # self.result.append(unichr(codepoint))
+ # self.result.append(chr(codepoint))
self.result.append(name)
def get_text(self):
- return u''.join(self.result).strip()
+ return ''.join(self.result).strip()
-def html_to_text(html):
- html = html.replace('\n', ' ')
- html = ' '.join(html.split())
- s = HTMLTextExtractor()
- s.feed(html)
- return s.get_text()
+def html_to_text(html_str):
+ """Extract text from a HTML string
+ Args:
+ * html_str (str): string HTML
-class UnicodeWriter:
- """
- A CSV writer which will write rows to CSV file "f",
- which is encoded in the given encoding.
- """
-
- def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
- # Redirect output to a queue
- self.queue = StringIO()
- self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
- self.stream = f
- self.encoder = getincrementalencoder(encoding)()
-
- def writerow(self, row):
- if IS_PY2:
- row = [s.encode("utf-8") if hasattr(s, 'encode') else s for s in row]
- self.writer.writerow(row)
- # Fetch UTF-8 output from the queue ...
- data = self.queue.getvalue()
- if IS_PY2:
- data = data.decode("utf-8")
- else:
- data = data.strip('\x00')
- # ... and reencode it into the target encoding
- data = self.encoder.encode(data)
- # write to the target stream
- if IS_PY2:
- self.stream.write(data)
- else:
- self.stream.write(data.decode("utf-8"))
- # empty queue
- self.queue.truncate(0)
-
- def writerows(self, rows):
- for row in rows:
- self.writerow(row)
-
-
-def get_resources_directory(searx_directory, subdirectory, resources_directory):
- if not resources_directory:
- resources_directory = os.path.join(searx_directory, subdirectory)
- if not os.path.isdir(resources_directory):
- raise Exception(resources_directory + " is not a directory")
- return resources_directory
-
-
-def get_themes(templates_path):
- """Returns available themes list."""
- themes = os.listdir(templates_path)
- if '__common__' in themes:
- themes.remove('__common__')
- return themes
-
-
-def get_static_files(static_path):
- static_files = set()
- static_path_length = len(static_path) + 1
- for directory, _, files in os.walk(static_path):
- for filename in files:
- f = os.path.join(directory[static_path_length:], filename)
- static_files.add(f)
- return static_files
-
-
-def get_result_templates(templates_path):
- result_templates = set()
- templates_path_length = len(templates_path) + 1
- for directory, _, files in os.walk(templates_path):
- if directory.endswith('result_templates'):
- for filename in files:
- f = os.path.join(directory[templates_path_length:], filename)
- result_templates.add(f)
- return result_templates
+ Returns:
+ * str: extracted text
+ Examples:
+ >>> html_to_text('Example <span id="42">#2</span>')
+ 'Example #2'
-def format_date_by_locale(date, locale_string):
- # strftime works only on dates after 1900
+ >>> html_to_text('<style>.span { color: red; }</style><span>Example</span>')
+ 'Example'
+ """
+ html_str = html_str.replace('\n', ' ')
+ html_str = ' '.join(html_str.split())
+ s = HTMLTextExtractor()
+ try:
+ s.feed(html_str)
+ except HTMLTextExtractorException:
+ logger.debug("HTMLTextExtractor: invalid HTML\n%s", html_str)
+ return s.get_text()
- if date.year <= 1900:
- return date.isoformat().split('T')[0]
- if locale_string == 'all':
- locale_string = settings['ui']['default_locale'] or 'en_US'
+def extract_text(xpath_results, allow_none=False):
+ """Extract text from a lxml result
- # to avoid crashing if locale is not supported by babel
- try:
- formatted_date = format_date(date, locale=locale_string)
- except:
- formatted_date = format_date(date, "YYYY-MM-dd")
+ * if xpath_results is list, extract the text from each result and concat the list
+ * if xpath_results is a xml element, extract all the text node from it
+ ( text_content() method from lxml )
+ * if xpath_results is a string element, then it's already done
+ """
+ if isinstance(xpath_results, list):
+ # it's list of result : concat everything using recursive call
+ result = ''
+ for e in xpath_results:
+ result = result + extract_text(e)
+ return result.strip()
+ elif isinstance(xpath_results, ElementBase):
+ # it's a element
+ text = html.tostring(
+ xpath_results, encoding='unicode', method='text', with_tail=False
+ )
+ text = text.strip().replace('\n', ' ')
+ return ' '.join(text.split())
+ elif isinstance(xpath_results, (_ElementStringResult, _ElementUnicodeResult, str, Number, bool)):
+ return str(xpath_results)
+ elif xpath_results is None and allow_none:
+ return None
+ elif xpath_results is None and not allow_none:
+ raise ValueError('extract_text(None, allow_none=False)')
+ else:
+ raise ValueError('unsupported type')
+
+
+def normalize_url(url, base_url):
+ """Normalize URL: add protocol, join URL with base_url, add trailing slash if there is no path
+
+ Args:
+ * url (str): Relative URL
+ * base_url (str): Base URL, it must be an absolute URL.
+
+ Example:
+ >>> normalize_url('https://example.com', 'http://example.com/')
+ 'https://example.com/'
+ >>> normalize_url('//example.com', 'http://example.com/')
+ 'http://example.com/'
+ >>> normalize_url('//example.com', 'https://example.com/')
+ 'https://example.com/'
+ >>> normalize_url('/path?a=1', 'https://example.com')
+ 'https://example.com/path?a=1'
+ >>> normalize_url('', 'https://example.com')
+ 'https://example.com/'
+ >>> normalize_url('/test', '/path')
+ raise ValueError
+
+ Raises:
+ * lxml.etree.ParserError
+
+ Returns:
+ * str: normalized URL
+ """
+ if url.startswith('//'):
+ # add http or https to this kind of url //example.com/
+ parsed_search_url = urlparse(base_url)
+ url = '{0}:{1}'.format(parsed_search_url.scheme or 'http', url)
+ elif url.startswith('/'):
+ # fix relative url to the search engine
+ url = urljoin(base_url, url)
+
+ # fix relative urls that fall through the crack
+ if '://' not in url:
+ url = urljoin(base_url, url)
+
+ parsed_url = urlparse(url)
+
+ # add a / at this end of the url if there is no path
+ if not parsed_url.netloc:
+ raise ValueError('Cannot parse url')
+ if not parsed_url.path:
+ url += '/'
+
+ return url
+
+
+def extract_url(xpath_results, base_url):
+ """Extract and normalize URL from lxml Element
+
+ Args:
+ * xpath_results (Union[List[html.HtmlElement], html.HtmlElement]): lxml Element(s)
+ * base_url (str): Base URL
+
+ Example:
+ >>> def f(s, search_url):
+ >>> return searx.utils.extract_url(html.fromstring(s), search_url)
+ >>> f('<span id="42">https://example.com</span>', 'http://example.com/')
+ 'https://example.com/'
+ >>> f('https://example.com', 'http://example.com/')
+ 'https://example.com/'
+ >>> f('//example.com', 'http://example.com/')
+ 'http://example.com/'
+ >>> f('//example.com', 'https://example.com/')
+ 'https://example.com/'
+ >>> f('/path?a=1', 'https://example.com')
+ 'https://example.com/path?a=1'
+ >>> f('', 'https://example.com')
+ raise lxml.etree.ParserError
+ >>> searx.utils.extract_url([], 'https://example.com')
+ raise ValueError
+
+ Raises:
+ * ValueError
+ * lxml.etree.ParserError
+
+ Returns:
+ * str: normalized URL
+ """
+ if xpath_results == []:
+ raise ValueError('Empty url resultset')
- return formatted_date
+ url = extract_text(xpath_results)
+ return normalize_url(url, base_url)
def dict_subset(d, properties):
+ """Extract a subset of a dict
+
+ Examples:
+ >>> dict_subset({'A': 'a', 'B': 'b', 'C': 'c'}, ['A', 'C'])
+ {'A': 'a', 'C': 'c'}
+ >>> >> dict_subset({'A': 'a', 'B': 'b', 'C': 'c'}, ['A', 'D'])
+ {'A': 'a'}
+ """
result = {}
for k in properties:
if k in d:
@@ -250,23 +269,22 @@ def dict_subset(d, properties):
return result
-def prettify_url(url, max_length=74):
- if len(url) > max_length:
- chunk_len = int(max_length / 2 + 1)
- return u'{0}[...]{1}'.format(url[:chunk_len], url[-chunk_len:])
- else:
- return url
-
+def get_torrent_size(filesize, filesize_multiplier):
+ """
-# get element in list or default value
-def list_get(a_list, index, default=None):
- if len(a_list) > index:
- return a_list[index]
- else:
- return default
+ Args:
+ * filesize (str): size
+ * filesize_multiplier (str): TB, GB, .... TiB, GiB...
+ Returns:
+ * int: number of bytes
-def get_torrent_size(filesize, filesize_multiplier):
+ Example:
+ >>> get_torrent_size('5', 'GB')
+ 5368709120
+ >>> get_torrent_size('3.14', 'MiB')
+ 3140000
+ """
try:
filesize = float(filesize)
@@ -286,21 +304,25 @@ def get_torrent_size(filesize, filesize_multiplier):
filesize = int(filesize * 1000 * 1000)
elif filesize_multiplier == 'KiB':
filesize = int(filesize * 1000)
- except:
+ except ValueError:
filesize = None
return filesize
def convert_str_to_int(number_str):
+ """Convert number_str to int or 0 if number_str is not a number."""
if number_str.isdigit():
return int(number_str)
else:
return 0
-# convert a variable to integer or return 0 if it's not a number
def int_or_zero(num):
+ """Convert num to int or 0. num can be either a str or a list.
+ If num is a list, the first element is converted to int (or return 0 if the list is empty).
+ If num is a str, see convert_str_to_int
+ """
if isinstance(num, list):
if len(num) < 1:
return 0
@@ -309,8 +331,26 @@ def int_or_zero(num):
def is_valid_lang(lang):
+ """Return language code and name if lang describe a language.
+
+ Examples:
+ >>> is_valid_lang('zz')
+ False
+ >>> is_valid_lang('uk')
+ (True, 'uk', 'ukrainian')
+ >>> is_valid_lang(b'uk')
+ (True, 'uk', 'ukrainian')
+ >>> is_valid_lang('en')
+ (True, 'en', 'english')
+ >>> searx.utils.is_valid_lang('Español')
+ (True, 'es', 'spanish')
+ >>> searx.utils.is_valid_lang('Spanish')
+ (True, 'es', 'spanish')
+ """
+ if isinstance(lang, bytes):
+ lang = lang.decode()
is_abbr = (len(lang) == 2)
- lang = lang.lower().decode('utf-8')
+ lang = lang.lower()
if is_abbr:
for l in language_codes:
if l[0][:2] == lang:
@@ -334,8 +374,8 @@ def _get_lang_to_lc_dict(lang_list):
return value
-# auxiliary function to match lang_code in lang_list
-def _match_language(lang_code, lang_list=[], custom_aliases={}):
+def _match_language(lang_code, lang_list=[], custom_aliases={}): # pylint: disable=W0102
+ """auxiliary function to match lang_code in lang_list"""
# replace language code with a custom alias if necessary
if lang_code in custom_aliases:
lang_code = custom_aliases[lang_code]
@@ -357,8 +397,8 @@ def _match_language(lang_code, lang_list=[], custom_aliases={}):
return _get_lang_to_lc_dict(lang_list).get(lang_code, None)
-# get the language code from lang_list that best matches locale_code
-def match_language(locale_code, lang_list=[], custom_aliases={}, fallback='en-US'):
+def match_language(locale_code, lang_list=[], custom_aliases={}, fallback='en-US'): # pylint: disable=W0102
+ """get the language code from lang_list that best matches locale_code"""
# try to get language from given locale_code
language = _match_language(locale_code, lang_list, custom_aliases)
if language:
@@ -394,30 +434,20 @@ def load_module(filename, module_dir):
if modname in sys.modules:
del sys.modules[modname]
filepath = join(module_dir, filename)
- module = load_source(modname, filepath)
- module.name = modname
+ # and https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
+ spec = importlib.util.spec_from_file_location(modname, filepath)
+ module = importlib.util.module_from_spec(spec)
+ sys.modules[modname] = module
+ spec.loader.exec_module(module)
return module
-def new_hmac(secret_key, url):
- try:
- secret_key_bytes = bytes(secret_key, 'utf-8')
- except TypeError as err:
- if isinstance(secret_key, bytes):
- secret_key_bytes = secret_key
- else:
- raise err
- if sys.version_info[0] == 2:
- return hmac.new(bytes(secret_key), url, hashlib.sha256).hexdigest()
- else:
- return hmac.new(secret_key_bytes, url, hashlib.sha256).hexdigest()
-
-
def to_string(obj):
- if isinstance(obj, basestring):
+ """Convert obj to its string representation."""
+ if isinstance(obj, str):
return obj
if isinstance(obj, Number):
- return unicode(obj)
+ return str(obj)
if hasattr(obj, '__str__'):
return obj.__str__()
if hasattr(obj, '__repr__'):
@@ -425,20 +455,36 @@ def to_string(obj):
def ecma_unescape(s):
- """
- python implementation of the unescape javascript function
+ """Python implementation of the unescape javascript function
https://www.ecma-international.org/ecma-262/6.0/#sec-unescape-string
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/unescape
+
+ Examples:
+ >>> ecma_unescape('%u5409')
+ '吉'
+ >>> ecma_unescape('%20')
+ ' '
+ >>> ecma_unescape('%F3')
+ 'ó'
"""
- # s = unicode(s)
# "%u5409" becomes "吉"
- s = ecma_unescape4_re.sub(lambda e: unichr(int(e.group(1), 16)), s)
+ s = ecma_unescape4_re.sub(lambda e: chr(int(e.group(1), 16)), s)
# "%20" becomes " ", "%F3" becomes "ó"
- s = ecma_unescape2_re.sub(lambda e: unichr(int(e.group(1), 16)), s)
+ s = ecma_unescape2_re.sub(lambda e: chr(int(e.group(1), 16)), s)
return s
+def get_string_replaces_function(replaces):
+ rep = {re.escape(k): v for k, v in replaces.items()}
+ pattern = re.compile("|".join(rep.keys()))
+
+ def f(text):
+ return pattern.sub(lambda m: rep[re.escape(m.group(0))], text)
+
+ return f
+
+
def get_engine_from_settings(name):
"""Return engine configuration from settings.yml of a given engine name"""
@@ -454,14 +500,110 @@ def get_engine_from_settings(name):
return {}
-def get_xpath(xpath_str):
- result = xpath_cache.get(xpath_str, None)
- if result is None:
- result = XPath(xpath_str)
- xpath_cache[xpath_str] = result
+def get_xpath(xpath_spec):
+ """Return cached compiled XPath
+
+ There is no thread lock.
+ Worst case scenario, xpath_str is compiled more than one time.
+
+ Args:
+ * xpath_spec (str|lxml.etree.XPath): XPath as a str or lxml.etree.XPath
+
+ Returns:
+ * result (bool, float, list, str): Results.
+
+ Raises:
+ * TypeError: Raise when xpath_spec is neither a str nor a lxml.etree.XPath
+ * SearxXPathSyntaxException: Raise when there is a syntax error in the XPath
+ """
+ if isinstance(xpath_spec, str):
+ result = xpath_cache.get(xpath_spec, None)
+ if result is None:
+ try:
+ result = XPath(xpath_spec)
+ except XPathSyntaxError as e:
+ raise SearxXPathSyntaxException(xpath_spec, str(e.msg))
+ xpath_cache[xpath_spec] = result
+ return result
+
+ if isinstance(xpath_spec, XPath):
+ return xpath_spec
+
+ raise TypeError('xpath_spec must be either a str or a lxml.etree.XPath')
+
+
+def eval_xpath(element, xpath_spec):
+ """Equivalent of element.xpath(xpath_str) but compile xpath_str once for all.
+ See https://lxml.de/xpathxslt.html#xpath-return-values
+
+ Args:
+ * element (ElementBase): [description]
+ * xpath_spec (str|lxml.etree.XPath): XPath as a str or lxml.etree.XPath
+
+ Returns:
+ * result (bool, float, list, str): Results.
+
+ Raises:
+ * TypeError: Raise when xpath_spec is neither a str nor a lxml.etree.XPath
+ * SearxXPathSyntaxException: Raise when there is a syntax error in the XPath
+ * SearxEngineXPathException: Raise when the XPath can't be evaluated.
+ """
+ xpath = get_xpath(xpath_spec)
+ try:
+ return xpath(element)
+ except XPathError as e:
+ arg = ' '.join([str(i) for i in e.args])
+ raise SearxEngineXPathException(xpath_spec, arg)
+
+
+def eval_xpath_list(element, xpath_spec, min_len=None):
+ """Same as eval_xpath, check if the result is a list
+
+ Args:
+ * element (ElementBase): [description]
+ * xpath_spec (str|lxml.etree.XPath): XPath as a str or lxml.etree.XPath
+ * min_len (int, optional): [description]. Defaults to None.
+
+ Raises:
+ * TypeError: Raise when xpath_spec is neither a str nor a lxml.etree.XPath
+ * SearxXPathSyntaxException: Raise when there is a syntax error in the XPath
+ * SearxEngineXPathException: raise if the result is not a list
+
+ Returns:
+ * result (bool, float, list, str): Results.
+ """
+ result = eval_xpath(element, xpath_spec)
+ if not isinstance(result, list):
+ raise SearxEngineXPathException(xpath_spec, 'the result is not a list')
+ if min_len is not None and min_len > len(result):
+ raise SearxEngineXPathException(xpath_spec, 'len(xpath_str) < ' + str(min_len))
return result
-def eval_xpath(element, xpath_str):
- xpath = get_xpath(xpath_str)
- return xpath(element)
+def eval_xpath_getindex(elements, xpath_spec, index, default=NOTSET):
+ """Call eval_xpath_list then get one element using the index parameter.
+ If the index does not exist, either aise an exception is default is not set,
+ other return the default value (can be None).
+
+ Args:
+ * elements (ElementBase): lxml element to apply the xpath.
+ * xpath_spec (str|lxml.etree.XPath): XPath as a str or lxml.etree.XPath.
+ * index (int): index to get
+ * default (Object, optional): Defaults if index doesn't exist.
+
+ Raises:
+ * TypeError: Raise when xpath_spec is neither a str nor a lxml.etree.XPath
+ * SearxXPathSyntaxException: Raise when there is a syntax error in the XPath
+ * SearxEngineXPathException: if the index is not found. Also see eval_xpath.
+
+ Returns:
+ * result (bool, float, list, str): Results.
+ """
+ result = eval_xpath_list(elements, xpath_spec)
+ if index >= -len(result) and index < len(result):
+ return result[index]
+ if default == NOTSET:
+ # raise an SearxEngineXPathException instead of IndexError
+ # to record xpath_spec
+ raise SearxEngineXPathException(xpath_spec, 'index ' + str(index) + ' not found')
+ return default
diff --git a/searx/version.py b/searx/version.py
index 75bb3c4..d9bf907 100644
--- a/searx/version.py
+++ b/searx/version.py
@@ -18,7 +18,7 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
# version of searx
VERSION_MAJOR = 0
-VERSION_MINOR = 17
+VERSION_MINOR = 18
VERSION_BUILD = 0
VERSION_STRING = "{0}.{1}.{2}".format(VERSION_MAJOR,
diff --git a/searx/webadapter.py b/searx/webadapter.py
new file mode 100644
index 0000000..7c71b72
--- /dev/null
+++ b/searx/webadapter.py
@@ -0,0 +1,255 @@
+from typing import Dict, List, Optional, Tuple
+from searx.exceptions import SearxParameterException
+from searx.webutils import VALID_LANGUAGE_CODE
+from searx.query import RawTextQuery
+from searx.engines import categories, engines
+from searx.search import SearchQuery, EngineRef
+from searx.preferences import Preferences, is_locked
+
+
+# remove duplicate queries.
+# FIXME: does not fix "!music !soundcloud", because the categories are 'none' and 'music'
+def deduplicate_engineref_list(engineref_list: List[EngineRef]) -> List[EngineRef]:
+ engineref_dict = {q.category + '|' + q.name: q for q in engineref_list}
+ return list(engineref_dict.values())
+
+
+def validate_engineref_list(engineref_list: List[EngineRef], preferences: Preferences)\
+ -> Tuple[List[EngineRef], List[EngineRef], List[EngineRef]]:
+ """Validate query_engines according to the preferences
+
+ Returns:
+ List[EngineRef]: list of existing engines with a validated token
+ List[EngineRef]: list of unknown engine
+ List[EngineRef]: list of engine with invalid token according to the preferences
+ """
+ valid = []
+ unknown = []
+ no_token = []
+ for engineref in engineref_list:
+ if engineref.name not in engines:
+ unknown.append(engineref)
+ continue
+
+ engine = engines[engineref.name]
+ if not preferences.validate_token(engine):
+ no_token.append(engineref)
+ continue
+
+ valid.append(engineref)
+ return valid, unknown, no_token
+
+
+def parse_pageno(form: Dict[str, str]) -> int:
+ pageno_param = form.get('pageno', '1')
+ if not pageno_param.isdigit() or int(pageno_param) < 1:
+ raise SearxParameterException('pageno', pageno_param)
+ return int(pageno_param)
+
+
+def parse_lang(preferences: Preferences, form: Dict[str, str], raw_text_query: RawTextQuery) -> str:
+ if is_locked('language'):
+ return preferences.get_value('langueage')
+ # get language
+ # set specific language if set on request, query or preferences
+ # TODO support search with multible languages
+ if len(raw_text_query.languages):
+ query_lang = raw_text_query.languages[-1]
+ elif 'language' in form:
+ query_lang = form.get('language')
+ else:
+ query_lang = preferences.get_value('language')
+
+ # check language
+ if not VALID_LANGUAGE_CODE.match(query_lang):
+ raise SearxParameterException('language', query_lang)
+
+ return query_lang
+
+
+def parse_safesearch(preferences: Preferences, form: Dict[str, str]) -> int:
+ if is_locked('safesearch'):
+ return preferences.get_value('safesearch')
+
+ if 'safesearch' in form:
+ query_safesearch = form.get('safesearch')
+ # first check safesearch
+ if not query_safesearch.isdigit():
+ raise SearxParameterException('safesearch', query_safesearch)
+ query_safesearch = int(query_safesearch)
+ else:
+ query_safesearch = preferences.get_value('safesearch')
+
+ # safesearch : second check
+ if query_safesearch < 0 or query_safesearch > 2:
+ raise SearxParameterException('safesearch', query_safesearch)
+
+ return query_safesearch
+
+
+def parse_time_range(form: Dict[str, str]) -> Optional[str]:
+ query_time_range = form.get('time_range')
+ # check time_range
+ query_time_range = None if query_time_range in ('', 'None') else query_time_range
+ if query_time_range not in (None, 'day', 'week', 'month', 'year'):
+ raise SearxParameterException('time_range', query_time_range)
+ return query_time_range
+
+
+def parse_timeout(form: Dict[str, str], raw_text_query: RawTextQuery) -> Optional[float]:
+ timeout_limit = raw_text_query.timeout_limit
+ if timeout_limit is None:
+ timeout_limit = form.get('timeout_limit')
+
+ if timeout_limit is None or timeout_limit in ['None', '']:
+ return None
+ try:
+ return float(timeout_limit)
+ except ValueError:
+ raise SearxParameterException('timeout_limit', timeout_limit)
+
+
+def parse_specific(raw_text_query: RawTextQuery) -> Tuple[List[EngineRef], List[str]]:
+ query_engineref_list = raw_text_query.enginerefs
+ additional_categories = set()
+ for engineref in raw_text_query.enginerefs:
+ if engineref.from_bang:
+ additional_categories.add('none')
+ else:
+ additional_categories.add(engineref.category)
+ query_categories = list(additional_categories)
+ return query_engineref_list, query_categories
+
+
+def parse_category_form(query_categories: List[str], name: str, value: str) -> None:
+ if name == 'categories':
+ query_categories.extend(categ for categ in map(str.strip, value.split(',')) if categ in categories)
+ elif name.startswith('category_'):
+ category = name[9:]
+
+ # if category is not found in list, skip
+ if category not in categories:
+ return
+
+ if value != 'off':
+ # add category to list
+ query_categories.append(category)
+ elif category in query_categories:
+ # remove category from list if property is set to 'off'
+ query_categories.remove(category)
+
+
+def get_selected_categories(preferences: Preferences, form: Optional[Dict[str, str]]) -> List[str]:
+ selected_categories = []
+
+ if not is_locked('categories') and form is not None:
+ for name, value in form.items():
+ parse_category_form(selected_categories, name, value)
+
+ # if no category is specified for this search,
+ # using user-defined default-configuration which
+ # (is stored in cookie)
+ if not selected_categories:
+ cookie_categories = preferences.get_value('categories')
+ for ccateg in cookie_categories:
+ selected_categories.append(ccateg)
+
+ # if still no category is specified, using general
+ # as default-category
+ if not selected_categories:
+ selected_categories = ['general']
+
+ return selected_categories
+
+
+def get_engineref_from_category_list(category_list: List[str], disabled_engines: List[str]) -> List[EngineRef]:
+ result = []
+ for categ in category_list:
+ result.extend(EngineRef(engine.name, categ)
+ for engine in categories[categ]
+ if (engine.name, categ) not in disabled_engines)
+ return result
+
+
+def parse_generic(preferences: Preferences, form: Dict[str, str], disabled_engines: List[str])\
+ -> Tuple[List[EngineRef], List[str]]:
+ query_engineref_list = []
+ query_categories = []
+
+ # set categories/engines
+ explicit_engine_list = False
+ if not is_locked('categories'):
+ # parse the form only if the categories are not locked
+ for pd_name, pd in form.items():
+ if pd_name == 'engines':
+ pd_engines = [EngineRef(engine_name, engines[engine_name].categories[0])
+ for engine_name in map(str.strip, pd.split(',')) if engine_name in engines]
+ if pd_engines:
+ query_engineref_list.extend(pd_engines)
+ explicit_engine_list = True
+ else:
+ parse_category_form(query_categories, pd_name, pd)
+
+ if explicit_engine_list:
+ # explicit list of engines with the "engines" parameter in the form
+ if query_categories:
+ # add engines from referenced by the "categories" parameter and the "category_*"" parameters
+ query_engineref_list.extend(get_engineref_from_category_list(query_categories, disabled_engines))
+ # get categories from the query_engineref_list
+ query_categories = list(set(engine.category for engine in query_engineref_list))
+ else:
+ # no "engines" parameters in the form
+ if not query_categories:
+ # and neither "categories" parameter nor "category_*"" parameters in the form
+ # -> get the categories from the preferences (the cookies or the settings)
+ query_categories = get_selected_categories(preferences, None)
+
+ # using all engines for that search, which are
+ # declared under the specific categories
+ query_engineref_list.extend(get_engineref_from_category_list(query_categories, disabled_engines))
+
+ return query_engineref_list, query_categories
+
+
+def get_search_query_from_webapp(preferences: Preferences, form: Dict[str, str])\
+ -> Tuple[SearchQuery, RawTextQuery, List[EngineRef], List[EngineRef]]:
+ # no text for the query ?
+ if not form.get('q'):
+ raise SearxParameterException('q', '')
+
+ # set blocked engines
+ disabled_engines = preferences.engines.get_disabled()
+
+ # parse query, if tags are set, which change
+ # the serch engine or search-language
+ raw_text_query = RawTextQuery(form['q'], disabled_engines)
+
+ # set query
+ query = raw_text_query.getQuery()
+ query_pageno = parse_pageno(form)
+ query_lang = parse_lang(preferences, form, raw_text_query)
+ query_safesearch = parse_safesearch(preferences, form)
+ query_time_range = parse_time_range(form)
+ query_timeout = parse_timeout(form, raw_text_query)
+ external_bang = raw_text_query.external_bang
+
+ if not is_locked('categories') and raw_text_query.enginerefs and raw_text_query.specific:
+ # if engines are calculated from query,
+ # set categories by using that informations
+ query_engineref_list, query_categories = parse_specific(raw_text_query)
+ else:
+ # otherwise, using defined categories to
+ # calculate which engines should be used
+ query_engineref_list, query_categories = parse_generic(preferences, form, disabled_engines)
+
+ query_engineref_list = deduplicate_engineref_list(query_engineref_list)
+ query_engineref_list, query_engineref_list_unknown, query_engineref_list_notoken =\
+ validate_engineref_list(query_engineref_list, preferences)
+
+ return (SearchQuery(query, query_engineref_list, query_categories,
+ query_lang, query_safesearch, query_pageno,
+ query_time_range, query_timeout,
+ external_bang=external_bang),
+ raw_text_query,
+ query_engineref_list_unknown,
+ query_engineref_list_notoken)
diff --git a/searx/webapp.py b/searx/webapp.py
index 4b52c0c..ace5a12 100755
--- a/searx/webapp.py
+++ b/searx/webapp.py
@@ -17,37 +17,35 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2013- by Adam Tauber, <asciimoo@gmail.com>
'''
+import sys
+if sys.version_info[0] < 3:
+ print('\033[1;31m Python2 is no longer supported\033[0m')
+ exit(1)
+
if __name__ == '__main__':
- from sys import path
from os.path import realpath, dirname
- path.append(realpath(dirname(realpath(__file__)) + '/../'))
+ sys.path.append(realpath(dirname(realpath(__file__)) + '/../'))
import hashlib
import hmac
import json
import os
-import sys
import requests
from searx import logger
logger = logger.getChild('webapp')
-try:
- from pygments import highlight
- from pygments.lexers import get_lexer_by_name
- from pygments.formatters import HtmlFormatter
-except:
- logger.critical("cannot import dependency: pygments")
- from sys import exit
- exit(1)
-try:
- from cgi import escape
-except:
- from html import escape
-from six import next
from datetime import datetime, timedelta
from time import time
+from html import escape
+from io import StringIO
+from urllib.parse import urlencode, urljoin, urlparse
+
+from pygments import highlight
+from pygments.lexers import get_lexer_by_name
+from pygments.formatters import HtmlFormatter # pylint: disable=no-name-in-module
+
from werkzeug.middleware.proxy_fix import ProxyFix
from flask import (
Flask, request, render_template, url_for, Response, make_response,
@@ -58,49 +56,31 @@ import flask_babel
from flask_babel import Babel, gettext, format_date, format_decimal
from flask.ctx import has_request_context
from flask.json import jsonify
-from searx import brand
+from searx import brand, static_path
from searx import settings, searx_dir, searx_debug
from searx.exceptions import SearxParameterException
from searx.engines import (
categories, engines, engine_shortcuts, get_engines_stats, initialize_engines
)
-from searx.utils import (
- UnicodeWriter, highlight_content, html_to_text, get_resources_directory,
- get_static_files, get_result_templates, get_themes, gen_useragent,
- dict_subset, prettify_url, match_language
+from searx.webutils import (
+ UnicodeWriter, highlight_content, get_resources_directory,
+ get_static_files, get_result_templates, get_themes,
+ prettify_url, new_hmac, is_flask_run_cmdline
)
+from searx.webadapter import get_search_query_from_webapp, get_selected_categories
+from searx.utils import html_to_text, gen_useragent, dict_subset, match_language
from searx.version import VERSION_STRING
from searx.languages import language_codes as languages
-from searx.search import SearchWithPlugins, get_search_query_from_webapp
+from searx.search import SearchWithPlugins
from searx.query import RawTextQuery
from searx.autocomplete import searx_bang, backends as autocomplete_backends
from searx.plugins import plugins
from searx.plugins.oa_doi_rewrite import get_doi_resolver
from searx.preferences import Preferences, ValidationException, LANGUAGE_CODES
from searx.answerers import answerers
-from searx.url_utils import urlencode, urlparse, urljoin
-from searx.utils import new_hmac
-
-# check if the pyopenssl package is installed.
-# It is needed for SSL connection without trouble, see #298
-try:
- import OpenSSL.SSL # NOQA
-except ImportError:
- logger.critical("The pyopenssl package has to be installed.\n"
- "Some HTTPS connections will fail")
-
-try:
- from cStringIO import StringIO
-except:
- from io import StringIO
-
-
-if sys.version_info[0] == 3:
- unicode = str
- PY3 = True
-else:
- logger.warning('\033[1;31m Python2 is no longer supported\033[0m')
- exit(1)
+from searx.poolrequests import get_global_proxies
+from searx.metrology.error_recorder import errors_per_engines
+
# serve pages with HTTP/1.1
from werkzeug.serving import WSGIRequestHandler
@@ -133,12 +113,24 @@ app = Flask(
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
-app.jinja_env.add_extension('jinja2.ext.loopcontrols')
+app.jinja_env.add_extension('jinja2.ext.loopcontrols') # pylint: disable=no-member
app.secret_key = settings['server']['secret_key']
-if not searx_debug \
- or os.environ.get("WERKZEUG_RUN_MAIN") == "true" \
- or os.environ.get('UWSGI_ORIGINAL_PROC_NAME') is not None:
+# see https://flask.palletsprojects.com/en/1.1.x/cli/
+# True if "FLASK_APP=searx/webapp.py FLASK_ENV=development flask run"
+flask_run_development = \
+ os.environ.get("FLASK_APP") is not None\
+ and os.environ.get("FLASK_ENV") == 'development'\
+ and is_flask_run_cmdline()
+
+# True if reload feature is activated of werkzeug, False otherwise (including uwsgi, etc..)
+# __name__ != "__main__" if searx.webapp is imported (make test, make docs, uwsgi...)
+# see run() at the end of this file : searx_debug activates the reload feature.
+werkzeug_reloader = flask_run_development or (searx_debug and __name__ == "__main__")
+
+# initialize the engines except on the first run of the werkzeug server.
+if not werkzeug_reloader\
+ or (werkzeug_reloader and os.environ.get("WERKZEUG_RUN_MAIN") == "true"):
initialize_engines(settings['engines'])
babel = Babel(app)
@@ -156,10 +148,9 @@ _category_names = (gettext('files'),
gettext('it'),
gettext('news'),
gettext('map'),
+ gettext('onions'),
gettext('science'))
-outgoing_proxies = settings['outgoing'].get('proxies') or None
-
_flask_babel_get_translations = flask_babel.get_translations
@@ -175,7 +166,7 @@ def _get_translations():
flask_babel.get_translations = _get_translations
-def _get_browser_language(request, lang_list):
+def _get_browser_or_settings_language(request, lang_list):
for lang in request.headers.get("Accept-Language", "en").split(","):
if ';' in lang:
lang = lang.split(';')[0]
@@ -187,26 +178,31 @@ def _get_browser_language(request, lang_list):
@babel.localeselector
def get_locale():
- locale = _get_browser_language(request, settings['locales'].keys())
-
- logger.debug("default locale from browser info is `%s`", locale)
-
- if request.preferences.get_value('locale') != '':
- locale = request.preferences.get_value('locale')
-
if 'locale' in request.form\
and request.form['locale'] in settings['locales']:
+ # use locale from the form
locale = request.form['locale']
+ locale_source = 'form'
+ elif request.preferences.get_value('locale') != '':
+ # use locale from the preferences
+ locale = request.preferences.get_value('locale')
+ locale_source = 'preferences'
+ else:
+ # use local from the browser
+ locale = _get_browser_or_settings_language(request, settings['locales'].keys())
+ locale_source = 'browser'
+ #
if locale == 'zh_TW':
locale = 'zh_Hant_TW'
+ # see _get_translations function
+ # and https://github.com/searx/searx/pull/1863
if locale == 'oc':
request.form['use-translation'] = 'oc'
locale = 'fr_FR'
- logger.debug("selected locale is `%s`", locale)
-
+ logger.debug("%s uses locale `%s` from %s", request.url, locale, locale_source)
return locale
@@ -305,7 +301,12 @@ def url_for_theme(endpoint, override_theme=None, **values):
filename_with_theme = "themes/{}/{}".format(theme_name, values['filename'])
if filename_with_theme in static_files:
values['filename'] = filename_with_theme
- return url_for(endpoint, **values)
+ url = url_for(endpoint, **values)
+ if settings['server']['base_url']:
+ if url.startswith('/'):
+ url = url[1:]
+ url = urljoin(settings['server']['base_url'], url)
+ return url
def proxify(url):
@@ -315,11 +316,11 @@ def proxify(url):
if not settings.get('result_proxy'):
return url
- url_params = dict(mortyurl=url.encode('utf-8'))
+ url_params = dict(mortyurl=url.encode())
if settings['result_proxy'].get('key'):
url_params['mortyhash'] = hmac.new(settings['result_proxy']['key'],
- url.encode('utf-8'),
+ url.encode(),
hashlib.sha256).hexdigest()
return '{0}?{1}'.format(settings['result_proxy']['url'],
@@ -347,10 +348,10 @@ def image_proxify(url):
if settings.get('result_proxy'):
return proxify(url)
- h = new_hmac(settings['server']['secret_key'], url.encode('utf-8'))
+ h = new_hmac(settings['server']['secret_key'], url.encode())
return '{0}?{1}'.format(url_for('image_proxy'),
- urlencode(dict(url=url.encode('utf-8'), h=h)))
+ urlencode(dict(url=url.encode(), h=h)))
def render(template_name, override_theme=None, **kwargs):
@@ -365,25 +366,6 @@ def render(template_name, override_theme=None, **kwargs):
_get_ordered_categories()
if x in enabled_categories]
- if 'all_categories' not in kwargs:
- kwargs['all_categories'] = _get_ordered_categories()
-
- if 'selected_categories' not in kwargs:
- kwargs['selected_categories'] = []
- for arg in request.args:
- if arg.startswith('category_'):
- c = arg.split('_', 1)[1]
- if c in categories:
- kwargs['selected_categories'].append(c)
-
- if not kwargs['selected_categories']:
- cookie_categories = request.preferences.get_value('categories')
- for ccateg in cookie_categories:
- kwargs['selected_categories'].append(ccateg)
-
- if not kwargs['selected_categories']:
- kwargs['selected_categories'] = ['general']
-
if 'autocomplete' not in kwargs:
kwargs['autocomplete'] = request.preferences.get_value('autocomplete')
@@ -410,6 +392,9 @@ def render(template_name, override_theme=None, **kwargs):
kwargs['proxify'] = proxify if settings.get('result_proxy', {}).get('url') else None
+ kwargs['opensearch_url'] = url_for('opensearch') + '?' \
+ + urlencode({'method': kwargs['method'], 'autocomplete': kwargs['autocomplete']})
+
kwargs['get_result_template'] = get_result_template
kwargs['theme'] = get_current_theme_name(override=override_theme)
@@ -424,8 +409,6 @@ def render(template_name, override_theme=None, **kwargs):
kwargs['results_on_new_tab'] = request.preferences.get_value('results_on_new_tab')
- kwargs['unicode'] = unicode
-
kwargs['preferences'] = request.preferences
kwargs['brand'] = brand
@@ -463,6 +446,9 @@ def pre_request():
request.errors = []
preferences = Preferences(themes, list(categories.keys()), engines, plugins)
+ user_agent = request.headers.get('User-Agent', '').lower()
+ if 'webkit' in user_agent and 'android' in user_agent:
+ preferences.key_value_settings['method'].value = 'GET'
request.preferences = preferences
try:
preferences.parse_dict(request.cookies)
@@ -481,13 +467,13 @@ def pre_request():
else:
try:
preferences.parse_dict(request.form)
- except Exception as e:
+ except Exception:
logger.exception('invalid settings')
request.errors.append(gettext('Invalid settings'))
# init search language and locale
if not preferences.get_value("language"):
- preferences.parse_dict({"language": _get_browser_language(request, LANGUAGE_CODES)})
+ preferences.parse_dict({"language": _get_browser_or_settings_language(request, LANGUAGE_CODES)})
if not preferences.get_value("locale"):
preferences.parse_dict({"locale": get_locale()})
@@ -502,6 +488,16 @@ def pre_request():
@app.after_request
+def add_default_headers(response):
+ # set default http headers
+ for header, value in settings['server'].get('default_http_headers', {}).items():
+ if header in response.headers:
+ continue
+ response.headers[header] = value
+ return response
+
+
+@app.after_request
def post_request(response):
total_time = time() - request.start_time
timings_all = ['total;dur=' + str(round(total_time * 1000, 3))]
@@ -541,13 +537,32 @@ def index_error(output_format, error_message):
request.errors.append(gettext('search error'))
return render(
'index.html',
+ selected_categories=get_selected_categories(request.preferences, request.form),
)
-@app.route('/search', methods=['GET', 'POST'])
@app.route('/', methods=['GET', 'POST'])
def index():
- """Render index page.
+ """Render index page."""
+
+ # UI
+ advanced_search = request.preferences.get_value('advanced_search')
+
+ # redirect to search if there's a query in the request
+ if request.form.get('q'):
+ query = ('?' + request.query_string.decode()) if request.query_string else ''
+ return redirect(url_for('search') + query, 308)
+
+ return render(
+ 'index.html',
+ selected_categories=get_selected_categories(request.preferences, request.form),
+ advanced_search=advanced_search,
+ )
+
+
+@app.route('/search', methods=['GET', 'POST'])
+def search():
+ """Search query in q and return results.
Supported outputs: html, json, csv, rss.
"""
@@ -557,11 +572,13 @@ def index():
if output_format not in ['html', 'csv', 'json', 'rss']:
output_format = 'html'
- # check if there is query
- if request.form.get('q') is None:
+ # check if there is query (not None and not an empty string)
+ if not request.form.get('q'):
if output_format == 'html':
return render(
'index.html',
+ advanced_search=request.preferences.get_value('advanced_search'),
+ selected_categories=get_selected_categories(request.preferences, request.form),
)
else:
return index_error(output_format, 'No query'), 400
@@ -571,21 +588,18 @@ def index():
raw_text_query = None
result_container = None
try:
- search_query, raw_text_query = get_search_query_from_webapp(request.preferences, request.form)
+ search_query, raw_text_query, _, _ = get_search_query_from_webapp(request.preferences, request.form)
# search = Search(search_query) # without plugins
search = SearchWithPlugins(search_query, request.user_plugins, request)
result_container = search.search()
+ except SearxParameterException as e:
+ logger.exception('search error: SearxParameterException')
+ return index_error(output_format, e.message), 400
except Exception as e:
- # log exception
logger.exception('search error')
-
- # is it an invalid input parameter or something else ?
- if (issubclass(e.__class__, SearxParameterException)):
- return index_error(output_format, e.message), 400
- else:
- return index_error(output_format, gettext('search error')), 500
+ return index_error(output_format, gettext('search error')), 500
# results
results = result_container.get_ordered_results()
@@ -597,9 +611,6 @@ def index():
if result_container.redirect_url:
return redirect(result_container.redirect_url)
- # UI
- advanced_search = request.form.get('advanced_search', None)
-
# Server-Timing header
request.timings = result_container.get_timings()
@@ -609,7 +620,7 @@ def index():
if 'content' in result and result['content']:
result['content'] = highlight_content(escape(result['content'][:1024]), search_query.query)
if 'title' in result and result['title']:
- result['title'] = highlight_content(escape(result['title'] or u''), search_query.query)
+ result['title'] = highlight_content(escape(result['title'] or ''), search_query.query)
else:
if result.get('content'):
result['content'] = html_to_text(result['content']).strip()
@@ -631,14 +642,14 @@ def index():
minutes = int((timedifference.seconds / 60) % 60)
hours = int(timedifference.seconds / 60 / 60)
if hours == 0:
- result['publishedDate'] = gettext(u'{minutes} minute(s) ago').format(minutes=minutes)
+ result['publishedDate'] = gettext('{minutes} minute(s) ago').format(minutes=minutes)
else:
- result['publishedDate'] = gettext(u'{hours} hour(s), {minutes} minute(s) ago').format(hours=hours, minutes=minutes) # noqa
+ result['publishedDate'] = gettext('{hours} hour(s), {minutes} minute(s) ago').format(hours=hours, minutes=minutes) # noqa
else:
result['publishedDate'] = format_date(result['publishedDate'])
if output_format == 'json':
- return Response(json.dumps({'query': search_query.query.decode('utf-8'),
+ return Response(json.dumps({'query': search_query.query,
'number_of_results': number_of_results,
'results': results,
'answers': list(result_container.answers),
@@ -667,7 +678,7 @@ def index():
csv.writerow([row.get(key, '') for key in keys])
csv.stream.seek(0)
response = Response(csv.stream.read(), mimetype='application/csv')
- cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query.decode('utf-8'))
+ cont_disp = 'attachment;Filename=searx_-_{0}.csv'.format(search_query.query)
response.headers.add('Content-Disposition', cont_disp)
return response
@@ -689,13 +700,13 @@ def index():
# suggestions: use RawTextQuery to get the suggestion URLs with the same bang
suggestion_urls = list(map(lambda suggestion: {
- 'url': raw_text_query.changeSearchQuery(suggestion).getFullQuery(),
+ 'url': raw_text_query.changeQuery(suggestion).getFullQuery(),
'title': suggestion
},
result_container.suggestions))
correction_urls = list(map(lambda correction: {
- 'url': raw_text_query.changeSearchQuery(correction).getFullQuery(),
+ 'url': raw_text_query.changeQuery(correction).getFullQuery(),
'title': correction
},
result_container.corrections))
@@ -708,7 +719,6 @@ def index():
pageno=search_query.pageno,
time_range=search_query.time_range,
number_of_results=format_decimal(number_of_results),
- advanced_search=advanced_search,
suggestions=suggestion_urls,
answers=result_container.answers,
corrections=correction_urls,
@@ -726,12 +736,12 @@ def index():
def __get_translated_errors(unresponsive_engines):
- translated_errors = []
+ translated_errors = set()
for unresponsive_engine in unresponsive_engines:
error_msg = gettext(unresponsive_engine[1])
if unresponsive_engine[2]:
error_msg = "{} {}".format(error_msg, unresponsive_engine[2])
- translated_errors.append((unresponsive_engine[0], error_msg))
+ translated_errors.add((unresponsive_engine[0], error_msg))
return translated_errors
@@ -751,14 +761,10 @@ def autocompleter():
disabled_engines = request.preferences.engines.get_disabled()
# parse query
- if PY3:
- raw_text_query = RawTextQuery(request.form.get('q', b''), disabled_engines)
- else:
- raw_text_query = RawTextQuery(request.form.get('q', u'').encode('utf-8'), disabled_engines)
- raw_text_query.parse_query()
+ raw_text_query = RawTextQuery(request.form.get('q', ''), disabled_engines)
# check if search query is set
- if not raw_text_query.getSearchQuery():
+ if not raw_text_query.getQuery():
return '', 400
# run autocompleter
@@ -779,23 +785,23 @@ def autocompleter():
else:
language = language.split('-')[0]
# run autocompletion
- raw_results.extend(completer(raw_text_query.getSearchQuery(), language))
+ raw_results.extend(completer(raw_text_query.getQuery(), language))
# parse results (write :language and !engine back to result string)
results = []
for result in raw_results:
- raw_text_query.changeSearchQuery(result)
+ raw_text_query.changeQuery(result)
# add parsed result
results.append(raw_text_query.getFullQuery())
# return autocompleter results
- if request.form.get('format') == 'x-suggestions':
- return Response(json.dumps([raw_text_query.query, results]),
+ if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
+ return Response(json.dumps(results),
mimetype='application/json')
- return Response(json.dumps(results),
- mimetype='application/json')
+ return Response(json.dumps([raw_text_query.query, results]),
+ mimetype='application/x-suggestions+json')
@app.route('/preferences', methods=['GET', 'POST'])
@@ -814,7 +820,6 @@ def preferences():
# render preferences
image_proxy = request.preferences.get_value('image_proxy')
- lang = request.preferences.get_value('language')
disabled_engines = request.preferences.engines.get_disabled()
allowed_plugins = request.preferences.plugins.get_enabled()
@@ -845,7 +850,13 @@ def preferences():
stats[engine_stat.get('name')]['warn_time'] = True
# end of stats
+ locked_preferences = list()
+ if 'preferences' in settings and 'lock' in settings['preferences']:
+ locked_preferences = settings['preferences']['lock']
+
return render('preferences.html',
+ selected_categories=get_selected_categories(request.preferences, request.form),
+ all_categories=_get_ordered_categories(),
locales=settings['locales'],
current_locale=request.preferences.get_value("locale"),
image_proxy=image_proxy,
@@ -863,6 +874,7 @@ def preferences():
theme=get_current_theme_name(),
preferences_url_params=request.preferences.get_as_url_params(),
base_url=get_base_url(),
+ locked_preferences=locked_preferences,
preferences=True)
@@ -876,7 +888,7 @@ def _is_selected_language_supported(engine, preferences):
@app.route('/image_proxy', methods=['GET'])
def image_proxy():
- url = request.args.get('url').encode('utf-8')
+ url = request.args.get('url').encode()
if not url:
return '', 400
@@ -893,7 +905,7 @@ def image_proxy():
stream=True,
timeout=settings['outgoing']['request_timeout'],
headers=headers,
- proxies=outgoing_proxies)
+ proxies=get_global_proxies())
if resp.status_code == 304:
return '', resp.status_code
@@ -932,6 +944,34 @@ def stats():
)
+@app.route('/stats/errors', methods=['GET'])
+def stats_errors():
+ result = {}
+ engine_names = list(errors_per_engines.keys())
+ engine_names.sort()
+ for engine_name in engine_names:
+ error_stats = errors_per_engines[engine_name]
+ sent_search_count = max(engines[engine_name].stats['sent_search_count'], 1)
+ sorted_context_count_list = sorted(error_stats.items(), key=lambda context_count: context_count[1])
+ r = []
+ percentage_sum = 0
+ for context, count in sorted_context_count_list:
+ percentage = round(20 * count / sent_search_count) * 5
+ percentage_sum += percentage
+ r.append({
+ 'filename': context.filename,
+ 'function': context.function,
+ 'line_no': context.line_no,
+ 'code': context.code,
+ 'exception_classname': context.exception_classname,
+ 'log_message': context.log_message,
+ 'log_parameters': context.log_parameters,
+ 'percentage': percentage,
+ })
+ result[engine_name] = sorted(r, reverse=True, key=lambda d: d['percentage'])
+ return jsonify(result)
+
+
@app.route('/robots.txt', methods=['GET'])
def robots():
return Response("""User-agent: *
@@ -1058,7 +1098,7 @@ def run():
)
-class ReverseProxyPathFix(object):
+class ReverseProxyPathFix:
'''Wrap the application in this middleware and configure the
front-end server to add these headers, to let you quietly bind
this to a URL other than / and to an HTTP scheme that is
diff --git a/searx/webutils.py b/searx/webutils.py
new file mode 100644
index 0000000..8be8fce
--- /dev/null
+++ b/searx/webutils.py
@@ -0,0 +1,145 @@
+# -*- coding: utf-8 -*-
+import os
+import csv
+import hashlib
+import hmac
+import re
+import inspect
+
+from io import StringIO
+from codecs import getincrementalencoder
+
+from searx import logger
+
+
+VALID_LANGUAGE_CODE = re.compile(r'^[a-z]{2,3}(-[a-zA-Z]{2})?$')
+
+logger = logger.getChild('webutils')
+
+
+class UnicodeWriter:
+ """
+ A CSV writer which will write rows to CSV file "f",
+ which is encoded in the given encoding.
+ """
+
+ def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
+ # Redirect output to a queue
+ self.queue = StringIO()
+ self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
+ self.stream = f
+ self.encoder = getincrementalencoder(encoding)()
+
+ def writerow(self, row):
+ self.writer.writerow(row)
+ # Fetch UTF-8 output from the queue ...
+ data = self.queue.getvalue()
+ data = data.strip('\x00')
+ # ... and reencode it into the target encoding
+ data = self.encoder.encode(data)
+ # write to the target stream
+ self.stream.write(data.decode())
+ # empty queue
+ self.queue.truncate(0)
+
+ def writerows(self, rows):
+ for row in rows:
+ self.writerow(row)
+
+
+def get_resources_directory(searx_directory, subdirectory, resources_directory):
+ if not resources_directory:
+ resources_directory = os.path.join(searx_directory, subdirectory)
+ if not os.path.isdir(resources_directory):
+ raise Exception(resources_directory + " is not a directory")
+ return resources_directory
+
+
+def get_themes(templates_path):
+ """Returns available themes list."""
+ themes = os.listdir(templates_path)
+ if '__common__' in themes:
+ themes.remove('__common__')
+ return themes
+
+
+def get_static_files(static_path):
+ static_files = set()
+ static_path_length = len(static_path) + 1
+ for directory, _, files in os.walk(static_path):
+ for filename in files:
+ f = os.path.join(directory[static_path_length:], filename)
+ static_files.add(f)
+ return static_files
+
+
+def get_result_templates(templates_path):
+ result_templates = set()
+ templates_path_length = len(templates_path) + 1
+ for directory, _, files in os.walk(templates_path):
+ if directory.endswith('result_templates'):
+ for filename in files:
+ f = os.path.join(directory[templates_path_length:], filename)
+ result_templates.add(f)
+ return result_templates
+
+
+def new_hmac(secret_key, url):
+ try:
+ secret_key_bytes = bytes(secret_key, 'utf-8')
+ except TypeError as err:
+ if isinstance(secret_key, bytes):
+ secret_key_bytes = secret_key
+ else:
+ raise err
+ return hmac.new(secret_key_bytes, url, hashlib.sha256).hexdigest()
+
+
+def prettify_url(url, max_length=74):
+ if len(url) > max_length:
+ chunk_len = int(max_length / 2 + 1)
+ return '{0}[...]{1}'.format(url[:chunk_len], url[-chunk_len:])
+ else:
+ return url
+
+
+def highlight_content(content, query):
+
+ if not content:
+ return None
+ # ignoring html contents
+ # TODO better html content detection
+ if content.find('<') != -1:
+ return content
+
+ if content.lower().find(query.lower()) > -1:
+ query_regex = '({0})'.format(re.escape(query))
+ content = re.sub(query_regex, '<span class="highlight">\\1</span>',
+ content, flags=re.I | re.U)
+ else:
+ regex_parts = []
+ for chunk in query.split():
+ if len(chunk) == 1:
+ regex_parts.append('\\W+{0}\\W+'.format(re.escape(chunk)))
+ else:
+ regex_parts.append('{0}'.format(re.escape(chunk)))
+ query_regex = '({0})'.format('|'.join(regex_parts))
+ content = re.sub(query_regex, '<span class="highlight">\\1</span>',
+ content, flags=re.I | re.U)
+
+ return content
+
+
+def is_flask_run_cmdline():
+ """Check if the application was started using "flask run" command line
+
+ Inspect the callstack.
+ See https://github.com/pallets/flask/blob/master/src/flask/__main__.py
+
+ Returns:
+ bool: True if the application was started using "flask run".
+ """
+ frames = inspect.stack()
+ if len(frames) < 2:
+ return False
+ return frames[-2].filename.endswith('flask/cli.py')
diff --git a/setup.py b/setup.py
index 97a3270..6a78f61 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@ sys.path.insert(0, './searx')
from version import VERSION_STRING
import brand
-with open('README.rst') as f:
+with open('README.rst', encoding='utf-8') as f:
long_description = f.read()
with open('requirements.txt') as f:
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29..18bf7ca 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1,2 @@
+import os
+os.environ['SEARX_DEBUG'] = '1'
diff --git a/tests/test_robot.py b/tests/test_robot.py
deleted file mode 100644
index b48153f..0000000
--- a/tests/test_robot.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-import unittest2 as unittest
-from plone.testing import layered
-from robotsuite import RobotTestSuite
-from searx.testing import SEARXROBOTLAYER
-
-
-def test_suite():
- suite = unittest.TestSuite()
- current_dir = os.path.abspath(os.path.dirname(__file__))
- robot_dir = os.path.join(current_dir, 'robot')
- tests = [
- os.path.join('robot', f) for f in
- os.listdir(robot_dir) if f.endswith('.robot') and
- f.startswith('test_')
- ]
- for test in tests:
- suite.addTests([
- layered(RobotTestSuite(test), layer=SEARXROBOTLAYER),
- ])
- return suite
diff --git a/tests/unit/engines/test_command.py b/tests/unit/engines/test_command.py
new file mode 100644
index 0000000..8f88b61
--- /dev/null
+++ b/tests/unit/engines/test_command.py
@@ -0,0 +1,240 @@
+'''
+searx is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+searx is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with searx. If not, see < http://www.gnu.org/licenses/ >.
+
+'''
+
+
+from searx.engines import command as command_engine
+from searx.testing import SearxTestCase
+
+
+class TestCommandEngine(SearxTestCase):
+ def test_basic_seq_command_engine(self):
+ ls_engine = command_engine
+ ls_engine.command = ['seq', '{{QUERY}}']
+ ls_engine.delimiter = {'chars': ' ', 'keys': ['number']}
+ expected_results = [
+ {'number': '1', 'template': 'key-value.html'},
+ {'number': '2', 'template': 'key-value.html'},
+ {'number': '3', 'template': 'key-value.html'},
+ {'number': '4', 'template': 'key-value.html'},
+ {'number': '5', 'template': 'key-value.html'},
+ ]
+ results = ls_engine.search('5'.encode('utf-8'), {'pageno': 1})
+ self.assertEqual(results, expected_results)
+
+ def test_delimiter_parsing_command_engine(self):
+ searx_logs = '''DEBUG:searx.webapp:static directory is /home/n/p/searx/searx/static
+DEBUG:searx.webapp:templates directory is /home/n/p/searx/searx/templates
+DEBUG:searx.engines:soundcloud engine: Starting background initialization
+DEBUG:searx.engines:wolframalpha engine: Starting background initialization
+DEBUG:searx.engines:locate engine: Starting background initialization
+DEBUG:searx.engines:regex search in files engine: Starting background initialization
+DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): www.wolframalpha.com
+DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): soundcloud.com
+DEBUG:searx.engines:find engine: Starting background initialization
+DEBUG:searx.engines:pattern search in files engine: Starting background initialization
+DEBUG:searx.webapp:starting webserver on 127.0.0.1:8888
+WARNING:werkzeug: * Debugger is active!
+INFO:werkzeug: * Debugger PIN: 299-578-362'''
+ echo_engine = command_engine
+ echo_engine.command = ['echo', searx_logs]
+ echo_engine.delimiter = {'chars': ':', 'keys': ['level', 'component', 'message']}
+
+ expected_results_by_page = [
+ [
+ {
+ 'component': 'searx.webapp',
+ 'message': 'static directory is /home/n/p/searx/searx/static',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'searx.webapp',
+ 'message': 'templates directory is /home/n/p/searx/searx/templates',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'searx.engines',
+ 'message': 'soundcloud engine: Starting background initialization',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'searx.engines',
+ 'message': 'wolframalpha engine: Starting background initialization',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'searx.engines',
+ 'message': 'locate engine: Starting background initialization',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'searx.engines',
+ 'message': 'regex search in files engine: Starting background initialization',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'urllib3.connectionpool',
+ 'message': 'Starting new HTTPS connection (1): www.wolframalpha.com',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'urllib3.connectionpool',
+ 'message': 'Starting new HTTPS connection (1): soundcloud.com',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'searx.engines',
+ 'message': 'find engine: Starting background initialization',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'searx.engines',
+ 'message': 'pattern search in files engine: Starting background initialization',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+
+ ],
+ [
+ {
+ 'component': 'searx.webapp',
+ 'message': 'starting webserver on 127.0.0.1:8888',
+ 'template': 'key-value.html',
+ 'level': 'DEBUG',
+ },
+ {
+ 'component': 'werkzeug',
+ 'message': ' * Debugger is active!',
+ 'template': 'key-value.html',
+ 'level': 'WARNING',
+ },
+ {
+ 'component': 'werkzeug',
+ 'message': ' * Debugger PIN: 299-578-362',
+ 'template': 'key-value.html',
+ 'level': 'INFO',
+ },
+ ],
+
+ ]
+
+ for i in [0, 1]:
+ results = echo_engine.search(''.encode('utf-8'), {'pageno': i + 1})
+ self.assertEqual(results, expected_results_by_page[i])
+
+ def test_regex_parsing_command_engine(self):
+ txt = '''commit 35f9a8c81d162a361b826bbcd4a1081a4fbe76a7
+Author: Noémi Ványi <sitbackandwait@gmail.com>
+Date: Tue Oct 15 11:31:33 2019 +0200
+
+first interesting message
+
+commit 6c3c206316153ccc422755512bceaa9ab0b14faa
+Author: Noémi Ványi <sitbackandwait@gmail.com>
+Date: Mon Oct 14 17:10:08 2019 +0200
+
+second interesting message
+
+commit d8594d2689b4d5e0d2f80250223886c3a1805ef5
+Author: Noémi Ványi <sitbackandwait@gmail.com>
+Date: Mon Oct 14 14:45:05 2019 +0200
+
+third interesting message
+
+commit '''
+ git_log_engine = command_engine
+ git_log_engine.command = ['echo', txt]
+ git_log_engine.result_separator = '\n\ncommit '
+ git_log_engine.delimiter = {}
+ git_log_engine.parse_regex = {
+ 'commit': '\w{40}',
+ 'author': '[\w* ]* <\w*@?\w*\.?\w*>',
+ 'date': 'Date: .*',
+ 'message': '\n\n.*$'
+ }
+ expected_results = [
+ {
+ 'commit': '35f9a8c81d162a361b826bbcd4a1081a4fbe76a7',
+ 'author': ' Noémi Ványi <sitbackandwait@gmail.com>',
+ 'date': 'Date: Tue Oct 15 11:31:33 2019 +0200',
+ 'message': '\n\nfirst interesting message',
+ 'template': 'key-value.html',
+ },
+ {
+ 'commit': '6c3c206316153ccc422755512bceaa9ab0b14faa',
+ 'author': ' Noémi Ványi <sitbackandwait@gmail.com>',
+ 'date': 'Date: Mon Oct 14 17:10:08 2019 +0200',
+ 'message': '\n\nsecond interesting message',
+ 'template': 'key-value.html',
+ },
+ {
+ 'commit': 'd8594d2689b4d5e0d2f80250223886c3a1805ef5',
+ 'author': ' Noémi Ványi <sitbackandwait@gmail.com>',
+ 'date': 'Date: Mon Oct 14 14:45:05 2019 +0200',
+ 'message': '\n\nthird interesting message',
+ 'template': 'key-value.html',
+ },
+
+ ]
+
+ results = git_log_engine.search(''.encode('utf-8'), {'pageno': 1})
+ self.assertEqual(results, expected_results)
+
+ def test_working_dir_path_query(self):
+ ls_engine = command_engine
+ ls_engine.command = ['ls', '{{QUERY}}']
+ ls_engine.result_separator = '\n'
+ ls_engine.delimiter = {'chars': ' ', 'keys': ['file']}
+ ls_engine.query_type = 'path'
+
+ results = ls_engine.search('.'.encode(), {'pageno': 1})
+ self.assertTrue(len(results) != 0)
+
+ forbidden_paths = [
+ '..',
+ '../..',
+ './..',
+ '~',
+ '/var',
+ ]
+ for forbidden_path in forbidden_paths:
+ self.assertRaises(ValueError, ls_engine.search, '..'.encode(), {'pageno': 1})
+
+ def test_enum_queries(self):
+ echo_engine = command_engine
+ echo_engine.command = ['echo', '{{QUERY}}']
+ echo_engine.query_type = 'enum'
+ echo_engine.query_enum = ['i-am-allowed-to-say-this', 'and-that']
+
+ for allowed in echo_engine.query_enum:
+ results = echo_engine.search(allowed.encode(), {'pageno': 1})
+ self.assertTrue(len(results) != 0)
+
+ forbidden_queries = [
+ 'forbidden',
+ 'banned',
+ 'prohibited',
+ ]
+ for forbidden in forbidden_queries:
+ self.assertRaises(ValueError, echo_engine.search, forbidden.encode(), {'pageno': 1})
diff --git a/tests/unit/engines/test_xpath.py b/tests/unit/engines/test_xpath.py
new file mode 100644
index 0000000..963a44a
--- /dev/null
+++ b/tests/unit/engines/test_xpath.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+from collections import defaultdict
+import mock
+from searx.engines import xpath
+from searx.testing import SearxTestCase
+
+
+class TestXpathEngine(SearxTestCase):
+
+ def test_request(self):
+ xpath.search_url = 'https://url.com/{query}'
+ xpath.categories = []
+ xpath.paging = False
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ params = xpath.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertEquals('https://url.com/test_query', params['url'])
+
+ xpath.search_url = 'https://url.com/q={query}&p={pageno}'
+ xpath.paging = True
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = xpath.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertEquals('https://url.com/q=test_query&p=1', params['url'])
+
+ def test_response(self):
+ # without results_xpath
+ xpath.url_xpath = '//div[@class="search_result"]//a[@class="result"]/@href'
+ xpath.title_xpath = '//div[@class="search_result"]//a[@class="result"]'
+ xpath.content_xpath = '//div[@class="search_result"]//p[@class="content"]'
+
+ self.assertRaises(AttributeError, xpath.response, None)
+ self.assertRaises(AttributeError, xpath.response, [])
+ self.assertRaises(AttributeError, xpath.response, '')
+ self.assertRaises(AttributeError, xpath.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(xpath.response(response), [])
+
+ html = u"""
+ <div>
+ <div class="search_result">
+ <a class="result" href="https://result1.com">Result 1</a>
+ <p class="content">Content 1</p>
+ <a class="cached" href="https://cachedresult1.com">Cache</a>
+ </div>
+ <div class="search_result">
+ <a class="result" href="https://result2.com">Result 2</a>
+ <p class="content">Content 2</p>
+ <a class="cached" href="https://cachedresult2.com">Cache</a>
+ </div>
+ </div>
+ """
+ response = mock.Mock(text=html)
+ results = xpath.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'Result 1')
+ self.assertEqual(results[0]['url'], 'https://result1.com/')
+ self.assertEqual(results[0]['content'], 'Content 1')
+ self.assertEqual(results[1]['title'], 'Result 2')
+ self.assertEqual(results[1]['url'], 'https://result2.com/')
+ self.assertEqual(results[1]['content'], 'Content 2')
+
+ # with cached urls, without results_xpath
+ xpath.cached_xpath = '//div[@class="search_result"]//a[@class="cached"]/@href'
+ results = xpath.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['cached_url'], 'https://cachedresult1.com')
+ self.assertEqual(results[1]['cached_url'], 'https://cachedresult2.com')
+ self.assertFalse(results[0].get('is_onion', False))
+
+ # results are onion urls (no results_xpath)
+ xpath.categories = ['onions']
+ results = xpath.response(response)
+ self.assertTrue(results[0]['is_onion'])
+
+ # with results_xpath
+ xpath.results_xpath = '//div[@class="search_result"]'
+ xpath.url_xpath = './/a[@class="result"]/@href'
+ xpath.title_xpath = './/a[@class="result"]'
+ xpath.content_xpath = './/p[@class="content"]'
+ xpath.cached_xpath = None
+ xpath.categories = []
+
+ self.assertRaises(AttributeError, xpath.response, None)
+ self.assertRaises(AttributeError, xpath.response, [])
+ self.assertRaises(AttributeError, xpath.response, '')
+ self.assertRaises(AttributeError, xpath.response, '[]')
+
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(xpath.response(response), [])
+
+ response = mock.Mock(text=html)
+ results = xpath.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['title'], 'Result 1')
+ self.assertEqual(results[0]['url'], 'https://result1.com/')
+ self.assertEqual(results[0]['content'], 'Content 1')
+ self.assertEqual(results[1]['title'], 'Result 2')
+ self.assertEqual(results[1]['url'], 'https://result2.com/')
+ self.assertEqual(results[1]['content'], 'Content 2')
+
+ # with cached urls, with results_xpath
+ xpath.cached_xpath = './/a[@class="cached"]/@href'
+ results = xpath.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 2)
+ self.assertEqual(results[0]['cached_url'], 'https://cachedresult1.com')
+ self.assertEqual(results[1]['cached_url'], 'https://cachedresult2.com')
+ self.assertFalse(results[0].get('is_onion', False))
+
+ # results are onion urls (with results_xpath)
+ xpath.categories = ['onions']
+ results = xpath.response(response)
+ self.assertTrue(results[0]['is_onion'])
diff --git a/tests/unit/settings/empty_settings.yml b/tests/unit/settings/empty_settings.yml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/unit/settings/empty_settings.yml
diff --git a/tests/unit/settings/syntaxerror_settings.yml b/tests/unit/settings/syntaxerror_settings.yml
new file mode 100644
index 0000000..6d3b0f9
--- /dev/null
+++ b/tests/unit/settings/syntaxerror_settings.yml
@@ -0,0 +1,2 @@
+Test:
+ **********
diff --git a/tests/unit/settings/user_settings.yml b/tests/unit/settings/user_settings.yml
new file mode 100644
index 0000000..f5b6c71
--- /dev/null
+++ b/tests/unit/settings/user_settings.yml
@@ -0,0 +1,111 @@
+general:
+ debug : False
+ instance_name : "searx"
+
+search:
+ safe_search : 0
+ autocomplete : ""
+ default_lang : ""
+ ban_time_on_fail : 5
+ max_ban_time_on_fail : 120
+
+server:
+ port : 9000
+ bind_address : "0.0.0.0"
+ secret_key : "user_settings_secret"
+ base_url : False
+ image_proxy : False
+ http_protocol_version : "1.0"
+ method: "POST"
+ default_http_headers:
+ X-Content-Type-Options : nosniff
+ X-XSS-Protection : 1; mode=block
+ X-Download-Options : noopen
+ X-Robots-Tag : noindex, nofollow
+ Referrer-Policy : no-referrer
+
+ui:
+ static_path : ""
+ templates_path : ""
+ default_theme : oscar
+ default_locale : ""
+ theme_args :
+ oscar_style : logicodev
+
+engines:
+ - name : wikidata
+ engine : wikidata
+ shortcut : wd
+ timeout : 3.0
+ weight : 2
+
+ - name : wikibooks
+ engine : mediawiki
+ shortcut : wb
+ categories : general
+ base_url : "https://{language}.wikibooks.org/"
+ number_of_results : 5
+ search_type : text
+
+ - name : wikinews
+ engine : mediawiki
+ shortcut : wn
+ categories : news
+ base_url : "https://{language}.wikinews.org/"
+ number_of_results : 5
+ search_type : text
+
+ - name : wikiquote
+ engine : mediawiki
+ shortcut : wq
+ categories : general
+ base_url : "https://{language}.wikiquote.org/"
+ number_of_results : 5
+ search_type : text
+
+locales:
+ en : English
+ ar : العَرَبِيَّة (Arabic)
+ bg : Български (Bulgarian)
+ bo : བོད་སྐད་ (Tibetian)
+ ca : Català (Catalan)
+ cs : Čeština (Czech)
+ cy : Cymraeg (Welsh)
+ da : Dansk (Danish)
+ de : Deutsch (German)
+ el_GR : Ελληνικά (Greek_Greece)
+ eo : Esperanto (Esperanto)
+ es : Español (Spanish)
+ et : Eesti (Estonian)
+ eu : Euskara (Basque)
+ fa_IR : (fārsī) فارسى (Persian)
+ fi : Suomi (Finnish)
+ fil : Wikang Filipino (Filipino)
+ fr : Français (French)
+ gl : Galego (Galician)
+ he : עברית (Hebrew)
+ hr : Hrvatski (Croatian)
+ hu : Magyar (Hungarian)
+ ia : Interlingua (Interlingua)
+ it : Italiano (Italian)
+ ja : 日本語 (Japanese)
+ lt : Lietuvių (Lithuanian)
+ nl : Nederlands (Dutch)
+ nl_BE : Vlaams (Dutch_Belgium)
+ oc : Lenga D'òc (Occitan)
+ pl : Polski (Polish)
+ pt : Português (Portuguese)
+ pt_BR : Português (Portuguese_Brazil)
+ ro : Română (Romanian)
+ ru : Русский (Russian)
+ sk : Slovenčina (Slovak)
+ sl : Slovenski (Slovene)
+ sr : српски (Serbian)
+ sv : Svenska (Swedish)
+ te : తెలుగు (telugu)
+ ta : தமிழ் (Tamil)
+ tr : Türkçe (Turkish)
+ uk : українська мова (Ukrainian)
+ vi : tiếng việt (Vietnamese)
+ zh : 中文 (Chinese)
+ zh_TW : 國語 (Taiwanese Mandarin)
diff --git a/tests/unit/settings/user_settings_keep_only.yml b/tests/unit/settings/user_settings_keep_only.yml
new file mode 100644
index 0000000..518f18b
--- /dev/null
+++ b/tests/unit/settings/user_settings_keep_only.yml
@@ -0,0 +1,14 @@
+use_default_settings:
+ engines:
+ keep_only:
+ - wikibooks
+ - wikinews
+server:
+ secret_key: "user_secret_key"
+ bind_address: "0.0.0.0"
+ default_http_headers:
+ Custom-Header: Custom-Value
+engines:
+ - name: wikipedia
+ - name: newengine
+ engine: dummy
diff --git a/tests/unit/settings/user_settings_remove.yml b/tests/unit/settings/user_settings_remove.yml
new file mode 100644
index 0000000..c4fd85d
--- /dev/null
+++ b/tests/unit/settings/user_settings_remove.yml
@@ -0,0 +1,10 @@
+use_default_settings:
+ engines:
+ remove:
+ - wikibooks
+ - wikinews
+server:
+ secret_key: "user_secret_key"
+ bind_address: "0.0.0.0"
+ default_http_headers:
+ Custom-Header: Custom-Value
diff --git a/tests/unit/settings/user_settings_remove2.yml b/tests/unit/settings/user_settings_remove2.yml
new file mode 100644
index 0000000..e9be325
--- /dev/null
+++ b/tests/unit/settings/user_settings_remove2.yml
@@ -0,0 +1,15 @@
+use_default_settings:
+ engines:
+ remove:
+ - wikibooks
+ - wikinews
+server:
+ secret_key: "user_secret_key"
+ bind_address: "0.0.0.0"
+ default_http_headers:
+ Custom-Header: Custom-Value
+engines:
+ - name: wikipedia
+ tokens: ['secret_token']
+ - name: newengine
+ engine: dummy
diff --git a/tests/unit/settings/user_settings_simple.yml b/tests/unit/settings/user_settings_simple.yml
new file mode 100644
index 0000000..bc6ed0c
--- /dev/null
+++ b/tests/unit/settings/user_settings_simple.yml
@@ -0,0 +1,9 @@
+use_default_settings: True
+server:
+ secret_key: "user_secret_key"
+ bind_address: "0.0.0.0"
+ default_http_headers:
+ Custom-Header: Custom-Value
+result_proxy:
+ url : https://localhost/morty
+ key : "$ecretKey"
diff --git a/tests/unit/test_answerers.py b/tests/unit/test_answerers.py
index bd8789a..73d8d26 100644
--- a/tests/unit/test_answerers.py
+++ b/tests/unit/test_answerers.py
@@ -10,7 +10,7 @@ class AnswererTest(SearxTestCase):
def test_unicode_input(self):
query = Mock()
- unicode_payload = u'árvíztűrő tükörfúrógép'
+ unicode_payload = 'árvíztűrő tükörfúrógép'
for answerer in answerers:
- query.query = u'{} {}'.format(answerer.keywords[0], unicode_payload)
+ query.query = '{} {}'.format(answerer.keywords[0], unicode_payload)
self.assertTrue(isinstance(answerer.answer(query), list))
diff --git a/tests/unit/test_engines_init.py b/tests/unit/test_engines_init.py
new file mode 100644
index 0000000..cf4d503
--- /dev/null
+++ b/tests/unit/test_engines_init.py
@@ -0,0 +1,44 @@
+from searx.testing import SearxTestCase
+from searx import settings, engines
+
+
+class TestEnginesInit(SearxTestCase):
+
+ @classmethod
+ def tearDownClass(cls):
+ settings['outgoing']['using_tor_proxy'] = False
+ settings['outgoing']['extra_proxy_timeout'] = 0
+
+ def test_initialize_engines_default(self):
+ engine_list = [{'engine': 'dummy', 'name': 'engine1', 'shortcut': 'e1'},
+ {'engine': 'dummy', 'name': 'engine2', 'shortcut': 'e2'}]
+
+ engines.initialize_engines(engine_list)
+ self.assertEqual(len(engines.engines), 2)
+ self.assertIn('engine1', engines.engines)
+ self.assertIn('engine2', engines.engines)
+
+ def test_initialize_engines_exclude_onions(self):
+ settings['outgoing']['using_tor_proxy'] = False
+ engine_list = [{'engine': 'dummy', 'name': 'engine1', 'shortcut': 'e1', 'categories': 'general'},
+ {'engine': 'dummy', 'name': 'engine2', 'shortcut': 'e2', 'categories': 'onions'}]
+
+ engines.initialize_engines(engine_list)
+ self.assertEqual(len(engines.engines), 1)
+ self.assertIn('engine1', engines.engines)
+ self.assertNotIn('onions', engines.categories)
+
+ def test_initialize_engines_include_onions(self):
+ settings['outgoing']['using_tor_proxy'] = True
+ settings['outgoing']['extra_proxy_timeout'] = 100.0
+ engine_list = [{'engine': 'dummy', 'name': 'engine1', 'shortcut': 'e1', 'categories': 'general',
+ 'timeout': 20.0, 'onion_url': 'http://engine1.onion'},
+ {'engine': 'dummy', 'name': 'engine2', 'shortcut': 'e2', 'categories': 'onions'}]
+
+ engines.initialize_engines(engine_list)
+ self.assertEqual(len(engines.engines), 2)
+ self.assertIn('engine1', engines.engines)
+ self.assertIn('engine2', engines.engines)
+ self.assertIn('onions', engines.categories)
+ self.assertIn('http://engine1.onion', engines.engines['engine1'].search_url)
+ self.assertEqual(engines.engines['engine1'].timeout, 120.0)
diff --git a/tests/unit/test_plugins.py b/tests/unit/test_plugins.py
index 10de847..9ef4cd6 100644
--- a/tests/unit/test_plugins.py
+++ b/tests/unit/test_plugins.py
@@ -31,10 +31,10 @@ class PluginStoreTest(SearxTestCase):
request = Mock()
store.call([], 'asdf', request, Mock())
- self.assertFalse(testplugin.asdf.called)
+ self.assertFalse(testplugin.asdf.called) # pylint: disable=E1101
store.call([testplugin], 'asdf', request, Mock())
- self.assertTrue(testplugin.asdf.called)
+ self.assertTrue(testplugin.asdf.called) # pylint: disable=E1101
class SelfIPTest(SearxTestCase):
@@ -48,11 +48,11 @@ class SelfIPTest(SearxTestCase):
# IP test
request = Mock(remote_addr='127.0.0.1')
request.headers.getlist.return_value = []
- search = get_search_mock(query=b'ip', pageno=1)
+ search = get_search_mock(query='ip', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertTrue('127.0.0.1' in search.result_container.answers["ip"]["answer"])
- search = get_search_mock(query=b'ip', pageno=2)
+ search = get_search_mock(query='ip', pageno=2)
store.call(store.plugins, 'post_search', request, search)
self.assertFalse('ip' in search.result_container.answers)
@@ -60,26 +60,80 @@ class SelfIPTest(SearxTestCase):
request = Mock(user_agent='Mock')
request.headers.getlist.return_value = []
- search = get_search_mock(query=b'user-agent', pageno=1)
+ search = get_search_mock(query='user-agent', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertTrue('Mock' in search.result_container.answers["user-agent"]["answer"])
- search = get_search_mock(query=b'user-agent', pageno=2)
+ search = get_search_mock(query='user-agent', pageno=2)
store.call(store.plugins, 'post_search', request, search)
self.assertFalse('user-agent' in search.result_container.answers)
- search = get_search_mock(query=b'user-agent', pageno=1)
+ search = get_search_mock(query='user-agent', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertTrue('Mock' in search.result_container.answers["user-agent"]["answer"])
- search = get_search_mock(query=b'user-agent', pageno=2)
+ search = get_search_mock(query='user-agent', pageno=2)
store.call(store.plugins, 'post_search', request, search)
self.assertFalse('user-agent' in search.result_container.answers)
- search = get_search_mock(query=b'What is my User-Agent?', pageno=1)
+ search = get_search_mock(query='What is my User-Agent?', pageno=1)
store.call(store.plugins, 'post_search', request, search)
self.assertTrue('Mock' in search.result_container.answers["user-agent"]["answer"])
- search = get_search_mock(query=b'What is my User-Agent?', pageno=2)
+ search = get_search_mock(query='What is my User-Agent?', pageno=2)
store.call(store.plugins, 'post_search', request, search)
self.assertFalse('user-agent' in search.result_container.answers)
+
+
+class HashPluginTest(SearxTestCase):
+
+ def test_PluginStore_init(self):
+ store = plugins.PluginStore()
+ store.register(plugins.hash_plugin)
+
+ self.assertTrue(len(store.plugins) == 1)
+
+ request = Mock(remote_addr='127.0.0.1')
+ request.headers.getlist.return_value = []
+
+ # MD5
+ search = get_search_mock(query='md5 test', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('md5 hash digest: 098f6bcd4621d373cade4e832627b4f6'
+ in search.result_container.answers['hash']['answer'])
+
+ search = get_search_mock(query=b'md5 test', pageno=2)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertFalse('hash' in search.result_container.answers)
+
+ # SHA1
+ search = get_search_mock(query='sha1 test', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('sha1 hash digest: a94a8fe5ccb19ba61c4c0873d391e9879'
+ '82fbbd3' in search.result_container.answers['hash']['answer'])
+
+ # SHA224
+ search = get_search_mock(query='sha224 test', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('sha224 hash digest: 90a3ed9e32b2aaf4c61c410eb9254261'
+ '19e1a9dc53d4286ade99a809' in search.result_container.answers['hash']['answer'])
+
+ # SHA256
+ search = get_search_mock(query='sha256 test', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('sha256 hash digest: 9f86d081884c7d659a2feaa0c55ad015a'
+ '3bf4f1b2b0b822cd15d6c15b0f00a08' in search.result_container.answers['hash']['answer'])
+
+ # SHA384
+ search = get_search_mock(query='sha384 test', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('sha384 hash digest: 768412320f7b0aa5812fce428dc4706b3c'
+ 'ae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf1'
+ '7a0a9' in search.result_container.answers['hash']['answer'])
+
+ # SHA512
+ search = get_search_mock(query='sha512 test', pageno=1)
+ store.call(store.plugins, 'post_search', request, search)
+ self.assertTrue('sha512 hash digest: ee26b0dd4af7e749aa1a8ee3c10ae9923f6'
+ '18980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5'
+ 'fa9ad8e6f57f50028a8ff' in search.result_container.answers['hash']['answer'])
diff --git a/tests/unit/test_poolrequests.py b/tests/unit/test_poolrequests.py
new file mode 100644
index 0000000..b22685f
--- /dev/null
+++ b/tests/unit/test_poolrequests.py
@@ -0,0 +1,89 @@
+from unittest.mock import patch
+from requests.models import Response
+
+from searx.testing import SearxTestCase
+
+import searx.poolrequests
+from searx.poolrequests import get_proxy_cycles, get_proxies
+
+
+CONFIG = {'http': ['http://localhost:9090', 'http://localhost:9092'],
+ 'https': ['http://localhost:9091', 'http://localhost:9093']}
+
+
+class TestProxy(SearxTestCase):
+
+ def test_noconfig(self):
+ cycles = get_proxy_cycles(None)
+ self.assertIsNone(cycles)
+
+ cycles = get_proxy_cycles(False)
+ self.assertIsNone(cycles)
+
+ def test_oldconfig(self):
+ config = {
+ 'http': 'http://localhost:9090',
+ 'https': 'http://localhost:9091',
+ }
+ cycles = get_proxy_cycles(config)
+ self.assertEqual(next(cycles['http']), 'http://localhost:9090')
+ self.assertEqual(next(cycles['http']), 'http://localhost:9090')
+ self.assertEqual(next(cycles['https']), 'http://localhost:9091')
+ self.assertEqual(next(cycles['https']), 'http://localhost:9091')
+
+ def test_one_proxy(self):
+ config = {
+ 'http': ['http://localhost:9090'],
+ 'https': ['http://localhost:9091'],
+ }
+ cycles = get_proxy_cycles(config)
+ self.assertEqual(next(cycles['http']), 'http://localhost:9090')
+ self.assertEqual(next(cycles['http']), 'http://localhost:9090')
+ self.assertEqual(next(cycles['https']), 'http://localhost:9091')
+ self.assertEqual(next(cycles['https']), 'http://localhost:9091')
+
+ def test_multiple_proxies(self):
+ cycles = get_proxy_cycles(CONFIG)
+ self.assertEqual(next(cycles['http']), 'http://localhost:9090')
+ self.assertEqual(next(cycles['http']), 'http://localhost:9092')
+ self.assertEqual(next(cycles['http']), 'http://localhost:9090')
+ self.assertEqual(next(cycles['https']), 'http://localhost:9091')
+ self.assertEqual(next(cycles['https']), 'http://localhost:9093')
+ self.assertEqual(next(cycles['https']), 'http://localhost:9091')
+
+ def test_getproxies_none(self):
+ self.assertIsNone(get_proxies(None))
+
+ def test_getproxies_config(self):
+ cycles = get_proxy_cycles(CONFIG)
+ self.assertEqual(get_proxies(cycles), {
+ 'http': 'http://localhost:9090',
+ 'https': 'http://localhost:9091'
+ })
+ self.assertEqual(get_proxies(cycles), {
+ 'http': 'http://localhost:9092',
+ 'https': 'http://localhost:9093'
+ })
+
+ @patch('searx.poolrequests.get_global_proxies')
+ def test_request(self, mock_get_global_proxies):
+ method = 'GET'
+ url = 'http://localhost'
+ custom_proxies = {
+ 'https': 'http://localhost:1080'
+ }
+ global_proxies = {
+ 'http': 'http://localhost:9092',
+ 'https': 'http://localhost:9093'
+ }
+ mock_get_global_proxies.return_value = global_proxies
+
+ # check the global proxies usage
+ with patch.object(searx.poolrequests.SessionSinglePool, 'request', return_value=Response()) as mock_method:
+ searx.poolrequests.request(method, url)
+ mock_method.assert_called_once_with(method=method, url=url, proxies=global_proxies)
+
+ # check if the proxies parameter overrides the global proxies
+ with patch.object(searx.poolrequests.SessionSinglePool, 'request', return_value=Response()) as mock_method:
+ searx.poolrequests.request(method, url, proxies=custom_proxies)
+ mock_method.assert_called_once_with(method=method, url=url, proxies=custom_proxies)
diff --git a/tests/unit/test_preferences.py b/tests/unit/test_preferences.py
index 61ac0e8..510d498 100644
--- a/tests/unit/test_preferences.py
+++ b/tests/unit/test_preferences.py
@@ -3,10 +3,10 @@ from searx.preferences import (EnumStringSetting, MapSetting, MissingArgumentExc
from searx.testing import SearxTestCase
-class PluginStub(object):
+class PluginStub:
- def __init__(self, id, default_on):
- self.id = id
+ def __init__(self, plugin_id, default_on):
+ self.id = plugin_id
self.default_on = default_on
@@ -15,11 +15,11 @@ class TestSettings(SearxTestCase):
def test_map_setting_invalid_initialization(self):
with self.assertRaises(MissingArgumentException):
- setting = MapSetting(3, wrong_argument={'0': 0})
+ MapSetting(3, wrong_argument={'0': 0})
def test_map_setting_invalid_default_value(self):
with self.assertRaises(ValidationException):
- setting = MapSetting(3, map={'dog': 1, 'bat': 2})
+ MapSetting(3, map={'dog': 1, 'bat': 2})
def test_map_setting_invalid_choice(self):
setting = MapSetting(2, map={'dog': 1, 'bat': 2})
@@ -28,26 +28,22 @@ class TestSettings(SearxTestCase):
def test_map_setting_valid_default(self):
setting = MapSetting(3, map={'dog': 1, 'bat': 2, 'cat': 3})
- self.assertEquals(setting.get_value(), 3)
+ self.assertEqual(setting.get_value(), 3)
def test_map_setting_valid_choice(self):
setting = MapSetting(3, map={'dog': 1, 'bat': 2, 'cat': 3})
- self.assertEquals(setting.get_value(), 3)
+ self.assertEqual(setting.get_value(), 3)
setting.parse('bat')
- self.assertEquals(setting.get_value(), 2)
-
- def test_enum_setting_invalid_initialization(self):
- with self.assertRaises(MissingArgumentException):
- setting = EnumStringSetting('cat', wrong_argument=[0, 1, 2])
+ self.assertEqual(setting.get_value(), 2)
# enum settings
def test_enum_setting_invalid_initialization(self):
with self.assertRaises(MissingArgumentException):
- setting = EnumStringSetting('cat', wrong_argument=[0, 1, 2])
+ EnumStringSetting('cat', wrong_argument=[0, 1, 2])
def test_enum_setting_invalid_default_value(self):
with self.assertRaises(ValidationException):
- setting = EnumStringSetting(3, choices=[0, 1, 2])
+ EnumStringSetting(3, choices=[0, 1, 2])
def test_enum_setting_invalid_choice(self):
setting = EnumStringSetting(0, choices=[0, 1, 2])
@@ -56,22 +52,22 @@ class TestSettings(SearxTestCase):
def test_enum_setting_valid_default(self):
setting = EnumStringSetting(3, choices=[1, 2, 3])
- self.assertEquals(setting.get_value(), 3)
+ self.assertEqual(setting.get_value(), 3)
def test_enum_setting_valid_choice(self):
setting = EnumStringSetting(3, choices=[1, 2, 3])
- self.assertEquals(setting.get_value(), 3)
+ self.assertEqual(setting.get_value(), 3)
setting.parse(2)
- self.assertEquals(setting.get_value(), 2)
+ self.assertEqual(setting.get_value(), 2)
# multiple choice settings
def test_multiple_setting_invalid_initialization(self):
with self.assertRaises(MissingArgumentException):
- setting = MultipleChoiceSetting(['2'], wrong_argument=['0', '1', '2'])
+ MultipleChoiceSetting(['2'], wrong_argument=['0', '1', '2'])
def test_multiple_setting_invalid_default_value(self):
with self.assertRaises(ValidationException):
- setting = MultipleChoiceSetting(['3', '4'], choices=['0', '1', '2'])
+ MultipleChoiceSetting(['3', '4'], choices=['0', '1', '2'])
def test_multiple_setting_invalid_choice(self):
setting = MultipleChoiceSetting(['1', '2'], choices=['0', '1', '2'])
@@ -80,48 +76,48 @@ class TestSettings(SearxTestCase):
def test_multiple_setting_valid_default(self):
setting = MultipleChoiceSetting(['3'], choices=['1', '2', '3'])
- self.assertEquals(setting.get_value(), ['3'])
+ self.assertEqual(setting.get_value(), ['3'])
def test_multiple_setting_valid_choice(self):
setting = MultipleChoiceSetting(['3'], choices=['1', '2', '3'])
- self.assertEquals(setting.get_value(), ['3'])
+ self.assertEqual(setting.get_value(), ['3'])
setting.parse('2')
- self.assertEquals(setting.get_value(), ['2'])
+ self.assertEqual(setting.get_value(), ['2'])
# search language settings
def test_lang_setting_valid_choice(self):
setting = SearchLanguageSetting('all', choices=['all', 'de', 'en'])
setting.parse('de')
- self.assertEquals(setting.get_value(), 'de')
+ self.assertEqual(setting.get_value(), 'de')
def test_lang_setting_invalid_choice(self):
setting = SearchLanguageSetting('all', choices=['all', 'de', 'en'])
setting.parse('xx')
- self.assertEquals(setting.get_value(), 'all')
+ self.assertEqual(setting.get_value(), 'all')
def test_lang_setting_old_cookie_choice(self):
setting = SearchLanguageSetting('all', choices=['all', 'es', 'es-ES'])
setting.parse('es_XA')
- self.assertEquals(setting.get_value(), 'es')
+ self.assertEqual(setting.get_value(), 'es')
def test_lang_setting_old_cookie_format(self):
setting = SearchLanguageSetting('all', choices=['all', 'es', 'es-ES'])
setting.parse('es_ES')
- self.assertEquals(setting.get_value(), 'es-ES')
+ self.assertEqual(setting.get_value(), 'es-ES')
# plugins settings
def test_plugins_setting_all_default_enabled(self):
plugin1 = PluginStub('plugin1', True)
plugin2 = PluginStub('plugin2', True)
setting = PluginsSetting(['3'], choices=[plugin1, plugin2])
- self.assertEquals(setting.get_enabled(), set(['plugin1', 'plugin2']))
+ self.assertEqual(setting.get_enabled(), set(['plugin1', 'plugin2']))
def test_plugins_setting_few_default_enabled(self):
plugin1 = PluginStub('plugin1', True)
plugin2 = PluginStub('plugin2', False)
plugin3 = PluginStub('plugin3', True)
setting = PluginsSetting('name', choices=[plugin1, plugin2, plugin3])
- self.assertEquals(setting.get_enabled(), set(['plugin1', 'plugin3']))
+ self.assertEqual(setting.get_enabled(), set(['plugin1', 'plugin3']))
class TestPreferences(SearxTestCase):
@@ -141,4 +137,4 @@ class TestPreferences(SearxTestCase):
pref.parse_encoded_data(url_params)
self.assertEqual(
vars(pref.key_value_settings['categories']),
- {'value': ['general'], 'choices': ['general', 'none']})
+ {'value': ['general'], 'locked': False, 'choices': ['general', 'none']})
diff --git a/tests/unit/test_query.py b/tests/unit/test_query.py
index e4c0bde..eff4d64 100644
--- a/tests/unit/test_query.py
+++ b/tests/unit/test_query.py
@@ -7,11 +7,11 @@ class TestQuery(SearxTestCase):
def test_simple_query(self):
query_text = 'the query'
query = RawTextQuery(query_text, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), query_text)
- self.assertEquals(len(query.query_parts), 1)
- self.assertEquals(len(query.languages), 0)
+ self.assertEqual(query.getFullQuery(), query_text)
+ self.assertEqual(len(query.query_parts), 0)
+ self.assertEqual(len(query.user_query_parts), 2)
+ self.assertEqual(len(query.languages), 0)
self.assertFalse(query.specific)
def test_language_code(self):
@@ -19,11 +19,10 @@ class TestQuery(SearxTestCase):
query_text = 'the query'
full_query = ':' + language + ' ' + query_text
query = RawTextQuery(full_query, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), full_query)
- self.assertEquals(len(query.query_parts), 3)
- self.assertEquals(len(query.languages), 1)
+ self.assertEqual(query.getFullQuery(), full_query)
+ self.assertEqual(len(query.query_parts), 1)
+ self.assertEqual(len(query.languages), 1)
self.assertIn(language, query.languages)
self.assertFalse(query.specific)
@@ -32,10 +31,9 @@ class TestQuery(SearxTestCase):
query_text = 'the query'
full_query = ':' + language + ' ' + query_text
query = RawTextQuery(full_query, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), full_query)
- self.assertEquals(len(query.query_parts), 3)
+ self.assertEqual(query.getFullQuery(), full_query)
+ self.assertEqual(len(query.query_parts), 1)
self.assertIn('en', query.languages)
self.assertFalse(query.specific)
@@ -44,10 +42,9 @@ class TestQuery(SearxTestCase):
query_text = 'the query'
full_query = ':' + language + ' ' + query_text
query = RawTextQuery(full_query, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), full_query)
- self.assertEquals(len(query.query_parts), 3)
+ self.assertEqual(query.getFullQuery(), full_query)
+ self.assertEqual(len(query.query_parts), 1)
self.assertIn('all', query.languages)
self.assertFalse(query.specific)
@@ -56,51 +53,46 @@ class TestQuery(SearxTestCase):
query_text = 'the query'
full_query = ':' + language + ' ' + query_text
query = RawTextQuery(full_query, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), full_query)
- self.assertEquals(len(query.query_parts), 1)
- self.assertEquals(len(query.languages), 0)
+ self.assertEqual(query.getFullQuery(), full_query)
+ self.assertEqual(len(query.query_parts), 0)
+ self.assertEqual(len(query.languages), 0)
self.assertFalse(query.specific)
def test_timeout_below100(self):
query_text = '<3 the query'
query = RawTextQuery(query_text, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), query_text)
- self.assertEquals(len(query.query_parts), 3)
- self.assertEquals(query.timeout_limit, 3)
+ self.assertEqual(query.getFullQuery(), query_text)
+ self.assertEqual(len(query.query_parts), 1)
+ self.assertEqual(query.timeout_limit, 3)
self.assertFalse(query.specific)
def test_timeout_above100(self):
query_text = '<350 the query'
query = RawTextQuery(query_text, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), query_text)
- self.assertEquals(len(query.query_parts), 3)
- self.assertEquals(query.timeout_limit, 0.35)
+ self.assertEqual(query.getFullQuery(), query_text)
+ self.assertEqual(len(query.query_parts), 1)
+ self.assertEqual(query.timeout_limit, 0.35)
self.assertFalse(query.specific)
def test_timeout_above1000(self):
query_text = '<3500 the query'
query = RawTextQuery(query_text, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), query_text)
- self.assertEquals(len(query.query_parts), 3)
- self.assertEquals(query.timeout_limit, 3.5)
+ self.assertEqual(query.getFullQuery(), query_text)
+ self.assertEqual(len(query.query_parts), 1)
+ self.assertEqual(query.timeout_limit, 3.5)
self.assertFalse(query.specific)
def test_timeout_invalid(self):
# invalid number: it is not bang but it is part of the query
query_text = '<xxx the query'
query = RawTextQuery(query_text, [])
- query.parse_query()
- self.assertEquals(query.getFullQuery(), query_text)
- self.assertEquals(len(query.query_parts), 1)
- self.assertEquals(query.query_parts[0], query_text)
- self.assertEquals(query.timeout_limit, None)
+ self.assertEqual(query.getFullQuery(), query_text)
+ self.assertEqual(len(query.query_parts), 0)
+ self.assertEqual(query.getQuery(), query_text)
+ self.assertEqual(query.timeout_limit, None)
self.assertFalse(query.specific)
diff --git a/tests/unit/test_search.py b/tests/unit/test_search.py
index ca9fe86..464a9b3 100644
--- a/tests/unit/test_search.py
+++ b/tests/unit/test_search.py
@@ -1,16 +1,14 @@
# -*- coding: utf-8 -*-
from searx.testing import SearxTestCase
-from searx.preferences import Preferences
-from searx.engines import engines
-
+from searx.search import SearchQuery, EngineRef
import searx.search
+import searx.engines
SAFESEARCH = 0
PAGENO = 1
PUBLIC_ENGINE_NAME = 'general dummy'
-PRIVATE_ENGINE_NAME = 'general private offline'
TEST_ENGINES = [
{
'name': PUBLIC_ENGINE_NAME,
@@ -20,18 +18,23 @@ TEST_ENGINES = [
'timeout': 3.0,
'tokens': [],
},
- {
- 'name': PRIVATE_ENGINE_NAME,
- 'engine': 'dummy-offline',
- 'categories': 'general',
- 'shortcut': 'do',
- 'timeout': 3.0,
- 'offline': True,
- 'tokens': ['my-token'],
- },
]
+class SearchQueryTestCase(SearxTestCase):
+
+ def test_repr(self):
+ s = SearchQuery('test', [EngineRef('bing', 'general', False)], ['general'], 'all', 0, 1, '1', 5.0, 'g')
+ self.assertEqual(repr(s),
+ "SearchQuery('test', [EngineRef('bing', 'general', False)], ['general'], 'all', 0, 1, '1', 5.0, 'g')") # noqa
+
+ def test_eq(self):
+ s = SearchQuery('test', [EngineRef('bing', 'general', False)], ['general'], 'all', 0, 1, None, None, None)
+ t = SearchQuery('test', [EngineRef('google', 'general', False)], ['general'], 'all', 0, 1, None, None, None)
+ self.assertEqual(s, s)
+ self.assertNotEqual(s, t)
+
+
class SearchTestCase(SearxTestCase):
@classmethod
@@ -40,92 +43,57 @@ class SearchTestCase(SearxTestCase):
def test_timeout_simple(self):
searx.search.max_request_timeout = None
- search_query = searx.query.SearchQuery('test', [{'category': 'general', 'name': PUBLIC_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, None,
- preferences=Preferences(['oscar'], ['general'], engines, []))
+ search_query = SearchQuery('test', [EngineRef(PUBLIC_ENGINE_NAME, 'general')],
+ ['general'], 'en-US', SAFESEARCH, PAGENO, None, None)
search = searx.search.Search(search_query)
search.search()
- self.assertEquals(search.actual_timeout, 3.0)
+ self.assertEqual(search.actual_timeout, 3.0)
def test_timeout_query_above_default_nomax(self):
searx.search.max_request_timeout = None
- search_query = searx.query.SearchQuery('test', [{'category': 'general', 'name': PUBLIC_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, 5.0,
- preferences=Preferences(['oscar'], ['general'], engines, []))
+ search_query = SearchQuery('test', [EngineRef(PUBLIC_ENGINE_NAME, 'general')],
+ ['general'], 'en-US', SAFESEARCH, PAGENO, None, 5.0)
search = searx.search.Search(search_query)
search.search()
- self.assertEquals(search.actual_timeout, 3.0)
+ self.assertEqual(search.actual_timeout, 3.0)
def test_timeout_query_below_default_nomax(self):
searx.search.max_request_timeout = None
- search_query = searx.query.SearchQuery('test', [{'category': 'general', 'name': PUBLIC_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, 1.0,
- preferences=Preferences(['oscar'], ['general'], engines, []))
+ search_query = SearchQuery('test', [EngineRef(PUBLIC_ENGINE_NAME, 'general')],
+ ['general'], 'en-US', SAFESEARCH, PAGENO, None, 1.0)
search = searx.search.Search(search_query)
search.search()
- self.assertEquals(search.actual_timeout, 1.0)
+ self.assertEqual(search.actual_timeout, 1.0)
def test_timeout_query_below_max(self):
searx.search.max_request_timeout = 10.0
- search_query = searx.query.SearchQuery('test', [{'category': 'general', 'name': PUBLIC_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, 5.0,
- preferences=Preferences(['oscar'], ['general'], engines, []))
+ search_query = SearchQuery('test', [EngineRef(PUBLIC_ENGINE_NAME, 'general')],
+ ['general'], 'en-US', SAFESEARCH, PAGENO, None, 5.0)
search = searx.search.Search(search_query)
search.search()
- self.assertEquals(search.actual_timeout, 5.0)
+ self.assertEqual(search.actual_timeout, 5.0)
def test_timeout_query_above_max(self):
searx.search.max_request_timeout = 10.0
- search_query = searx.query.SearchQuery('test', [{'category': 'general', 'name': PUBLIC_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, 15.0,
- preferences=Preferences(['oscar'], ['general'], engines, []))
+ search_query = SearchQuery('test', [EngineRef(PUBLIC_ENGINE_NAME, 'general')],
+ ['general'], 'en-US', SAFESEARCH, PAGENO, None, 15.0)
search = searx.search.Search(search_query)
search.search()
- self.assertEquals(search.actual_timeout, 10.0)
-
- def test_query_private_engine_without_token(self):
- search_query = searx.query.SearchQuery('test', [{'category': 'general', 'name': PRIVATE_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, 2.0,
- preferences=Preferences(['oscar'], ['general'], engines, []))
- search = searx.search.Search(search_query)
- results = search.search()
- self.assertEquals(results.results_length(), 0)
-
- def test_query_private_engine_with_incorrect_token(self):
- preferences_with_tokens = Preferences(['oscar'], ['general'], engines, [])
- preferences_with_tokens.parse_dict({'tokens': 'bad-token'})
- search_query = searx.query.SearchQuery('test', [{'category': 'general', 'name': PRIVATE_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, 2.0,
- preferences=preferences_with_tokens)
- search = searx.search.Search(search_query)
- results = search.search()
- self.assertEquals(results.results_length(), 0)
-
- def test_query_private_engine_with_correct_token(self):
- preferences_with_tokens = Preferences(['oscar'], ['general'], engines, [])
- preferences_with_tokens.parse_dict({'tokens': 'my-token'})
- search_query = searx.query.SearchQuery('test', [{'category': 'general', 'name': PRIVATE_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, 2.0,
- preferences=preferences_with_tokens)
- search = searx.search.Search(search_query)
- results = search.search()
- self.assertEquals(results.results_length(), 1)
+ self.assertEqual(search.actual_timeout, 10.0)
def test_external_bang(self):
- search_query = searx.query.SearchQuery('yes yes',
- [{'category': 'general', 'name': PUBLIC_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, None,
- preferences=Preferences(['oscar'], ['general'], engines, [],),
- external_bang="yt")
+ search_query = SearchQuery('yes yes',
+ [EngineRef(PUBLIC_ENGINE_NAME, 'general')],
+ ['general'], 'en-US', SAFESEARCH, PAGENO, None, None,
+ external_bang="yt")
search = searx.search.Search(search_query)
results = search.search()
# For checking if the user redirected with the youtube external bang
self.assertTrue(results.redirect_url is not None)
- search_query = searx.query.SearchQuery('youtube never gonna give you up',
- [{'category': 'general', 'name': PUBLIC_ENGINE_NAME}],
- ['general'], 'en-US', SAFESEARCH, PAGENO, None, None,
- preferences=Preferences(['oscar'], ['general'], engines, []),)
+ search_query = SearchQuery('youtube never gonna give you up',
+ [EngineRef(PUBLIC_ENGINE_NAME, 'general')],
+ ['general'], 'en-US', SAFESEARCH, PAGENO, None, None)
search = searx.search.Search(search_query)
results = search.search()
diff --git a/tests/unit/test_settings_loader.py b/tests/unit/test_settings_loader.py
new file mode 100644
index 0000000..7df64e5
--- /dev/null
+++ b/tests/unit/test_settings_loader.py
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+from os.path import dirname, join, abspath
+from unittest.mock import patch
+
+from searx.testing import SearxTestCase
+from searx.exceptions import SearxSettingsException
+from searx import settings_loader
+
+
+test_dir = abspath(dirname(__file__))
+
+
+class TestLoad(SearxTestCase):
+
+ def test_load_zero(self):
+ with self.assertRaises(SearxSettingsException):
+ settings_loader.load_yaml('/dev/zero')
+
+ with self.assertRaises(SearxSettingsException):
+ settings_loader.load_yaml(join(test_dir, '/settings/syntaxerror_settings.yml'))
+
+ with self.assertRaises(SearxSettingsException):
+ settings_loader.load_yaml(join(test_dir, '/settings/empty_settings.yml'))
+
+ def test_check_settings_yml(self):
+ self.assertIsNone(settings_loader.check_settings_yml('/dev/zero'))
+
+ bad_settings_path = join(test_dir, 'settings/syntaxerror_settings.yml')
+ self.assertEqual(settings_loader.check_settings_yml(bad_settings_path), bad_settings_path)
+
+
+class TestDefaultSettings(SearxTestCase):
+
+ def test_load(self):
+ settings, msg = settings_loader.load_settings(load_user_setttings=False)
+ self.assertTrue(msg.startswith('load the default settings from'))
+ self.assertFalse(settings['general']['debug'])
+ self.assertTrue(isinstance(settings['general']['instance_name'], str))
+ self.assertEqual(settings['server']['secret_key'], "ultrasecretkey")
+ self.assertTrue(isinstance(settings['server']['port'], int))
+ self.assertTrue(isinstance(settings['server']['bind_address'], str))
+ self.assertTrue(isinstance(settings['engines'], list))
+ self.assertTrue(isinstance(settings['locales'], dict))
+ self.assertTrue(isinstance(settings['doi_resolvers'], dict))
+ self.assertTrue(isinstance(settings['default_doi_resolver'], str))
+
+
+class TestUserSettings(SearxTestCase):
+
+ def test_is_use_default_settings(self):
+ self.assertFalse(settings_loader.is_use_default_settings({}))
+ self.assertTrue(settings_loader.is_use_default_settings({'use_default_settings': True}))
+ self.assertTrue(settings_loader.is_use_default_settings({'use_default_settings': {}}))
+ with self.assertRaises(ValueError):
+ self.assertFalse(settings_loader.is_use_default_settings({'use_default_settings': 1}))
+ with self.assertRaises(ValueError):
+ self.assertFalse(settings_loader.is_use_default_settings({'use_default_settings': 0}))
+
+ def test_user_settings_not_found(self):
+ with patch.dict(settings_loader.environ,
+ {'SEARX_SETTINGS_PATH': '/dev/null'}):
+ settings, msg = settings_loader.load_settings()
+ self.assertTrue(msg.startswith('load the default settings from'))
+ self.assertEqual(settings['server']['secret_key'], "ultrasecretkey")
+
+ def test_user_settings(self):
+ with patch.dict(settings_loader.environ,
+ {'SEARX_SETTINGS_PATH': join(test_dir, 'settings/user_settings_simple.yml')}):
+ settings, msg = settings_loader.load_settings()
+ self.assertTrue(msg.startswith('merge the default settings'))
+ self.assertEqual(settings['server']['secret_key'], "user_secret_key")
+ self.assertEqual(settings['server']['default_http_headers']['Custom-Header'], "Custom-Value")
+
+ def test_user_settings_remove(self):
+ with patch.dict(settings_loader.environ,
+ {'SEARX_SETTINGS_PATH': join(test_dir, 'settings/user_settings_remove.yml')}):
+ settings, msg = settings_loader.load_settings()
+ self.assertTrue(msg.startswith('merge the default settings'))
+ self.assertEqual(settings['server']['secret_key'], "user_secret_key")
+ self.assertEqual(settings['server']['default_http_headers']['Custom-Header'], "Custom-Value")
+ engine_names = [engine['name'] for engine in settings['engines']]
+ self.assertNotIn('wikinews', engine_names)
+ self.assertNotIn('wikibooks', engine_names)
+ self.assertIn('wikipedia', engine_names)
+
+ def test_user_settings_remove2(self):
+ with patch.dict(settings_loader.environ,
+ {'SEARX_SETTINGS_PATH': join(test_dir, 'settings/user_settings_remove2.yml')}):
+ settings, msg = settings_loader.load_settings()
+ self.assertTrue(msg.startswith('merge the default settings'))
+ self.assertEqual(settings['server']['secret_key'], "user_secret_key")
+ self.assertEqual(settings['server']['default_http_headers']['Custom-Header'], "Custom-Value")
+ engine_names = [engine['name'] for engine in settings['engines']]
+ self.assertNotIn('wikinews', engine_names)
+ self.assertNotIn('wikibooks', engine_names)
+ self.assertIn('wikipedia', engine_names)
+ wikipedia = list(filter(lambda engine: (engine.get('name')) == 'wikipedia', settings['engines']))
+ self.assertEqual(wikipedia[0]['engine'], 'wikipedia')
+ self.assertEqual(wikipedia[0]['tokens'], ['secret_token'])
+ newengine = list(filter(lambda engine: (engine.get('name')) == 'newengine', settings['engines']))
+ self.assertEqual(newengine[0]['engine'], 'dummy')
+
+ def test_user_settings_keep_only(self):
+ with patch.dict(settings_loader.environ,
+ {'SEARX_SETTINGS_PATH': join(test_dir, 'settings/user_settings_keep_only.yml')}):
+ settings, msg = settings_loader.load_settings()
+ self.assertTrue(msg.startswith('merge the default settings'))
+ engine_names = [engine['name'] for engine in settings['engines']]
+ self.assertEqual(engine_names, ['wikibooks', 'wikinews', 'wikipedia', 'newengine'])
+ # wikipedia has been removed, then added again with the "engine" section of user_settings_keep_only.yml
+ self.assertEqual(len(settings['engines'][2]), 1)
+
+ def test_custom_settings(self):
+ with patch.dict(settings_loader.environ,
+ {'SEARX_SETTINGS_PATH': join(test_dir, 'settings/user_settings.yml')}):
+ settings, msg = settings_loader.load_settings()
+ self.assertTrue(msg.startswith('load the user settings from'))
+ self.assertEqual(settings['server']['port'], 9000)
+ self.assertEqual(settings['server']['secret_key'], "user_settings_secret")
+ engine_names = [engine['name'] for engine in settings['engines']]
+ self.assertEqual(engine_names, ['wikidata', 'wikibooks', 'wikinews', 'wikiquote'])
diff --git a/tests/unit/test_standalone_searx.py b/tests/unit/test_standalone_searx.py
new file mode 100644
index 0000000..74e3136
--- /dev/null
+++ b/tests/unit/test_standalone_searx.py
@@ -0,0 +1,130 @@
+# -*- coding: utf-8 -*-
+"""Test utils/standalone_searx.py"""
+import datetime
+import importlib.util
+import io
+import sys
+
+from mock import Mock, patch
+from nose2.tools import params
+
+from searx.search import SearchQuery, EngineRef
+from searx.engines import initialize_engines
+from searx.testing import SearxTestCase
+
+
+def get_standalone_searx_module():
+ """Get standalone_searx module."""
+ module_name = 'utils.standalone_searx'
+ filename = 'utils/standalone_searx.py'
+ spec = importlib.util.spec_from_file_location(module_name, filename)
+ sas = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(sas)
+ return sas
+
+
+class StandaloneSearx(SearxTestCase):
+ """Unit test for standalone_searx."""
+
+ @classmethod
+ def setUpClass(cls):
+ engine_list = [{'engine': 'dummy', 'name': 'engine1', 'shortcut': 'e1'}]
+
+ initialize_engines(engine_list)
+
+ def test_parse_argument_no_args(self):
+ """Test parse argument without args."""
+ sas = get_standalone_searx_module()
+ with patch.object(sys, 'argv', ['standalone_searx']), \
+ self.assertRaises(SystemExit):
+ sys.stderr = io.StringIO()
+ sas.parse_argument()
+ sys.stdout = sys.__stderr__
+
+ def test_parse_argument_basic_args(self):
+ """Test parse argument with basic args."""
+ sas = get_standalone_searx_module()
+ query = 'red box'
+ exp_dict = {
+ 'query': query, 'category': 'general', 'lang': 'all', 'pageno': 1,
+ 'safesearch': '0', 'timerange': None}
+ args = ['standalone_searx', query]
+ with patch.object(sys, 'argv', args):
+ res = sas.parse_argument()
+ self.assertEqual(exp_dict, vars(res))
+ res2 = sas.parse_argument(args[1:])
+ self.assertEqual(exp_dict, vars(res2))
+
+ def test_to_dict(self):
+ """test to_dict."""
+ sas = get_standalone_searx_module()
+ self.assertEqual(
+ sas.to_dict(
+ sas.get_search_query(sas.parse_argument(['red box']))),
+ {
+ 'search': {
+ 'q': 'red box', 'pageno': 1, 'lang': 'all',
+ 'safesearch': 0, 'timerange': None
+ },
+ 'results': [], 'infoboxes': [], 'suggestions': [],
+ 'answers': [], 'paging': False, 'results_number': 0
+ }
+ )
+
+ def test_to_dict_with_mock(self):
+ """test to dict."""
+ sas = get_standalone_searx_module()
+ with patch.object(sas.searx.search, 'Search') as mock_s:
+ m_search = mock_s().search()
+ m_sq = Mock()
+ self.assertEqual(
+ sas.to_dict(m_sq),
+ {
+ 'answers': [],
+ 'infoboxes': m_search.infoboxes,
+ 'paging': m_search.paging,
+ 'results': m_search.get_ordered_results(),
+ 'results_number': m_search.results_number(),
+ 'search': {
+ 'lang': m_sq.lang,
+ 'pageno': m_sq.pageno,
+ 'q': m_sq.query,
+ 'safesearch': m_sq.safesearch,
+ 'timerange': m_sq.time_range,
+ },
+ 'suggestions': []
+ }
+ )
+
+ def test_get_search_query(self):
+ """test get_search_query."""
+ sas = get_standalone_searx_module()
+ args = sas.parse_argument(['rain', ])
+ search_q = sas.get_search_query(args)
+ self.assertTrue(search_q)
+ self.assertEqual(search_q, SearchQuery('rain', [EngineRef('engine1', 'general', False)],
+ ['general'], 'all', 0, 1, None, None, None))
+
+ def test_no_parsed_url(self):
+ """test no_parsed_url func"""
+ sas = get_standalone_searx_module()
+ self.assertEqual(
+ sas.no_parsed_url([{'parsed_url': 'http://example.com'}]),
+ [{}]
+ )
+
+ @params(
+ (datetime.datetime(2020, 1, 1), '2020-01-01T00:00:00'),
+ ('a'.encode('utf8'), 'a'),
+ (set([1]), [1])
+ )
+ def test_json_serial(self, arg, exp_res):
+ """test json_serial func"""
+ sas = get_standalone_searx_module()
+ self.assertEqual(sas.json_serial(arg), exp_res)
+
+ def test_json_serial_error(self):
+ """test error on json_serial."""
+ sas = get_standalone_searx_module()
+ with self.assertRaises(TypeError):
+ sas.json_serial('a')
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index b09b9d4..2c24496 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
-import mock
-import sys
+import lxml.etree
+from lxml import html
+
from searx.testing import SearxTestCase
+from searx.exceptions import SearxXPathSyntaxException, SearxEngineXPathException
from searx import utils
-if sys.version_info[0] == 3:
- unicode = str
-
class TestUtils(SearxTestCase):
@@ -20,28 +19,14 @@ class TestUtils(SearxTestCase):
self.assertIsNotNone(utils.searx_useragent())
self.assertTrue(utils.searx_useragent().startswith('searx'))
- def test_highlight_content(self):
- self.assertEqual(utils.highlight_content(0, None), None)
- self.assertEqual(utils.highlight_content(None, None), None)
- self.assertEqual(utils.highlight_content('', None), None)
- self.assertEqual(utils.highlight_content(False, None), None)
-
- contents = [
- '<html></html>'
- 'not<'
- ]
- for content in contents:
- self.assertEqual(utils.highlight_content(content, None), content)
-
- content = 'a'
- query = b'test'
- self.assertEqual(utils.highlight_content(content, query), content)
- query = b'a test'
- self.assertEqual(utils.highlight_content(content, query), content)
-
def test_html_to_text(self):
- html = """
+ html_str = """
<a href="/testlink" class="link_access_account">
+ <style>
+ .toto {
+ color: red;
+ }
+ </style>
<span class="toto">
<span>
<img src="test.jpg" />
@@ -50,20 +35,56 @@ class TestUtils(SearxTestCase):
<span class="titi">
Test text
</span>
+ <script>value='dummy';</script>
</a>
"""
- self.assertIsInstance(utils.html_to_text(html), unicode)
- self.assertIsNotNone(utils.html_to_text(html))
- self.assertEqual(utils.html_to_text(html), "Test text")
+ self.assertIsInstance(utils.html_to_text(html_str), str)
+ self.assertIsNotNone(utils.html_to_text(html_str))
+ self.assertEqual(utils.html_to_text(html_str), "Test text")
- def test_prettify_url(self):
- data = (('https://searx.me/', 'https://searx.me/'),
- (u'https://searx.me/ű', u'https://searx.me/ű'),
- ('https://searx.me/' + (100 * 'a'), 'https://searx.me/[...]aaaaaaaaaaaaaaaaa'),
- (u'https://searx.me/' + (100 * u'ű'), u'https://searx.me/[...]űűűűűűűűűűűűűűűűű'))
-
- for test_url, expected in data:
- self.assertEqual(utils.prettify_url(test_url, max_length=32), expected)
+ def test_extract_text(self):
+ html_str = """
+ <a href="/testlink" class="link_access_account">
+ <span class="toto">
+ <span>
+ <img src="test.jpg" />
+ </span>
+ </span>
+ <span class="titi">
+ Test text
+ </span>
+ </a>
+ """
+ dom = html.fromstring(html_str)
+ self.assertEqual(utils.extract_text(dom), 'Test text')
+ self.assertEqual(utils.extract_text(dom.xpath('//span')), 'Test text')
+ self.assertEqual(utils.extract_text(dom.xpath('//span/text()')), 'Test text')
+ self.assertEqual(utils.extract_text(dom.xpath('count(//span)')), '3.0')
+ self.assertEqual(utils.extract_text(dom.xpath('boolean(//span)')), 'True')
+ self.assertEqual(utils.extract_text(dom.xpath('//img/@src')), 'test.jpg')
+ self.assertEqual(utils.extract_text(dom.xpath('//unexistingtag')), '')
+ self.assertEqual(utils.extract_text(None, allow_none=True), None)
+ with self.assertRaises(ValueError):
+ utils.extract_text(None)
+ with self.assertRaises(ValueError):
+ utils.extract_text({})
+
+ def test_extract_url(self):
+ def f(html_str, search_url):
+ return utils.extract_url(html.fromstring(html_str), search_url)
+ self.assertEqual(f('<span id="42">https://example.com</span>', 'http://example.com/'), 'https://example.com/')
+ self.assertEqual(f('https://example.com', 'http://example.com/'), 'https://example.com/')
+ self.assertEqual(f('//example.com', 'http://example.com/'), 'http://example.com/')
+ self.assertEqual(f('//example.com', 'https://example.com/'), 'https://example.com/')
+ self.assertEqual(f('/path?a=1', 'https://example.com'), 'https://example.com/path?a=1')
+ with self.assertRaises(lxml.etree.ParserError):
+ f('', 'https://example.com')
+ with self.assertRaises(Exception):
+ utils.extract_url([], 'https://example.com')
+
+ def test_html_to_text_invalid(self):
+ html = '<p><b>Lorem ipsum</i>dolor sit amet</p>'
+ self.assertEqual(utils.html_to_text(html), "Lorem ipsum")
def test_match_language(self):
self.assertEqual(utils.match_language('es', ['es']), 'es')
@@ -108,44 +129,100 @@ class TestHTMLTextExtractor(SearxTestCase):
def test_handle_charref(self):
self.html_text_extractor.handle_charref('xF')
- self.assertIn(u'\x0f', self.html_text_extractor.result)
+ self.assertIn('\x0f', self.html_text_extractor.result)
self.html_text_extractor.handle_charref('XF')
- self.assertIn(u'\x0f', self.html_text_extractor.result)
+ self.assertIn('\x0f', self.html_text_extractor.result)
self.html_text_extractor.handle_charref('97')
- self.assertIn(u'a', self.html_text_extractor.result)
+ self.assertIn('a', self.html_text_extractor.result)
def test_handle_entityref(self):
entity = 'test'
self.html_text_extractor.handle_entityref(entity)
self.assertIn(entity, self.html_text_extractor.result)
+ def test_invalid_html(self):
+ text = '<p><b>Lorem ipsum</i>dolor sit amet</p>'
+ with self.assertRaises(utils.HTMLTextExtractorException):
+ self.html_text_extractor.feed(text)
-class TestUnicodeWriter(SearxTestCase):
- def setUp(self):
- self.unicode_writer = utils.UnicodeWriter(mock.MagicMock())
-
- def test_write_row(self):
- row = [1, 2, 3]
- self.assertEqual(self.unicode_writer.writerow(row), None)
-
- def test_write_rows(self):
- self.unicode_writer.writerow = mock.MagicMock()
- rows = [1, 2, 3]
- self.unicode_writer.writerows(rows)
- self.assertEqual(self.unicode_writer.writerow.call_count, len(rows))
-
-
-class TestNewHmac(SearxTestCase):
-
- def test_bytes(self):
- for secret_key in ['secret', b'secret', 1]:
- if secret_key == 1:
- with self.assertRaises(TypeError):
- utils.new_hmac(secret_key, b'http://example.com')
- continue
- res = utils.new_hmac(secret_key, b'http://example.com')
- self.assertEqual(
- res,
- '23e2baa2404012a5cc8e4a18b4aabf0dde4cb9b56f679ddc0fd6d7c24339d819')
+class TestXPathUtils(SearxTestCase):
+
+ TEST_DOC = """<ul>
+ <li>Text in <b>bold</b> and <i>italic</i> </li>
+ <li>Another <b>text</b> <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="></li>
+ </ul>"""
+
+ def test_get_xpath_cache(self):
+ xp1 = utils.get_xpath('//a')
+ xp2 = utils.get_xpath('//div')
+ xp3 = utils.get_xpath('//a')
+
+ self.assertEqual(id(xp1), id(xp3))
+ self.assertNotEqual(id(xp1), id(xp2))
+
+ def test_get_xpath_type(self):
+ utils.get_xpath(lxml.etree.XPath('//a'))
+
+ with self.assertRaises(TypeError):
+ utils.get_xpath([])
+
+ def test_get_xpath_invalid(self):
+ invalid_xpath = '//a[0].text'
+ with self.assertRaises(SearxXPathSyntaxException) as context:
+ utils.get_xpath(invalid_xpath)
+
+ self.assertEqual(context.exception.message, 'Invalid expression')
+ self.assertEqual(context.exception.xpath_str, invalid_xpath)
+
+ def test_eval_xpath_unregistered_function(self):
+ doc = html.fromstring(TestXPathUtils.TEST_DOC)
+
+ invalid_function_xpath = 'int(//a)'
+ with self.assertRaises(SearxEngineXPathException) as context:
+ utils.eval_xpath(doc, invalid_function_xpath)
+
+ self.assertEqual(context.exception.message, 'Unregistered function')
+ self.assertEqual(context.exception.xpath_str, invalid_function_xpath)
+
+ def test_eval_xpath(self):
+ doc = html.fromstring(TestXPathUtils.TEST_DOC)
+
+ self.assertEqual(utils.eval_xpath(doc, '//p'), [])
+ self.assertEqual(utils.eval_xpath(doc, '//i/text()'), ['italic'])
+ self.assertEqual(utils.eval_xpath(doc, 'count(//i)'), 1.0)
+
+ def test_eval_xpath_list(self):
+ doc = html.fromstring(TestXPathUtils.TEST_DOC)
+
+ # check a not empty list
+ self.assertEqual(utils.eval_xpath_list(doc, '//i/text()'), ['italic'])
+
+ # check min_len parameter
+ with self.assertRaises(SearxEngineXPathException) as context:
+ utils.eval_xpath_list(doc, '//p', min_len=1)
+ self.assertEqual(context.exception.message, 'len(xpath_str) < 1')
+ self.assertEqual(context.exception.xpath_str, '//p')
+
+ def test_eval_xpath_getindex(self):
+ doc = html.fromstring(TestXPathUtils.TEST_DOC)
+
+ # check index 0
+ self.assertEqual(utils.eval_xpath_getindex(doc, '//i/text()', 0), 'italic')
+
+ # default is 'something'
+ self.assertEqual(utils.eval_xpath_getindex(doc, '//i/text()', 1, default='something'), 'something')
+
+ # default is None
+ self.assertEqual(utils.eval_xpath_getindex(doc, '//i/text()', 1, default=None), None)
+
+ # index not found
+ with self.assertRaises(SearxEngineXPathException) as context:
+ utils.eval_xpath_getindex(doc, '//i/text()', 1)
+ self.assertEqual(context.exception.message, 'index 1 not found')
+
+ # not a list
+ with self.assertRaises(SearxEngineXPathException) as context:
+ utils.eval_xpath_getindex(doc, 'count(//i)', 1)
+ self.assertEqual(context.exception.message, 'the result is not a list')
diff --git a/tests/unit/test_webadapter.py b/tests/unit/test_webadapter.py
new file mode 100644
index 0000000..f620cf3
--- /dev/null
+++ b/tests/unit/test_webadapter.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+
+from searx.testing import SearxTestCase
+from searx.preferences import Preferences
+from searx.engines import engines
+
+import searx.search
+from searx.search import EngineRef
+from searx.webadapter import validate_engineref_list
+
+
+PRIVATE_ENGINE_NAME = 'general private offline'
+TEST_ENGINES = [
+ {
+ 'name': PRIVATE_ENGINE_NAME,
+ 'engine': 'dummy-offline',
+ 'categories': 'general',
+ 'shortcut': 'do',
+ 'timeout': 3.0,
+ 'offline': True,
+ 'tokens': ['my-token'],
+ },
+]
+SEARCHQUERY = [EngineRef(PRIVATE_ENGINE_NAME, 'general')]
+
+
+class ValidateQueryCase(SearxTestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ searx.engines.initialize_engines(TEST_ENGINES)
+
+ def test_query_private_engine_without_token(self):
+ preferences = Preferences(['oscar'], ['general'], engines, [])
+ valid, unknown, invalid_token = validate_engineref_list(SEARCHQUERY, preferences)
+ self.assertEqual(len(valid), 0)
+ self.assertEqual(len(unknown), 0)
+ self.assertEqual(len(invalid_token), 1)
+
+ def test_query_private_engine_with_incorrect_token(self):
+ preferences_with_tokens = Preferences(['oscar'], ['general'], engines, [])
+ preferences_with_tokens.parse_dict({'tokens': 'bad-token'})
+ valid, unknown, invalid_token = validate_engineref_list(SEARCHQUERY, preferences_with_tokens)
+ self.assertEqual(len(valid), 0)
+ self.assertEqual(len(unknown), 0)
+ self.assertEqual(len(invalid_token), 1)
+
+ def test_query_private_engine_with_correct_token(self):
+ preferences_with_tokens = Preferences(['oscar'], ['general'], engines, [])
+ preferences_with_tokens.parse_dict({'tokens': 'my-token'})
+ valid, unknown, invalid_token = validate_engineref_list(SEARCHQUERY, preferences_with_tokens)
+ self.assertEqual(len(valid), 1)
+ self.assertEqual(len(unknown), 0)
+ self.assertEqual(len(invalid_token), 0)
diff --git a/tests/unit/test_webapp.py b/tests/unit/test_webapp.py
index 8eed607..ac8851e 100644
--- a/tests/unit/test_webapp.py
+++ b/tests/unit/test_webapp.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
import json
+from urllib.parse import ParseResult
from mock import Mock
from searx import webapp
from searx.testing import SearxTestCase
from searx.search import Search
-from searx.url_utils import ParseResult
class ViewsTestCase(SearxTestCase):
@@ -75,8 +75,36 @@ class ViewsTestCase(SearxTestCase):
self.assertEqual(result.status_code, 200)
self.assertIn(b'<div class="title"><h1>searx</h1></div>', result.data)
- def test_index_html(self):
+ def test_index_html_post(self):
result = self.app.post('/', data={'q': 'test'})
+ self.assertEqual(result.status_code, 308)
+ self.assertEqual(result.location, 'http://localhost/search')
+
+ def test_index_html_get(self):
+ result = self.app.post('/?q=test')
+ self.assertEqual(result.status_code, 308)
+ self.assertEqual(result.location, 'http://localhost/search?q=test')
+
+ def test_search_empty_html(self):
+ result = self.app.post('/search', data={'q': ''})
+ self.assertEqual(result.status_code, 200)
+ self.assertIn(b'<div class="title"><h1>searx</h1></div>', result.data)
+
+ def test_search_empty_json(self):
+ result = self.app.post('/search', data={'q': '', 'format': 'json'})
+ self.assertEqual(result.status_code, 400)
+
+ def test_search_empty_csv(self):
+ result = self.app.post('/search', data={'q': '', 'format': 'csv'})
+ self.assertEqual(result.status_code, 400)
+
+ def test_search_empty_rss(self):
+ result = self.app.post('/search', data={'q': '', 'format': 'rss'})
+ self.assertEqual(result.status_code, 400)
+
+ def test_search_html(self):
+ result = self.app.post('/search', data={'q': 'test'})
+
self.assertIn(
b'<h3 class="result_title"><img width="14" height="14" class="favicon" src="/static/themes/legacy/img/icons/icon_youtube.ico" alt="youtube" /><a href="http://second.test.xyz" rel="noreferrer">Second <span class="highlight">Test</span></a></h3>', # noqa
result.data
@@ -88,8 +116,11 @@ class ViewsTestCase(SearxTestCase):
def test_index_json(self):
result = self.app.post('/', data={'q': 'test', 'format': 'json'})
+ self.assertEqual(result.status_code, 308)
- result_dict = json.loads(result.data.decode('utf-8'))
+ def test_search_json(self):
+ result = self.app.post('/search', data={'q': 'test', 'format': 'json'})
+ result_dict = json.loads(result.data.decode())
self.assertEqual('test', result_dict['query'])
self.assertEqual(len(result_dict['results']), 2)
@@ -98,6 +129,10 @@ class ViewsTestCase(SearxTestCase):
def test_index_csv(self):
result = self.app.post('/', data={'q': 'test', 'format': 'csv'})
+ self.assertEqual(result.status_code, 308)
+
+ def test_search_csv(self):
+ result = self.app.post('/search', data={'q': 'test', 'format': 'csv'})
self.assertEqual(
b'title,url,content,host,engine,score,type\r\n'
@@ -108,6 +143,10 @@ class ViewsTestCase(SearxTestCase):
def test_index_rss(self):
result = self.app.post('/', data={'q': 'test', 'format': 'rss'})
+ self.assertEqual(result.status_code, 308)
+
+ def test_search_rss(self):
+ result = self.app.post('/search', data={'q': 'test', 'format': 'rss'})
self.assertIn(
b'<description>Search results for "test" - searx</description>',
diff --git a/tests/unit/test_webutils.py b/tests/unit/test_webutils.py
new file mode 100644
index 0000000..aa46468
--- /dev/null
+++ b/tests/unit/test_webutils.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+import mock
+from searx.testing import SearxTestCase
+from searx import webutils
+
+
+class TestWebUtils(SearxTestCase):
+
+ def test_prettify_url(self):
+ data = (('https://searx.me/', 'https://searx.me/'),
+ ('https://searx.me/ű', 'https://searx.me/ű'),
+ ('https://searx.me/' + (100 * 'a'), 'https://searx.me/[...]aaaaaaaaaaaaaaaaa'),
+ ('https://searx.me/' + (100 * 'ű'), 'https://searx.me/[...]űűűűűűűűűűűűűűűűű'))
+
+ for test_url, expected in data:
+ self.assertEqual(webutils.prettify_url(test_url, max_length=32), expected)
+
+ def test_highlight_content(self):
+ self.assertEqual(webutils.highlight_content(0, None), None)
+ self.assertEqual(webutils.highlight_content(None, None), None)
+ self.assertEqual(webutils.highlight_content('', None), None)
+ self.assertEqual(webutils.highlight_content(False, None), None)
+
+ contents = [
+ '<html></html>'
+ 'not<'
+ ]
+ for content in contents:
+ self.assertEqual(webutils.highlight_content(content, None), content)
+
+ content = 'a'
+ query = 'test'
+ self.assertEqual(webutils.highlight_content(content, query), content)
+ query = 'a test'
+ self.assertEqual(webutils.highlight_content(content, query), content)
+
+
+class TestUnicodeWriter(SearxTestCase):
+
+ def setUp(self):
+ self.unicode_writer = webutils.UnicodeWriter(mock.MagicMock())
+
+ def test_write_row(self):
+ row = [1, 2, 3]
+ self.assertEqual(self.unicode_writer.writerow(row), None)
+
+ def test_write_rows(self):
+ self.unicode_writer.writerow = mock.MagicMock()
+ rows = [1, 2, 3]
+ self.unicode_writer.writerows(rows)
+ self.assertEqual(self.unicode_writer.writerow.call_count, len(rows))
+
+
+class TestNewHmac(SearxTestCase):
+
+ def test_bytes(self):
+ for secret_key in ['secret', b'secret', 1]:
+ if secret_key == 1:
+ with self.assertRaises(TypeError):
+ webutils.new_hmac(secret_key, b'http://example.com')
+ continue
+ res = webutils.new_hmac(secret_key, b'http://example.com')
+ self.assertEqual(
+ res,
+ '23e2baa2404012a5cc8e4a18b4aabf0dde4cb9b56f679ddc0fd6d7c24339d819')
diff --git a/utils/brand.env b/utils/brand.env
index 5d5b395..55244bd 100644
--- a/utils/brand.env
+++ b/utils/brand.env
@@ -1,6 +1,6 @@
-export GIT_URL='https://github.com/asciimoo/searx'
+export GIT_URL='https://github.com/searx/searx'
export GIT_BRANCH='master'
-export ISSUE_URL='https://github.com/asciimoo/searx/issues'
+export ISSUE_URL='https://github.com/searx/searx/issues'
export SEARX_URL='https://searx.me'
-export DOCS_URL='https://asciimoo.github.io/searx'
+export DOCS_URL='https://searx.github.io/searx'
export PUBLIC_INSTANCES='https://searx.space'
diff --git a/utils/fabfile.py b/utils/fabfile.py
index 559e2ab..136ef62 100644
--- a/utils/fabfile.py
+++ b/utils/fabfile.py
@@ -1,5 +1,5 @@
from fabric.api import cd, run, sudo, put
-from cStringIO import StringIO
+from io import StringIO
base_dir = '/usr/local'
@@ -86,7 +86,7 @@ def init():
sudo('/etc/init.d/nginx restart')
with cd(base_dir):
- sudo('git clone https://github.com/asciimoo/searx')
+ sudo('git clone https://github.com/searx/searx')
sudo('chown -R {user}:{user} {searx_dir}'.format(user=current_user, searx_dir=searx_dir))
put(StringIO(uwsgi_file), searx_dir + '/uwsgi.ini')
diff --git a/utils/fetch_ahmia_blacklist.py b/utils/fetch_ahmia_blacklist.py
new file mode 100755
index 0000000..3e393ed
--- /dev/null
+++ b/utils/fetch_ahmia_blacklist.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+
+# This script saves Ahmia's blacklist for onion sites.
+# More info in https://ahmia.fi/blacklist/
+
+# set path
+from sys import path
+from os.path import realpath, dirname, join
+path.append(realpath(dirname(realpath(__file__)) + '/../'))
+
+#
+import requests
+from searx import searx_dir
+
+URL = 'https://ahmia.fi/blacklist/banned/'
+
+
+def fetch_ahmia_blacklist():
+ resp = requests.get(URL, timeout=3.0)
+ if resp.status_code != 200:
+ raise Exception("Error fetching Ahmia blacklist, HTTP code " + resp.status_code)
+ else:
+ blacklist = resp.text.split()
+ return blacklist
+
+
+def get_ahmia_blacklist_filename():
+ return join(join(searx_dir, "data"), "ahmia_blacklist.txt")
+
+
+blacklist = fetch_ahmia_blacklist()
+with open(get_ahmia_blacklist_filename(), "w") as f:
+ f.write('\n'.join(blacklist))
diff --git a/utils/fetch_currencies.py b/utils/fetch_currencies.py
index 5605fb3..437c375 100644
--- a/utils/fetch_currencies.py
+++ b/utils/fetch_currencies.py
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
-from __future__ import print_function
+
import json
import re
import unicodedata
import string
-from urllib import urlencode
+from urllib.parse import urlencode
from requests import get
languages = {'de', 'en', 'es', 'fr', 'hu', 'it', 'nl', 'jp'}
@@ -39,7 +39,7 @@ def add_currency_name(name, iso4217):
db_names = db['names']
- if not isinstance(iso4217, basestring):
+ if not isinstance(iso4217, str):
print("problem", name, iso4217)
return
@@ -126,7 +126,7 @@ def wdq_query(query):
url = url_wmflabs_template + query
htmlresponse = get(url)
jsonresponse = json.loads(htmlresponse.content)
- qlist = map(add_q, jsonresponse.get('items', {}))
+ qlist = list(map(add_q, jsonresponse.get('items', {})))
error = jsonresponse.get('status', {}).get('error', None)
if error is not None and error != 'OK':
print("error for query '" + query + "' :" + error)
@@ -150,12 +150,12 @@ for q in wmflabs_queries:
wdq_query(q)
# static
-add_currency_name(u"euro", 'EUR')
-add_currency_name(u"euros", 'EUR')
-add_currency_name(u"dollar", 'USD')
-add_currency_name(u"dollars", 'USD')
-add_currency_name(u"peso", 'MXN')
-add_currency_name(u"pesos", 'MXN')
+add_currency_name("euro", 'EUR')
+add_currency_name("euros", 'EUR')
+add_currency_name("dollar", 'USD')
+add_currency_name("dollars", 'USD')
+add_currency_name("peso", 'MXN')
+add_currency_name("pesos", 'MXN')
# write
f = open("currencies.json", "wb")
diff --git a/utils/fetch_firefox_version.py b/utils/fetch_firefox_version.py
index 722c482..997a752 100755
--- a/utils/fetch_firefox_version.py
+++ b/utils/fetch_firefox_version.py
@@ -9,9 +9,9 @@ path.append(realpath(dirname(realpath(__file__)) + '/../'))
import json
import requests
import re
+from urllib.parse import urlparse, urljoin
from distutils.version import LooseVersion, StrictVersion
from lxml import html
-from searx.url_utils import urlparse, urljoin
from searx import searx_dir
URL = 'https://ftp.mozilla.org/pub/firefox/releases/'
diff --git a/utils/fetch_languages.py b/utils/fetch_languages.py
index 77ec0bf..1f8dc77 100644
--- a/utils/fetch_languages.py
+++ b/utils/fetch_languages.py
@@ -6,7 +6,7 @@
# are written in current directory to avoid overwriting in case something goes wrong.
import json
-import io
+from pprint import pformat
from sys import path
from babel import Locale, UnknownLocaleError
from babel.languages import get_global
@@ -23,7 +23,7 @@ languages_file = 'languages.py'
# Fetchs supported languages for each engine and writes json file with those.
def fetch_supported_languages():
- engines_languages = {}
+ engines_languages = dict()
names = list(engines)
names.sort()
@@ -51,19 +51,9 @@ def get_locale(lang_code):
return None
-# Append engine_name to list of engines that support locale.
-def add_engine_counter(lang_code, engine_name, languages):
- if lang_code in languages:
- if 'counter' not in languages[lang_code]:
- languages[lang_code]['counter'] = [engine_name]
- elif engine_name not in languages[lang_code]['counter']:
- languages[lang_code]['counter'].append(engine_name)
-
-
# Join all language lists.
-# TODO: Add language names from engine's language list if name not known by babel.
def join_language_lists(engines_languages):
- language_list = {}
+ language_list = dict()
for engine_name in engines_languages:
for lang_code in engines_languages[engine_name]:
@@ -76,32 +66,51 @@ def join_language_lists(engines_languages):
# ensure that lang_code uses standard language and country codes
if locale and locale.territory:
- lang_code = locale.language + '-' + locale.territory
+ lang_code = "{lang}-{country}".format(lang=locale.language, country=locale.territory)
+ short_code = lang_code.split('-')[0]
- # add locale if it's not in list
- if lang_code not in language_list:
+ # add language without country if not in list
+ if short_code not in language_list:
if locale:
- language_list[lang_code] = {'name': locale.get_language_name().title(),
- 'english_name': locale.english_name,
- 'country': locale.get_territory_name() or ''}
-
- # also add language without country
- if locale.language not in language_list:
- language_list[locale.language] = {'name': locale.get_language_name().title(),
- 'english_name': locale.english_name}
+ # get language's data from babel's Locale object
+ language_name = locale.get_language_name().title()
+ english_name = locale.english_name.split(' (')[0]
+ elif short_code in engines_languages['wikipedia']:
+ # get language's data from wikipedia if not known by babel
+ language_name = engines_languages['wikipedia'][short_code]['name']
+ english_name = engines_languages['wikipedia'][short_code]['english_name']
else:
- language_list[lang_code] = {}
+ language_name = None
+ english_name = None
+
+ # add language to list
+ language_list[short_code] = {'name': language_name,
+ 'english_name': english_name,
+ 'counter': set(),
+ 'countries': dict()}
+
+ # add language with country if not in list
+ if lang_code != short_code and lang_code not in language_list[short_code]['countries']:
+ country_name = ''
+ if locale:
+ # get country name from babel's Locale object
+ country_name = locale.get_territory_name()
+
+ language_list[short_code]['countries'][lang_code] = {'country_name': country_name,
+ 'counter': set()}
# count engine for both language_country combination and language alone
- add_engine_counter(lang_code, engine_name, language_list)
- add_engine_counter(lang_code.split('-')[0], engine_name, language_list)
+ language_list[short_code]['counter'].add(engine_name)
+ if lang_code != short_code:
+ language_list[short_code]['countries'][lang_code]['counter'].add(engine_name)
return language_list
-# Filter language list so it only includes the most supported languages and countries.
+# Filter language list so it only includes the most supported languages and countries
def filter_language_list(all_languages):
- min_supported_engines = 10
+ min_engines_per_lang = 15
+ min_engines_per_country = 10
main_engines = [engine_name for engine_name in engines.keys()
if 'general' in engines[engine_name].categories and
engines[engine_name].supported_languages and
@@ -110,79 +119,84 @@ def filter_language_list(all_languages):
# filter list to include only languages supported by most engines or all default general engines
filtered_languages = {code: lang for code, lang
in all_languages.items()
- if (len(lang.get('counter', [])) >= min_supported_engines or
- all(main_engine in lang.get('counter', [])
+ if (len(lang['counter']) >= min_engines_per_lang or
+ all(main_engine in lang['counter']
for main_engine in main_engines))}
- return filtered_languages
-
-
-# Add country codes to languages without one and filter out language codes.
-def assign_country_codes(filtered_languages, all_languages):
- sorted_languages = sorted(all_languages,
- key=lambda lang: len(all_languages[lang].get('counter', [])),
- reverse=True)
- previous_lang = None
- previous_code = None
- countries = 0
- for current_code in sorted(filtered_languages):
- current_lang = current_code.split('-')[0]
-
- # count country codes per language
- if current_lang == previous_lang:
- countries += 1
-
- else:
- if previous_lang is not None:
- # if language has no single country code
- if countries == 0:
- # try to get country code with most supported engines
- for l in sorted_languages:
- l_parts = l.split('-')
- if len(l_parts) == 2 and l_parts[0] == previous_lang:
- filtered_languages[l] = all_languages[l]
- filtered_languages[l]['country'] = ''
- countries = 1
- break
-
- if countries == 0:
- # get most likely country code from babel
- subtags = get_global('likely_subtags').get(previous_lang)
- if subtags:
- subtag_parts = subtags.split('_')
- new_code = subtag_parts[0] + '-' + subtag_parts[-1]
- filtered_languages[new_code] = all_languages[previous_lang]
- countries = 1
-
- if countries == 1:
- # remove countryless version of language if there's only one country
- del filtered_languages[previous_lang]
- if previous_code in filtered_languages:
- filtered_languages[previous_code]['country'] = ''
-
- countries = 0
- previous_lang = current_lang
-
- previous_code = current_code
+ def _copy_lang_data(lang, country_name=None):
+ new_dict = dict()
+ new_dict['name'] = all_languages[lang]['name']
+ new_dict['english_name'] = all_languages[lang]['english_name']
+ if country_name:
+ new_dict['country_name'] = country_name
+ return new_dict
+
+ def _country_count(i):
+ return len(countries[sorted_countries[i]]['counter'])
+
+ # for each language get country codes supported by most engines or at least one country code
+ filtered_languages_with_countries = dict()
+ for lang, lang_data in filtered_languages.items():
+ countries = lang_data['countries']
+ filtered_countries = dict()
+
+ # get language's country codes with enough supported engines
+ for lang_country, country_data in countries.items():
+ if len(country_data['counter']) >= min_engines_per_country:
+ filtered_countries[lang_country] = _copy_lang_data(lang, country_data['country_name'])
+
+ # add language without countries too if there's more than one country to choose from
+ if len(filtered_countries) > 1:
+ filtered_countries[lang] = _copy_lang_data(lang)
+ elif len(filtered_countries) == 1:
+ # if there's only one country per language, it's not necessary to show country name
+ lang_country = next(iter(filtered_countries))
+ filtered_countries[lang_country]['country_name'] = None
+
+ # if no country has enough engines try to get most likely country code from babel
+ if not filtered_countries:
+ lang_country = None
+ subtags = get_global('likely_subtags').get(lang)
+ if subtags:
+ country_code = subtags.split('_')[-1]
+ if len(country_code) == 2:
+ lang_country = "{lang}-{country}".format(lang=lang, country=country_code)
+
+ if lang_country:
+ filtered_countries[lang_country] = _copy_lang_data(lang)
+ else:
+ filtered_countries[lang] = _copy_lang_data(lang)
+
+ filtered_languages_with_countries.update(filtered_countries)
+
+ return filtered_languages_with_countries
# Write languages.py.
def write_languages_file(languages):
- new_file = open(languages_file, 'wb')
- file_content = '# -*- coding: utf-8 -*-\n'\
- + '# list of language codes\n'\
- + '# this file is generated automatically by utils/update_search_languages.py\n'\
- + '\nlanguage_codes = ('
- for code in sorted(languages):
- file_content += '\n (u"' + code + '"'\
- + ', u"' + languages[code]['name'].split(' (')[0] + '"'\
- + ', u"' + languages[code].get('country', '') + '"'\
- + ', u"' + languages[code].get('english_name', '').split(' (')[0] + '"),'
- # remove last comma
- file_content = file_content[:-1]
- file_content += '\n)\n'
- new_file.write(file_content.encode('utf8'))
- new_file.close()
+ file_headers = (
+ "# -*- coding: utf-8 -*-",
+ "# list of language codes",
+ "# this file is generated automatically by utils/fetch_languages.py",
+ "language_codes ="
+ )
+
+ language_codes = tuple([
+ (
+ code,
+ languages[code]['name'].split(' (')[0],
+ languages[code].get('country_name') or '',
+ languages[code].get('english_name') or ''
+ ) for code in sorted(languages)
+ ])
+
+ with open(languages_file, 'w') as new_file:
+ file_content = "{file_headers} \\\n{language_codes}".format(
+ file_headers='\n'.join(file_headers),
+ language_codes=pformat(language_codes, indent=4)
+ )
+ new_file.write(file_content)
+ new_file.close()
if __name__ == "__main__":
@@ -190,5 +204,4 @@ if __name__ == "__main__":
engines_languages = fetch_supported_languages()
all_languages = join_language_lists(engines_languages)
filtered_languages = filter_language_list(all_languages)
- assign_country_codes(filtered_languages, all_languages)
write_languages_file(filtered_languages)
diff --git a/utils/fetch_wikidata_units.py b/utils/fetch_wikidata_units.py
new file mode 100644
index 0000000..6950596
--- /dev/null
+++ b/utils/fetch_wikidata_units.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+import json
+import collections
+
+# set path
+from sys import path
+from os.path import realpath, dirname, join
+path.append(realpath(dirname(realpath(__file__)) + '/../'))
+
+from searx import searx_dir
+from searx.engines.wikidata import send_wikidata_query
+
+
+SARQL_REQUEST = """
+SELECT DISTINCT ?item ?symbol ?P2370 ?P2370Unit ?P2442 ?P2442Unit
+WHERE
+{
+?item wdt:P31/wdt:P279 wd:Q47574.
+?item wdt:P5061 ?symbol.
+FILTER(LANG(?symbol) = "en").
+}
+ORDER BY ?item
+"""
+
+
+def get_data():
+ def get_key(unit):
+ return unit['item']['value'].replace('http://www.wikidata.org/entity/', '')
+
+ def get_value(unit):
+ return unit['symbol']['value']
+
+ result = send_wikidata_query(SARQL_REQUEST)
+ if result is not None:
+ # sort the unit by entity name
+ # so different fetchs keep the file unchanged.
+ list(result['results']['bindings']).sort(key=get_key)
+ return collections.OrderedDict([(get_key(unit), get_value(unit)) for unit in result['results']['bindings']])
+
+
+def get_wikidata_units_filename():
+ return join(join(searx_dir, "data"), "wikidata_units.json")
+
+
+with open(get_wikidata_units_filename(), 'w') as f:
+ json.dump(get_data(), f, indent=4, ensure_ascii=False)
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 8986fb0..6034cb3 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -546,7 +546,7 @@ EOF
eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
echo -e "\n.. END install systemd unit"
- # for DIST_NAME in ubuntu-20.04 arch fedora; do
+ # for DIST_NAME in ubuntu-20.04 arch fedora centos; do
# (
# DIST_ID=${DIST_NAME%-*}
# DIST_VERS=${DIST_NAME#*-}
diff --git a/utils/lib.sh b/utils/lib.sh
index 922227a..aa49da5 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# shellcheck disable=SC2059,SC1117
-# ubuntu, debian, arch, fedora ...
+# ubuntu, debian, arch, fedora, centos ...
DIST_ID=$(source /etc/os-release; echo "$ID");
# shellcheck disable=SC2034
DIST_VERS=$(source /etc/os-release; echo "$VERSION_ID");
@@ -639,7 +639,7 @@ nginx_distro_setup() {
NGINX_DEFAULT_SERVER=/etc/nginx/nginx.conf
# Including *location* directives from a dedicated config-folder into the
- # server directive is, what what fedora (already) does.
+ # server directive is, what what fedora and centos (already) does.
NGINX_APPS_ENABLED="/etc/nginx/default.d"
# We add a apps-available folder and linking configurations into the
@@ -654,7 +654,7 @@ nginx_distro_setup() {
arch-*)
NGINX_PACKAGES="nginx-mainline"
;;
- fedora-*)
+ fedora-*|centos-7)
NGINX_PACKAGES="nginx"
;;
*)
@@ -668,7 +668,7 @@ install_nginx(){
info_msg "installing nginx ..."
pkg_install "${NGINX_PACKAGES}"
case $DIST_ID-$DIST_VERS in
- arch-*|fedora-*)
+ arch-*|fedora-*|centos-7)
systemctl enable nginx
systemctl start nginx
;;
@@ -718,7 +718,7 @@ nginx_install_app() {
nginx_include_apps_enabled() {
# Add the *NGINX_APPS_ENABLED* infrastruture to a nginx server block. Such
- # infrastruture is already known from fedora, including apps (location
+ # infrastruture is already known from fedora and centos, including apps (location
# directives) from the /etc/nginx/default.d folder into the *default* nginx
# server.
@@ -819,7 +819,7 @@ apache_distro_setup() {
APACHE_MODULES="modules"
APACHE_PACKAGES="apache"
;;
- fedora-*)
+ fedora-*|centos-7)
APACHE_SITES_AVAILABLE="/etc/httpd/sites-available"
APACHE_SITES_ENABLED="/etc/httpd/sites-enabled"
APACHE_MODULES="modules"
@@ -837,7 +837,7 @@ install_apache(){
info_msg "installing apache ..."
pkg_install "$APACHE_PACKAGES"
case $DIST_ID-$DIST_VERS in
- arch-*|fedora-*)
+ arch-*|fedora-*|centos-7)
if ! grep "IncludeOptional sites-enabled" "/etc/httpd/conf/httpd.conf"; then
echo "IncludeOptional sites-enabled/*.conf" >> "/etc/httpd/conf/httpd.conf"
fi
@@ -851,7 +851,7 @@ apache_is_installed() {
case $DIST_ID-$DIST_VERS in
ubuntu-*|debian-*) (command -v apachectl) &>/dev/null;;
arch-*) (command -v httpd) &>/dev/null;;
- fedora-*) (command -v httpd) &>/dev/null;;
+ fedora-*|centos-7) (command -v httpd) &>/dev/null;;
esac
}
@@ -864,7 +864,7 @@ apache_reload() {
sudo -H apachectl configtest
sudo -H systemctl force-reload apache2
;;
- arch-*| fedora-*)
+ arch-*|fedora-*|centos-7)
sudo -H httpd -t
sudo -H systemctl force-reload httpd
;;
@@ -920,7 +920,7 @@ apache_enable_site() {
rm -f "${APACHE_SITES_ENABLED}/${CONF}"
ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
;;
- fedora-*)
+ fedora-*|centos-7)
mkdir -p "${APACHE_SITES_ENABLED}"
rm -f "${APACHE_SITES_ENABLED}/${CONF}"
ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
@@ -944,7 +944,7 @@ apache_dissable_site() {
arch-*)
rm -f "${APACHE_SITES_ENABLED}/${CONF}"
;;
- fedora-*)
+ fedora-*|centos-7)
rm -f "${APACHE_SITES_ENABLED}/${CONF}"
;;
esac
@@ -980,7 +980,7 @@ uWSGI_distro_setup() {
uWSGI_APPS_ENABLED="${uWSGI_SETUP}"
uWSGI_PACKAGES="uwsgi"
;;
- fedora-*)
+ fedora-*|centos-7)
# systemd --> /usr/lib/systemd/system/uwsgi.service
# The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
# - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
@@ -1002,7 +1002,7 @@ install_uwsgi(){
info_msg "installing uwsgi ..."
pkg_install "$uWSGI_PACKAGES"
case $DIST_ID-$DIST_VERS in
- fedora-*)
+ fedora-*|centos-7)
# enable & start should be called once at uWSGI installation time
systemctl enable uwsgi
systemctl restart uwsgi
@@ -1032,7 +1032,7 @@ uWSGI_restart() {
info_msg "[uWSGI:systemd-template] ${CONF} not installed (no need to restart)"
fi
;;
- fedora-*)
+ fedora-*|centos-7)
# in emperor mode, just touch the file to restart
if uWSGI_app_enabled "${CONF}"; then
touch "${uWSGI_APPS_ENABLED}/${CONF}"
@@ -1059,7 +1059,7 @@ uWSGI_prepare_app() {
local APP="${1%.*}"
case $DIST_ID-$DIST_VERS in
- fedora-*)
+ fedora-*|centos-7)
# in emperor mode, the uwsgi user is the owner of the sockets
info_msg "prepare (uwsgi:uwsgi) /run/uwsgi/app/${APP}"
mkdir -p "/run/uwsgi/app/${APP}"
@@ -1135,7 +1135,7 @@ uWSGI_app_enabled() {
systemctl -q is-enabled "uwsgi@${CONF%.*}"
exit_val=$?
;;
- fedora-*)
+ fedora-*|centos-7)
[[ -f "${uWSGI_APPS_ENABLED}/${CONF}" ]]
exit_val=$?
;;
@@ -1170,7 +1170,7 @@ uWSGI_enable_app() {
systemctl enable "uwsgi@${CONF%.*}"
info_msg "enabled uWSGI app: ${CONF} (restart required)"
;;
- fedora-*)
+ fedora-*|centos-7)
mkdir -p "${uWSGI_APPS_ENABLED}"
rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}"
@@ -1202,7 +1202,7 @@ uWSGI_disable_app() {
systemctl disable "uwsgi@${CONF%.*}"
rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
;;
- fedora-*)
+ fedora-*|centos-7)
# in emperor mode, just remove the app.ini file
rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
;;
@@ -1247,6 +1247,10 @@ pkg_install() {
# shellcheck disable=SC2068
dnf install -y $@
;;
+ centos)
+ # shellcheck disable=SC2068
+ yum install -y $@
+ ;;
esac
}
@@ -1275,6 +1279,10 @@ pkg_remove() {
# shellcheck disable=SC2068
dnf remove -y $@
;;
+ centos)
+ # shellcheck disable=SC2068
+ yum remove -y $@
+ ;;
esac
}
@@ -1295,6 +1303,10 @@ pkg_is_installed() {
dnf list -q --installed "$1" &> /dev/null
return $?
;;
+ centos)
+ yum list -q --installed "$1" &> /dev/null
+ return $?
+ ;;
esac
}
@@ -1313,7 +1325,7 @@ git_clone() {
# into <path>. If repository is allready cloned, pull from <branch> and
# update working tree (if needed, the caller has to stash local changes).
#
- # git clone https://github.com/asciimoo/searx searx-src origin/master searxlogin
+ # git clone https://github.com/searx/searx searx-src origin/master searxlogin
#
local url="$1"
@@ -1395,10 +1407,14 @@ LXC_BASE_PACKAGES_arch="bash git base-devel python python-virtualenv"
# dnf packages
LXC_BASE_PACKAGES_fedora="bash git @development-tools python virtualenv"
+# yum packages
+LXC_BASE_PACKAGES_centos="bash git @development-tools python python-virtualenv"
+
case $DIST_ID in
ubuntu|debian) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_debian}" ;;
arch) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_arch}" ;;
- fedora) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_fedora}" ;;
+ fedora) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_fedora}" ;;
+ centos) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_centos}" ;;
*) err_msg "$DIST_ID-$DIST_VERS: pkg_install LXC_BASE_PACKAGES not yet implemented" ;;
esac
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index a51312f..3ce1002 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -31,6 +31,9 @@ lxc_set_suite_env() {
# rolling releases see https://www.archlinux.org/releng/releases/
"$LINUXCONTAINERS_ORG_NAME:archlinux" "archlinux"
+
+ # EOL 30 June 2024
+ "$LINUXCONTAINERS_ORG_NAME:centos/7" "centos7"
)
PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
diff --git a/utils/lxc.sh b/utils/lxc.sh
index a0688bc..30534c6 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -54,6 +54,13 @@ dnf install -y git curl wget hostname
echo 'Set disable_coredump false' >> /etc/sudo.conf
"
+# shellcheck disable=SC2034
+centos7_boilerplate="
+yum update -y
+yum install -y git curl wget hostname sudo
+echo 'Set disable_coredump false' >> /etc/sudo.conf
+"
+
REMOTE_IMAGES=()
CONTAINERS=()
LOCAL_IMAGES=()
diff --git a/utils/makefile.python b/utils/makefile.python
index df16acb..668b089 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -69,11 +69,11 @@ python-help::
@echo ' py[un]install - [un]install python objects in editable mode'
@echo ' upload-pypi - upload $(PYDIST)/* files to PyPi'
@echo 'options:'
- @echo ' make PY=2 [targets] => to eval targets with python 2 ($(PY))'
- @echo ' make PIP_INST= => to set/unset pip install options ($(PIP_INST))'
- @echo ' make TEST=. => choose test from $(TEST_FOLDER) (default "." runs all)'
- @echo ' make DEBUG= => target "debug": do not invoke PDB on errors'
- @echo ' make PY_SETUP_EXTRAS => also install extras_require from setup.py \[develop,test\]'
+ @echo ' make PY=3.7 [targets] => to eval targets with python 3.7 ($(PY))'
+ @echo ' make PIP_INST= => to set/unset pip install options ($(PIP_INST))'
+ @echo ' make TEST=. => choose test from $(TEST_FOLDER) (default "." runs all)'
+ @echo ' make DEBUG= => target "debug": do not invoke PDB on errors'
+ @echo ' make PY_SETUP_EXTRAS => also install extras_require from setup.py \[develop,test\]'
@echo ' when using target "pydebug", set breakpoints within py-source by adding::'
@echo ' DEBUG()'
@@ -252,7 +252,7 @@ pyenv-python: pyenv-install
# PyPi is required and since uploads via setuptools is not recommended, we have
# to imstall / use twine ... its really a mess.
#
-# [1] http://python-packaging.readthedocs.io/en/latest/dependencies.html#packages-not-on-pypi
+# [1] https://python-packaging.readthedocs.io/en/latest/dependencies.html#packages-not-on-pypi
# [2] https://github.com/pypa/pip/pull/1519
# https://github.com/pypa/twine
diff --git a/utils/makefile.sphinx b/utils/makefile.sphinx
index 1b0f42c..b4f298d 100644
--- a/utils/makefile.sphinx
+++ b/utils/makefile.sphinx
@@ -21,23 +21,6 @@ else
SPHINX_VERBOSE =
endif
-## SPHINXVERS variable
-## ===================
-##
-## .. _requirement-specifiers: https://pip.pypa.io/en/stable/reference/pip_install/#requirement-specifiers
-##
-## Sphinx version to use, when building documentation. Set this when calling
-## build target. The default value is empty (install latest), to select a
-## specific version use a requirement-specifiers_. E.g. to build your target
-## 'doc' with a select sphinx-doc_ version 1.7.9::
-##
-## make SPHINXVERS='==1.7.9' docs
-##
-## To build with latest 1.7::
-##
-## make SPHINXVERS='>=1.7,<1.8' docs
-##
-SPHINXVERS ?=
docs-help:
@echo 'makefile.sphinx:'
@@ -56,17 +39,6 @@ docs-help:
# requirements
# ------------------------------------------------------------------------------
-sphinx-doc-prebuilds:: $(PY_ENV)
-
-sphinx-doc: sphinx-doc-prebuilds
- @echo "PYENV installing Sphinx$(SPHINXVERS)"
- $(Q)$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) 'Sphinx$(SPHINXVERS)'
-
-sphinx-live: sphinx-doc-prebuilds
- @echo "PYENV installing Sphinx$(SPHINXVERS)"
- $(Q)$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) 'Sphinx$(SPHINXVERS)' sphinx-autobuild
-
-
PHONY += msg-texlive texlive
ifeq ($(shell which xelatex >/dev/null 2>&1; echo $$?), 1)
@@ -107,7 +79,7 @@ quiet_cmd_sphinx = SPHINX $@ --> file://$(abspath $(DOCS_DIST)/$5)
-b $2 -c $3 -d $(DOCS_BUILD)/.doctrees $4 $(DOCS_DIST)/$5
quiet_cmd_sphinx_autobuild = SPHINX $@ --> file://$(abspath $(DOCS_DIST)/$5)
- cmd_sphinx_autobuild = PATH="$(PY_ENV_BIN):$(PATH)" $(PY_ENV_BIN)/sphinx-autobuild $(SPHINX_VERBOSE) --poll -B --host 0.0.0.0 --port 8080 $(SPHINXOPTS)\
+ cmd_sphinx_autobuild = PATH="$(PY_ENV_BIN):$(PATH)" $(PY_ENV_BIN)/sphinx-autobuild $(SPHINX_VERBOSE) --open-browser --host 0.0.0.0 $(SPHINXOPTS)\
-b $2 -c $3 -d $(DOCS_BUILD)/.doctrees $4 $(DOCS_DIST)/$5
quiet_cmd_sphinx_clean = CLEAN $@
@@ -196,6 +168,11 @@ $(BOOKS_CLEAN):
$(DOCS_BUILD)/latex/$(patsubst books/%.clean,%,$@)
# github pages
+PHONY += prepare-gh-pages
+prepare-gh-pages:
+ cp -r $(DOCS_DIST)/* $(GH_PAGES)/
+ touch $(GH_PAGES)/.nojekyll
+ echo "<html><head><META http-equiv='refresh' content='0;URL=index.html'></head></html>" > $(GH_PAGES)/404.html
PHONY += $(GH_PAGES)
$(GH_PAGES)::
@@ -204,14 +181,18 @@ $(GH_PAGES)::
-cd $(GH_PAGES); git checkout gh-pages >/dev/null
-cd $(GH_PAGES); git pull
-cd $(GH_PAGES); ls -A | grep -v '.git$$' | xargs rm -rf
- cp -r $(DOCS_DIST)/* $(GH_PAGES)/
- touch $(GH_PAGES)/.nojekyll
- echo "<html><head><META http-equiv='refresh' content='0;URL=index.html'></head></html>" > $(GH_PAGES)/404.html
+ $(MAKE) prepare-gh-pages
cd $(GH_PAGES);\
git add --all . ;\
git commit -m "gh-pages: updated" ;\
git push origin gh-pages
+PHONY += travis-gh-pages
+travis-gh-pages:
+ $(MAKE) docs
+ rm -Rf $(GH_PAGES)
+ mkdir -p $(GH_PAGES)
+ $(MAKE) prepare-gh-pages
PHONY += docs-clean
docs-clean: $(BOOKS_CLEAN)
diff --git a/utils/morty.sh b/utils/morty.sh
index 75bfeee..24d160b 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -530,7 +530,7 @@ EOF
eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
echo -e "\n.. END install systemd unit"
- # for DIST_NAME in ubuntu-20.04 arch fedora; do
+ # for DIST_NAME in ubuntu-20.04 arch fedora centos; do
# (
# DIST_ID=${DIST_NAME%-*}
# DIST_VERS=${DIST_NAME#*-}
diff --git a/utils/searx.sh b/utils/searx.sh
index 886b38f..06b3c2d 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -36,6 +36,7 @@ GIT_BRANCH="${GIT_BRANCH:-master}"
SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
SEARX_SRC="${SERVICE_HOME}/searx-src"
SEARX_SETTINGS_PATH="/etc/searx/settings.yml"
+SEARX_SETTINGS_TEMPLATE="${REPO_ROOT}/utils/templates/etc/searx/use_default_settings.yml"
SEARX_UWSGI_APP="searx.ini"
# shellcheck disable=SC2034
SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
@@ -76,6 +77,19 @@ texlive-xetex-bin texlive-collection-fontsrecommended
texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
dejavu-sans-mono-fonts"
+# yum packages
+SEARX_PACKAGES_centos="\
+python36-virtualenv python36 python36-pip python36-lxml python-babel
+uwsgi uwsgi-plugin-python3
+git @development-tools libxml2
+ShellCheck"
+
+BUILD_PACKAGES_centos="\
+firefox graphviz graphviz-gd ImageMagick librsvg2-tools
+texlive-xetex-bin texlive-collection-fontsrecommended
+texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
+dejavu-sans-mono-fonts"
+
case $DIST_ID-$DIST_VERS in
ubuntu-16.04|ubuntu-18.04)
SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
@@ -99,6 +113,10 @@ case $DIST_ID-$DIST_VERS in
SEARX_PACKAGES="${SEARX_PACKAGES_fedora}"
BUILD_PACKAGES="${BUILD_PACKAGES_fedora}"
;;
+ centos-7)
+ SEARX_PACKAGES="${SEARX_PACKAGES_centos}"
+ BUILD_PACKAGES="${BUILD_PACKAGES_centos}"
+ ;;
esac
# Apache Settings
@@ -122,7 +140,7 @@ usage() {
cat <<EOF
usage::
$(basename "$0") shell
- $(basename "$0") install [all|user|searx-src|pyenv|uwsgi|packages|buildhost]
+ $(basename "$0") install [all|user|searx-src|pyenv|uwsgi|packages|settings|buildhost]
$(basename "$0") update [searx]
$(basename "$0") remove [all|user|pyenv|searx-src]
$(basename "$0") activate [service]
@@ -396,14 +414,14 @@ install_settings() {
if [[ ! -f ${SEARX_SETTINGS_PATH} ]]; then
info_msg "install settings ${REPO_ROOT}/searx/settings.yml"
info_msg " --> ${SEARX_SETTINGS_PATH}"
- cp "${REPO_ROOT}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+ cp "${SEARX_SETTINGS_TEMPLATE}" "${SEARX_SETTINGS_PATH}"
configure_searx
return
fi
rst_para "Diff between origin's setting file (+) and current (-):"
- echo
- $DIFF_CMD "${SEARX_SETTINGS_PATH}" "${SEARX_SRC}/searx/settings.yml"
+ echo "${SEARX_SETTINGS_PATH}" "${SEARX_SETTINGS_TEMPLATE}"
+ $DIFF_CMD "${SEARX_SETTINGS_PATH}" "${SEARX_SETTINGS_TEMPLATE}"
local action
choose_one action "What should happen to the settings file? " \
@@ -417,7 +435,7 @@ install_settings() {
"use origin settings")
backup_file "${SEARX_SETTINGS_PATH}"
info_msg "install origin settings"
- cp "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+ cp "${SEARX_SETTINGS_TEMPLATE}" "${SEARX_SETTINGS_PATH}"
;;
"start interactiv shell")
backup_file "${SEARX_SETTINGS_PATH}"
@@ -425,7 +443,7 @@ install_settings() {
sudo -H -i
rst_para 'Diff between new setting file (-) and current (+):'
echo
- $DIFF_CMD "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+ $DIFF_CMD "${SEARX_SETTINGS_TEMPLATE}" "${SEARX_SETTINGS_PATH}"
wait_key
;;
esac
@@ -714,7 +732,7 @@ EOF
arch-*)
systemctl --no-pager -l status "uwsgi@${SERVICE_NAME%.*}"
;;
- fedora-*)
+ fedora-*|centos-7)
systemctl --no-pager -l status uwsgi
;;
esac
@@ -729,7 +747,7 @@ EOF
case $DIST_ID-$DIST_VERS in
ubuntu-*|debian-*) tail -f /var/log/uwsgi/app/searx.log ;;
arch-*) journalctl -f -u "uwsgi@${SERVICE_NAME%.*}" ;;
- fedora-*) journalctl -f -u uwsgi ;;
+ fedora-*|centos-7) journalctl -f -u uwsgi ;;
esac
done
@@ -790,15 +808,19 @@ rst-doc() {
local debian="${SEARX_PACKAGES_debian}"
local arch="${SEARX_PACKAGES_arch}"
local fedora="${SEARX_PACKAGES_fedora}"
+ local centos="${SEARX_PACKAGES_centos}"
local debian_build="${BUILD_PACKAGES_debian}"
local arch_build="${BUILD_PACKAGES_arch}"
local fedora_build="${BUILD_PACKAGES_fedora}"
+ local centos_build="${SEARX_PACKAGES_centos}"
debian="$(echo "${debian}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
arch="$(echo "${arch}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
fedora="$(echo "${fedora}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
+ centos="$(echo "${centos}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
debian_build="$(echo "${debian_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
arch_build="$(echo "${arch_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
fedora_build="$(echo "${fedora_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
+ centos_build="$(echo "${centos_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/searx.rst")\""
@@ -850,7 +872,7 @@ EOF
EOF
;;
- fedora-*) cat <<EOF
+ fedora-*|centos-7) cat <<EOF
.. code:: bash
diff --git a/utils/standalone_searx.py b/utils/standalone_searx.py
index 7bc1d32..0a35cc4 100755
--- a/utils/standalone_searx.py
+++ b/utils/standalone_searx.py
@@ -1,5 +1,63 @@
#!/usr/bin/env python
+"""Script to run searx from terminal.
+Getting categories without initiate the engine will only return `['general']`
+
+>>> import searx.engines
+... list(searx.engines.categories.keys())
+['general']
+>>> import searx
+... searx.engines.initialize_engines(searx.settings['engines'])
+... list(searx.engines.categories.keys())
+['general', 'it', 'science', 'images', 'news', 'videos', 'music', 'files', 'social media', 'map']
+
+Example to use this script:
+
+.. code:: bash
+
+ $ SEARX_DEBUG=1 python3 utils/standalone_searx.py rain
+
+Example to run it from python:
+
+>>> import importlib
+... import json
+... import sys
+... import searx
+... import searx.engines
+... search_query = 'rain'
+... # initialize engines
+... searx.engines.initialize_engines(searx.settings['engines'])
+... # load engines categories once instead of each time the function called
+... engine_cs = list(searx.engines.categories.keys())
+... # load module
+... spec = importlib.util.spec_from_file_location(
+... 'utils.standalone_searx', 'utils/standalone_searx.py')
+... sas = importlib.util.module_from_spec(spec)
+... spec.loader.exec_module(sas)
+... # use function from module
+... prog_args = sas.parse_argument([search_query], category_choices=engine_cs)
+... search_q = sas.get_search_query(prog_args, engine_categories=engine_cs)
+... res_dict = sas.to_dict(search_q)
+... sys.stdout.write(json.dumps(
+... res_dict, sort_keys=True, indent=4, ensure_ascii=False,
+... default=sas.json_serial))
+{
+ "answers": [],
+ "infoboxes": [ {...} ],
+ "paging": true,
+ "results": [... ],
+ "results_number": 820000000.0,
+ "search": {
+ "lang": "all",
+ "pageno": 1,
+ "q": "rain",
+ "safesearch": 0,
+ "timerange": null
+ },
+ "suggestions": [...]
+}
+""" # noqa: E501
+# pylint: disable=pointless-string-statement
'''
searx is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
@@ -16,89 +74,145 @@ along with searx. If not, see < http://www.gnu.org/licenses/ >.
(C) 2016- by Alexandre Flament, <alex@al-f.net>
'''
-
-# set path
-from sys import path
-from os.path import realpath, dirname
-path.append(realpath(dirname(realpath(__file__)) + '/../'))
-
-# initialization
-from json import dumps
-from searx import settings
+# pylint: disable=wrong-import-position
+import argparse
import sys
-import codecs
-import searx.query
-import searx.search
+from datetime import datetime
+from json import dumps
+from typing import Any, Dict, List, Optional
+
+import searx
import searx.engines
import searx.preferences
-import argparse
+import searx.query
+import searx.search
+import searx.webadapter
-searx.engines.initialize_engines(settings['engines'])
-
-# command line parsing
-parser = argparse.ArgumentParser(description='Standalone searx.')
-parser.add_argument('query', type=str,
- help='Text query')
-parser.add_argument('--category', type=str, nargs='?',
- choices=searx.engines.categories.keys(),
- default='general',
- help='Search category')
-parser.add_argument('--lang', type=str, nargs='?',default='all',
- help='Search language')
-parser.add_argument('--pageno', type=int, nargs='?', default=1,
- help='Page number starting from 1')
-parser.add_argument('--safesearch', type=str, nargs='?', choices=['0', '1', '2'], default='0',
- help='Safe content filter from none to strict')
-parser.add_argument('--timerange', type=str, nargs='?', choices=['day', 'week', 'month', 'year'],
- help='Filter by time range')
-args = parser.parse_args()
-
-# search results for the query
-form = {
- "q":args.query,
- "categories":args.category.decode('utf-8'),
- "pageno":str(args.pageno),
- "language":args.lang,
- "time_range":args.timerange
-}
-preferences = searx.preferences.Preferences(['oscar'], searx.engines.categories.keys(), searx.engines.engines, [])
-preferences.key_value_settings['safesearch'].parse(args.safesearch)
+EngineCategoriesVar = Optional[List[str]]
-search_query, raw_text_query = searx.search.get_search_query_from_webapp(preferences, form)
-search = searx.search.Search(search_query)
-result_container = search.search()
-# output
-from datetime import datetime
+def get_search_query(
+ args: argparse.Namespace, engine_categories: EngineCategoriesVar = None
+) -> searx.search.SearchQuery:
+ """Get search results for the query"""
+ if engine_categories is None:
+ engine_categories = list(searx.engines.categories.keys())
+ try:
+ category = args.category.decode('utf-8')
+ except AttributeError:
+ category = args.category
+ form = {
+ "q": args.query,
+ "categories": category,
+ "pageno": str(args.pageno),
+ "language": args.lang,
+ "time_range": args.timerange
+ }
+ preferences = searx.preferences.Preferences(
+ ['oscar'], engine_categories, searx.engines.engines, [])
+ preferences.key_value_settings['safesearch'].parse(args.safesearch)
+
+ search_query = searx.webadapter.get_search_query_from_webapp(
+ preferences, form)[0]
+ return search_query
-def no_parsed_url(results):
+
+def no_parsed_url(results: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
+ """Remove parsed url from dict."""
for result in results:
del result['parsed_url']
return results
-def json_serial(obj):
- """JSON serializer for objects not serializable by default json code"""
+
+def json_serial(obj: Any) -> Any:
+ """JSON serializer for objects not serializable by default json code.
+
+ :raise TypeError: raised when **obj** is not serializable
+ """
if isinstance(obj, datetime):
serial = obj.isoformat()
return serial
- raise TypeError ("Type not serializable")
+ if isinstance(obj, bytes):
+ return obj.decode('utf8')
+ if isinstance(obj, set):
+ return list(obj)
+ raise TypeError("Type ({}) not serializable".format(type(obj)))
+
+
+def to_dict(search_query: searx.search.SearchQuery) -> Dict[str, Any]:
+ """Get result from parsed arguments."""
+ result_container = searx.search.Search(search_query).search()
+ result_container_json = {
+ "search": {
+ "q": search_query.query,
+ "pageno": search_query.pageno,
+ "lang": search_query.lang,
+ "safesearch": search_query.safesearch,
+ "timerange": search_query.time_range,
+ },
+ "results": no_parsed_url(result_container.get_ordered_results()),
+ "infoboxes": result_container.infoboxes,
+ "suggestions": list(result_container.suggestions),
+ "answers": list(result_container.answers),
+ "paging": result_container.paging,
+ "results_number": result_container.results_number()
+ }
+ return result_container_json
+
+
+def parse_argument(
+ args: Optional[List[str]]=None,
+ category_choices: EngineCategoriesVar=None
+) -> argparse.Namespace:
+ """Parse command line.
+
+ :raise SystemExit: Query argument required on `args`
+
+ Examples:
+
+ >>> import importlib
+ ... # load module
+ ... spec = importlib.util.spec_from_file_location(
+ ... 'utils.standalone_searx', 'utils/standalone_searx.py')
+ ... sas = importlib.util.module_from_spec(spec)
+ ... spec.loader.exec_module(sas)
+ ... sas.parse_argument()
+ usage: ptipython [-h] [--category [{general}]] [--lang [LANG]] [--pageno [PAGENO]] [--safesearch [{0,1,2}]] [--timerange [{day,week,month,year}]]
+ query
+ SystemExit: 2
+ >>> sas.parse_argument(['rain'])
+ Namespace(category='general', lang='all', pageno=1, query='rain', safesearch='0', timerange=None)
+ """ # noqa: E501
+ if not category_choices:
+ category_choices = list(searx.engines.categories.keys())
+ parser = argparse.ArgumentParser(description='Standalone searx.')
+ parser.add_argument('query', type=str,
+ help='Text query')
+ parser.add_argument('--category', type=str, nargs='?',
+ choices=category_choices,
+ default='general',
+ help='Search category')
+ parser.add_argument('--lang', type=str, nargs='?', default='all',
+ help='Search language')
+ parser.add_argument('--pageno', type=int, nargs='?', default=1,
+ help='Page number starting from 1')
+ parser.add_argument(
+ '--safesearch', type=str, nargs='?',
+ choices=['0', '1', '2'], default='0',
+ help='Safe content filter from none to strict')
+ parser.add_argument(
+ '--timerange', type=str,
+ nargs='?', choices=['day', 'week', 'month', 'year'],
+ help='Filter by time range')
+ return parser.parse_args(args)
-result_container_json = {
- "search": {
- "q": search_query.query,
- "pageno": search_query.pageno,
- "lang": search_query.lang,
- "safesearch": search_query.safesearch,
- "timerange": search_query.time_range,
- "engines": search_query.engines
- },
- "results": no_parsed_url(result_container.get_ordered_results()),
- "infoboxes": result_container.infoboxes,
- "suggestions": list(result_container.suggestions),
- "answers": list(result_container.answers),
- "paging": result_container.paging,
- "results_number": result_container.results_number()
-}
-sys.stdout = codecs.getwriter("UTF-8")(sys.stdout)
-sys.stdout.write(dumps(result_container_json, sort_keys=True, indent=4, ensure_ascii=False, encoding="utf-8", default=json_serial))
+if __name__ == '__main__':
+ searx.engines.initialize_engines(searx.settings['engines'])
+ engine_cs = list(searx.engines.categories.keys())
+ prog_args = parse_argument(category_choices=engine_cs)
+ search_q = get_search_query(prog_args, engine_categories=engine_cs)
+ res_dict = to_dict(search_q)
+ sys.stdout.write(dumps(
+ res_dict, sort_keys=True, indent=4, ensure_ascii=False,
+ default=json_serial))
diff --git a/utils/templates/etc/searx/use_default_settings.yml b/utils/templates/etc/searx/use_default_settings.yml
new file mode 100644
index 0000000..e019a25
--- /dev/null
+++ b/utils/templates/etc/searx/use_default_settings.yml
@@ -0,0 +1,22 @@
+use_default_settings: True
+
+general:
+ debug : False # Debug mode, only for development
+ instance_name : "searx" # displayed name
+
+search:
+ safe_search : 0 # Filter results. 0: None, 1: Moderate, 2: Strict
+ autocomplete : "" # Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "startpage", "swisscows", "qwant", "wikipedia" - leave blank to turn it off by default
+ default_lang : "" # Default search language - leave blank to detect from browser information or use codes from 'languages.py'
+
+server:
+ port : 8888
+ bind_address : "127.0.0.1" # address to listen on
+ secret_key : "ultrasecretkey" # change this!
+ base_url : False # Set custom base_url. Possible values: False or "https://your.custom.host/location/"
+ image_proxy : False # Proxying image results through searx
+
+# uncomment below section if you have running morty proxy
+#result_proxy:
+# url : http://127.0.0.1:3000/
+# key : !!binary "your_morty_proxy_key"
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
index 51f659d..9dd2e6f 100644
--- a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
@@ -9,6 +9,11 @@
uid = ${SERVICE_USER}
gid = ${SERVICE_GROUP}
+# set (python) default encoding UTF-8
+env = LANG=C.UTF-8
+env = LANGUAGE=C.UTF-8
+env = LC_ALL=C.UTF-8
+
# chdir to specified directory before apps loading
chdir = ${SEARX_SRC}/searx
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket
index eeabb37..1a0fda9 100644
--- a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket
@@ -9,6 +9,11 @@
uid = ${SERVICE_USER}
gid = ${SERVICE_GROUP}
+# set (python) default encoding UTF-8
+env = LANG=C.UTF-8
+env = LANGUAGE=C.UTF-8
+env = LC_ALL=C.UTF-8
+
# chdir to specified directory before apps loading
chdir = ${SEARX_SRC}/searx
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
index 9785d7c..4d69da0 100644
--- a/utils/templates/etc/uwsgi/apps-available/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -9,6 +9,11 @@
uid = ${SERVICE_USER}
gid = ${SERVICE_GROUP}
+# set (python) default encoding UTF-8
+env = LANG=C.UTF-8
+env = LANGUAGE=C.UTF-8
+env = LC_ALL=C.UTF-8
+
# chdir to specified directory before apps loading
chdir = ${SEARX_SRC}/searx
@@ -74,6 +79,6 @@ http = ${SEARX_INTERNAL_HTTP}
# On some distributions you need to create the app folder for the sockets::
#
# mkdir -p /run/uwsgi/app/searx
-# chmod -R ${SERVICE_USER}:${SERVICE_GROUP} /run/uwsgi/app/searx
+# chown -R ${SERVICE_USER}:${SERVICE_GROUP} /run/uwsgi/app/searx
#
-# socket = /run/uwsgi/app/searx/socket \ No newline at end of file
+# socket = /run/uwsgi/app/searx/socket
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini:socket b/utils/templates/etc/uwsgi/apps-available/searx.ini:socket
index 88436e5..9cb2921 100644
--- a/utils/templates/etc/uwsgi/apps-available/searx.ini:socket
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini:socket
@@ -9,6 +9,11 @@
uid = ${SERVICE_USER}
gid = ${SERVICE_GROUP}
+# set (python) default encoding UTF-8
+env = LANG=C.UTF-8
+env = LANGUAGE=C.UTF-8
+env = LC_ALL=C.UTF-8
+
# chdir to specified directory before apps loading
chdir = ${SEARX_SRC}/searx