diff options
| author | 2025-05-18 12:00:19 -0400 | |
|---|---|---|
| committer | 2025-05-18 12:00:19 -0400 | |
| commit | 01f852081b1291da7fc342976bf5228ffb618736 (patch) | |
| tree | 9701e47aaa34e1fd777cea4371d17103f5d90932 /src | |
| parent | cb342b234defc1205de5d8b10ddf79deae0a551a (diff) | |
| download | camu-01f852081b1291da7fc342976bf5228ffb618736.tar.gz camu-01f852081b1291da7fc342976bf5228ffb618736.tar.bz2 camu-01f852081b1291da7fc342976bf5228ffb618736.zip | |
Portal update
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'src')
25 files changed, 1809 insertions, 4081 deletions
diff --git a/src/portal/cpy/portal.pyx b/src/portal/cpy/portal.pyx index b9b6e8d..6307a2d 100644 --- a/src/portal/cpy/portal.pyx +++ b/src/portal/cpy/portal.pyx @@ -69,7 +69,7 @@ class Portal(): def print_modules(self): for name in ALL_MODULES.keys(): - log.info('Registered module \'{}\'.'.format(name)) + log.info(f'Registered module \'{name}\'.') def new_id(self): while True: diff --git a/src/portal/patches/instagrapi_no_android.diff b/src/portal/patches/instagrapi_no_android.diff deleted file mode 100644 index 6f4fd34..0000000 --- a/src/portal/patches/instagrapi_no_android.diff +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/instagrapi/mixins/private.py b/instagrapi/mixins/private.py -index 9db04b0..2b17d75 100644 ---- a/instagrapi/mixins/private.py -+++ b/instagrapi/mixins/private.py -@@ -129,9 +129,9 @@ class PrivateRequestMixin: - # X-IG-WWW-Claim: hmac.AR3zruvyGTlwHvVd2ACpGCWLluOppXX4NAVDV-iYslo9CaDd - "X-Bloks-Is-Layout-RTL": "false", - "X-Bloks-Is-Panorama-Enabled": "true", -- "X-IG-Device-ID": self.uuid, -- "X-IG-Family-Device-ID": self.phone_id, -- "X-IG-Android-ID": self.android_device_id, -+ #"X-IG-Device-ID": self.uuid, -+ #"X-IG-Family-Device-ID": self.phone_id, -+ #"X-IG-Android-ID": self.android_device_id, - "X-IG-Timezone-Offset": str(self.timezone_offset), - "X-IG-Connection-Type": "WIFI", - "X-IG-Capabilities": "3brTvx0=", # "3brTvwE=" in instabot diff --git a/src/portal/patches/pixivpy_missing_is_followed.diff b/src/portal/patches/pixivpy_missing_is_followed.diff new file mode 100644 index 0000000..0d9674b --- /dev/null +++ b/src/portal/patches/pixivpy_missing_is_followed.diff @@ -0,0 +1,13 @@ +diff --git a/pixivpy3/models.py b/pixivpy3/models.py +index fa9a950..b2d81a0 100644 +--- a/pixivpy3/models.py ++++ b/pixivpy3/models.py +@@ -113,7 +113,7 @@ class UserInfo(BasePixivpyModel): + account: str + profile_image_urls: ProfileImageUrls + comment: Optional[str] = None # present only on `user_detail` endpoint +- is_followed: Optional[bool] ++ is_followed: Optional[bool] = None + is_access_blocking_user: Optional[bool] = None + + diff --git a/src/portal/patches/pixivpy_reauth.diff b/src/portal/patches/pixivpy_reauth.diff index d9ce0d6..386adb3 100644 --- a/src/portal/patches/pixivpy_reauth.diff +++ b/src/portal/patches/pixivpy_reauth.diff @@ -1,30 +1,8 @@ -diff --git a/pixivpy3/aapi.py b/pixivpy3/aapi.py -index 1492385..ffc88c0 100644 ---- a/pixivpy3/aapi.py -+++ b/pixivpy3/aapi.py -@@ -76,12 +76,11 @@ class AppPixivAPI(BasePixivAPI): - ) -> Response: - headers_ = CaseInsensitiveDict(headers or {}) - if self.hosts != "https://app-api.pixiv.net": -- headers_["host"] = "app-api.pixiv.net" -- if "user-agent" not in headers_: -- # Set User-Agent if not provided -- headers_["app-os"] = "ios" -- headers_["app-os-version"] = "14.6" -- headers_["user-agent"] = "PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)" -+ headers_["Host"] = "app-api.pixiv.net" -+ headers_["App-OS"] = "ios" -+ headers_["App-OS-Version"] = "16.7.2" -+ headers_["App-Version"] = "7.19.6" -+ headers_["User-Agent"] = "PixivIOSApp/7.19.6 (iOS 16.7.2; iPhone13,2)" - - if not req_auth: - return self.requests_call(method, url, headers_, params, data) diff --git a/pixivpy3/api.py b/pixivpy3/api.py -index c26dda2..d0e1c0f 100644 +index 8a246b6..473c620 100644 --- a/pixivpy3/api.py +++ b/pixivpy3/api.py -@@ -26,6 +26,7 @@ class BasePixivAPI: +@@ -24,6 +24,7 @@ class BasePixivAPI: self.user_id: int | str = 0 self.access_token: str | None = None self.refresh_token: str | None = None @@ -32,65 +10,48 @@ index c26dda2..d0e1c0f 100644 self.hosts = "https://app-api.pixiv.net" # self.requests = requests.Session() -@@ -62,6 +63,11 @@ class BasePixivAPI: +@@ -63,6 +64,11 @@ class BasePixivAPI: stream: bool = False, ) -> Response: - """requests http/https call for Pixiv API""" -+ if self.token_expiration is not None and datetime.utcnow().timestamp() >= self.token_expiration: -+ # Token expired refresh auth -+ print('Token expired, refreshing auth') + """Requests http/https call for Pixiv API""" ++ if self.token_expiration and datetime.now().timestamp() >= self.token_expiration: ++ # Token expired refresh auth. ++ print("Token expired, refreshing auth.") + self.token_expiration = None + self.auth() merged_headers = self.additional_headers.copy() if headers: # Use the headers in the parameter to override the -@@ -118,23 +124,22 @@ class BasePixivAPI: +@@ -123,11 +129,12 @@ class BasePixivAPI: headers: ParamDict = None, ) -> ParsedJson: """Login with password, or use the refresh_token to acquire a new bearer token""" -- local_time = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S+00:00") -+ local_time = datetime.utcnow() +- local_time = datetime.now().strftime("%Y-%m-%dT%H:%M:%S+00:00") ++ local_time = datetime.now() + local_time_str = local_time.strftime("%Y-%m-%dT%H:%M:%S+00:00") headers_ = CaseInsensitiveDict(headers or {}) - headers_["x-client-time"] = local_time -- headers_["x-client-hash"] = hashlib.md5((local_time + self.hash_secret).encode("utf-8")).hexdigest() -- # Allow mock UA due to #171: https://github.com/upbit/pixivpy/issues/171 -- if "user-agent" not in headers_: -- headers_["app-os"] = "ios" -- headers_["app-os-version"] = "14.6" -- headers_["user-agent"] = "PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)" -- -+ headers_["X-Client-Time"] = local_time_str -+ headers_["X-Client-Hash"] = hashlib.md5((local_time_str + self.hash_secret).encode("utf-8")).hexdigest() -+ headers_["App-OS"] = "ios" -+ headers_["App-OS-Version"] = "16.7.2" -+ headers_["App-Version"] = "7.19.6" -+ headers_["User-Agent"] = "PixivIOSApp/7.19.6 (iOS 16.7.2; iPhone13,2)" - # noinspection PyUnresolvedReferences - if not hasattr(self, "hosts") or self.hosts == "https://app-api.pixiv.net": - auth_hosts = "https://oauth.secure.pixiv.net" - else: - # noinspection PyUnresolvedReferences - auth_hosts = self.hosts # BAPI解析成IP的场景 -- headers_["host"] = "oauth.secure.pixiv.net" -+ headers_["Host"] = "oauth.secure.pixiv.net" - url = "%s/auth/token" % auth_hosts - data = { - "get_secure_url": 1, -@@ -149,6 +154,8 @@ class BasePixivAPI: ++ headers_["x-client-time"] = local_time_str + headers_["x-client-hash"] = hashlib.md5( +- (local_time + self.hash_secret).encode("utf-8") ++ (local_time_str + self.hash_secret).encode("utf-8") + ).hexdigest() + # Allow mock UA due to #171: https://github.com/upbit/pixivpy/issues/171 + if "user-agent" not in headers_: +@@ -156,6 +163,8 @@ class BasePixivAPI: elif refresh_token or self.refresh_token: data["grant_type"] = "refresh_token" data["refresh_token"] = refresh_token or self.refresh_token + if self.access_token: + data["access_token"] = self.access_token else: - raise PixivError("[ERROR] auth() but no password or refresh_token is set.") - -@@ -174,6 +181,7 @@ class BasePixivAPI: + msg = "[ERROR] auth() but no password or refresh_token is set." + raise PixivError(msg) +@@ -183,6 +192,7 @@ class BasePixivAPI: self.user_id = token.response.user.id self.access_token = token.response.access_token self.refresh_token = token.response.refresh_token + self.token_expiration = local_time.timestamp() + token.response.expires_in except json.JSONDecodeError: + msg = f"Get access_token error! Response: {token}" raise PixivError( - "Get access_token error! Response: %s" % token, diff --git a/src/portal/patches/searxng_disable_engines_default.diff b/src/portal/patches/searxng_disable_engines_default.diff deleted file mode 100644 index 923519c..0000000 --- a/src/portal/patches/searxng_disable_engines_default.diff +++ /dev/null @@ -1,3675 +0,0 @@ -diff --git a/searx/settings.yml b/searx/settings.yml -index 6946b89d..92378207 100644 ---- a/searx/settings.yml -+++ b/searx/settings.yml -@@ -294,468 +294,6 @@ categories_as_tabs: - social media: - - engines: -- - name: 9gag -- engine: 9gag -- shortcut: 9g -- disabled: true -- -- - name: annas archive -- engine: annas_archive -- disabled: true -- shortcut: aa -- -- # - name: annas articles -- # engine: annas_archive -- # shortcut: aaa -- # # https://docs.searxng.org/dev/engines/online/annas_archive.html -- # aa_content: 'journal_article' # book_any .. magazine, standards_document -- # aa_ext: 'pdf' # pdf, epub, .. -- # aa_sort: 'newest' # newest, oldest, largest, smallest -- -- - name: apk mirror -- engine: apkmirror -- timeout: 4.0 -- shortcut: apkm -- disabled: true -- -- - name: apple app store -- engine: apple_app_store -- shortcut: aps -- disabled: true -- -- # Requires Tor -- - name: ahmia -- engine: ahmia -- categories: onions -- enable_http: true -- shortcut: ah -- -- - name: anaconda -- engine: xpath -- paging: true -- first_page_num: 0 -- search_url: https://anaconda.org/search?q={query}&page={pageno} -- results_xpath: //tbody/tr -- url_xpath: ./td/h5/a[last()]/@href -- title_xpath: ./td/h5 -- content_xpath: ./td[h5]/text() -- categories: it -- timeout: 6.0 -- shortcut: conda -- disabled: true -- -- - name: arch linux wiki -- engine: archlinux -- shortcut: al -- -- - name: artic -- engine: artic -- shortcut: arc -- timeout: 4.0 -- -- - name: arxiv -- engine: arxiv -- shortcut: arx -- timeout: 4.0 -- -- # tmp suspended: dh key too small -- # - name: base -- # engine: base -- # shortcut: bs -- -- - name: bandcamp -- engine: bandcamp -- shortcut: bc -- categories: music -- -- - name: wikipedia -- engine: wikipedia -- shortcut: wp -- # add "list" to the array to get results in the results list -- display_type: ["infobox"] -- base_url: 'https://{language}.wikipedia.org/' -- categories: [general] -- -- - name: bilibili -- engine: bilibili -- shortcut: bil -- disabled: true -- -- - name: bing -- engine: bing -- shortcut: bi -- disabled: true -- -- - name: bing images -- engine: bing_images -- shortcut: bii -- -- - name: bing news -- engine: bing_news -- shortcut: bin -- -- - name: bing videos -- engine: bing_videos -- shortcut: biv -- -- - name: bitbucket -- engine: xpath -- paging: true -- search_url: https://bitbucket.org/repo/all/{pageno}?name={query} -- url_xpath: //article[@class="repo-summary"]//a[@class="repo-link"]/@href -- title_xpath: //article[@class="repo-summary"]//a[@class="repo-link"] -- content_xpath: //article[@class="repo-summary"]/p -- categories: [it, repos] -- timeout: 4.0 -- disabled: true -- shortcut: bb -- about: -- website: https://bitbucket.org/ -- wikidata_id: Q2493781 -- official_api_documentation: https://developer.atlassian.com/bitbucket -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: btdigg -- engine: btdigg -- shortcut: bt -- disabled: true -- -- - name: ccc-tv -- engine: xpath -- paging: false -- search_url: https://media.ccc.de/search/?q={query} -- url_xpath: //div[@class="caption"]/h3/a/@href -- title_xpath: //div[@class="caption"]/h3/a/text() -- content_xpath: //div[@class="caption"]/h4/@title -- categories: videos -- disabled: true -- shortcut: c3tv -- about: -- website: https://media.ccc.de/ -- wikidata_id: Q80729951 -- official_api_documentation: https://github.com/voc/voctoweb -- use_official_api: false -- require_api_key: false -- results: HTML -- # We don't set language: de here because media.ccc.de is not just -- # for a German audience. It contains many English videos and many -- # German videos have English subtitles. -- -- - name: openverse -- engine: openverse -- categories: images -- shortcut: opv -- -- - name: chefkoch -- engine: chefkoch -- shortcut: chef -- # to show premium or plus results too: -- # skip_premium: false -- -- # - name: core.ac.uk -- # engine: core -- # categories: science -- # shortcut: cor -- # # get your API key from: https://core.ac.uk/api-keys/register/ -- # api_key: 'unset' -- -- - name: crossref -- engine: crossref -- shortcut: cr -- timeout: 30 -- disabled: true -- -- - name: crowdview -- engine: json_engine -- shortcut: cv -- categories: general -- paging: false -- search_url: https://crowdview-next-js.onrender.com/api/search-v3?query={query} -- results_query: results -- url_query: link -- title_query: title -- content_query: snippet -- disabled: true -- about: -- website: https://crowdview.ai/ -- -- - name: yep -- engine: json_engine -- shortcut: yep -- categories: general -- disabled: true -- paging: false -- content_html_to_text: true -- title_html_to_text: true -- search_url: https://api.yep.com/fs/1/?type=web&q={query}&no_correct=false&limit=100 -- results_query: 1/results -- title_query: title -- url_query: url -- content_query: snippet -- about: -- website: https://yep.com -- use_official_api: false -- require_api_key: false -- results: JSON -- -- - name: curlie -- engine: xpath -- shortcut: cl -- categories: general -- disabled: true -- paging: true -- lang_all: '' -- search_url: https://curlie.org/search?q={query}&lang={lang}&start={pageno}&stime=92452189 -- page_size: 20 -- results_xpath: //div[@id="site-list-content"]/div[@class="site-item"] -- url_xpath: ./div[@class="title-and-desc"]/a/@href -- title_xpath: ./div[@class="title-and-desc"]/a/div -- content_xpath: ./div[@class="title-and-desc"]/div[@class="site-descr"] -- about: -- website: https://curlie.org/ -- wikidata_id: Q60715723 -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: currency -- engine: currency_convert -- categories: general -- shortcut: cc -- -- - name: deezer -- engine: deezer -- shortcut: dz -- disabled: true -- -- - name: deviantart -- engine: deviantart -- shortcut: da -- timeout: 3.0 -- -- - name: ddg definitions -- engine: duckduckgo_definitions -- shortcut: ddd -- weight: 2 -- disabled: true -- tests: *tests_infobox -- -- # cloudflare protected -- # - name: digbt -- # engine: digbt -- # shortcut: dbt -- # timeout: 6.0 -- # disabled: true -- -- - name: docker hub -- engine: docker_hub -- shortcut: dh -- categories: [it, packages] -- -- - name: erowid -- engine: xpath -- paging: true -- first_page_num: 0 -- page_size: 30 -- search_url: https://www.erowid.org/search.php?q={query}&s={pageno} -- url_xpath: //dl[@class="results-list"]/dt[@class="result-title"]/a/@href -- title_xpath: //dl[@class="results-list"]/dt[@class="result-title"]/a/text() -- content_xpath: //dl[@class="results-list"]/dd[@class="result-details"] -- categories: [] -- shortcut: ew -- disabled: true -- about: -- website: https://www.erowid.org/ -- wikidata_id: Q1430691 -- official_api_documentation: -- use_official_api: false -- require_api_key: false -- results: HTML -- -- # - 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 -- timeout: 3.0 -- weight: 2 -- # add "list" to the array to get results in the results list -- display_type: ["infobox"] -- tests: *tests_infobox -- categories: [general] -- -- - name: duckduckgo -- engine: duckduckgo -- shortcut: ddg -- -- - name: duckduckgo images -- engine: duckduckgo_images -- shortcut: ddi -- timeout: 3.0 -- disabled: true -- -- - name: duckduckgo weather -- engine: duckduckgo_weather -- shortcut: ddw -- disabled: true -- -- - name: apple maps -- engine: apple_maps -- shortcut: apm -- disabled: true -- timeout: 5.0 -- -- - name: emojipedia -- engine: emojipedia -- timeout: 4.0 -- shortcut: em -- disabled: true -- -- - name: tineye -- engine: tineye -- shortcut: tin -- timeout: 9.0 -- disabled: true -- -- - name: etymonline -- engine: xpath -- paging: true -- search_url: https://etymonline.com/search?page={pageno}&q={query} -- url_xpath: //a[contains(@class, "word__name--")]/@href -- title_xpath: //a[contains(@class, "word__name--")] -- content_xpath: //section[contains(@class, "word__defination")] -- first_page_num: 1 -- shortcut: et -- categories: [dictionaries] -- about: -- website: https://www.etymonline.com/ -- wikidata_id: Q1188617 -- official_api_documentation: -- use_official_api: false -- require_api_key: false -- results: HTML -- -- # - name: ebay -- # engine: ebay -- # shortcut: eb -- # base_url: 'https://www.ebay.com' -- # disabled: true -- # timeout: 5 -- -- - name: 1x -- engine: www1x -- shortcut: 1x -- timeout: 3.0 -- disabled: true -- -- - name: fdroid -- engine: fdroid -- shortcut: fd -- disabled: true -- -- - name: flickr -- categories: images -- shortcut: fl -- # You can use the engine using the official stable API, but you need an API -- # key, see: https://www.flickr.com/services/apps/create/ -- # engine: flickr -- # api_key: 'apikey' # required! -- # Or you can use the html non-stable engine, activated by default -- engine: flickr_noapi -- -- - name: free software directory -- engine: mediawiki -- shortcut: fsd -- categories: [it, software wikis] -- base_url: https://directory.fsf.org/ -- search_type: title -- timeout: 5.0 -- disabled: true -- about: -- website: https://directory.fsf.org/ -- wikidata_id: Q2470288 -- -- # - name: freesound -- # engine: freesound -- # shortcut: fnd -- # disabled: true -- # timeout: 15.0 -- # API key required, see: https://freesound.org/docs/api/overview.html -- # api_key: MyAPIkey -- -- - name: frinkiac -- engine: frinkiac -- shortcut: frk -- disabled: true -- -- - name: genius -- engine: genius -- shortcut: gen -- -- - name: gentoo -- engine: gentoo -- shortcut: ge -- timeout: 10.0 -- -- - name: gitlab -- engine: json_engine -- paging: true -- search_url: https://gitlab.com/api/v4/projects?search={query}&page={pageno} -- url_query: web_url -- title_query: name_with_namespace -- content_query: description -- page_size: 20 -- categories: [it, repos] -- shortcut: gl -- timeout: 10.0 -- disabled: true -- about: -- website: https://about.gitlab.com/ -- wikidata_id: Q16639197 -- official_api_documentation: https://docs.gitlab.com/ee/api/ -- use_official_api: false -- require_api_key: false -- results: JSON -- -- - name: github -- 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, repos] -- shortcut: cb -- disabled: true -- about: -- website: https://codeberg.org/ -- wikidata_id: -- official_api_documentation: https://try.gitea.io/api/swagger -- use_official_api: false -- require_api_key: false -- results: JSON -- - - name: google - engine: google - shortcut: go -@@ -774,1372 +312,1837 @@ engines: - # result_container: - # - ['one_title_contains', 'Salvador'] - -- - name: google news -- engine: google_news -- shortcut: gon -- # additional_tests: -- # android: *test_android -- -- - name: google videos -- engine: google_videos -- shortcut: gov -- # additional_tests: -- # android: *test_android -- -- - name: google scholar -- engine: google_scholar -- shortcut: gos -- -- - name: google play apps -- engine: google_play -- categories: [files, apps] -- shortcut: gpa -- play_categ: apps -- disabled: true -- -- - name: google play movies -- engine: google_play -- categories: videos -- shortcut: gpm -- play_categ: movies -- disabled: true -- -- - name: material icons -- engine: material_icons -- categories: images -- shortcut: mi -- disabled: true -- -- - name: gpodder -- engine: json_engine -- shortcut: gpod -- timeout: 4.0 -- paging: false -- search_url: https://gpodder.net/search.json?q={query} -- url_query: url -- title_query: title -- content_query: description -- page_size: 19 -- categories: music -- disabled: true -- about: -- website: https://gpodder.net -- wikidata_id: Q3093354 -- official_api_documentation: https://gpoddernet.readthedocs.io/en/latest/api/ -- use_official_api: false -- requires_api_key: false -- results: JSON -- -- - name: habrahabr -- engine: xpath -- paging: true -- search_url: https://habr.com/en/search/page{pageno}/?q={query} -- results_xpath: //article[contains(@class, "tm-articles-list__item")] -- url_xpath: .//a[@class="tm-title__link"]/@href -- title_xpath: .//a[@class="tm-title__link"] -- content_xpath: .//div[contains(@class, "article-formatted-body")] -- categories: it -- timeout: 4.0 -- disabled: true -- shortcut: habr -- about: -- website: https://habr.com/ -- wikidata_id: Q4494434 -- official_api_documentation: https://habr.com/en/docs/help/api/ -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: hoogle -- engine: xpath -- paging: true -- search_url: https://hoogle.haskell.org/?hoogle={query}&start={pageno} -- results_xpath: '//div[@class="result"]' -- title_xpath: './/div[@class="ans"]//a' -- url_xpath: './/div[@class="ans"]//a/@href' -- content_xpath: './/div[@class="from"]' -- page_size: 20 -- categories: [it, packages] -- shortcut: ho -- about: -- website: https://hoogle.haskell.org/ -- wikidata_id: Q34010 -- official_api_documentation: https://hackage.haskell.org/api -- use_official_api: false -- require_api_key: false -- results: JSON -- -- - name: imdb -- engine: imdb -- shortcut: imdb -- timeout: 6.0 -- disabled: true -- -- - name: ina -- engine: ina -- shortcut: in -- timeout: 6.0 -- disabled: true -- -- - name: invidious -- engine: invidious -- # Instanes will be selected randomly, see https://api.invidious.io/ for -- # instances that are stable (good uptime) and close to you. -- base_url: -- - https://invidious.io.lol -- - https://invidious.fdn.fr -- - https://yt.artemislena.eu -- - https://invidious.tiekoetter.com -- - https://invidious.flokinet.to -- - https://vid.puffyan.us -- - https://invidious.privacydev.net -- - https://inv.tux.pizza -- shortcut: iv -- timeout: 3.0 -- disabled: true -- -- - name: jisho -- engine: jisho -- shortcut: js -- timeout: 3.0 -- disabled: true -- -- - name: kickass -- engine: kickass -- shortcut: kc -- timeout: 4.0 -- disabled: true -- -- - name: lemmy communities -- engine: lemmy -- lemmy_type: Communities -- shortcut: leco -- -- - name: lemmy users -- engine: lemmy -- network: lemmy communities -- lemmy_type: Users -- shortcut: leus -- -- - name: lemmy posts -- engine: lemmy -- network: lemmy communities -- lemmy_type: Posts -- shortcut: lepo -- -- - name: lemmy comments -- engine: lemmy -- network: lemmy communities -- lemmy_type: Comments -- shortcut: lecom -- -- - name: library genesis -- engine: xpath -- search_url: https://libgen.fun/search.php?req={query} -- url_xpath: //a[contains(@href,"get.php?md5")]/@href -- title_xpath: //a[contains(@href,"book/")]/text()[1] -- content_xpath: //td/a[1][contains(@href,"=author")]/text() -- categories: files -- timeout: 7.0 -- disabled: true -- shortcut: lg -- about: -- website: https://libgen.fun/ -- wikidata_id: Q22017206 -- official_api_documentation: -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: z-library -- engine: zlibrary -- shortcut: zlib -- categories: files -- timeout: 7.0 -- -- - name: library of congress -- engine: loc -- shortcut: loc -- categories: images -- -- - name: lingva -- engine: lingva -- shortcut: lv -- # set lingva instance in url, by default it will use the official instance -- # url: https://lingva.ml -- -- - name: lobste.rs -- 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: .//a[@class="u-url"]/@href -- title_xpath: .//a[@class="u-url"] -- content_xpath: .//a[@class="domain"] -- categories: it -- shortcut: lo -- timeout: 5.0 -- disabled: true -- about: -- website: https://lobste.rs/ -- wikidata_id: Q60762874 -- official_api_documentation: -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: azlyrics -- shortcut: lyrics -- engine: xpath -- timeout: 4.0 -- disabled: true -- categories: [music, lyrics] -- paging: true -- search_url: https://search.azlyrics.com/search.php?q={query}&w=lyrics&p={pageno} -- url_xpath: //td[@class="text-left visitedlyr"]/a/@href -- title_xpath: //span/b/text() -- content_xpath: //td[@class="text-left visitedlyr"]/a/small -- about: -- website: https://azlyrics.com -- wikidata_id: Q66372542 -- official_api_documentation: -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: metacpan -- engine: metacpan -- shortcut: cpan -- disabled: true -- number_of_results: 20 -- -- # - name: meilisearch -- # engine: meilisearch -- # shortcut: mes -- # enable_http: true -- # base_url: http://localhost:7700 -- # index: my-index -- -- - name: mixcloud -- engine: mixcloud -- shortcut: mc -- -- # MongoDB engine -- # Required dependency: pymongo -- # - name: mymongo -- # engine: mongodb -- # shortcut: md -- # exact_match_only: false -- # host: '127.0.0.1' -- # port: 27017 -- # enable_http: true -- # results_per_page: 20 -- # database: 'business' -- # collection: 'reviews' # name of the db collection -- # key: 'name' # key in the collection to search for -- -- - name: mwmbl -- engine: mwmbl -- # api_url: https://api.mwmbl.org -- shortcut: mwm -- disabled: true -- -- - name: npm -- engine: json_engine -- paging: true -- first_page_num: 0 -- search_url: https://api.npms.io/v2/search?q={query}&size=25&from={pageno} -- results_query: results -- url_query: package/links/npm -- title_query: package/name -- content_query: package/description -- page_size: 25 -- categories: [it, packages] -- disabled: true -- timeout: 5.0 -- shortcut: npm -- about: -- website: https://npms.io/ -- wikidata_id: Q7067518 -- official_api_documentation: https://api-docs.npms.io/ -- use_official_api: false -- require_api_key: false -- results: JSON -- -- - name: nyaa -- engine: nyaa -- shortcut: nt -- disabled: true -- -- - name: mankier -- engine: json_engine -- search_url: https://www.mankier.com/api/v2/mans/?q={query} -- results_query: results -- url_query: url -- title_query: name -- content_query: description -- categories: it -- shortcut: man -- about: -- website: https://www.mankier.com/ -- official_api_documentation: https://www.mankier.com/api -- use_official_api: true -- require_api_key: false -- results: JSON -- -- - name: odysee -- engine: odysee -- shortcut: od -- disabled: true -- -- - name: openairedatasets -- engine: json_engine -- paging: true -- search_url: https://api.openaire.eu/search/datasets?format=json&page={pageno}&size=10&title={query} -- results_query: response/results/result -- url_query: metadata/oaf:entity/oaf:result/children/instance/webresource/url/$ -- title_query: metadata/oaf:entity/oaf:result/title/$ -- content_query: metadata/oaf:entity/oaf:result/description/$ -- content_html_to_text: true -- categories: "science" -- shortcut: oad -- timeout: 5.0 -- about: -- website: https://www.openaire.eu/ -- wikidata_id: Q25106053 -- official_api_documentation: https://api.openaire.eu/ -- use_official_api: false -- require_api_key: false -- results: JSON -- -- - name: openairepublications -- engine: json_engine -- paging: true -- search_url: https://api.openaire.eu/search/publications?format=json&page={pageno}&size=10&title={query} -- results_query: response/results/result -- url_query: metadata/oaf:entity/oaf:result/children/instance/webresource/url/$ -- title_query: metadata/oaf:entity/oaf:result/title/$ -- content_query: metadata/oaf:entity/oaf:result/description/$ -- content_html_to_text: true -- categories: science -- shortcut: oap -- timeout: 5.0 -- about: -- website: https://www.openaire.eu/ -- wikidata_id: Q25106053 -- official_api_documentation: https://api.openaire.eu/ -- use_official_api: false -- require_api_key: false -- results: JSON -- -- # - name: opensemanticsearch -- # engine: opensemantic -- # shortcut: oss -- # base_url: 'http://localhost:8983/solr/opensemanticsearch/' -- -- - name: openstreetmap -- engine: openstreetmap -- shortcut: osm -- -- - name: openrepos -- engine: xpath -- paging: true -- search_url: https://openrepos.net/search/node/{query}?page={pageno} -- url_xpath: //li[@class="search-result"]//h3[@class="title"]/a/@href -- title_xpath: //li[@class="search-result"]//h3[@class="title"]/a -- content_xpath: //li[@class="search-result"]//div[@class="search-snippet-info"]//p[@class="search-snippet"] -- categories: files -- timeout: 4.0 -- disabled: true -- shortcut: or -- about: -- website: https://openrepos.net/ -- wikidata_id: -- official_api_documentation: -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: packagist -- engine: json_engine -- paging: true -- search_url: https://packagist.org/search.json?q={query}&page={pageno} -- results_query: results -- url_query: url -- title_query: name -- content_query: description -- categories: [it, packages] -- disabled: true -- timeout: 5.0 -- shortcut: pack -- about: -- website: https://packagist.org -- wikidata_id: Q108311377 -- official_api_documentation: https://packagist.org/apidoc -- use_official_api: true -- require_api_key: false -- results: JSON -- -- - name: pdbe -- engine: pdbe -- shortcut: pdb -- # Hide obsolete PDB entries. Default is not to hide obsolete structures -- # hide_obsolete: false -- -- - name: photon -- engine: photon -- shortcut: ph -- -- - name: piped -- engine: piped -- shortcut: ppd -- categories: videos -- piped_filter: videos -- timeout: 3.0 -- -- # URL to use as link and for embeds -- frontend_url: https://srv.piped.video -- # Instance will be selected randomly, for more see https://piped-instances.kavin.rocks/ -- backend_url: -- - https://pipedapi.kavin.rocks -- - https://pipedapi-libre.kavin.rocks -- - https://pipedapi.adminforge.de -- -- - name: piped.music -- engine: piped -- network: piped -- shortcut: ppdm -- categories: music -- piped_filter: music_songs -- timeout: 3.0 -- -- - name: piratebay -- engine: piratebay -- shortcut: tpb -- # You may need to change this URL to a proxy if piratebay is blocked in your -- # country -- url: https://thepiratebay.org/ -- timeout: 3.0 -- -- # Required dependency: psychopg2 -- # - name: postgresql -- # engine: postgresql -- # database: postgres -- # username: postgres -- # password: postgres -- # limit: 10 -- # query_str: 'SELECT * from my_table WHERE my_column = %(query)s' -- # shortcut : psql -- -- - name: pub.dev -- engine: xpath -- shortcut: pd -- search_url: https://pub.dev/packages?q={query}&page={pageno} -- paging: true -- results_xpath: //div[contains(@class,"packages-item")] -- url_xpath: ./div/h3/a/@href -- title_xpath: ./div/h3/a -- content_xpath: ./div/div/div[contains(@class,"packages-description")]/span -- categories: [packages, it] -- timeout: 3.0 -- disabled: true -- first_page_num: 1 -- about: -- website: https://pub.dev/ -- official_api_documentation: https://pub.dev/help/api -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: pubmed -- engine: pubmed -- shortcut: pub -- timeout: 3.0 -- -- - name: pypi -- shortcut: pypi -- engine: xpath -- paging: true -- search_url: https://pypi.org/search/?q={query}&page={pageno} -- results_xpath: /html/body/main/div/div/div/form/div/ul/li/a[@class="package-snippet"] -- url_xpath: ./@href -- title_xpath: ./h3/span[@class="package-snippet__name"] -- content_xpath: ./p -- suggestion_xpath: /html/body/main/div/div/div/form/div/div[@class="callout-block"]/p/span/a[@class="link"] -- first_page_num: 1 -- categories: [it, packages] -- about: -- website: https://pypi.org -- wikidata_id: Q2984686 -- official_api_documentation: https://warehouse.readthedocs.io/api-reference/index.html -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: qwant -- qwant_categ: web -- engine: qwant -- shortcut: qw -- categories: [general, web] -- additional_tests: -- rosebud: *test_rosebud -- -- - name: qwant news -- qwant_categ: news -- engine: qwant -- shortcut: qwn -- categories: news -- network: qwant -- -- - name: qwant images -- qwant_categ: images -- engine: qwant -- shortcut: qwi -- categories: [images, web] -- network: qwant -- -- - name: qwant videos -- qwant_categ: videos -- engine: qwant -- shortcut: qwv -- categories: [videos, web] -- network: qwant -- -- # - 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 -- shortcut: re -- page_size: 25 -- -- # Required dependency: redis -- # - name: myredis -- # shortcut : rds -- # engine: redis_server -- # exact_match_only: false -- # host: '127.0.0.1' -- # port: 6379 -- # enable_http: true -- # password: '' -- # db: 0 -+ - name: bing images -+ engine: bing_images -+ shortcut: bii - -- # tmp suspended: bad certificate -- # - name: scanr structures -- # shortcut: scs -- # engine: scanr_structures -+ # - name: 9gag -+ # engine: 9gag -+ # shortcut: 9g - # disabled: true -- -- - name: sepiasearch -- engine: sepiasearch -- shortcut: sep -- -- - name: soundcloud -- engine: soundcloud -- shortcut: sc -- -- - name: stackoverflow -- engine: stackexchange -- shortcut: st -- api_site: 'stackoverflow' -- categories: [it, q&a] -- -- - name: askubuntu -- engine: stackexchange -- shortcut: ubuntu -- api_site: 'askubuntu' -- categories: [it, q&a] -- -- - name: internetarchivescholar -- engine: internet_archive_scholar -- shortcut: ias -- timeout: 5.0 -- -- - name: superuser -- engine: stackexchange -- shortcut: su -- api_site: 'superuser' -- categories: [it, q&a] -- -- - name: searchcode code -- engine: searchcode_code -- shortcut: scc -- disabled: true -- -- - name: framalibre -- engine: framalibre -- shortcut: frl -- disabled: true -- -- # - name: searx -- # engine: searx_engine -- # shortcut: se -- # instance_urls : -- # - http://127.0.0.1:8888/ -- # - ... -- # disabled: true -- -- - name: semantic scholar -- engine: semantic_scholar -- disabled: true -- shortcut: se -- -- # Spotify needs API credentials -- # - name: spotify -- # engine: spotify -- # shortcut: stf -- # api_client_id: ******* -- # api_client_secret: ******* -- -- # - name: solr -- # engine: solr -- # shortcut: slr -- # base_url: http://localhost:8983 -- # collection: collection_name -- # sort: '' # sorting: asc or desc -- # field_list: '' # comma separated list of field names to display on the UI -- # default_fields: '' # default field to query -- # query_fields: '' # query fields -- # enable_http: true -- -- # - name: springer nature -- # engine: springer -- # # get your API key from: https://dev.springernature.com/signup -- # # working API key, for test & debug: "a69685087d07eca9f13db62f65b8f601" -- # api_key: 'unset' -- # shortcut: springer -- # timeout: 15.0 -- -- - name: startpage -- engine: startpage -- shortcut: sp -- timeout: 6.0 -- disabled: true -- additional_tests: -- rosebud: *test_rosebud -- -- - name: tokyotoshokan -- engine: tokyotoshokan -- shortcut: tt -- timeout: 6.0 -- disabled: true -- -- - name: solidtorrents -- engine: solidtorrents -- shortcut: solid -- timeout: 4.0 -- base_url: -- - https://solidtorrents.to -- - https://bitsearch.to -- -- # For this demo of the sqlite engine download: -- # https://liste.mediathekview.de/filmliste-v2.db.bz2 -- # and unpack into searx/data/filmliste-v2.db -- # Query to test: "!demo concert" -- # -- # - name: demo -- # engine: sqlite -- # shortcut: demo -- # categories: general -- # result_template: default.html -- # database: searx/data/filmliste-v2.db -- # query_str: >- -- # SELECT title || ' (' || time(duration, 'unixepoch') || ')' AS title, -- # COALESCE( NULLIF(url_video_hd,''), NULLIF(url_video_sd,''), url_video) AS url, -- # description AS content -- # FROM film -- # WHERE title LIKE :wildcard OR description LIKE :wildcard -- # ORDER BY duration DESC -- -- - name: tagesschau -- engine: tagesschau -- shortcut: ts -- disabled: true -- -- - name: tmdb -- engine: xpath -- paging: true -- search_url: https://www.themoviedb.org/search?page={pageno}&query={query} -- results_xpath: //div[contains(@class,"movie") or contains(@class,"tv")]//div[contains(@class,"card")] -- url_xpath: .//div[contains(@class,"poster")]/a/@href -- thumbnail_xpath: .//img/@src -- title_xpath: .//div[contains(@class,"title")]//h2 -- content_xpath: .//div[contains(@class,"overview")] -- shortcut: tm -- disabled: true -- -- # 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 -- enable_http: true -- shortcut: tch -- -- # torznab engine lets you query any torznab compatible indexer. Using this -- # engine in combination with Jackett opens the possibility to query a lot of -- # public and private indexers directly from SearXNG. More details at: -- # https://docs.searxng.org/dev/engines/online/torznab.html -- # -- # - name: Torznab EZTV -- # engine: torznab -- # shortcut: eztv -- # base_url: http://localhost:9117/api/v2.0/indexers/eztv/results/torznab -- # enable_http: true # if using localhost -- # api_key: xxxxxxxxxxxxxxx -- # show_magnet_links: true -- # show_torrent_files: false -- # # https://github.com/Jackett/Jackett/wiki/Jackett-Categories -- # torznab_categories: # optional -- # - 2000 -- # - 5000 -- -- - name: twitter -- shortcut: tw -- engine: twitter -- disabled: true -- -- # tmp suspended - too slow, too many errors -- # - name: urbandictionary -- # engine : xpath -- # search_url : https://www.urbandictionary.com/define.php?term={query} -- # url_xpath : //*[@class="word"]/@href -- # title_xpath : //*[@class="def-header"] -- # content_xpath: //*[@class="meaning"] -- # shortcut: ud -- -- - name: unsplash -- engine: unsplash -- shortcut: us -- -- - name: yahoo -- engine: yahoo -- shortcut: yh -- disabled: true -- -- - name: yahoo news -- engine: yahoo_news -- shortcut: yhn -- -- - name: youtube -- shortcut: yt -- # You can use the engine using the official stable API, but you need an API -- # key See: https://console.developers.google.com/project -- # -- # engine: youtube_api -- # api_key: 'apikey' # required! -- # -- # Or you can use the html non-stable engine, activated by default -- engine: youtube_noapi -- -- - name: dailymotion -- engine: dailymotion -- shortcut: dm -- -- - name: vimeo -- engine: vimeo -- shortcut: vm -- -- - name: wiby -- engine: json_engine -- paging: true -- search_url: https://wiby.me/json/?q={query}&p={pageno} -- url_query: URL -- title_query: Title -- content_query: Snippet -- categories: [general, web] -- shortcut: wib -- disabled: true -- about: -- website: https://wiby.me/ -- -- - name: alexandria -- engine: json_engine -- shortcut: alx -- categories: general -- paging: true -- search_url: https://api.alexandria.org/?a=1&q={query}&p={pageno} -- results_query: results -- title_query: title -- url_query: url -- content_query: snippet -- timeout: 1.5 -- disabled: true -- about: -- website: https://alexandria.org/ -- official_api_documentation: https://github.com/alexandria-org/alexandria-api/raw/master/README.md -- use_official_api: true -- require_api_key: false -- results: JSON -- -- - name: wikibooks -- engine: mediawiki -- weight: 0.5 -- shortcut: wb -- categories: [general, wikimedia] -- base_url: "https://{language}.wikibooks.org/" -- search_type: text -- disabled: true -- about: -- website: https://www.wikibooks.org/ -- wikidata_id: Q367 -- -- - name: wikinews -- engine: mediawiki -- shortcut: wn -- categories: [news, wikimedia] -- base_url: "https://{language}.wikinews.org/" -- search_type: text -- srsort: create_timestamp_desc -- about: -- website: https://www.wikinews.org/ -- wikidata_id: Q964 -- -- - name: wikiquote -- engine: mediawiki -- weight: 0.5 -- shortcut: wq -- categories: [general, wikimedia] -- base_url: "https://{language}.wikiquote.org/" -- search_type: text -- disabled: true -- additional_tests: -- rosebud: *test_rosebud -- about: -- website: https://www.wikiquote.org/ -- wikidata_id: Q369 -- -- - name: wikisource -- engine: mediawiki -- weight: 0.5 -- shortcut: ws -- categories: [general, wikimedia] -- base_url: "https://{language}.wikisource.org/" -- search_type: text -- disabled: true -- about: -- website: https://www.wikisource.org/ -- wikidata_id: Q263 -- -- - name: wikispecies -- engine: mediawiki -- shortcut: wsp -- categories: [general, science, wikimedia] -- base_url: "https://species.wikimedia.org/" -- search_type: text -- disabled: true -- about: -- website: https://species.wikimedia.org/ -- wikidata_id: Q13679 -- -- - name: wiktionary -- engine: mediawiki -- shortcut: wt -- categories: [dictionaries, wikimedia] -- base_url: "https://{language}.wiktionary.org/" -- search_type: text -- about: -- website: https://www.wiktionary.org/ -- wikidata_id: Q151 -- -- - name: wikiversity -- engine: mediawiki -- weight: 0.5 -- shortcut: wv -- categories: [general, wikimedia] -- base_url: "https://{language}.wikiversity.org/" -- search_type: text -- disabled: true -- about: -- website: https://www.wikiversity.org/ -- wikidata_id: Q370 -- -- - name: wikivoyage -- engine: mediawiki -- weight: 0.5 -- shortcut: wy -- categories: [general, wikimedia] -- base_url: "https://{language}.wikivoyage.org/" -- search_type: text -- disabled: true -- about: -- website: https://www.wikivoyage.org/ -- wikidata_id: Q373 -- -- - name: wikicommons.images -- engine: wikicommons -- shortcut: wc -- categories: images -- number_of_results: 10 -- -- - name: wolframalpha -- shortcut: wa -- # You can use the engine using the official stable API, but you need an API -- # key. See: https://products.wolframalpha.com/api/ -- # -- # engine: wolframalpha_api -- # api_key: '' -- # -- # Or you can use the html non-stable engine, activated by default -- engine: wolframalpha_noapi -- timeout: 6.0 -- categories: general -- disabled: true -- -- - name: dictzone -- engine: dictzone -- shortcut: dc -- -- - name: mymemory translated -- engine: translated -- shortcut: tl -- timeout: 5.0 -- # You can use without an API key, but you are limited to 1000 words/day -- # See: https://mymemory.translated.net/doc/usagelimits.php -- # api_key: '' -- -- # Required dependency: mysql-connector-python -- # - name: mysql -- # engine: mysql_server -- # database: mydatabase -- # username: user -- # password: pass -- # limit: 10 -- # query_str: 'SELECT * from mytable WHERE fieldname=%(query)s' -- # shortcut: mysql -- -- - name: 1337x -- engine: 1337x -- shortcut: 1337x -- disabled: true -- -- - name: duden -- engine: duden -- shortcut: du -- disabled: true -- -- - name: seznam -- shortcut: szn -- engine: seznam -- disabled: true -- -- # - name: deepl -- # engine: deepl -- # shortcut: dpl -- # # You can use the engine using the official stable API, but you need an API key -- # # See: https://www.deepl.com/pro-api?cta=header-pro-api -- # api_key: '' # required! -- # timeout: 5.0 -- # disabled: true -- -- - name: mojeek -- shortcut: mjk -- engine: xpath -- paging: true -- categories: [general, web] -- search_url: https://www.mojeek.com/search?q={query}&s={pageno}&lang={lang}&lb={lang} -- results_xpath: //ul[@class="results-standard"]/li/a[@class="ob"] -- url_xpath: ./@href -- title_xpath: ../h2/a -- content_xpath: ..//p[@class="s"] -- suggestion_xpath: //div[@class="top-info"]/p[@class="top-info spell"]/em/a -- first_page_num: 0 -- page_size: 10 -- disabled: true -- about: -- website: https://www.mojeek.com/ -- wikidata_id: Q60747299 -- official_api_documentation: https://www.mojeek.com/services/api.html/ -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: moviepilot -- engine: moviepilot -- shortcut: mp -- disabled: true -- -- - name: naver -- shortcut: nvr -- categories: [general, web] -- engine: xpath -- paging: true -- search_url: https://search.naver.com/search.naver?where=webkr&sm=osp_hty&ie=UTF-8&query={query}&start={pageno} -- url_xpath: //a[@class="link_tit"]/@href -- title_xpath: //a[@class="link_tit"] -- content_xpath: //a[@class="total_dsc"]/div -- first_page_num: 1 -- page_size: 10 -- disabled: true -- about: -- website: https://www.naver.com/ -- wikidata_id: Q485639 -- official_api_documentation: https://developers.naver.com/docs/nmt/examples/ -- use_official_api: false -- require_api_key: false -- results: HTML -- language: ko -- -- - name: rubygems -- shortcut: rbg -- engine: xpath -- paging: true -- search_url: https://rubygems.org/search?page={pageno}&query={query} -- results_xpath: /html/body/main/div/a[@class="gems__gem"] -- url_xpath: ./@href -- title_xpath: ./span/h2 -- content_xpath: ./span/p -- suggestion_xpath: /html/body/main/div/div[@class="search__suggestions"]/p/a -- first_page_num: 1 -- categories: [it, packages] -- disabled: true -- about: -- website: https://rubygems.org/ -- wikidata_id: Q1853420 -- official_api_documentation: https://guides.rubygems.org/rubygems-org-api/ -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: peertube -- engine: peertube -- shortcut: ptb -- paging: true -- # alternatives see: https://instances.joinpeertube.org/instances -- # base_url: https://tube.4aem.com -- categories: videos -- disabled: true -- timeout: 6.0 -- -- - name: mediathekviewweb -- engine: mediathekviewweb -- shortcut: mvw -- disabled: true -- -- # - name: yacy -- # engine: yacy -- # shortcut: ya -- # base_url: http://localhost:8090 -- # # required if you aren't using HTTPS for your local yacy instance' -- # enable_http: true -- # timeout: 3.0 -- # # Yacy search mode. 'global' or 'local'. -- # search_mode: 'global' -- -- - name: rumble -- engine: rumble -- shortcut: ru -- base_url: https://rumble.com/ -- paging: true -- categories: videos -- disabled: true -- -- - name: wordnik -- engine: wordnik -- shortcut: def -- base_url: https://www.wordnik.com/ -- categories: [dictionaries] -- timeout: 5.0 -- -- - name: woxikon.de synonyme -- engine: xpath -- shortcut: woxi -- categories: [dictionaries] -- timeout: 5.0 -- disabled: true -- search_url: https://synonyme.woxikon.de/synonyme/{query}.php -- url_xpath: //div[@class="upper-synonyms"]/a/@href -- content_xpath: //div[@class="synonyms-list-group"] -- title_xpath: //div[@class="upper-synonyms"]/a -- no_result_for_http_status: [404] -- about: -- website: https://www.woxikon.de/ -- wikidata_id: # No Wikidata ID -- use_official_api: false -- require_api_key: false -- results: HTML -- language: de -- -- - name: seekr news -- engine: seekr -- shortcut: senews -- categories: news -- seekr_category: news -- disabled: true -- -- - name: seekr images -- engine: seekr -- network: seekr news -- shortcut: seimg -- categories: images -- seekr_category: images -- disabled: true -- -- - name: seekr videos -- engine: seekr -- network: seekr news -- shortcut: sevid -- categories: videos -- seekr_category: videos -- disabled: true -- -- - name: sjp.pwn -- engine: sjp -- shortcut: sjp -- base_url: https://sjp.pwn.pl/ -- timeout: 5.0 -- disabled: true -- -- - name: svgrepo -- engine: svgrepo -- shortcut: svg -- timeout: 10.0 -- disabled: true -- -- - name: wallhaven -- engine: wallhaven -- # api_key: abcdefghijklmnopqrstuvwxyz -- shortcut: wh -- -- # wikimini: online encyclopedia for children -- # The fulltext and title parameter is necessary for Wikimini because -- # sometimes it will not show the results and redirect instead -- - name: wikimini -- engine: xpath -- shortcut: wkmn -- search_url: https://fr.wikimini.org/w/index.php?search={query}&title=Sp%C3%A9cial%3ASearch&fulltext=Search -- url_xpath: //li/div[@class="mw-search-result-heading"]/a/@href -- title_xpath: //li//div[@class="mw-search-result-heading"]/a -- content_xpath: //li/div[@class="searchresult"] -- categories: general -- disabled: true -- about: -- website: https://wikimini.org/ -- wikidata_id: Q3568032 -- use_official_api: false -- require_api_key: false -- results: HTML -- language: fr -- -- - name: wttr.in -- engine: wttr -- shortcut: wttr -- timeout: 9.0 -- -- - name: yummly -- engine: yummly -- shortcut: yum -- disabled: true -- -- - name: brave -- engine: brave -- shortcut: br -- time_range_support: true -- paging: true -- categories: [general, web] -- brave_category: search -- # brave_spellcheck: true -- -- - name: brave.images -- engine: brave -- network: brave -- shortcut: brimg -- categories: [images, web] -- brave_category: images -- -- - name: brave.videos -- engine: brave -- network: brave -- shortcut: brvid -- categories: [videos, web] -- brave_category: videos -- -- - name: brave.news -- engine: brave -- network: brave -- shortcut: brnews -- categories: news -- brave_category: news -- -- - name: lib.rs -- shortcut: lrs -- engine: xpath -- search_url: https://lib.rs/search?q={query} -- results_xpath: /html/body/main/div/ol/li/a -- url_xpath: ./@href -- title_xpath: ./div[@class="h"]/h4 -- content_xpath: ./div[@class="h"]/p -- categories: [it, packages] -- disabled: true -- about: -- website: https://lib.rs -- wikidata_id: Q113486010 -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: sourcehut -- shortcut: srht -- engine: xpath -- paging: true -- search_url: https://sr.ht/projects?page={pageno}&search={query} -- results_xpath: (//div[@class="event-list"])[1]/div[@class="event"] -- url_xpath: ./h4/a[2]/@href -- title_xpath: ./h4/a[2] -- content_xpath: ./p -- first_page_num: 1 -- categories: [it, repos] -- disabled: true -- about: -- website: https://sr.ht -- wikidata_id: Q78514485 -- official_api_documentation: https://man.sr.ht/ -- use_official_api: false -- require_api_key: false -- results: HTML -- -- - name: goo -- shortcut: goo -- engine: xpath -- paging: true -- search_url: https://search.goo.ne.jp/web.jsp?MT={query}&FR={pageno}0 -- url_xpath: //div[@class="result"]/p[@class='title fsL1']/a/@href -- title_xpath: //div[@class="result"]/p[@class='title fsL1']/a -- content_xpath: //p[contains(@class,'url fsM')]/following-sibling::p -- first_page_num: 0 -- categories: [general, web] -- disabled: true -- timeout: 4.0 -- about: -- website: https://search.goo.ne.jp -- wikidata_id: Q249044 -- use_official_api: false -- require_api_key: false -- results: HTML -- language: ja -- -- - name: bt4g -- engine: bt4g -- shortcut: bt4g -- -- - name: pkg.go.dev -- engine: xpath -- shortcut: pgo -- search_url: https://pkg.go.dev/search?limit=100&m=package&q={query} -- results_xpath: /html/body/main/div[contains(@class,"SearchResults")]/div[not(@class)]/div[@class="SearchSnippet"] -- url_xpath: ./div[@class="SearchSnippet-headerContainer"]/h2/a/@href -- title_xpath: ./div[@class="SearchSnippet-headerContainer"]/h2/a -- content_xpath: ./p[@class="SearchSnippet-synopsis"] -- categories: [packages, it] -- timeout: 3.0 -- disabled: true -- about: -- website: https://pkg.go.dev/ -- use_official_api: false -- require_api_key: false -- results: HTML -- --# Doku engine lets you access to any Doku wiki instance: --# A public one or a privete/corporate one. --# - name: ubuntuwiki --# engine: doku --# shortcut: uw --# base_url: 'https://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'] -+ # -+ # - name: annas archive -+ # engine: annas_archive -+ # disabled: true -+ # shortcut: aa -+ # -+ # # - name: annas articles -+ # # engine: annas_archive -+ # # shortcut: aaa -+ # # # https://docs.searxng.org/dev/engines/online/annas_archive.html -+ # # aa_content: 'journal_article' # book_any .. magazine, standards_document -+ # # aa_ext: 'pdf' # pdf, epub, .. -+ # # aa_sort: 'newest' # newest, oldest, largest, smallest -+ # -+ # - name: apk mirror -+ # engine: apkmirror -+ # timeout: 4.0 -+ # shortcut: apkm -+ # disabled: true -+ # -+ # - name: apple app store -+ # engine: apple_app_store -+ # shortcut: aps -+ # disabled: true -+ # -+ # # Requires Tor -+ # - name: ahmia -+ # engine: ahmia -+ # categories: onions -+ # enable_http: true -+ # shortcut: ah -+ # -+ # - name: anaconda -+ # engine: xpath -+ # paging: true -+ # first_page_num: 0 -+ # search_url: https://anaconda.org/search?q={query}&page={pageno} -+ # results_xpath: //tbody/tr -+ # url_xpath: ./td/h5/a[last()]/@href -+ # title_xpath: ./td/h5 -+ # content_xpath: ./td[h5]/text() -+ # categories: it -+ # timeout: 6.0 -+ # shortcut: conda -+ # disabled: true -+ # -+ # - name: arch linux wiki -+ # engine: archlinux -+ # shortcut: al -+ # -+ # - name: artic -+ # engine: artic -+ # shortcut: arc -+ # timeout: 4.0 -+ # -+ # - name: arxiv -+ # engine: arxiv -+ # shortcut: arx -+ # timeout: 4.0 -+ # -+ # # tmp suspended: dh key too small -+ # # - name: base -+ # # engine: base -+ # # shortcut: bs -+ # -+ # - name: bandcamp -+ # engine: bandcamp -+ # shortcut: bc -+ # categories: music -+ # -+ # - name: wikipedia -+ # engine: wikipedia -+ # shortcut: wp -+ # # add "list" to the array to get results in the results list -+ # display_type: ["infobox"] -+ # base_url: 'https://{language}.wikipedia.org/' -+ # categories: [general] -+ # -+ # - name: bilibili -+ # engine: bilibili -+ # shortcut: bil -+ # disabled: true -+ # -+ # - name: bing -+ # engine: bing -+ # shortcut: bi -+ # disabled: true -+ # -+ # -+ # - name: bing news -+ # engine: bing_news -+ # shortcut: bin -+ # -+ # - name: bing videos -+ # engine: bing_videos -+ # shortcut: biv -+ # -+ # - name: bitbucket -+ # engine: xpath -+ # paging: true -+ # search_url: https://bitbucket.org/repo/all/{pageno}?name={query} -+ # url_xpath: //article[@class="repo-summary"]//a[@class="repo-link"]/@href -+ # title_xpath: //article[@class="repo-summary"]//a[@class="repo-link"] -+ # content_xpath: //article[@class="repo-summary"]/p -+ # categories: [it, repos] -+ # timeout: 4.0 -+ # disabled: true -+ # shortcut: bb -+ # about: -+ # website: https://bitbucket.org/ -+ # wikidata_id: Q2493781 -+ # official_api_documentation: https://developer.atlassian.com/bitbucket -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: btdigg -+ # engine: btdigg -+ # shortcut: bt -+ # disabled: true -+ # -+ # - name: ccc-tv -+ # engine: xpath -+ # paging: false -+ # search_url: https://media.ccc.de/search/?q={query} -+ # url_xpath: //div[@class="caption"]/h3/a/@href -+ # title_xpath: //div[@class="caption"]/h3/a/text() -+ # content_xpath: //div[@class="caption"]/h4/@title -+ # categories: videos -+ # disabled: true -+ # shortcut: c3tv -+ # about: -+ # website: https://media.ccc.de/ -+ # wikidata_id: Q80729951 -+ # official_api_documentation: https://github.com/voc/voctoweb -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # # We don't set language: de here because media.ccc.de is not just -+ # # for a German audience. It contains many English videos and many -+ # # German videos have English subtitles. -+ # -+ # - name: openverse -+ # engine: openverse -+ # categories: images -+ # shortcut: opv -+ # -+ # - name: chefkoch -+ # engine: chefkoch -+ # shortcut: chef -+ # # to show premium or plus results too: -+ # # skip_premium: false -+ # -+ # # - name: core.ac.uk -+ # # engine: core -+ # # categories: science -+ # # shortcut: cor -+ # # # get your API key from: https://core.ac.uk/api-keys/register/ -+ # # api_key: 'unset' -+ # -+ # - name: crossref -+ # engine: crossref -+ # shortcut: cr -+ # timeout: 30 -+ # disabled: true -+ # -+ # - name: crowdview -+ # engine: json_engine -+ # shortcut: cv -+ # categories: general -+ # paging: false -+ # search_url: https://crowdview-next-js.onrender.com/api/search-v3?query={query} -+ # results_query: results -+ # url_query: link -+ # title_query: title -+ # content_query: snippet -+ # disabled: true -+ # about: -+ # website: https://crowdview.ai/ -+ # -+ # - name: yep -+ # engine: json_engine -+ # shortcut: yep -+ # categories: general -+ # disabled: true -+ # paging: false -+ # content_html_to_text: true -+ # title_html_to_text: true -+ # search_url: https://api.yep.com/fs/1/?type=web&q={query}&no_correct=false&limit=100 -+ # results_query: 1/results -+ # title_query: title -+ # url_query: url -+ # content_query: snippet -+ # about: -+ # website: https://yep.com -+ # use_official_api: false -+ # require_api_key: false -+ # results: JSON -+ # -+ # - name: curlie -+ # engine: xpath -+ # shortcut: cl -+ # categories: general -+ # disabled: true -+ # paging: true -+ # lang_all: '' -+ # search_url: https://curlie.org/search?q={query}&lang={lang}&start={pageno}&stime=92452189 -+ # page_size: 20 -+ # results_xpath: //div[@id="site-list-content"]/div[@class="site-item"] -+ # url_xpath: ./div[@class="title-and-desc"]/a/@href -+ # title_xpath: ./div[@class="title-and-desc"]/a/div -+ # content_xpath: ./div[@class="title-and-desc"]/div[@class="site-descr"] -+ # about: -+ # website: https://curlie.org/ -+ # wikidata_id: Q60715723 -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: currency -+ # engine: currency_convert -+ # categories: general -+ # shortcut: cc -+ # -+ # - name: deezer -+ # engine: deezer -+ # shortcut: dz -+ # disabled: true -+ # -+ # - name: deviantart -+ # engine: deviantart -+ # shortcut: da -+ # timeout: 3.0 -+ # -+ # - name: ddg definitions -+ # engine: duckduckgo_definitions -+ # shortcut: ddd -+ # weight: 2 -+ # disabled: true -+ # tests: *tests_infobox -+ # -+ # # cloudflare protected -+ # # - name: digbt -+ # # engine: digbt -+ # # shortcut: dbt -+ # # timeout: 6.0 -+ # # disabled: true -+ # -+ # - name: docker hub -+ # engine: docker_hub -+ # shortcut: dh -+ # categories: [it, packages] -+ # -+ # - name: erowid -+ # engine: xpath -+ # paging: true -+ # first_page_num: 0 -+ # page_size: 30 -+ # search_url: https://www.erowid.org/search.php?q={query}&s={pageno} -+ # url_xpath: //dl[@class="results-list"]/dt[@class="result-title"]/a/@href -+ # title_xpath: //dl[@class="results-list"]/dt[@class="result-title"]/a/text() -+ # content_xpath: //dl[@class="results-list"]/dd[@class="result-details"] -+ # categories: [] -+ # shortcut: ew -+ # disabled: true -+ # about: -+ # website: https://www.erowid.org/ -+ # wikidata_id: Q1430691 -+ # official_api_documentation: -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # # - 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 -+ # timeout: 3.0 -+ # weight: 2 -+ # # add "list" to the array to get results in the results list -+ # display_type: ["infobox"] -+ # tests: *tests_infobox -+ # categories: [general] -+ # -+ # - name: duckduckgo -+ # engine: duckduckgo -+ # shortcut: ddg -+ # -+ # - name: duckduckgo images -+ # engine: duckduckgo_images -+ # shortcut: ddi -+ # timeout: 3.0 -+ # disabled: true -+ # -+ # - name: duckduckgo weather -+ # engine: duckduckgo_weather -+ # shortcut: ddw -+ # disabled: true -+ # -+ # - name: apple maps -+ # engine: apple_maps -+ # shortcut: apm -+ # disabled: true -+ # timeout: 5.0 -+ # -+ # - name: emojipedia -+ # engine: emojipedia -+ # timeout: 4.0 -+ # shortcut: em -+ # disabled: true -+ # -+ # - name: tineye -+ # engine: tineye -+ # shortcut: tin -+ # timeout: 9.0 -+ # disabled: true -+ # -+ # - name: etymonline -+ # engine: xpath -+ # paging: true -+ # search_url: https://etymonline.com/search?page={pageno}&q={query} -+ # url_xpath: //a[contains(@class, "word__name--")]/@href -+ # title_xpath: //a[contains(@class, "word__name--")] -+ # content_xpath: //section[contains(@class, "word__defination")] -+ # first_page_num: 1 -+ # shortcut: et -+ # categories: [dictionaries] -+ # about: -+ # website: https://www.etymonline.com/ -+ # wikidata_id: Q1188617 -+ # official_api_documentation: -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # # - name: ebay -+ # # engine: ebay -+ # # shortcut: eb -+ # # base_url: 'https://www.ebay.com' -+ # # disabled: true -+ # # timeout: 5 -+ # -+ # - name: 1x -+ # engine: www1x -+ # shortcut: 1x -+ # timeout: 3.0 -+ # disabled: true -+ # -+ # - name: fdroid -+ # engine: fdroid -+ # shortcut: fd -+ # disabled: true -+ # -+ # - name: flickr -+ # categories: images -+ # shortcut: fl -+ # # You can use the engine using the official stable API, but you need an API -+ # # key, see: https://www.flickr.com/services/apps/create/ -+ # # engine: flickr -+ # # api_key: 'apikey' # required! -+ # # Or you can use the html non-stable engine, activated by default -+ # engine: flickr_noapi -+ # -+ # - name: free software directory -+ # engine: mediawiki -+ # shortcut: fsd -+ # categories: [it, software wikis] -+ # base_url: https://directory.fsf.org/ -+ # search_type: title -+ # timeout: 5.0 -+ # disabled: true -+ # about: -+ # website: https://directory.fsf.org/ -+ # wikidata_id: Q2470288 -+ # -+ # # - name: freesound -+ # # engine: freesound -+ # # shortcut: fnd -+ # # disabled: true -+ # # timeout: 15.0 -+ # # API key required, see: https://freesound.org/docs/api/overview.html -+ # # api_key: MyAPIkey -+ # -+ # - name: frinkiac -+ # engine: frinkiac -+ # shortcut: frk -+ # disabled: true -+ # -+ # - name: genius -+ # engine: genius -+ # shortcut: gen -+ # -+ # - name: gentoo -+ # engine: gentoo -+ # shortcut: ge -+ # timeout: 10.0 -+ # -+ # - name: gitlab -+ # engine: json_engine -+ # paging: true -+ # search_url: https://gitlab.com/api/v4/projects?search={query}&page={pageno} -+ # url_query: web_url -+ # title_query: name_with_namespace -+ # content_query: description -+ # page_size: 20 -+ # categories: [it, repos] -+ # shortcut: gl -+ # timeout: 10.0 -+ # disabled: true -+ # about: -+ # website: https://about.gitlab.com/ -+ # wikidata_id: Q16639197 -+ # official_api_documentation: https://docs.gitlab.com/ee/api/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: JSON -+ # -+ # - name: github -+ # 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, repos] -+ # shortcut: cb -+ # disabled: true -+ # about: -+ # website: https://codeberg.org/ -+ # wikidata_id: -+ # official_api_documentation: https://try.gitea.io/api/swagger -+ # use_official_api: false -+ # require_api_key: false -+ # results: JSON -+ # -+ # -+ # -+ # - name: google news -+ # engine: google_news -+ # shortcut: gon -+ # # additional_tests: -+ # # android: *test_android -+ # -+ # - name: google videos -+ # engine: google_videos -+ # shortcut: gov -+ # # additional_tests: -+ # # android: *test_android -+ # -+ # - name: google scholar -+ # engine: google_scholar -+ # shortcut: gos -+ # -+ # - name: google play apps -+ # engine: google_play -+ # categories: [files, apps] -+ # shortcut: gpa -+ # play_categ: apps -+ # disabled: true -+ # -+ # - name: google play movies -+ # engine: google_play -+ # categories: videos -+ # shortcut: gpm -+ # play_categ: movies -+ # disabled: true -+ # -+ # - name: material icons -+ # engine: material_icons -+ # categories: images -+ # shortcut: mi -+ # disabled: true -+ # -+ # - name: gpodder -+ # engine: json_engine -+ # shortcut: gpod -+ # timeout: 4.0 -+ # paging: false -+ # search_url: https://gpodder.net/search.json?q={query} -+ # url_query: url -+ # title_query: title -+ # content_query: description -+ # page_size: 19 -+ # categories: music -+ # disabled: true -+ # about: -+ # website: https://gpodder.net -+ # wikidata_id: Q3093354 -+ # official_api_documentation: https://gpoddernet.readthedocs.io/en/latest/api/ -+ # use_official_api: false -+ # requires_api_key: false -+ # results: JSON -+ # -+ # - name: habrahabr -+ # engine: xpath -+ # paging: true -+ # search_url: https://habr.com/en/search/page{pageno}/?q={query} -+ # results_xpath: //article[contains(@class, "tm-articles-list__item")] -+ # url_xpath: .//a[@class="tm-title__link"]/@href -+ # title_xpath: .//a[@class="tm-title__link"] -+ # content_xpath: .//div[contains(@class, "article-formatted-body")] -+ # categories: it -+ # timeout: 4.0 -+ # disabled: true -+ # shortcut: habr -+ # about: -+ # website: https://habr.com/ -+ # wikidata_id: Q4494434 -+ # official_api_documentation: https://habr.com/en/docs/help/api/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: hoogle -+ # engine: xpath -+ # paging: true -+ # search_url: https://hoogle.haskell.org/?hoogle={query}&start={pageno} -+ # results_xpath: '//div[@class="result"]' -+ # title_xpath: './/div[@class="ans"]//a' -+ # url_xpath: './/div[@class="ans"]//a/@href' -+ # content_xpath: './/div[@class="from"]' -+ # page_size: 20 -+ # categories: [it, packages] -+ # shortcut: ho -+ # about: -+ # website: https://hoogle.haskell.org/ -+ # wikidata_id: Q34010 -+ # official_api_documentation: https://hackage.haskell.org/api -+ # use_official_api: false -+ # require_api_key: false -+ # results: JSON -+ # -+ # - name: imdb -+ # engine: imdb -+ # shortcut: imdb -+ # timeout: 6.0 -+ # disabled: true -+ # -+ # - name: ina -+ # engine: ina -+ # shortcut: in -+ # timeout: 6.0 -+ # disabled: true -+ # -+ # - name: invidious -+ # engine: invidious -+ # # Instanes will be selected randomly, see https://api.invidious.io/ for -+ # # instances that are stable (good uptime) and close to you. -+ # base_url: -+ # - https://invidious.io.lol -+ # - https://invidious.fdn.fr -+ # - https://yt.artemislena.eu -+ # - https://invidious.tiekoetter.com -+ # - https://invidious.flokinet.to -+ # - https://vid.puffyan.us -+ # - https://invidious.privacydev.net -+ # - https://inv.tux.pizza -+ # shortcut: iv -+ # timeout: 3.0 -+ # disabled: true -+ # -+ # - name: jisho -+ # engine: jisho -+ # shortcut: js -+ # timeout: 3.0 -+ # disabled: true -+ # -+ # - name: kickass -+ # engine: kickass -+ # shortcut: kc -+ # timeout: 4.0 -+ # disabled: true -+ # -+ # - name: lemmy communities -+ # engine: lemmy -+ # lemmy_type: Communities -+ # shortcut: leco -+ # -+ # - name: lemmy users -+ # engine: lemmy -+ # network: lemmy communities -+ # lemmy_type: Users -+ # shortcut: leus -+ # -+ # - name: lemmy posts -+ # engine: lemmy -+ # network: lemmy communities -+ # lemmy_type: Posts -+ # shortcut: lepo -+ # -+ # - name: lemmy comments -+ # engine: lemmy -+ # network: lemmy communities -+ # lemmy_type: Comments -+ # shortcut: lecom -+ # -+ # - name: library genesis -+ # engine: xpath -+ # search_url: https://libgen.fun/search.php?req={query} -+ # url_xpath: //a[contains(@href,"get.php?md5")]/@href -+ # title_xpath: //a[contains(@href,"book/")]/text()[1] -+ # content_xpath: //td/a[1][contains(@href,"=author")]/text() -+ # categories: files -+ # timeout: 7.0 -+ # disabled: true -+ # shortcut: lg -+ # about: -+ # website: https://libgen.fun/ -+ # wikidata_id: Q22017206 -+ # official_api_documentation: -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: z-library -+ # engine: zlibrary -+ # shortcut: zlib -+ # categories: files -+ # timeout: 7.0 -+ # -+ # - name: library of congress -+ # engine: loc -+ # shortcut: loc -+ # categories: images -+ # -+ # - name: lingva -+ # engine: lingva -+ # shortcut: lv -+ # # set lingva instance in url, by default it will use the official instance -+ # # url: https://lingva.ml -+ # -+ # - name: lobste.rs -+ # 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: .//a[@class="u-url"]/@href -+ # title_xpath: .//a[@class="u-url"] -+ # content_xpath: .//a[@class="domain"] -+ # categories: it -+ # shortcut: lo -+ # timeout: 5.0 -+ # disabled: true -+ # about: -+ # website: https://lobste.rs/ -+ # wikidata_id: Q60762874 -+ # official_api_documentation: -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: azlyrics -+ # shortcut: lyrics -+ # engine: xpath -+ # timeout: 4.0 -+ # disabled: true -+ # categories: [music, lyrics] -+ # paging: true -+ # search_url: https://search.azlyrics.com/search.php?q={query}&w=lyrics&p={pageno} -+ # url_xpath: //td[@class="text-left visitedlyr"]/a/@href -+ # title_xpath: //span/b/text() -+ # content_xpath: //td[@class="text-left visitedlyr"]/a/small -+ # about: -+ # website: https://azlyrics.com -+ # wikidata_id: Q66372542 -+ # official_api_documentation: -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: metacpan -+ # engine: metacpan -+ # shortcut: cpan -+ # disabled: true -+ # number_of_results: 20 -+ # -+ # # - name: meilisearch -+ # # engine: meilisearch -+ # # shortcut: mes -+ # # enable_http: true -+ # # base_url: http://localhost:7700 -+ # # index: my-index -+ # -+ # - name: mixcloud -+ # engine: mixcloud -+ # shortcut: mc -+ # -+ # # MongoDB engine -+ # # Required dependency: pymongo -+ # # - name: mymongo -+ # # engine: mongodb -+ # # shortcut: md -+ # # exact_match_only: false -+ # # host: '127.0.0.1' -+ # # port: 27017 -+ # # enable_http: true -+ # # results_per_page: 20 -+ # # database: 'business' -+ # # collection: 'reviews' # name of the db collection -+ # # key: 'name' # key in the collection to search for -+ # -+ # - name: mwmbl -+ # engine: mwmbl -+ # # api_url: https://api.mwmbl.org -+ # shortcut: mwm -+ # disabled: true -+ # -+ # - name: npm -+ # engine: json_engine -+ # paging: true -+ # first_page_num: 0 -+ # search_url: https://api.npms.io/v2/search?q={query}&size=25&from={pageno} -+ # results_query: results -+ # url_query: package/links/npm -+ # title_query: package/name -+ # content_query: package/description -+ # page_size: 25 -+ # categories: [it, packages] -+ # disabled: true -+ # timeout: 5.0 -+ # shortcut: npm -+ # about: -+ # website: https://npms.io/ -+ # wikidata_id: Q7067518 -+ # official_api_documentation: https://api-docs.npms.io/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: JSON -+ # -+ # - name: nyaa -+ # engine: nyaa -+ # shortcut: nt -+ # disabled: true -+ # -+ # - name: mankier -+ # engine: json_engine -+ # search_url: https://www.mankier.com/api/v2/mans/?q={query} -+ # results_query: results -+ # url_query: url -+ # title_query: name -+ # content_query: description -+ # categories: it -+ # shortcut: man -+ # about: -+ # website: https://www.mankier.com/ -+ # official_api_documentation: https://www.mankier.com/api -+ # use_official_api: true -+ # require_api_key: false -+ # results: JSON -+ # -+ # - name: odysee -+ # engine: odysee -+ # shortcut: od -+ # disabled: true -+ # -+ # - name: openairedatasets -+ # engine: json_engine -+ # paging: true -+ # search_url: https://api.openaire.eu/search/datasets?format=json&page={pageno}&size=10&title={query} -+ # results_query: response/results/result -+ # url_query: metadata/oaf:entity/oaf:result/children/instance/webresource/url/$ -+ # title_query: metadata/oaf:entity/oaf:result/title/$ -+ # content_query: metadata/oaf:entity/oaf:result/description/$ -+ # content_html_to_text: true -+ # categories: "science" -+ # shortcut: oad -+ # timeout: 5.0 -+ # about: -+ # website: https://www.openaire.eu/ -+ # wikidata_id: Q25106053 -+ # official_api_documentation: https://api.openaire.eu/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: JSON -+ # -+ # - name: openairepublications -+ # engine: json_engine -+ # paging: true -+ # search_url: https://api.openaire.eu/search/publications?format=json&page={pageno}&size=10&title={query} -+ # results_query: response/results/result -+ # url_query: metadata/oaf:entity/oaf:result/children/instance/webresource/url/$ -+ # title_query: metadata/oaf:entity/oaf:result/title/$ -+ # content_query: metadata/oaf:entity/oaf:result/description/$ -+ # content_html_to_text: true -+ # categories: science -+ # shortcut: oap -+ # timeout: 5.0 -+ # about: -+ # website: https://www.openaire.eu/ -+ # wikidata_id: Q25106053 -+ # official_api_documentation: https://api.openaire.eu/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: JSON -+ # -+ # # - name: opensemanticsearch -+ # # engine: opensemantic -+ # # shortcut: oss -+ # # base_url: 'http://localhost:8983/solr/opensemanticsearch/' -+ # -+ # - name: openstreetmap -+ # engine: openstreetmap -+ # shortcut: osm -+ # -+ # - name: openrepos -+ # engine: xpath -+ # paging: true -+ # search_url: https://openrepos.net/search/node/{query}?page={pageno} -+ # url_xpath: //li[@class="search-result"]//h3[@class="title"]/a/@href -+ # title_xpath: //li[@class="search-result"]//h3[@class="title"]/a -+ # content_xpath: //li[@class="search-result"]//div[@class="search-snippet-info"]//p[@class="search-snippet"] -+ # categories: files -+ # timeout: 4.0 -+ # disabled: true -+ # shortcut: or -+ # about: -+ # website: https://openrepos.net/ -+ # wikidata_id: -+ # official_api_documentation: -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: packagist -+ # engine: json_engine -+ # paging: true -+ # search_url: https://packagist.org/search.json?q={query}&page={pageno} -+ # results_query: results -+ # url_query: url -+ # title_query: name -+ # content_query: description -+ # categories: [it, packages] -+ # disabled: true -+ # timeout: 5.0 -+ # shortcut: pack -+ # about: -+ # website: https://packagist.org -+ # wikidata_id: Q108311377 -+ # official_api_documentation: https://packagist.org/apidoc -+ # use_official_api: true -+ # require_api_key: false -+ # results: JSON -+ # -+ # - name: pdbe -+ # engine: pdbe -+ # shortcut: pdb -+ # # Hide obsolete PDB entries. Default is not to hide obsolete structures -+ # # hide_obsolete: false -+ # -+ # - name: photon -+ # engine: photon -+ # shortcut: ph -+ # -+ # - name: piped -+ # engine: piped -+ # shortcut: ppd -+ # categories: videos -+ # piped_filter: videos -+ # timeout: 3.0 -+ # -+ # # URL to use as link and for embeds -+ # frontend_url: https://srv.piped.video -+ # # Instance will be selected randomly, for more see https://piped-instances.kavin.rocks/ -+ # backend_url: -+ # - https://pipedapi.kavin.rocks -+ # - https://pipedapi-libre.kavin.rocks -+ # - https://pipedapi.adminforge.de -+ # -+ # - name: piped.music -+ # engine: piped -+ # network: piped -+ # shortcut: ppdm -+ # categories: music -+ # piped_filter: music_songs -+ # timeout: 3.0 -+ # -+ # - name: piratebay -+ # engine: piratebay -+ # shortcut: tpb -+ # # You may need to change this URL to a proxy if piratebay is blocked in your -+ # # country -+ # url: https://thepiratebay.org/ -+ # timeout: 3.0 -+ # -+ # # Required dependency: psychopg2 -+ # # - name: postgresql -+ # # engine: postgresql -+ # # database: postgres -+ # # username: postgres -+ # # password: postgres -+ # # limit: 10 -+ # # query_str: 'SELECT * from my_table WHERE my_column = %(query)s' -+ # # shortcut : psql -+ # -+ # - name: pub.dev -+ # engine: xpath -+ # shortcut: pd -+ # search_url: https://pub.dev/packages?q={query}&page={pageno} -+ # paging: true -+ # results_xpath: //div[contains(@class,"packages-item")] -+ # url_xpath: ./div/h3/a/@href -+ # title_xpath: ./div/h3/a -+ # content_xpath: ./div/div/div[contains(@class,"packages-description")]/span -+ # categories: [packages, it] -+ # timeout: 3.0 -+ # disabled: true -+ # first_page_num: 1 -+ # about: -+ # website: https://pub.dev/ -+ # official_api_documentation: https://pub.dev/help/api -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: pubmed -+ # engine: pubmed -+ # shortcut: pub -+ # timeout: 3.0 -+ # -+ # - name: pypi -+ # shortcut: pypi -+ # engine: xpath -+ # paging: true -+ # search_url: https://pypi.org/search/?q={query}&page={pageno} -+ # results_xpath: /html/body/main/div/div/div/form/div/ul/li/a[@class="package-snippet"] -+ # url_xpath: ./@href -+ # title_xpath: ./h3/span[@class="package-snippet__name"] -+ # content_xpath: ./p -+ # suggestion_xpath: /html/body/main/div/div/div/form/div/div[@class="callout-block"]/p/span/a[@class="link"] -+ # first_page_num: 1 -+ # categories: [it, packages] -+ # about: -+ # website: https://pypi.org -+ # wikidata_id: Q2984686 -+ # official_api_documentation: https://warehouse.readthedocs.io/api-reference/index.html -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: qwant -+ # qwant_categ: web -+ # engine: qwant -+ # shortcut: qw -+ # categories: [general, web] -+ # additional_tests: -+ # rosebud: *test_rosebud -+ # -+ # - name: qwant news -+ # qwant_categ: news -+ # engine: qwant -+ # shortcut: qwn -+ # categories: news -+ # network: qwant -+ # -+ # - name: qwant images -+ # qwant_categ: images -+ # engine: qwant -+ # shortcut: qwi -+ # categories: [images, web] -+ # network: qwant -+ # -+ # - name: qwant videos -+ # qwant_categ: videos -+ # engine: qwant -+ # shortcut: qwv -+ # categories: [videos, web] -+ # network: qwant -+ # -+ # # - 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 -+ # shortcut: re -+ # page_size: 25 -+ # -+ # # Required dependency: redis -+ # # - name: myredis -+ # # shortcut : rds -+ # # engine: redis_server -+ # # exact_match_only: false -+ # # host: '127.0.0.1' -+ # # port: 6379 -+ # # enable_http: true -+ # # password: '' -+ # # db: 0 -+ # -+ # # tmp suspended: bad certificate -+ # # - name: scanr structures -+ # # shortcut: scs -+ # # engine: scanr_structures -+ # # disabled: true -+ # -+ # - name: sepiasearch -+ # engine: sepiasearch -+ # shortcut: sep -+ # -+ # - name: soundcloud -+ # engine: soundcloud -+ # shortcut: sc -+ # -+ # - name: stackoverflow -+ # engine: stackexchange -+ # shortcut: st -+ # api_site: 'stackoverflow' -+ # categories: [it, q&a] -+ # -+ # - name: askubuntu -+ # engine: stackexchange -+ # shortcut: ubuntu -+ # api_site: 'askubuntu' -+ # categories: [it, q&a] -+ # -+ # - name: internetarchivescholar -+ # engine: internet_archive_scholar -+ # shortcut: ias -+ # timeout: 5.0 -+ # -+ # - name: superuser -+ # engine: stackexchange -+ # shortcut: su -+ # api_site: 'superuser' -+ # categories: [it, q&a] -+ # -+ # - name: searchcode code -+ # engine: searchcode_code -+ # shortcut: scc -+ # disabled: true -+ # -+ # - name: framalibre -+ # engine: framalibre -+ # shortcut: frl -+ # disabled: true -+ # -+ # # - name: searx -+ # # engine: searx_engine -+ # # shortcut: se -+ # # instance_urls : -+ # # - http://127.0.0.1:8888/ -+ # # - ... -+ # # disabled: true -+ # -+ # - name: semantic scholar -+ # engine: semantic_scholar -+ # disabled: true -+ # shortcut: se -+ # -+ # # Spotify needs API credentials -+ # # - name: spotify -+ # # engine: spotify -+ # # shortcut: stf -+ # # api_client_id: ******* -+ # # api_client_secret: ******* -+ # -+ # # - name: solr -+ # # engine: solr -+ # # shortcut: slr -+ # # base_url: http://localhost:8983 -+ # # collection: collection_name -+ # # sort: '' # sorting: asc or desc -+ # # field_list: '' # comma separated list of field names to display on the UI -+ # # default_fields: '' # default field to query -+ # # query_fields: '' # query fields -+ # # enable_http: true -+ # -+ # # - name: springer nature -+ # # engine: springer -+ # # # get your API key from: https://dev.springernature.com/signup -+ # # # working API key, for test & debug: "a69685087d07eca9f13db62f65b8f601" -+ # # api_key: 'unset' -+ # # shortcut: springer -+ # # timeout: 15.0 -+ # -+ # - name: startpage -+ # engine: startpage -+ # shortcut: sp -+ # timeout: 6.0 -+ # disabled: true -+ # additional_tests: -+ # rosebud: *test_rosebud -+ # -+ # - name: tokyotoshokan -+ # engine: tokyotoshokan -+ # shortcut: tt -+ # timeout: 6.0 -+ # disabled: true -+ # -+ # - name: solidtorrents -+ # engine: solidtorrents -+ # shortcut: solid -+ # timeout: 4.0 -+ # base_url: -+ # - https://solidtorrents.to -+ # - https://bitsearch.to -+ # -+ # # For this demo of the sqlite engine download: -+ # # https://liste.mediathekview.de/filmliste-v2.db.bz2 -+ # # and unpack into searx/data/filmliste-v2.db -+ # # Query to test: "!demo concert" -+ # # -+ # # - name: demo -+ # # engine: sqlite -+ # # shortcut: demo -+ # # categories: general -+ # # result_template: default.html -+ # # database: searx/data/filmliste-v2.db -+ # # query_str: >- -+ # # SELECT title || ' (' || time(duration, 'unixepoch') || ')' AS title, -+ # # COALESCE( NULLIF(url_video_hd,''), NULLIF(url_video_sd,''), url_video) AS url, -+ # # description AS content -+ # # FROM film -+ # # WHERE title LIKE :wildcard OR description LIKE :wildcard -+ # # ORDER BY duration DESC -+ # -+ # - name: tagesschau -+ # engine: tagesschau -+ # shortcut: ts -+ # disabled: true -+ # -+ # - name: tmdb -+ # engine: xpath -+ # paging: true -+ # search_url: https://www.themoviedb.org/search?page={pageno}&query={query} -+ # results_xpath: //div[contains(@class,"movie") or contains(@class,"tv")]//div[contains(@class,"card")] -+ # url_xpath: .//div[contains(@class,"poster")]/a/@href -+ # thumbnail_xpath: .//img/@src -+ # title_xpath: .//div[contains(@class,"title")]//h2 -+ # content_xpath: .//div[contains(@class,"overview")] -+ # shortcut: tm -+ # disabled: true -+ # -+ # # 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 -+ # enable_http: true -+ # shortcut: tch -+ # -+ # # torznab engine lets you query any torznab compatible indexer. Using this -+ # # engine in combination with Jackett opens the possibility to query a lot of -+ # # public and private indexers directly from SearXNG. More details at: -+ # # https://docs.searxng.org/dev/engines/online/torznab.html -+ # # -+ # # - name: Torznab EZTV -+ # # engine: torznab -+ # # shortcut: eztv -+ # # base_url: http://localhost:9117/api/v2.0/indexers/eztv/results/torznab -+ # # enable_http: true # if using localhost -+ # # api_key: xxxxxxxxxxxxxxx -+ # # show_magnet_links: true -+ # # show_torrent_files: false -+ # # # https://github.com/Jackett/Jackett/wiki/Jackett-Categories -+ # # torznab_categories: # optional -+ # # - 2000 -+ # # - 5000 -+ # -+ # - name: twitter -+ # shortcut: tw -+ # engine: twitter -+ # disabled: true -+ # -+ # # tmp suspended - too slow, too many errors -+ # # - name: urbandictionary -+ # # engine : xpath -+ # # search_url : https://www.urbandictionary.com/define.php?term={query} -+ # # url_xpath : //*[@class="word"]/@href -+ # # title_xpath : //*[@class="def-header"] -+ # # content_xpath: //*[@class="meaning"] -+ # # shortcut: ud -+ # -+ # - name: unsplash -+ # engine: unsplash -+ # shortcut: us -+ # -+ # - name: yahoo -+ # engine: yahoo -+ # shortcut: yh -+ # disabled: true -+ # -+ # - name: yahoo news -+ # engine: yahoo_news -+ # shortcut: yhn -+ # -+ # - name: youtube -+ # shortcut: yt -+ # # You can use the engine using the official stable API, but you need an API -+ # # key See: https://console.developers.google.com/project -+ # # -+ # # engine: youtube_api -+ # # api_key: 'apikey' # required! -+ # # -+ # # Or you can use the html non-stable engine, activated by default -+ # engine: youtube_noapi -+ # -+ # - name: dailymotion -+ # engine: dailymotion -+ # shortcut: dm -+ # -+ # - name: vimeo -+ # engine: vimeo -+ # shortcut: vm -+ # -+ # - name: wiby -+ # engine: json_engine -+ # paging: true -+ # search_url: https://wiby.me/json/?q={query}&p={pageno} -+ # url_query: URL -+ # title_query: Title -+ # content_query: Snippet -+ # categories: [general, web] -+ # shortcut: wib -+ # disabled: true -+ # about: -+ # website: https://wiby.me/ -+ # -+ # - name: alexandria -+ # engine: json_engine -+ # shortcut: alx -+ # categories: general -+ # paging: true -+ # search_url: https://api.alexandria.org/?a=1&q={query}&p={pageno} -+ # results_query: results -+ # title_query: title -+ # url_query: url -+ # content_query: snippet -+ # timeout: 1.5 -+ # disabled: true -+ # about: -+ # website: https://alexandria.org/ -+ # official_api_documentation: https://github.com/alexandria-org/alexandria-api/raw/master/README.md -+ # use_official_api: true -+ # require_api_key: false -+ # results: JSON -+ # -+ # - name: wikibooks -+ # engine: mediawiki -+ # weight: 0.5 -+ # shortcut: wb -+ # categories: [general, wikimedia] -+ # base_url: "https://{language}.wikibooks.org/" -+ # search_type: text -+ # disabled: true -+ # about: -+ # website: https://www.wikibooks.org/ -+ # wikidata_id: Q367 -+ # -+ # - name: wikinews -+ # engine: mediawiki -+ # shortcut: wn -+ # categories: [news, wikimedia] -+ # base_url: "https://{language}.wikinews.org/" -+ # search_type: text -+ # srsort: create_timestamp_desc -+ # about: -+ # website: https://www.wikinews.org/ -+ # wikidata_id: Q964 -+ # -+ # - name: wikiquote -+ # engine: mediawiki -+ # weight: 0.5 -+ # shortcut: wq -+ # categories: [general, wikimedia] -+ # base_url: "https://{language}.wikiquote.org/" -+ # search_type: text -+ # disabled: true -+ # additional_tests: -+ # rosebud: *test_rosebud -+ # about: -+ # website: https://www.wikiquote.org/ -+ # wikidata_id: Q369 -+ # -+ # - name: wikisource -+ # engine: mediawiki -+ # weight: 0.5 -+ # shortcut: ws -+ # categories: [general, wikimedia] -+ # base_url: "https://{language}.wikisource.org/" -+ # search_type: text -+ # disabled: true -+ # about: -+ # website: https://www.wikisource.org/ -+ # wikidata_id: Q263 -+ # -+ # - name: wikispecies -+ # engine: mediawiki -+ # shortcut: wsp -+ # categories: [general, science, wikimedia] -+ # base_url: "https://species.wikimedia.org/" -+ # search_type: text -+ # disabled: true -+ # about: -+ # website: https://species.wikimedia.org/ -+ # wikidata_id: Q13679 -+ # -+ # - name: wiktionary -+ # engine: mediawiki -+ # shortcut: wt -+ # categories: [dictionaries, wikimedia] -+ # base_url: "https://{language}.wiktionary.org/" -+ # search_type: text -+ # about: -+ # website: https://www.wiktionary.org/ -+ # wikidata_id: Q151 -+ # -+ # - name: wikiversity -+ # engine: mediawiki -+ # weight: 0.5 -+ # shortcut: wv -+ # categories: [general, wikimedia] -+ # base_url: "https://{language}.wikiversity.org/" -+ # search_type: text -+ # disabled: true -+ # about: -+ # website: https://www.wikiversity.org/ -+ # wikidata_id: Q370 -+ # -+ # - name: wikivoyage -+ # engine: mediawiki -+ # weight: 0.5 -+ # shortcut: wy -+ # categories: [general, wikimedia] -+ # base_url: "https://{language}.wikivoyage.org/" -+ # search_type: text -+ # disabled: true -+ # about: -+ # website: https://www.wikivoyage.org/ -+ # wikidata_id: Q373 -+ # -+ # - name: wikicommons.images -+ # engine: wikicommons -+ # shortcut: wc -+ # categories: images -+ # number_of_results: 10 -+ # -+ # - name: wolframalpha -+ # shortcut: wa -+ # # You can use the engine using the official stable API, but you need an API -+ # # key. See: https://products.wolframalpha.com/api/ -+ # # -+ # # engine: wolframalpha_api -+ # # api_key: '' -+ # # -+ # # Or you can use the html non-stable engine, activated by default -+ # engine: wolframalpha_noapi -+ # timeout: 6.0 -+ # categories: general -+ # disabled: true -+ # -+ # - name: dictzone -+ # engine: dictzone -+ # shortcut: dc -+ # -+ # - name: mymemory translated -+ # engine: translated -+ # shortcut: tl -+ # timeout: 5.0 -+ # # You can use without an API key, but you are limited to 1000 words/day -+ # # See: https://mymemory.translated.net/doc/usagelimits.php -+ # # api_key: '' -+ # -+ # # Required dependency: mysql-connector-python -+ # # - name: mysql -+ # # engine: mysql_server -+ # # database: mydatabase -+ # # username: user -+ # # password: pass -+ # # limit: 10 -+ # # query_str: 'SELECT * from mytable WHERE fieldname=%(query)s' -+ # # shortcut: mysql -+ # -+ # - name: 1337x -+ # engine: 1337x -+ # shortcut: 1337x -+ # disabled: true -+ # -+ # - name: duden -+ # engine: duden -+ # shortcut: du -+ # disabled: true -+ # -+ # - name: seznam -+ # shortcut: szn -+ # engine: seznam -+ # disabled: true -+ # -+ # # - name: deepl -+ # # engine: deepl -+ # # shortcut: dpl -+ # # # You can use the engine using the official stable API, but you need an API key -+ # # # See: https://www.deepl.com/pro-api?cta=header-pro-api -+ # # api_key: '' # required! -+ # # timeout: 5.0 -+ # # disabled: true -+ # -+ # - name: mojeek -+ # shortcut: mjk -+ # engine: xpath -+ # paging: true -+ # categories: [general, web] -+ # search_url: https://www.mojeek.com/search?q={query}&s={pageno}&lang={lang}&lb={lang} -+ # results_xpath: //ul[@class="results-standard"]/li/a[@class="ob"] -+ # url_xpath: ./@href -+ # title_xpath: ../h2/a -+ # content_xpath: ..//p[@class="s"] -+ # suggestion_xpath: //div[@class="top-info"]/p[@class="top-info spell"]/em/a -+ # first_page_num: 0 -+ # page_size: 10 -+ # disabled: true -+ # about: -+ # website: https://www.mojeek.com/ -+ # wikidata_id: Q60747299 -+ # official_api_documentation: https://www.mojeek.com/services/api.html/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: moviepilot -+ # engine: moviepilot -+ # shortcut: mp -+ # disabled: true -+ # -+ # - name: naver -+ # shortcut: nvr -+ # categories: [general, web] -+ # engine: xpath -+ # paging: true -+ # search_url: https://search.naver.com/search.naver?where=webkr&sm=osp_hty&ie=UTF-8&query={query}&start={pageno} -+ # url_xpath: //a[@class="link_tit"]/@href -+ # title_xpath: //a[@class="link_tit"] -+ # content_xpath: //a[@class="total_dsc"]/div -+ # first_page_num: 1 -+ # page_size: 10 -+ # disabled: true -+ # about: -+ # website: https://www.naver.com/ -+ # wikidata_id: Q485639 -+ # official_api_documentation: https://developers.naver.com/docs/nmt/examples/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # language: ko -+ # -+ # - name: rubygems -+ # shortcut: rbg -+ # engine: xpath -+ # paging: true -+ # search_url: https://rubygems.org/search?page={pageno}&query={query} -+ # results_xpath: /html/body/main/div/a[@class="gems__gem"] -+ # url_xpath: ./@href -+ # title_xpath: ./span/h2 -+ # content_xpath: ./span/p -+ # suggestion_xpath: /html/body/main/div/div[@class="search__suggestions"]/p/a -+ # first_page_num: 1 -+ # categories: [it, packages] -+ # disabled: true -+ # about: -+ # website: https://rubygems.org/ -+ # wikidata_id: Q1853420 -+ # official_api_documentation: https://guides.rubygems.org/rubygems-org-api/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: peertube -+ # engine: peertube -+ # shortcut: ptb -+ # paging: true -+ # # alternatives see: https://instances.joinpeertube.org/instances -+ # # base_url: https://tube.4aem.com -+ # categories: videos -+ # disabled: true -+ # timeout: 6.0 -+ # -+ # - name: mediathekviewweb -+ # engine: mediathekviewweb -+ # shortcut: mvw -+ # disabled: true -+ # -+ # # - name: yacy -+ # # engine: yacy -+ # # shortcut: ya -+ # # base_url: http://localhost:8090 -+ # # # required if you aren't using HTTPS for your local yacy instance' -+ # # enable_http: true -+ # # timeout: 3.0 -+ # # # Yacy search mode. 'global' or 'local'. -+ # # search_mode: 'global' -+ # -+ # - name: rumble -+ # engine: rumble -+ # shortcut: ru -+ # base_url: https://rumble.com/ -+ # paging: true -+ # categories: videos -+ # disabled: true -+ # -+ # - name: wordnik -+ # engine: wordnik -+ # shortcut: def -+ # base_url: https://www.wordnik.com/ -+ # categories: [dictionaries] -+ # timeout: 5.0 -+ # -+ # - name: woxikon.de synonyme -+ # engine: xpath -+ # shortcut: woxi -+ # categories: [dictionaries] -+ # timeout: 5.0 -+ # disabled: true -+ # search_url: https://synonyme.woxikon.de/synonyme/{query}.php -+ # url_xpath: //div[@class="upper-synonyms"]/a/@href -+ # content_xpath: //div[@class="synonyms-list-group"] -+ # title_xpath: //div[@class="upper-synonyms"]/a -+ # no_result_for_http_status: [404] -+ # about: -+ # website: https://www.woxikon.de/ -+ # wikidata_id: # No Wikidata ID -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # language: de -+ # -+ # - name: seekr news -+ # engine: seekr -+ # shortcut: senews -+ # categories: news -+ # seekr_category: news -+ # disabled: true -+ # -+ # - name: seekr images -+ # engine: seekr -+ # network: seekr news -+ # shortcut: seimg -+ # categories: images -+ # seekr_category: images -+ # disabled: true -+ # -+ # - name: seekr videos -+ # engine: seekr -+ # network: seekr news -+ # shortcut: sevid -+ # categories: videos -+ # seekr_category: videos -+ # disabled: true -+ # -+ # - name: sjp.pwn -+ # engine: sjp -+ # shortcut: sjp -+ # base_url: https://sjp.pwn.pl/ -+ # timeout: 5.0 -+ # disabled: true -+ # -+ # - name: svgrepo -+ # engine: svgrepo -+ # shortcut: svg -+ # timeout: 10.0 -+ # disabled: true -+ # -+ # - name: wallhaven -+ # engine: wallhaven -+ # # api_key: abcdefghijklmnopqrstuvwxyz -+ # shortcut: wh -+ # -+ # # wikimini: online encyclopedia for children -+ # # The fulltext and title parameter is necessary for Wikimini because -+ # # sometimes it will not show the results and redirect instead -+ # - name: wikimini -+ # engine: xpath -+ # shortcut: wkmn -+ # search_url: https://fr.wikimini.org/w/index.php?search={query}&title=Sp%C3%A9cial%3ASearch&fulltext=Search -+ # url_xpath: //li/div[@class="mw-search-result-heading"]/a/@href -+ # title_xpath: //li//div[@class="mw-search-result-heading"]/a -+ # content_xpath: //li/div[@class="searchresult"] -+ # categories: general -+ # disabled: true -+ # about: -+ # website: https://wikimini.org/ -+ # wikidata_id: Q3568032 -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # language: fr -+ # -+ # - name: wttr.in -+ # engine: wttr -+ # shortcut: wttr -+ # timeout: 9.0 -+ # -+ # - name: yummly -+ # engine: yummly -+ # shortcut: yum -+ # disabled: true -+ # -+ # - name: brave -+ # engine: brave -+ # shortcut: br -+ # time_range_support: true -+ # paging: true -+ # categories: [general, web] -+ # brave_category: search -+ # # brave_spellcheck: true -+ # -+ # - name: brave.images -+ # engine: brave -+ # network: brave -+ # shortcut: brimg -+ # categories: [images, web] -+ # brave_category: images -+ # -+ # - name: brave.videos -+ # engine: brave -+ # network: brave -+ # shortcut: brvid -+ # categories: [videos, web] -+ # brave_category: videos -+ # -+ # - name: brave.news -+ # engine: brave -+ # network: brave -+ # shortcut: brnews -+ # categories: news -+ # brave_category: news -+ # -+ # - name: lib.rs -+ # shortcut: lrs -+ # engine: xpath -+ # search_url: https://lib.rs/search?q={query} -+ # results_xpath: /html/body/main/div/ol/li/a -+ # url_xpath: ./@href -+ # title_xpath: ./div[@class="h"]/h4 -+ # content_xpath: ./div[@class="h"]/p -+ # categories: [it, packages] -+ # disabled: true -+ # about: -+ # website: https://lib.rs -+ # wikidata_id: Q113486010 -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: sourcehut -+ # shortcut: srht -+ # engine: xpath -+ # paging: true -+ # search_url: https://sr.ht/projects?page={pageno}&search={query} -+ # results_xpath: (//div[@class="event-list"])[1]/div[@class="event"] -+ # url_xpath: ./h4/a[2]/@href -+ # title_xpath: ./h4/a[2] -+ # content_xpath: ./p -+ # first_page_num: 1 -+ # categories: [it, repos] -+ # disabled: true -+ # about: -+ # website: https://sr.ht -+ # wikidata_id: Q78514485 -+ # official_api_documentation: https://man.sr.ht/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ # - name: goo -+ # shortcut: goo -+ # engine: xpath -+ # paging: true -+ # search_url: https://search.goo.ne.jp/web.jsp?MT={query}&FR={pageno}0 -+ # url_xpath: //div[@class="result"]/p[@class='title fsL1']/a/@href -+ # title_xpath: //div[@class="result"]/p[@class='title fsL1']/a -+ # content_xpath: //p[contains(@class,'url fsM')]/following-sibling::p -+ # first_page_num: 0 -+ # categories: [general, web] -+ # disabled: true -+ # timeout: 4.0 -+ # about: -+ # website: https://search.goo.ne.jp -+ # wikidata_id: Q249044 -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # language: ja -+ # -+ # - name: bt4g -+ # engine: bt4g -+ # shortcut: bt4g -+ # -+ # - name: pkg.go.dev -+ # engine: xpath -+ # shortcut: pgo -+ # search_url: https://pkg.go.dev/search?limit=100&m=package&q={query} -+ # results_xpath: /html/body/main/div[contains(@class,"SearchResults")]/div[not(@class)]/div[@class="SearchSnippet"] -+ # url_xpath: ./div[@class="SearchSnippet-headerContainer"]/h2/a/@href -+ # title_xpath: ./div[@class="SearchSnippet-headerContainer"]/h2/a -+ # content_xpath: ./p[@class="SearchSnippet-synopsis"] -+ # categories: [packages, it] -+ # timeout: 3.0 -+ # disabled: true -+ # about: -+ # website: https://pkg.go.dev/ -+ # use_official_api: false -+ # require_api_key: false -+ # results: HTML -+ # -+ ## Doku engine lets you access to any Doku wiki instance: -+ ## A public one or a privete/corporate one. -+ ## - name: ubuntuwiki -+ ## engine: doku -+ ## shortcut: uw -+ ## base_url: 'https://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'] - - doi_resolvers: - oadoi.org: 'https://oadoi.org/' diff --git a/src/portal/patches/snscrape_auth.diff b/src/portal/patches/snscrape_auth.diff index efdc6e0..c1fe487 100644 --- a/src/portal/patches/snscrape_auth.diff +++ b/src/portal/patches/snscrape_auth.diff @@ -1,5 +1,5 @@ diff --git a/snscrape/modules/twitter.py b/snscrape/modules/twitter.py -index d1719a8..43d9076 100644 +index d1719a8..5892340 100644 --- a/snscrape/modules/twitter.py +++ b/snscrape/modules/twitter.py @@ -92,6 +92,7 @@ import typing @@ -7,16 +7,16 @@ index d1719a8..43d9076 100644 import urllib3.util.ssl_ import warnings +import hashlib - - + + _logger = logging.getLogger(__name__) @@ -99,10 +100,12 @@ _API_AUTHORIZATION_HEADER = 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOu _globalGuestTokenManager = None _GUEST_TOKEN_VALIDITY = 10800 _CIPHERS_CHROME = 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA' +_AGE_RESTRICTED_MSG = 'Age-restricted adult content. This content might not be appropriate for people under 18 years old. To view this media, you’ll need to log in to Twitter. Learn more' - - + + @dataclasses.dataclass class Tweet(snscrape.base.Item): + rawResponses: typing.Dict[str, str] @@ -26,11 +26,11 @@ index d1719a8..43d9076 100644 @@ -793,17 +796,23 @@ class _TwitterAPIType(enum.Enum): V2 = 0 # Introduced with the redesign GRAPHQL = 1 - + +class _TwitterAPIAuthType(enum.Enum): + GUEST_TOKEN = 0 + OAUTH2 = 1 - + class _TwitterAPIScraper(snscrape.base.Scraper): - def __init__(self, baseUrl, *, guestTokenManager = None, maxEmptyPages = 0, **kwargs): + def __init__(self, baseUrl, *, guestTokenManager = None, maxEmptyPages = 0, cookies = None, **kwargs): @@ -58,12 +58,12 @@ index d1719a8..43d9076 100644 + self._lastResponse = None + self._lastResponseHash = None + self._lastSentResponseHash = None - + def _check_guest_token_response(self, r): if r.status_code != 200: @@ -820,6 +834,8 @@ class _TwitterAPIScraper(snscrape.base.Scraper): return True, None - + def _ensure_guest_token(self, url = None): + if not self._guestTokenManager: + return @@ -72,14 +72,14 @@ index d1719a8..43d9076 100644 r = self._get(self._baseUrl if url is None else url, responseOkCallback = self._check_guest_token_response) @@ -843,18 +859,42 @@ class _TwitterAPIScraper(snscrape.base.Scraper): self._apiHeaders['x-guest-token'] = self._guestTokenManager.token - + def _unset_guest_token(self, blockUntil): + if not self._guestTokenManager: + return self._guestTokenManager.reset(blockUntil = blockUntil) del self._session.cookies['gt'] del self._apiHeaders['x-guest-token'] - + + def _ensure_oauth(self): + if not self._cookies: + return @@ -135,7 +135,7 @@ index d1719a8..43d9076 100644 return False, msg + ''' return True, None - + def _get_api_data(self, endpoint, apiType, params, instructionsPath = None): - self._ensure_guest_token() + self._ensure_auth_updated() @@ -152,12 +152,12 @@ index d1719a8..43d9076 100644 + hasher.update(json.dumps(self._lastResponse).encode('utf-8')) + self._lastResponseHash = hasher.hexdigest() return r._snscrapeObj - + def _iter_api_data(self, endpoint, apiType, params, paginationParams = None, cursor = None, direction = _ScrollDirection.BOTTOM, instructionsPath = None): @@ -988,8 +1043,50 @@ class _TwitterAPIScraper(snscrape.base.Scraper): def _get_tweet_id(self, tweet): return tweet['id'] if 'id' in tweet else int(tweet['id_str']) - + + def _entry_is_age_restricted(self, entry): + if entry['entryId'].startswith('tombstone-') and entry['content']['entryType'] == 'TimelineTimelineItem': + if entry['content']['itemContent']['tombstoneInfo']['richText']['text'] == _AGE_RESTRICTED_MSG: @@ -205,9 +205,22 @@ index d1719a8..43d9076 100644 kwargs['id'] = tweetId if noteTweet and 'text' in noteTweet: kwargs['rawContent'] = noteTweet['text'] +@@ -1005,9 +1102,9 @@ class _TwitterAPIScraper(snscrape.base.Scraper): + kwargs['date'] = email.utils.parsedate_to_datetime(tweet['created_at']) + if links: + kwargs['links'] = [TextLink( +- text = u.get('display_url'), +- url = u['expanded_url'], +- tcourl = u['url'], ++ text = u.get('display_url', ''), ++ url = u.get('expanded_url', ''), ++ tcourl = u.get('url', ''), + indices = tuple(u['indices']), + ) for u in links] + kwargs['url'] = f'https://twitter.com/{getattr(user, "username", "i/web")}/status/{tweetId}' @@ -1789,7 +1886,7 @@ class TwitterUserScraper(TwitterSearchScraper): self._baseUrl = f'https://twitter.com/{self._user}' if not self._isUserId else f'https://twitter.com/i/user/{self._user}' - + def _get_entity(self): - self._ensure_guest_token() + self._ensure_auth_updated() diff --git a/src/portal/py/config.def.py b/src/portal/py/config.def.py index 8c0dab9..0c7ea30 100644 --- a/src/portal/py/config.def.py +++ b/src/portal/py/config.def.py @@ -6,7 +6,7 @@ TWITTER_ACCESS_TOKEN = '' TWITTER_ACCESS_TOKEN_SECRET = '' TWITTER_CONSUMER_TOKEN = '' TWITTER_CONSUMER_TOKEN_SECRET = '' -TWITTER_COOKIES_PATH = '{}/cookies_twitter.txt'.format(BASE_DIR) +TWITTER_COOKIES_PATH = f'{BASE_DIR}/cookies_twitter.txt' TWITTER_TIMEOUT = 5 # seconds FANBOX_SESSID = '' @@ -18,7 +18,7 @@ PIXIV_REFRESH_TOKEN = '' INSTAGRAM_USERNAME = '' INSTAGRAM_PASSWORD = '' INSTAGRAM_SESSION_ID = '' -INSTAGRAM_SETTINGS_PATH = Path('{}/ig_settings.json'.format(BASE_DIR)) +INSTAGRAM_SETTINGS_PATH = Path(f'{BASE_DIR}/ig_settings.json') INSTAGRAM_USER_AGENT = '' PATREON_USER_ID = '' diff --git a/src/portal/py/modules/__init__.py b/src/portal/py/modules/__init__.py index fee4c22..8a1e3cb 100644 --- a/src/portal/py/modules/__init__.py +++ b/src/portal/py/modules/__init__.py @@ -1,9 +1,9 @@ import config from modules.youtube import YoutubeModule -#from modules.twitter import TwitterScrapeModule -#from modules.pixiv_app import PixivAppModule -#from modules.pixiv_web import PixivWebModule +from modules.twitter import TwitterScrapeModule +from modules.pixiv_app import PixivAppModule +from modules.pixiv_web import PixivWebModule #from modules.fanbox import FanboxModule #from modules.instagram import InstagramModule #from modules.patreon import PatreonModule @@ -13,9 +13,9 @@ ALL_MODULES = { # 'twitter': (TwitterModule( # config.TWITTER_ACCESS_TOKEN, config.TWITTER_ACCESS_TOKEN_SECRET, # config.TWITTER_CONSUMER_TOKEN, config.TWITTER_CONSUMER_TOKEN_SECRET), []), -# 'twitter': (TwitterScrapeModule(config.TWITTER_COOKIES_PATH), []), -# 'pixiv_web': (PixivWebModule(config.PIXIV_SESSID, config.PIXIV_USERID), []), -# 'pixiv_app': (PixivAppModule(config.PIXIV_REFRESH_TOKEN), []), + 'twitter': (TwitterScrapeModule(config.TWITTER_COOKIES_PATH), []), + 'pixiv_web': (PixivWebModule(config.PIXIV_SESSID, config.PIXIV_USERID), []), + 'pixiv_app': (PixivAppModule(config.PIXIV_REFRESH_TOKEN), []), # 'fanbox': (FanboxModule(config.FANBOX_SESSID, config.FANBOX_CF_CLEARANCE), []), # 'instagram': (InstagramModule( # config.INSTAGRAM_USER_AGENT, config.INSTAGRAM_SETTINGS_PATH, diff --git a/src/portal/py/modules/fanbox.py b/src/portal/py/modules/fanbox.py index 9d233bb..01e2b8c 100644 --- a/src/portal/py/modules/fanbox.py +++ b/src/portal/py/modules/fanbox.py @@ -31,7 +31,7 @@ class FanboxBase(Search): unique_id = f'fanbox:p:{post_id}' kwargs['unique_id'] = unique_id kwargs['raw_responses'] = {} - kwargs['raw_responses']['api'] = json.dumps(data) + kwargs['raw_responses']['api'] = json.dumps(data, separators=(',', ':'), indent=None) publish_date = reformat_pixiv_date(data['publishedDatetime']).timestamp() current_date = get_current_utc_time().timestamp() kwargs['dates'] = [ @@ -56,12 +56,12 @@ class FanboxBase(Search): media = {} if data['type'] == 'image': text = data['body']['text'] - for i, value in enumerate(data['body']['images']): - media[str(i)] = Image(url=Url(value['originalUrl'], value['extension']), thumbnail_url=Url(value['thumbnailUrl'])) + for index, value in enumerate(data['body']['images']): + media[str(index)] = Image(url=Url(value['originalUrl'], value['extension']), thumbnail_url=Url(value['thumbnailUrl'])) elif data['type'] == 'file': text = data['body']['text'] - for i, value in enumerate(data['body']['files']): - media[str(i)] = File(url=Url(value['url'], value['extension']), name=value['name']) + for index, value in enumerate(data['body']['files']): + media[str(index)] = File(url=Url(value['url'], value['extension']), name=value['name']) elif data['type'] == 'article': for block in data['body']['blocks']: if block['type'] == 'p': diff --git a/src/portal/py/modules/instagram.py b/src/portal/py/modules/instagram.py index 014ffc1..7d71f9f 100644 --- a/src/portal/py/modules/instagram.py +++ b/src/portal/py/modules/instagram.py @@ -22,7 +22,7 @@ class InstagramSearch(Search): if num >= self.page + self.REACH_LIMIT: return False request_satisfied = False - for i in range(self.page, num + 1): + for index in range(self.page, num + 1): try: media, self.cursor = self.module.cl.user_medias_paginated_v1(self.pk, 0, end_cursor=self.cursor) #media, self.cursor = self.module.cl.user_clips_paginated_v1(self.pk, amount=12, end_cursor=self.cursor) @@ -34,24 +34,24 @@ class InstagramSearch(Search): return False if not self.cursor: self.completed = True - self.pages[i] = [] + self.pages[index] = [] for m in media: kwargs = {} kwargs['type'] = PostType.POST - unique_id = 'instagram:p:{}'.format(m.id) - #unique_id = 'instagram:r:{}'.format(m.id) + unique_id = f'instagram:p:{m.id}' + #unique_id = f'instagram:r:{m.id}' kwargs['unique_id'] = unique_id kwargs['raw_responses'] = {} kwargs['raw_responses']['tile'] = m.json() - kwargs['url'] = 'https://www.instagram.com/p/{}'.format(m.code) - #kwargs['url'] = 'https://www.instagram.com/reel/{}'.format(m.code) + kwargs['url'] = f'https://www.instagram.com/p/{m.code}' + #kwargs['url'] = f'https://www.instagram.com/reel/{m.code}' create_date = m.taken_at.timestamp() current_date = get_current_utc_time().timestamp() kwargs['dates'] = [ Date(DateType.CREATED, (create_date, create_date), DateMeta.NONE), Date(DateType.RETRIEVED, (current_date, current_date), DateMeta.NONE) ] - user = User(unique_id='instagram:u:{}'.format(m.user.pk)) + user = User(unique_id=f'instagram:u:{m.user.pk}') if m.user.username: user.username = m.user.username if m.user.full_name: @@ -79,9 +79,9 @@ class InstagramSearch(Search): kwargs['media'] = media post = Post(**kwargs) self.module.add_to_map(unique_id, post) - self.pages[i].append(unique_id) + self.pages[index].append(unique_id) self.page += 1 - if i == num: + if index == num: request_satisfied = True return request_satisfied diff --git a/src/portal/py/modules/patreon.py b/src/portal/py/modules/patreon.py index 0e4345a..bf2f0df 100644 --- a/src/portal/py/modules/patreon.py +++ b/src/portal/py/modules/patreon.py @@ -126,7 +126,7 @@ class PatreonUser(PatreonBase): return False request_satisfied = False - for i in range(self.page, num + 1): + for index in range(self.page, num + 1): url = f'{BASE_URL}/posts' #'filter[accessible_by_user_id]': self.module.user_id, params = { @@ -187,17 +187,17 @@ class PatreonUser(PatreonBase): else: log.warn(f'Unhandled include type {inc['type']}.') - self.pages[i] = [] + self.pages[index] = [] for entry in obj['data']: if entry['type'] != 'post': log.warn(f'Unhandled entry type {entry['type']}.') continue post = self.create_post(entry, hash) if post: - self.pages[i].append(post.unique_id) + self.pages[index].append(post.unique_id) self.page += 1 - if i == num: + if index == num: request_satisfied = True return request_satisfied diff --git a/src/portal/py/modules/pixiv_app.py b/src/portal/py/modules/pixiv_app.py index 02d5254..7bd341b 100644 --- a/src/portal/py/modules/pixiv_app.py +++ b/src/portal/py/modules/pixiv_app.py @@ -1,9 +1,9 @@ import json from typing import Optional, Any from base import USER_AGENT, Search, Module, ParsedJson -from post import Url, PostType, DateType, User, Post, Image, Tag, TagType +from post import Url, PostType, Date, DateType, DateMeta, User, Post, Image, Tag, TagType from query_parser import QueryParser -from pixivpy3 import AppPixivAPI +from pixivpy3 import AppPixivAPI, models from modules.common import get_current_utc_time, reformat_pixiv_date class PixivAppBase(Search): @@ -18,63 +18,77 @@ class PixivAppBase(Search): def request_page(self, num: int) -> bool: request_satisfied = False - for i in range(self.page, num + 1): + for index in range(self.page, num + 1): if not self.next_qs: break - if i in self.pages: + if index in self.pages: continue - self.pages[i] = [] - success, self.next_qs = self.api_request_page(i) + self.pages[index] = [] + success, self.next_qs = self.api_request_page(index) if not success: self.errored = True break - if i == num: + if index == num: request_satisfied = True self.page += 1 return request_satisfied - def create_media_url(self, url: str) -> Url: - return Url(url, url.split('.')[-1]) - def create_tag(self, data: ParsedJson) -> Tag: - tag = Tag(type=TagType.GENERAL, name=data['name']) + kwargs = {} + kwargs['type'] = TagType.GENERAL + kwargs['name'] = data['name'] + kwargs['alts'] = {} if data['translated_name']: - tag.alts['en'] = data['translated_name'] - return tag + kwargs['alts']['en'] = data['translated_name'] + return Tag(**kwargs) - def create_post(self, data: ParsedJson) -> Post: - if len(data['meta_pages']) == 0: - data['meta_pages'].append({ - 'image_urls': { - 'medium': data['image_urls']['medium'], - 'original': data['meta_single_page']['original_image_url'] - } - }) + # Other objects returned from pixivpy3 are likely also ModelT but it seems + # so inconsistent internally for now keep some params as ParsedJson. + def create_post(self, data: ParsedJson | models.ModelT, retrieved_date: Optional[float] = None) -> Post: + is_deleted = not data['visible'] kwargs = {} + kwargs['module'] = 'pixiv_app' kwargs['type'] = PostType.POST - unique_id = 'pixiv:i:{}'.format(data['id']) + unique_id = f'pixiv:i:{data['id']}' kwargs['unique_id'] = unique_id kwargs['raw_responses'] = {} - kwargs['raw_responses']['api'] = json.dumps(data) - kwargs['url'] = 'https://www.pixiv.net/en/artworks/{}'.format(data['id']) - kwargs['dates'] = { - DateType.CREATED: reformat_pixiv_date(data['create_date']).timestamp(), - DateType.RETRIEVED: get_current_utc_time().timestamp() - } + try: + kwargs['raw_responses']['api'] = json.dumps(data, separators=(',', ':'), indent=None) + except TypeError: + kwargs['raw_responses']['api'] = data.model_dump_json(indent=None) + kwargs['url'] = f'https://www.pixiv.net/en/artworks/{data['id']}' + if not retrieved_date: + retrieved_date = get_current_utc_time().timestamp() + kwargs['dates'] = [ + Date(DateType.RETRIEVED, (retrieved_date, retrieved_date), DateMeta.NONE) + ] + if not is_deleted: + create_date = reformat_pixiv_date(data['create_date']).timestamp() + kwargs['dates'].append(Date(DateType.CREATED, (create_date, create_date), DateMeta.NONE)) user = User(unique_id=f'pixiv:u:{data['user']['id']}', username=data['user']['account'], display_name=data['user']['name']) - user.profile_picture_url = data['user']['profile_image_urls']['medium'] + if not is_deleted: + user.profile_picture_url = Url(data['user']['profile_image_urls']['medium']) kwargs['author'] = user kwargs['title'] = data['title'] kwargs['text'] = data['caption'] - kwargs['likes'] = data['total_bookmarks'] + if not is_deleted: # Default to None instead of 0. + kwargs['bookmarks'] = data['total_bookmarks'] + kwargs['views'] = data['total_view'] if 'total_comments' in data: kwargs['comments'] = data['total_comments'] - kwargs['views'] = data['total_view'] - kwargs['tags'] = [self.create_tag(tag) for tag in data['tags']] - media = {} - for i, page in enumerate(data['meta_pages']): - media[str(i)] = Image(url=self.create_media_url(page['image_urls']['original']), thumbnail_url=self.create_media_url(page['image_urls']['medium'])) - kwargs['media'] = media + kwargs['tags'] = [self.create_tag(t) for t in data['tags']] + if not is_deleted: + if len(data['meta_pages']) == 0: + data['meta_pages'].append({ + 'image_urls': { + 'medium': data['image_urls']['medium'], + 'original': data['meta_single_page']['original_image_url'] + } + }) + media = {} + for index, page in enumerate(data['meta_pages']): + media[str(index)] = Image(url=Url(page['image_urls']['original']), thumbnail_url=Url(page['image_urls']['medium'])) + kwargs['media'] = media post = Post(**kwargs) self.module.add_to_map(unique_id, post) return post @@ -82,16 +96,23 @@ class PixivAppBase(Search): def create_user(self, data: ParsedJson) -> User: unique_id = f'pixiv:u:{data['id']}' user = User(unique_id=unique_id, username=data['account'], display_name=data['name']) + user.profile_picture_url = Url(data['profile_image_urls']['medium']) self.module.add_to_map(unique_id, user) return user + def remake_post(self, raw_responses: ParsedJson, original_date: float) -> Optional[Post]: + api = json.loads(raw_responses['api']) + return self.create_post(api, original_date) + class PixivAppSearch(PixivAppBase): def __init__(self, userdata: Any, arg: str) -> None: super().__init__(userdata) self.next_qs: Optional[dict[str, Any]] = {} self.next_qs['word'] = arg - self.next_qs['sort'] = 'popular_desc' - self.next_qs['search_ai_type'] = 1 + #self.next_qs['sort'] = 'popular_desc' + # https://github.com/upbit/pixivpy/issues/352 + # We want to include AI results for completeness, to be ignored further down the chain. + #self.next_qs['search_ai_type'] = 1 self.next_qs['start_date'] = '2019-01-01' self.next_qs['end_date'] = '2019-12-31' @@ -99,27 +120,10 @@ class PixivAppSearch(PixivAppBase): if not self.next_qs: return False, None obj = self.module.api.search_illust(**self.next_qs) - if 'illusts' not in obj: - return False, None - for post in obj['illusts']: + for post in obj.illusts: self.pages[num].append(self.create_post(post).unique_id) return True, self.module.api.parse_qs(obj['next_url']) -class PixivAppIllust(PixivAppBase): - def __init__(self, userdata: Any, arg: str) -> None: - super().__init__(userdata) - self.next_qs: Optional[dict[str, Any]] = {} - self.next_qs['illust_id'] = arg - - def api_request_page(self, num: int) -> tuple[bool, Optional[dict[str, Any]]]: - if not self.next_qs: - return False, None - obj = self.module.api.illust_detail(**self.next_qs) - if 'illust' not in obj: - return False, None - self.pages[num].append(self.create_post(obj['illust']).unique_id) - return True, None - class PixivAppUser(PixivAppBase): def __init__(self, userdata: Any, arg: str) -> None: super().__init__(userdata) @@ -131,9 +135,7 @@ class PixivAppUser(PixivAppBase): if not self.next_qs: return False, None obj = self.module.api.user_illusts(**self.next_qs) - if 'illusts' not in obj: - return False, None - for post in obj['illusts']: + for post in obj.illusts: self.pages[num].append(self.create_post(post).unique_id) return True, self.module.api.parse_qs(obj['next_url']) @@ -148,9 +150,7 @@ class PixivAppBookmarks(PixivAppBase): if not self.next_qs: return False, None obj = self.module.api.user_bookmarks_illust(**self.next_qs) - if 'illusts' not in obj: - return False, None - for post in obj['illusts']: + for post in obj.illusts: self.pages[num].append(self.create_post(post).unique_id) return True, self.module.api.parse_qs(obj['next_url']) @@ -165,22 +165,39 @@ class PixivAppFollowing(PixivAppBase): if not self.next_qs: return False, None obj = self.module.api.user_following(**self.next_qs) - if 'user_previews' not in obj: - return False, None - for user in obj['user_previews']: + for user in obj.user_previews: self.pages[num].append(self.create_user(user['user']).unique_id) return True, self.module.api.parse_qs(obj['next_url']) +class PixivAppIllust(PixivAppBase): + def __init__(self, userdata: Any, arg: str) -> None: + super().__init__(userdata) + self.next_qs: Optional[dict[str, Any]] = {} + self.next_qs['illust_id'] = arg + + def api_request_page(self, num: int) -> tuple[bool, Optional[dict[str, Any]]]: + if not self.next_qs: + return False, None + obj = self.module.api.illust_detail(**self.next_qs) + self.pages[num].append(self.create_post(obj.illust).unique_id) + return True, None + class PixivAppModule(Module): def __init__(self, refresh_token: str) -> None: super().__init__() self.parser: QueryParser = QueryParser(self, 'search') self.parser.add_command('search', PixivAppSearch) - self.parser.add_command('illust', PixivAppIllust) self.parser.add_command('user', PixivAppUser) self.parser.add_command('bookmarks', PixivAppBookmarks) self.parser.add_command('following', PixivAppFollowing) - self.api: AppPixivAPI = AppPixivAPI() + self.parser.add_command('illust', PixivAppIllust) + headers = { + "app-os": "ios", + "app-os-version": "16.7.2", + "app-version": "7.19.6", + "user-agent": "PixivIOSApp/7.19.6 (iOS 16.7.2; iPhone13,2)" + } + self.api: AppPixivAPI = AppPixivAPI(headers=headers) self.api.auth(refresh_token=refresh_token) def search(self, query: str, *extra_args: Any) -> Optional[Search]: diff --git a/src/portal/py/modules/pixiv_web.py b/src/portal/py/modules/pixiv_web.py index 3c04202..e7399dc 100644 --- a/src/portal/py/modules/pixiv_web.py +++ b/src/portal/py/modules/pixiv_web.py @@ -33,8 +33,8 @@ class PixivWebBase(Search): def parse_pages(self, data: ParsedJson) -> dict[str, Media]: media = {} - for i, m in enumerate(data): - media[str(i)] = Image(url=Url(m['urls']['original']), thumbnail_url=Url(m['urls']['small'])) + for index, m in enumerate(data): + media[str(index)] = Image(url=Url(m['urls']['original']), thumbnail_url=Url(m['urls']['small'])) return media def parse_ugoira(self, data: ParsedJson, thumbnail_url: str) -> Animation: @@ -43,7 +43,7 @@ class PixivWebBase(Search): animation.frames.append((frame['file'], frame['delay'])) return animation - def seek_profile_picture_url(self, user_illusts: ParsedJson) -> Optional[Url]: + def search_profile_picture_url(self, user_illusts: ParsedJson) -> Optional[Url]: for illust in user_illusts.values(): if illust is None: continue @@ -52,7 +52,7 @@ class PixivWebBase(Search): return None def create_tag(self, data: ParsedJson) -> Tag: - kwargs= {} + kwargs = {} kwargs['type'] = TagType.GENERAL kwargs['name'] = data['tag'] kwargs['alts'] = {} @@ -63,26 +63,27 @@ class PixivWebBase(Search): kwargs['alts']['en'] = data['translation']['en'] return Tag(**kwargs) - def create_post(self, data: ParsedJson, pages: Optional[ParsedJson] = None, ugoira: Optional[ParsedJson] = None) -> Optional[Post]: + def create_post(self, data: ParsedJson, pages: Optional[ParsedJson] = None, ugoira: Optional[ParsedJson] = None, retrieved_date: Optional[float] = None) -> Optional[Post]: kwargs = {} kwargs['type'] = PostType.POST illust_id = data['illustId'] unique_id = f'pixiv:i:{illust_id}' kwargs['unique_id'] = unique_id kwargs['raw_responses'] = {} - kwargs['raw_responses']['api'] = json.dumps(data) + kwargs['raw_responses']['api'] = json.dumps(data, separators=(',', ':'), indent=None) kwargs['url'] = f'https://www.pixiv.net/{LANG}/artworks/{data['illustId']}' create_date = reformat_pixiv_date(data['createDate']).timestamp() - current_date = get_current_utc_time().timestamp() + if not retrieved_date: + retrieved_date = get_current_utc_time().timestamp() kwargs['dates'] = [ Date(DateType.CREATED, (create_date, create_date), DateMeta.NONE), - Date(DateType.RETRIEVED, (current_date, current_date), DateMeta.NONE) + Date(DateType.RETRIEVED, (retrieved_date, retrieved_date), DateMeta.NONE) ] upload_date = reformat_pixiv_date(data['uploadDate']).timestamp() if upload_date != create_date: kwargs['dates'].append(Date(DateType.EDITED, (upload_date, upload_date), DateMeta.NONE)) user = User(unique_id=f'pixiv:u:{data['userId']}', username=data['userAccount'], display_name=data['userName']) - profile_picture_url = self.seek_profile_picture_url(data['userIllusts']) + profile_picture_url = self.search_profile_picture_url(data['userIllusts']) if profile_picture_url: user.profile_picture_url = profile_picture_url kwargs['author'] = user @@ -124,19 +125,6 @@ class PixivWebBase(Search): self.module.add_to_map(unique_id, post) return post - def remake_post(self, raw_responses: ParsedJson, original_date: float) -> Optional[Post]: - api = json.loads(raw_responses['api']) - pages = raw_responses.get('pages', None) - if pages: - pages = json.loads(pages) - ugoira = raw_responses.get('ugoira', None) - if ugoira: - ugoira = json.loads(ugoira) - post = self.create_post(api, pages, ugoira) - if post: - post.dates[DateType.RETRIEVED] = original_date - return post - def request_illust(self, illust_id: int) -> Optional[Post]: url = f'{BASE_URL}/illust/{illust_id}?lang={LANG}&version={VERSION}' obj = self.check_api_response(self.module.do_request(Method.GET, url)) @@ -151,6 +139,16 @@ class PixivWebBase(Search): self.module.add_to_map(unique_id, user) return user + def remake_post(self, raw_responses: ParsedJson, original_date: float) -> Optional[Post]: + api = json.loads(raw_responses['api']) + pages = raw_responses.get('pages', None) + if pages: + pages = json.loads(pages) + ugoira = raw_responses.get('ugoira', None) + if ugoira: + ugoira = json.loads(ugoira) + return self.create_post(api, pages, ugoira, original_date) + class PixivWebSearch(PixivWebBase): def __init__(self, userdata: Any, arg: str) -> None: super().__init__(userdata) @@ -178,23 +176,6 @@ class PixivWebSearch(PixivWebBase): self.pages[num].append(post.unique_id) return True -class PixivWebIllust(PixivWebBase): - def __init__(self, userdata: Any, arg: str) -> None: - super().__init__(userdata) - try: - self.id: int = int(arg) - except ValueError: - self.errored = True - - def request_page(self, num: int) -> bool: - post = self.request_illust(self.id) - if not post: - self.errored = True - return False - self.pages[num] = [post.unique_id] - self.completed = True - return True - class PixivWebUser(PixivWebBase): def __init__(self, userdata: Any, arg: str) -> None: super().__init__(userdata) @@ -285,15 +266,32 @@ class PixivWebFollowing(PixivWebBase): self.pages[num].append(self.create_user(user).unique_id) return True +class PixivWebIllust(PixivWebBase): + def __init__(self, userdata: Any, arg: str) -> None: + super().__init__(userdata) + try: + self.id: int = int(arg) + except ValueError: + self.errored = True + + def request_page(self, num: int) -> bool: + post = self.request_illust(self.id) + if not post: + self.errored = True + return False + self.pages[num] = [post.unique_id] + self.completed = True + return True + class PixivWebModule(Module): def __init__(self, sessid: str, user_id: str) -> None: super().__init__() self.parser: QueryParser = QueryParser(self, 'search') self.parser.add_command('search', PixivWebSearch) - self.parser.add_command('illust', PixivWebIllust) self.parser.add_command('user', PixivWebUser) self.parser.add_command('bookmarks', PixivWebBookmarks) self.parser.add_command('following', PixivWebFollowing) + self.parser.add_command('illust', PixivWebIllust) self.headers['User-Agent'] = USER_AGENT self.headers['Accept-Encoding'] = 'gzip, deflate, br, zstd' self.headers['Accept-Language'] = 'en-US,en;q=0.5' diff --git a/src/portal/py/modules/twitter.py b/src/portal/py/modules/twitter.py index 2f45ec1..93286c3 100644 --- a/src/portal/py/modules/twitter.py +++ b/src/portal/py/modules/twitter.py @@ -3,15 +3,15 @@ import http.cookiejar from typing import Optional, Any, Iterator from datetime import timezone from base import USER_AGENT, Search, Module -from post import Url, PostType, DateType, PostRef, Post, User, Media, Image, Video +from post import Url, PostType, Date, DateType, DateMeta, PostRef, Post, User, Media, Image, Video from query_parser import QueryParser from modules.common import get_current_utc_time # GraphQL API -from snscrape.modules import twitter from snscrape.base import ScraperException +from snscrape.modules import twitter from snscrape.modules.twitter import (Tweet, TweetRef, Tombstone, UserRef, - TwitterSearchScraper, TwitterProfileScraper, TwitterUserScraper, TwitterTweetScraper) + TwitterSearchScraper, TwitterSearchScraperMode, TwitterProfileScraper, TwitterUserScraper, TwitterTweetScraper) class TwitterScrapeBase(Search): # Posts per emulated page because snscrape returns an iterator. @@ -20,7 +20,7 @@ class TwitterScrapeBase(Search): # Don't allow requests for a page more than this amount past the current page. REACH_LIMIT = 4 - def __init__(self, userdata: Any): + def __init__(self, userdata: Any) -> None: super().__init__() self.module: TwitterScrapeModule = userdata self.iterator: Iterator[Tweet | TweetRef | Tombstone] @@ -28,38 +28,39 @@ class TwitterScrapeBase(Search): def create_media_url(self, url: str) -> Url: format = url.find('format=') + ext = None if format >= 0: - ext = url[format + 7:format + 10] - else: - question = url.rfind('?') - if question >= 0: - url = url[0:question] - ext = url.split('.')[-1] + ext = url[format + 7:] + amp = ext.find('&') + if amp >= 0: + ext = ext[:amp] return Url(url, ext) def parse_media(self, data: list[twitter.Medium]) -> dict[str, Media]: media = {} - for i, m in enumerate(data): + for index, m in enumerate(data): if isinstance(m, twitter.Photo): - media[str(i)] = Image(url=self.create_media_url(m.fullUrl), thumbnail_url=self.create_media_url(m.previewUrl)) + media[str(index)] = Image(url=self.create_media_url(m.fullUrl), thumbnail_url=self.create_media_url(m.previewUrl)) elif isinstance(m, twitter.Video) or isinstance(m, twitter.Gif): videos = sorted(m.variants, key=lambda x: x.bitrate if x.bitrate else 0, reverse=True) - media[str(i)] = Video(url=self.create_media_url(videos[0].url), thumbnail_url=self.create_media_url(m.thumbnailUrl)) + media[str(index)] = Video(url=self.create_media_url(videos[0].url), thumbnail_url=self.create_media_url(m.thumbnailUrl)) return media def create_post(self, tweet: Tweet | TweetRef | Tombstone) -> Post: kwargs = {} + kwargs['module'] = 'twitter_scrape' kwargs['unique_id'] = f'twitter:t:{tweet.id}' if isinstance(tweet, TweetRef) or isinstance(tweet, Tombstone): kwargs['type'] = PostType.TOMBSTONE return Post(**kwargs) kwargs['raw_responses'] = tweet.rawResponses kwargs['url'] = tweet.url - # @TODO: Replace correct? - kwargs['dates'] = { - DateType.CREATED: tweet.date.replace(tzinfo=timezone.utc).timestamp(), - DateType.RETRIEVED: get_current_utc_time().timestamp() - } + create_date = tweet.date.replace(tzinfo=timezone.utc).timestamp() + current_date = get_current_utc_time().timestamp() + kwargs['dates'] = [ + Date(DateType.CREATED, (create_date, create_date), DateMeta.NONE), + Date(DateType.RETRIEVED, (current_date, current_date), DateMeta.NONE) + ] author = User(unique_id=f'twitter:u:{tweet.user.id}') if not isinstance(tweet.user, UserRef): author.username = tweet.user.username @@ -116,40 +117,40 @@ class TwitterScrapeBase(Search): if num - self.page >= self.REACH_LIMIT: return False request_satisfied = False - for i in range(self.page, num + 1): + for index in range(self.page, num + 1): if self.completed: break - if i in self.pages: + if index in self.pages: continue - self.pages[i] = [] - if not self.step_iterator_for_page(i): + self.pages[index] = [] + if not self.step_iterator_for_page(index): break - self.page = i - if i == num: + self.page = index + if index == num: request_satisfied = True return request_satisfied class TwitterScrapeSearch(TwitterScrapeBase): - def __init__(self, userdata: Any, arg: str): + def __init__(self, userdata: Any, arg: str) -> None: super().__init__(userdata) - self.iterator = TwitterSearchScraper(arg, top=True, cookies=self.module.cookies).get_items() + self.iterator = TwitterSearchScraper(arg, mode=TwitterSearchScraperMode.TOP, cookies=self.module.cookies).get_items() class TwitterScrapeUser(TwitterScrapeBase): - def __init__(self, userdata: Any, arg: str): + def __init__(self, userdata: Any, arg: str) -> None: super().__init__(userdata) if arg.startswith('@'): arg = arg[1:] self.iterator = TwitterUserScraper(arg, cookies=self.module.cookies).get_items() class TwitterScrapeProfile(TwitterScrapeBase): - def __init__(self, userdata: Any, arg: str): + def __init__(self, userdata: Any, arg: str) -> None: super().__init__(userdata) if arg.startswith('@'): arg = arg[1:] self.iterator = TwitterProfileScraper(arg, cookies=self.module.cookies).get_items() class TwitterScrapeTweet(TwitterScrapeBase): - def __init__(self, userdata: Any, arg: str): + def __init__(self, userdata: Any, arg: str) -> None: super().__init__(userdata) if arg.startswith('https://'): arg = arg[arg.rfind('/') + 1:] @@ -159,7 +160,7 @@ class TwitterScrapeTweet(TwitterScrapeBase): self.iterator = TwitterTweetScraper(arg, cookies=self.module.cookies).get_items() class TwitterScrapeModule(Module): - def __init__(self, cookies_path: str): + def __init__(self, cookies_path: str) -> None: super().__init__() self.parser: QueryParser = QueryParser(self, 'search') self.parser.add_command('search', TwitterScrapeSearch) diff --git a/src/portal/py/post.py b/src/portal/py/post.py index 1a285c2..f173d0e 100644 --- a/src/portal/py/post.py +++ b/src/portal/py/post.py @@ -4,13 +4,6 @@ from typing import Optional, Any from enum import IntEnum, IntFlag from json import JSONEncoder -''' -List of Things: - - Social media posts - - Albums - - Loose files -''' - class PostType(IntEnum): UNKNOWN = 0 POST = 1 @@ -54,27 +47,26 @@ def df(c: Any) -> Any: return field(default_factory=lambda: c) def default_url_ext_guess(url: str) -> str: - # @TODO: List of known extensions. + slash = url.find('/') + if slash < 0: + return '' + url = url[slash:] question = url.rfind('?') if question >= 0: - guess = url[0:question].rsplit('.')[-1] - else: - guess = url.rsplit('.')[-1] - return guess + url = url[0:question] + s = url.split('.') + if len(s) > 1: + return s[-1] + return '' @dataclasses.dataclass class Url(): url: str ext: str - def __init__(self, url: str, ext: str = None) -> None: self.url = url if not ext: - ext_guess = default_url_ext_guess(url) - if ext_guess: - ext = ext_guess - else: - ext = 'unknown' + ext = default_url_ext_guess(url) self.ext = ext @dataclasses.dataclass @@ -158,6 +150,7 @@ class PostRef(): ''' V4: - Improved date. + - Track origin module. - Ends when all current modules are ported and/or completed. V5: - Formatted body/text. @@ -165,6 +158,7 @@ V5: @dataclasses.dataclass class Post(): version: int = 4 + module: str = '' type: PostType = PostType.UNKNOWN unique_id: str = '' raw_responses: dict[str, str] = df({}) diff --git a/src/portal/py/query_parser.py b/src/portal/py/query_parser.py index cd6c1e6..bf276ce 100644 --- a/src/portal/py/query_parser.py +++ b/src/portal/py/query_parser.py @@ -20,7 +20,7 @@ class QueryParser(): sp = query.split(' ') - for i, word in enumerate(sp): + for index, word in enumerate(sp): col = word.find(':') if col == -1: continue @@ -34,7 +34,7 @@ class QueryParser(): else: arg = None search = self.commands[command] - if i == 0: + if index == 0: if len(sp) > 1: query = query.replace(word + ' ', '', 1) else: diff --git a/src/portal/py/test.py b/src/portal/py/test.py index 0ca89a4..e862bf0 100644 --- a/src/portal/py/test.py +++ b/src/portal/py/test.py @@ -1,2 +1,4 @@ #import tests.test -import tests.archive_query +import tests.touch_all +#import tests.rewrite_post +#import tests.archive_query diff --git a/src/portal/py/tests/archive_query.py b/src/portal/py/tests/archive_query.py index 4cc1e1e..3de4329 100644 --- a/src/portal/py/tests/archive_query.py +++ b/src/portal/py/tests/archive_query.py @@ -15,8 +15,8 @@ mode = 'pixiv_web' #mode = 'fanbox' #mode = 'patreon' #mode = 'instagram' -#cmd = 'user' -cmd = 'bookmarks' +cmd = 'user' +#cmd = 'bookmarks' #cmd = 'search' module = ALL_MODULES[mode][0] if not module.init(): @@ -180,8 +180,8 @@ class QueryDownloadThread(threading.Thread): with queue_cond: queue_cond.notify() -#RUNTIME_PATH = "./run" -RUNTIME_PATH = "/mnt/store/files/tmp/run" +#RUNTIME_PATH = './run' +RUNTIME_PATH = '/mnt/store/files/tmp/run' LOG_FILE = f'{RUNTIME_PATH}/archive4.log' ARG_FILE = f'{RUNTIME_PATH}/completed_args4.log' @@ -216,10 +216,7 @@ def download_args(): os.mkdir(output_dir) os.mkdir(f'{output_dir}/raw_responses') - #args = module.search(f'following:{22781328}') - # @TODO: args = [] - # twitter test: butcha_u, sep__rina, nagayori000, sattinittas, threads = [] start = True diff --git a/src/portal/py/tests/old_twitter_api.py b/src/portal/py/tests/old_twitter_api.py index 40d0346..4427dfd 100644 --- a/src/portal/py/tests/old_twitter_api.py +++ b/src/portal/py/tests/old_twitter_api.py @@ -27,14 +27,14 @@ log = Logger(LOG_FILE) compat_thread = QueryDownloadThread(log, None, None, sys.argv[1]) def compat_media_download(obj, post, output_base): - for i, key in enumerate(post.media.keys()): + for index, key in enumerate(post.media.keys()): media = post.media[key] url = media.url media_path = f'{output_base}_media{key}.{url.ext}' if os.path.isfile(media_path): obj.log.write(f'Skipping media {media_path}.') continue - origin_path = f'{origin_base}/{post.author.unique_id.replace(':', '_')}/{post.unique_id.replace(':', '_')}_media{i}.{url.ext}' + origin_path = f'{origin_base}/{post.author.unique_id.replace(':', '_')}/{post.unique_id.replace(':', '_')}_media{index}.{url.ext}' if os.path.isfile(origin_path): shutil.copyfile(origin_path, media_path) #post.media[key].url.url = url.url.replace('name=orig', 'name=large') diff --git a/src/portal/py/tests/rewrite_post.py b/src/portal/py/tests/rewrite_post.py index 3bb152b..f00cfc7 100644 --- a/src/portal/py/tests/rewrite_post.py +++ b/src/portal/py/tests/rewrite_post.py @@ -2,20 +2,24 @@ import sys import os import gzip import json -import hashlib sys.path.append('../py') -import config -#from post import PostEncoder, DateType -#from modules.pixiv_web import PixivWebBase, PixivWebModule -#from modules.twitter import TwitterScrapeBase, TwitterScrapeModule -from logger import Logger +from modules import ALL_MODULES +from post import PostEncoder +from modules.pixiv_app import PixivAppBase -#module = PixivWebModule(config.PIXIV_SESSID) -#search = PixivWebBase(module) +module = ALL_MODULES['pixiv_app'][0] +module.init() +search = PixivAppBase(module) -log = Logger('deleted_posts.log') +target = sys.argv[1] +with gzip.open(target, 'rb') as f: + obj = json.loads(f.read()) + mtime = os.path.getmtime(target) + print(json.dumps(search.remake_post(obj['raw_responses'], mtime), cls=PostEncoder)) ''' +log = Logger('deleted_posts.log') + def rewrite_post(path): print(f'Rewriting post @ {path}') with gzip.open(path, 'rb') as f: @@ -27,7 +31,6 @@ def rewrite_post(path): #print(json.dumps(post, indent=4, cls=PostEncoder)) with gzip.open(path, 'wb') as f: f.write(json.dumps(post, cls=PostEncoder).encode('utf-8')) -''' def dump_raw_response(path, target): print(f'Extracting raw_response from {path}') @@ -54,3 +57,4 @@ for user in os.listdir(target): if file.split('.')[-1] == 'gz': dump_raw_response(f'{target}/{user}/{file}', raw_responses) #rewrite_post(f'{target}/{user}/{file}') +''' diff --git a/src/portal/py/tests/test.py b/src/portal/py/tests/test.py index ef166d9..d3e5640 100644 --- a/src/portal/py/tests/test.py +++ b/src/portal/py/tests/test.py @@ -1,13 +1,4 @@ -import sys -import json -import httpx -import os.path -from base import Method -from typing import Optional, Any -from post import DateType, DateMeta, Date, PostEncoder, Post, MediaType -from modules import ALL_MODULES -from twitter.scraper import Scraper - +#from twitter.scraper import Scraper #scraper = Scraper(cookies = { # 'ct0': '', # 'auth_token': '' @@ -15,101 +6,3 @@ from twitter.scraper import Scraper # #media = scraper.media([1557919548904419328], limit=10) #print(media) - -#output = '/mnt/store/files/ext/patreon_tmp/FPSBlyck' -# -#module = ALL_MODULES['patreon'][0] -#module.init() -#search = module.search('FPSBlyck') -#page_num = 0 -#mark = False -#while not mark: -# page = search.get_page(page_num) -# if not page: -# break -# page_num += 1 -# for unique_id in page: -# if unique_id == 'patreon:p:29707872': -# print('hit mark') -# mark = True -# post = module.get_item(unique_id) -# print(json.dumps(post, indent=4, cls=PostEncoder)) -# for key, m in post.media.items(): -# if m.type == MediaType.FILE: -# path = f'{output}/{post.unique_id.replace(':', '_')}_{m.name}' -# if not os.path.isfile(path): -# dl = module.get_download(post.unique_id, key) -# retries = 3 -# r: Optional[httpx.Response] = None -# while retries >= 0: -# try: -# r = httpx.get(dl['urls'][0].url, headers=dl['headers'], cookies=dl['cookies'], follow_redirects=True) -# except: -# retries -= 1 -# else: -# break -# if r: -# with open(path, 'wb+') as f: -# f.write(r.content) -# else: -# print('Download failed') -# else: -# print('Skipping file') - - -#module = ALL_MODULES['pixiv_web'][0] -#module.init() -#search = module.search('illust:91352621') -#page = search.get_page(0) -#for unique_id in page: -# post = module.get_item(unique_id) -# print(json.dumps(post, indent=4, cls=PostEncoder)) - -module = ALL_MODULES['instagram'][0] -module.init() -search = module.search('plottttwistttttt') -page = search.get_page(0) -for unique_id in page: - post = module.get_item(unique_id) - print(json.dumps(post, indent=4, cls=PostEncoder)) - -#d = Date(DateType.EDITED, (0.0, 0.0), DateMeta.EDITED_AT_UNKNOWN_TIME) -#d = Date(DateType.EDITED, (0.0, 0.0), DateMeta.NONE) -#print(json.dumps(d, indent=4, cls=PostEncoder)) -#sys.exit(1) - -#module = ALL_MODULES['fanbox'][0] -#module = ALL_MODULES['pixiv_web'][0] -#module = ALL_MODULES['youtube'][0] -#module = ALL_MODULES['twitter'][0] -#module.init() - -#search = module.search('opium_00pium') -#search = module.search('search:ddd') -#page = search.get_page(0) -#for unique_id in page: -# post = module.get_item(unique_id) -# print(json.dumps(post, indent=4, cls=PostEncoder)) -# break - -#post = module.get_item(page[0]) -#for key in post.media.keys(): -# download_params = module.get_download(post.unique_id, key) -# for url in download_params['urls']: -# #response = module.do_request(Method.GET, url.url) -# r = httpx.get(url.url, headers=download_params['headers']) -# with open(f'test_{key}.{url.ext}', 'wb+') as f: -# f.write(r.content) - -#print(json.dumps(page, indent=4, cls=PostEncoder)) -#for i in range(0, len(post.media)): -# download_params = module.get_download(post.unique_id, i) -# r = httpx.get(download_params['url'], headers=download_params['headers']) -# if r.status_code != 200: -# print('error{}'.format(r.status_code)) -# else: -# with open('test{}.png'.format(i), 'wb+') as f: -# f.write(r.content) - -#print(json.dumps(page, indent=4, cls=PostEncoder)) -#print(page) diff --git a/src/portal/py/tests/touch_all.py b/src/portal/py/tests/touch_all.py new file mode 100644 index 0000000..6d6bfa1 --- /dev/null +++ b/src/portal/py/tests/touch_all.py @@ -0,0 +1,79 @@ +import json +from typing import Optional, Any +from post import PostEncoder +from modules import ALL_MODULES + +''' +post = module.get_item(page[0]) +for key in post.media.keys(): + download_params = module.get_download(post.unique_id, key) + for url in download_params['urls']: + #response = module.do_request(Method.GET, url.url) + r = httpx.get(url.url, headers=download_params['headers']) + if r.status_code != 200: + print('error{}'.format(r.status_code)) + else: + with open(f'test_{key}.{url.ext}', 'wb+') as f: + f.write(r.content) +''' + +def print_page(module: Any, page: Optional[list[str]]) -> None: + if not page: + print('Page is None') + return + count = 0 + for unique_id in page: + post = module.get_item(unique_id) + print(json.dumps(post, indent=4, cls=PostEncoder)) + count = count + 1 + if count == 2: + break + +''' Blocked on certain endpoints, snscrape is outdated. +module = ALL_MODULES['twitter'][0] +module.init() + +search = module.search('search:#小関麗奈誕生祭2024') +print_page(module, search.get_page(0)) +search = module.search('user:xuuikie') +print_page(module, search.get_page(0)) +search = module.search('profile:@barackobama') +print_page(module, search.get_page(0)) +search = module.search('tweet:1905966372288434680') +print_page(module, search.get_page(0)) +''' + +''' +module = ALL_MODULES['pixiv_app'][0] +module.init() +search = module.search('search:サムス・アラン') +print_page(module, search.get_page(0)) +search = module.search('user:757584') +print_page(module, search.get_page(0)) +search = module.search('bookmarks:2034628') +print_page(module, search.get_page(0)) +search = module.search('following:22781328') +print_page(module, search.get_page(0)) +search = module.search('illust:91352621') +print_page(module, search.get_page(0)) +search = module.search('illust:26497574') # Deleted. +print_page(module, search.get_page(0)) +''' + +''' No on-demand loading (Will load entire user before returning). +module = ALL_MODULES['pixiv_web'][0] +module.init() + +search = module.search('search:サムス・アラン') +print_page(module, search.get_page(0)) +search = module.search('user:757584') +print_page(module, search.get_page(0)) +search = module.search('bookmarks:2034628') +print_page(module, search.get_page(0)) +search = module.search('following:22781328') +print_page(module, search.get_page(0)) +search = module.search('illust:91352621') +print_page(module, search.get_page(0)) +search = module.search('illust:100455669') # Deleted. +print_page(module, search.get_page(0)) +''' diff --git a/src/portal/py/tlds.py b/src/portal/py/tlds.py new file mode 100644 index 0000000..d0e4cdd --- /dev/null +++ b/src/portal/py/tlds.py @@ -0,0 +1,1445 @@ +# https://data.iana.org/TLD/tlds-alpha-by-domain.txt +# Version 2025051100, Last Updated Sun May 11 07:07:01 2025 UTC +tlds = [ + 'aaa', + 'aarp', + 'abb', + 'abbott', + 'abbvie', + 'abc', + 'able', + 'abogado', + 'abudhabi', + 'ac', + 'academy', + 'accenture', + 'accountant', + 'accountants', + 'aco', + 'actor', + 'ad', + 'ads', + 'adult', + 'ae', + 'aeg', + 'aero', + 'aetna', + 'af', + 'afl', + 'africa', + 'ag', + 'agakhan', + 'agency', + 'ai', + 'aig', + 'airbus', + 'airforce', + 'airtel', + 'akdn', + 'al', + 'alibaba', + 'alipay', + 'allfinanz', + 'allstate', + 'ally', + 'alsace', + 'alstom', + 'am', + 'amazon', + 'americanexpress', + 'americanfamily', + 'amex', + 'amfam', + 'amica', + 'amsterdam', + 'analytics', + 'android', + 'anquan', + 'anz', + 'ao', + 'aol', + 'apartments', + 'app', + 'apple', + 'aq', + 'aquarelle', + 'ar', + 'arab', + 'aramco', + 'archi', + 'army', + 'arpa', + 'art', + 'arte', + 'as', + 'asda', + 'asia', + 'associates', + 'at', + 'athleta', + 'attorney', + 'au', + 'auction', + 'audi', + 'audible', + 'audio', + 'auspost', + 'author', + 'auto', + 'autos', + 'aw', + 'aws', + 'ax', + 'axa', + 'az', + 'azure', + 'ba', + 'baby', + 'baidu', + 'banamex', + 'band', + 'bank', + 'bar', + 'barcelona', + 'barclaycard', + 'barclays', + 'barefoot', + 'bargains', + 'baseball', + 'basketball', + 'bauhaus', + 'bayern', + 'bb', + 'bbc', + 'bbt', + 'bbva', + 'bcg', + 'bcn', + 'bd', + 'be', + 'beats', + 'beauty', + 'beer', + 'berlin', + 'best', + 'bestbuy', + 'bet', + 'bf', + 'bg', + 'bh', + 'bharti', + 'bi', + 'bible', + 'bid', + 'bike', + 'bing', + 'bingo', + 'bio', + 'biz', + 'bj', + 'black', + 'blackfriday', + 'blockbuster', + 'blog', + 'bloomberg', + 'blue', + 'bm', + 'bms', + 'bmw', + 'bn', + 'bnpparibas', + 'bo', + 'boats', + 'boehringer', + 'bofa', + 'bom', + 'bond', + 'boo', + 'book', + 'booking', + 'bosch', + 'bostik', + 'boston', + 'bot', + 'boutique', + 'box', + 'br', + 'bradesco', + 'bridgestone', + 'broadway', + 'broker', + 'brother', + 'brussels', + 'bs', + 'bt', + 'build', + 'builders', + 'business', + 'buy', + 'buzz', + 'bv', + 'bw', + 'by', + 'bz', + 'bzh', + 'ca', + 'cab', + 'cafe', + 'cal', + 'call', + 'calvinklein', + 'cam', + 'camera', + 'camp', + 'canon', + 'capetown', + 'capital', + 'capitalone', + 'car', + 'caravan', + 'cards', + 'care', + 'career', + 'careers', + 'cars', + 'casa', + 'case', + 'cash', + 'casino', + 'cat', + 'catering', + 'catholic', + 'cba', + 'cbn', + 'cbre', + 'cc', + 'cd', + 'center', + 'ceo', + 'cern', + 'cf', + 'cfa', + 'cfd', + 'cg', + 'ch', + 'chanel', + 'channel', + 'charity', + 'chase', + 'chat', + 'cheap', + 'chintai', + 'christmas', + 'chrome', + 'church', + 'ci', + 'cipriani', + 'circle', + 'cisco', + 'citadel', + 'citi', + 'citic', + 'city', + 'ck', + 'cl', + 'claims', + 'cleaning', + 'click', + 'clinic', + 'clinique', + 'clothing', + 'cloud', + 'club', + 'clubmed', + 'cm', + 'cn', + 'co', + 'coach', + 'codes', + 'coffee', + 'college', + 'cologne', + 'com', + 'commbank', + 'community', + 'company', + 'compare', + 'computer', + 'comsec', + 'condos', + 'construction', + 'consulting', + 'contact', + 'contractors', + 'cooking', + 'cool', + 'coop', + 'corsica', + 'country', + 'coupon', + 'coupons', + 'courses', + 'cpa', + 'cr', + 'credit', + 'creditcard', + 'creditunion', + 'cricket', + 'crown', + 'crs', + 'cruise', + 'cruises', + 'cu', + 'cuisinella', + 'cv', + 'cw', + 'cx', + 'cy', + 'cymru', + 'cyou', + 'cz', + 'dad', + 'dance', + 'data', + 'date', + 'dating', + 'datsun', + 'day', + 'dclk', + 'dds', + 'de', + 'deal', + 'dealer', + 'deals', + 'degree', + 'delivery', + 'dell', + 'deloitte', + 'delta', + 'democrat', + 'dental', + 'dentist', + 'desi', + 'design', + 'dev', + 'dhl', + 'diamonds', + 'diet', + 'digital', + 'direct', + 'directory', + 'discount', + 'discover', + 'dish', + 'diy', + 'dj', + 'dk', + 'dm', + 'dnp', + 'do', + 'docs', + 'doctor', + 'dog', + 'domains', + 'dot', + 'download', + 'drive', + 'dtv', + 'dubai', + 'dunlop', + 'dupont', + 'durban', + 'dvag', + 'dvr', + 'dz', + 'earth', + 'eat', + 'ec', + 'eco', + 'edeka', + 'edu', + 'education', + 'ee', + 'eg', + 'email', + 'emerck', + 'energy', + 'engineer', + 'engineering', + 'enterprises', + 'epson', + 'equipment', + 'er', + 'ericsson', + 'erni', + 'es', + 'esq', + 'estate', + 'et', + 'eu', + 'eurovision', + 'eus', + 'events', + 'exchange', + 'expert', + 'exposed', + 'express', + 'extraspace', + 'fage', + 'fail', + 'fairwinds', + 'faith', + 'family', + 'fan', + 'fans', + 'farm', + 'farmers', + 'fashion', + 'fast', + 'fedex', + 'feedback', + 'ferrari', + 'ferrero', + 'fi', + 'fidelity', + 'fido', + 'film', + 'final', + 'finance', + 'financial', + 'fire', + 'firestone', + 'firmdale', + 'fish', + 'fishing', + 'fit', + 'fitness', + 'fj', + 'fk', + 'flickr', + 'flights', + 'flir', + 'florist', + 'flowers', + 'fly', + 'fm', + 'fo', + 'foo', + 'food', + 'football', + 'ford', + 'forex', + 'forsale', + 'forum', + 'foundation', + 'fox', + 'fr', + 'free', + 'fresenius', + 'frl', + 'frogans', + 'frontier', + 'ftr', + 'fujitsu', + 'fun', + 'fund', + 'furniture', + 'futbol', + 'fyi', + 'ga', + 'gal', + 'gallery', + 'gallo', + 'gallup', + 'game', + 'games', + 'gap', + 'garden', + 'gay', + 'gb', + 'gbiz', + 'gd', + 'gdn', + 'ge', + 'gea', + 'gent', + 'genting', + 'george', + 'gf', + 'gg', + 'ggee', + 'gh', + 'gi', + 'gift', + 'gifts', + 'gives', + 'giving', + 'gl', + 'glass', + 'gle', + 'global', + 'globo', + 'gm', + 'gmail', + 'gmbh', + 'gmo', + 'gmx', + 'gn', + 'godaddy', + 'gold', + 'goldpoint', + 'golf', + 'goo', + 'goodyear', + 'goog', + 'google', + 'gop', + 'got', + 'gov', + 'gp', + 'gq', + 'gr', + 'grainger', + 'graphics', + 'gratis', + 'green', + 'gripe', + 'grocery', + 'group', + 'gs', + 'gt', + 'gu', + 'gucci', + 'guge', + 'guide', + 'guitars', + 'guru', + 'gw', + 'gy', + 'hair', + 'hamburg', + 'hangout', + 'haus', + 'hbo', + 'hdfc', + 'hdfcbank', + 'health', + 'healthcare', + 'help', + 'helsinki', + 'here', + 'hermes', + 'hiphop', + 'hisamitsu', + 'hitachi', + 'hiv', + 'hk', + 'hkt', + 'hm', + 'hn', + 'hockey', + 'holdings', + 'holiday', + 'homedepot', + 'homegoods', + 'homes', + 'homesense', + 'honda', + 'horse', + 'hospital', + 'host', + 'hosting', + 'hot', + 'hotels', + 'hotmail', + 'house', + 'how', + 'hr', + 'hsbc', + 'ht', + 'hu', + 'hughes', + 'hyatt', + 'hyundai', + 'ibm', + 'icbc', + 'ice', + 'icu', + 'id', + 'ie', + 'ieee', + 'ifm', + 'ikano', + 'il', + 'im', + 'imamat', + 'imdb', + 'immo', + 'immobilien', + 'in', + 'inc', + 'industries', + 'infiniti', + 'info', + 'ing', + 'ink', + 'institute', + 'insurance', + 'insure', + 'int', + 'international', + 'intuit', + 'investments', + 'io', + 'ipiranga', + 'iq', + 'ir', + 'irish', + 'is', + 'ismaili', + 'ist', + 'istanbul', + 'it', + 'itau', + 'itv', + 'jaguar', + 'java', + 'jcb', + 'je', + 'jeep', + 'jetzt', + 'jewelry', + 'jio', + 'jll', + 'jm', + 'jmp', + 'jnj', + 'jo', + 'jobs', + 'joburg', + 'jot', + 'joy', + 'jp', + 'jpmorgan', + 'jprs', + 'juegos', + 'juniper', + 'kaufen', + 'kddi', + 'ke', + 'kerryhotels', + 'kerryproperties', + 'kfh', + 'kg', + 'kh', + 'ki', + 'kia', + 'kids', + 'kim', + 'kindle', + 'kitchen', + 'kiwi', + 'km', + 'kn', + 'koeln', + 'komatsu', + 'kosher', + 'kp', + 'kpmg', + 'kpn', + 'kr', + 'krd', + 'kred', + 'kuokgroup', + 'kw', + 'ky', + 'kyoto', + 'kz', + 'la', + 'lacaixa', + 'lamborghini', + 'lamer', + 'land', + 'landrover', + 'lanxess', + 'lasalle', + 'lat', + 'latino', + 'latrobe', + 'law', + 'lawyer', + 'lb', + 'lc', + 'lds', + 'lease', + 'leclerc', + 'lefrak', + 'legal', + 'lego', + 'lexus', + 'lgbt', + 'li', + 'lidl', + 'life', + 'lifeinsurance', + 'lifestyle', + 'lighting', + 'like', + 'lilly', + 'limited', + 'limo', + 'lincoln', + 'link', + 'live', + 'living', + 'lk', + 'llc', + 'llp', + 'loan', + 'loans', + 'locker', + 'locus', + 'lol', + 'london', + 'lotte', + 'lotto', + 'love', + 'lpl', + 'lplfinancial', + 'lr', + 'ls', + 'lt', + 'ltd', + 'ltda', + 'lu', + 'lundbeck', + 'luxe', + 'luxury', + 'lv', + 'ly', + 'ma', + 'madrid', + 'maif', + 'maison', + 'makeup', + 'man', + 'management', + 'mango', + 'map', + 'market', + 'marketing', + 'markets', + 'marriott', + 'marshalls', + 'mattel', + 'mba', + 'mc', + 'mckinsey', + 'md', + 'me', + 'med', + 'media', + 'meet', + 'melbourne', + 'meme', + 'memorial', + 'men', + 'menu', + 'merckmsd', + 'mg', + 'mh', + 'miami', + 'microsoft', + 'mil', + 'mini', + 'mint', + 'mit', + 'mitsubishi', + 'mk', + 'ml', + 'mlb', + 'mls', + 'mm', + 'mma', + 'mn', + 'mo', + 'mobi', + 'mobile', + 'moda', + 'moe', + 'moi', + 'mom', + 'monash', + 'money', + 'monster', + 'mormon', + 'mortgage', + 'moscow', + 'moto', + 'motorcycles', + 'mov', + 'movie', + 'mp', + 'mq', + 'mr', + 'ms', + 'msd', + 'mt', + 'mtn', + 'mtr', + 'mu', + 'museum', + 'music', + 'mv', + 'mw', + 'mx', + 'my', + 'mz', + 'na', + 'nab', + 'nagoya', + 'name', + 'navy', + 'nba', + 'nc', + 'ne', + 'nec', + 'net', + 'netbank', + 'netflix', + 'network', + 'neustar', + 'new', + 'news', + 'next', + 'nextdirect', + 'nexus', + 'nf', + 'nfl', + 'ng', + 'ngo', + 'nhk', + 'ni', + 'nico', + 'nike', + 'nikon', + 'ninja', + 'nissan', + 'nissay', + 'nl', + 'no', + 'nokia', + 'norton', + 'now', + 'nowruz', + 'nowtv', + 'np', + 'nr', + 'nra', + 'nrw', + 'ntt', + 'nu', + 'nyc', + 'nz', + 'obi', + 'observer', + 'office', + 'okinawa', + 'olayan', + 'olayangroup', + 'ollo', + 'om', + 'omega', + 'one', + 'ong', + 'onl', + 'online', + 'ooo', + 'open', + 'oracle', + 'orange', + 'org', + 'organic', + 'origins', + 'osaka', + 'otsuka', + 'ott', + 'ovh', + 'pa', + 'page', + 'panasonic', + 'paris', + 'pars', + 'partners', + 'parts', + 'party', + 'pay', + 'pccw', + 'pe', + 'pet', + 'pf', + 'pfizer', + 'pg', + 'ph', + 'pharmacy', + 'phd', + 'philips', + 'phone', + 'photo', + 'photography', + 'photos', + 'physio', + 'pics', + 'pictet', + 'pictures', + 'pid', + 'pin', + 'ping', + 'pink', + 'pioneer', + 'pizza', + 'pk', + 'pl', + 'place', + 'play', + 'playstation', + 'plumbing', + 'plus', + 'pm', + 'pn', + 'pnc', + 'pohl', + 'poker', + 'politie', + 'porn', + 'post', + 'pr', + 'pramerica', + 'praxi', + 'press', + 'prime', + 'pro', + 'prod', + 'productions', + 'prof', + 'progressive', + 'promo', + 'properties', + 'property', + 'protection', + 'pru', + 'prudential', + 'ps', + 'pt', + 'pub', + 'pw', + 'pwc', + 'py', + 'qa', + 'qpon', + 'quebec', + 'quest', + 'racing', + 'radio', + 're', + 'read', + 'realestate', + 'realtor', + 'realty', + 'recipes', + 'red', + 'redstone', + 'redumbrella', + 'rehab', + 'reise', + 'reisen', + 'reit', + 'reliance', + 'ren', + 'rent', + 'rentals', + 'repair', + 'report', + 'republican', + 'rest', + 'restaurant', + 'review', + 'reviews', + 'rexroth', + 'rich', + 'richardli', + 'ricoh', + 'ril', + 'rio', + 'rip', + 'ro', + 'rocks', + 'rodeo', + 'rogers', + 'room', + 'rs', + 'rsvp', + 'ru', + 'rugby', + 'ruhr', + 'run', + 'rw', + 'rwe', + 'ryukyu', + 'sa', + 'saarland', + 'safe', + 'safety', + 'sakura', + 'sale', + 'salon', + 'samsclub', + 'samsung', + 'sandvik', + 'sandvikcoromant', + 'sanofi', + 'sap', + 'sarl', + 'sas', + 'save', + 'saxo', + 'sb', + 'sbi', + 'sbs', + 'sc', + 'scb', + 'schaeffler', + 'schmidt', + 'scholarships', + 'school', + 'schule', + 'schwarz', + 'science', + 'scot', + 'sd', + 'se', + 'search', + 'seat', + 'secure', + 'security', + 'seek', + 'select', + 'sener', + 'services', + 'seven', + 'sew', + 'sex', + 'sexy', + 'sfr', + 'sg', + 'sh', + 'shangrila', + 'sharp', + 'shell', + 'shia', + 'shiksha', + 'shoes', + 'shop', + 'shopping', + 'shouji', + 'show', + 'si', + 'silk', + 'sina', + 'singles', + 'site', + 'sj', + 'sk', + 'ski', + 'skin', + 'sky', + 'skype', + 'sl', + 'sling', + 'sm', + 'smart', + 'smile', + 'sn', + 'sncf', + 'so', + 'soccer', + 'social', + 'softbank', + 'software', + 'sohu', + 'solar', + 'solutions', + 'song', + 'sony', + 'soy', + 'spa', + 'space', + 'sport', + 'spot', + 'sr', + 'srl', + 'ss', + 'st', + 'stada', + 'staples', + 'star', + 'statebank', + 'statefarm', + 'stc', + 'stcgroup', + 'stockholm', + 'storage', + 'store', + 'stream', + 'studio', + 'study', + 'style', + 'su', + 'sucks', + 'supplies', + 'supply', + 'support', + 'surf', + 'surgery', + 'suzuki', + 'sv', + 'swatch', + 'swiss', + 'sx', + 'sy', + 'sydney', + 'systems', + 'sz', + 'tab', + 'taipei', + 'talk', + 'taobao', + 'target', + 'tatamotors', + 'tatar', + 'tattoo', + 'tax', + 'taxi', + 'tc', + 'tci', + 'td', + 'tdk', + 'team', + 'tech', + 'technology', + 'tel', + 'temasek', + 'tennis', + 'teva', + 'tf', + 'tg', + 'th', + 'thd', + 'theater', + 'theatre', + 'tiaa', + 'tickets', + 'tienda', + 'tips', + 'tires', + 'tirol', + 'tj', + 'tjmaxx', + 'tjx', + 'tk', + 'tkmaxx', + 'tl', + 'tm', + 'tmall', + 'tn', + 'to', + 'today', + 'tokyo', + 'tools', + 'top', + 'toray', + 'toshiba', + 'total', + 'tours', + 'town', + 'toyota', + 'toys', + 'tr', + 'trade', + 'trading', + 'training', + 'travel', + 'travelers', + 'travelersinsurance', + 'trust', + 'trv', + 'tt', + 'tube', + 'tui', + 'tunes', + 'tushu', + 'tv', + 'tvs', + 'tw', + 'tz', + 'ua', + 'ubank', + 'ubs', + 'ug', + 'uk', + 'unicom', + 'university', + 'uno', + 'uol', + 'ups', + 'us', + 'uy', + 'uz', + 'va', + 'vacations', + 'vana', + 'vanguard', + 'vc', + 've', + 'vegas', + 'ventures', + 'verisign', + 'versicherung', + 'vet', + 'vg', + 'vi', + 'viajes', + 'video', + 'vig', + 'viking', + 'villas', + 'vin', + 'vip', + 'virgin', + 'visa', + 'vision', + 'viva', + 'vivo', + 'vlaanderen', + 'vn', + 'vodka', + 'volvo', + 'vote', + 'voting', + 'voto', + 'voyage', + 'vu', + 'wales', + 'walmart', + 'walter', + 'wang', + 'wanggou', + 'watch', + 'watches', + 'weather', + 'weatherchannel', + 'webcam', + 'weber', + 'website', + 'wed', + 'wedding', + 'weibo', + 'weir', + 'wf', + 'whoswho', + 'wien', + 'wiki', + 'williamhill', + 'win', + 'windows', + 'wine', + 'winners', + 'wme', + 'wolterskluwer', + 'woodside', + 'work', + 'works', + 'world', + 'wow', + 'ws', + 'wtc', + 'wtf', + 'xbox', + 'xerox', + 'xihuan', + 'xin', + 'xn--11b4c3d', + 'xn--1ck2e1b', + 'xn--1qqw23a', + 'xn--2scrj9c', + 'xn--30rr7y', + 'xn--3bst00m', + 'xn--3ds443g', + 'xn--3e0b707e', + 'xn--3hcrj9c', + 'xn--3pxu8k', + 'xn--42c2d9a', + 'xn--45br5cyl', + 'xn--45brj9c', + 'xn--45q11c', + 'xn--4dbrk0ce', + 'xn--4gbrim', + 'xn--54b7fta0cc', + 'xn--55qw42g', + 'xn--55qx5d', + 'xn--5su34j936bgsg', + 'xn--5tzm5g', + 'xn--6frz82g', + 'xn--6qq986b3xl', + 'xn--80adxhks', + 'xn--80ao21a', + 'xn--80aqecdr1a', + 'xn--80asehdb', + 'xn--80aswg', + 'xn--8y0a063a', + 'xn--90a3ac', + 'xn--90ae', + 'xn--90ais', + 'xn--9dbq2a', + 'xn--9et52u', + 'xn--9krt00a', + 'xn--b4w605ferd', + 'xn--bck1b9a5dre4c', + 'xn--c1avg', + 'xn--c2br7g', + 'xn--cck2b3b', + 'xn--cckwcxetd', + 'xn--cg4bki', + 'xn--clchc0ea0b2g2a9gcd', + 'xn--czr694b', + 'xn--czrs0t', + 'xn--czru2d', + 'xn--d1acj3b', + 'xn--d1alf', + 'xn--e1a4c', + 'xn--eckvdtc9d', + 'xn--efvy88h', + 'xn--fct429k', + 'xn--fhbei', + 'xn--fiq228c5hs', + 'xn--fiq64b', + 'xn--fiqs8s', + 'xn--fiqz9s', + 'xn--fjq720a', + 'xn--flw351e', + 'xn--fpcrj9c3d', + 'xn--fzc2c9e2c', + 'xn--fzys8d69uvgm', + 'xn--g2xx48c', + 'xn--gckr3f0f', + 'xn--gecrj9c', + 'xn--gk3at1e', + 'xn--h2breg3eve', + 'xn--h2brj9c', + 'xn--h2brj9c8c', + 'xn--hxt814e', + 'xn--i1b6b1a6a2e', + 'xn--imr513n', + 'xn--io0a7i', + 'xn--j1aef', + 'xn--j1amh', + 'xn--j6w193g', + 'xn--jlq480n2rg', + 'xn--jvr189m', + 'xn--kcrx77d1x4a', + 'xn--kprw13d', + 'xn--kpry57d', + 'xn--kput3i', + 'xn--l1acc', + 'xn--lgbbat1ad8j', + 'xn--mgb9awbf', + 'xn--mgba3a3ejt', + 'xn--mgba3a4f16a', + 'xn--mgba7c0bbn0a', + 'xn--mgbaam7a8h', + 'xn--mgbab2bd', + 'xn--mgbah1a3hjkrd', + 'xn--mgbai9azgqp6j', + 'xn--mgbayh7gpa', + 'xn--mgbbh1a', + 'xn--mgbbh1a71e', + 'xn--mgbc0a9azcg', + 'xn--mgbca7dzdo', + 'xn--mgbcpq6gpa1a', + 'xn--mgberp4a5d4ar', + 'xn--mgbgu82a', + 'xn--mgbi4ecexp', + 'xn--mgbpl2fh', + 'xn--mgbt3dhd', + 'xn--mgbtx2b', + 'xn--mgbx4cd0ab', + 'xn--mix891f', + 'xn--mk1bu44c', + 'xn--mxtq1m', + 'xn--ngbc5azd', + 'xn--ngbe9e0a', + 'xn--ngbrx', + 'xn--node', + 'xn--nqv7f', + 'xn--nqv7fs00ema', + 'xn--nyqy26a', + 'xn--o3cw4h', + 'xn--ogbpf8fl', + 'xn--otu796d', + 'xn--p1acf', + 'xn--p1ai', + 'xn--pgbs0dh', + 'xn--pssy2u', + 'xn--q7ce6a', + 'xn--q9jyb4c', + 'xn--qcka1pmc', + 'xn--qxa6a', + 'xn--qxam', + 'xn--rhqv96g', + 'xn--rovu88b', + 'xn--rvc1e0am3e', + 'xn--s9brj9c', + 'xn--ses554g', + 'xn--t60b56a', + 'xn--tckwe', + 'xn--tiq49xqyj', + 'xn--unup4y', + 'xn--vermgensberater-ctb', + 'xn--vermgensberatung-pwb', + 'xn--vhquv', + 'xn--vuq861b', + 'xn--w4r85el8fhu5dnra', + 'xn--w4rs40l', + 'xn--wgbh1c', + 'xn--wgbl6a', + 'xn--xhq521b', + 'xn--xkc2al3hye2a', + 'xn--xkc2dl3a5ee0h', + 'xn--y9a3aq', + 'xn--yfro4i67o', + 'xn--ygbi2ammx', + 'xn--zfr164b', + 'xxx', + 'xyz', + 'yachts', + 'yahoo', + 'yamaxun', + 'yandex', + 'ye', + 'yodobashi', + 'yoga', + 'yokohama', + 'you', + 'youtube', + 'yt', + 'yun', + 'za', + 'zappos', + 'zara', + 'zero', + 'zip', + 'zm', + 'zone', + 'zuerich', + 'zw' +] diff --git a/src/portal/requirements.txt b/src/portal/requirements.txt index c2e2915..0d31ac1 100644 --- a/src/portal/requirements.txt +++ b/src/portal/requirements.txt @@ -1,3 +1,2 @@ httpx[http2,brotli,zstd] blake3 -pillow diff --git a/src/portal/scripts/create_vendor.sh b/src/portal/scripts/create_vendor.sh index 87b0b5d..3dd8bc3 100755 --- a/src/portal/scripts/create_vendor.sh +++ b/src/portal/scripts/create_vendor.sh @@ -12,6 +12,10 @@ cd yt-dlp/ pip3 install . --upgrade --target=../vendor cd ../ +cd snscrape/ +pip3 install . --upgrade --target=../vendor +cd ../ + cd twitter-api-client/ pip3 install . --upgrade --target=../vendor cd ../ |