Support proxying results through Whoogle (aka "anonymous view") (#682)
* Expand `/window` endpoint to behave like a proxy The `/window` endpoint was previously used as a type of proxy, but only for removing Javascript from the result page. This expands the existing functionality to allow users to proxy search result pages (with or without Javascript) through their Whoogle instance. * Implement filtering of remote content from css * Condense NoJS feature into Anonymous View Enabling NoJS now removes Javascript from the Anonymous View, rather than creating a separate option. * Exclude 'data:' urls from filter, add translations The 'data:' url must be allowed in results to view certain elements on the page, such as stars for review based results. Add translations for the remaining languages. * Add cssutils to requirementsmain
parent
7d01620316
commit
9317d9217f
|
@ -2,11 +2,12 @@ from app.models.config import Config
|
|||
from app.models.endpoint import Endpoint
|
||||
from app.models.g_classes import GClasses
|
||||
from app.request import VALID_PARAMS, MAPS_URL
|
||||
from app.utils.misc import read_config_bool
|
||||
from app.utils.misc import get_abs_url, read_config_bool
|
||||
from app.utils.results import *
|
||||
from bs4 import BeautifulSoup
|
||||
from bs4.element import ResultSet, Tag
|
||||
from cryptography.fernet import Fernet
|
||||
import cssutils
|
||||
from flask import render_template
|
||||
import re
|
||||
import urllib.parse as urlparse
|
||||
|
@ -53,17 +54,50 @@ def clean_query(query: str) -> str:
|
|||
return query[:query.find('-site:')] if '-site:' in query else query
|
||||
|
||||
|
||||
def clean_css(css: str, page_url: str) -> str:
|
||||
"""Removes all remote URLs from a CSS string.
|
||||
|
||||
Args:
|
||||
css: The CSS string
|
||||
|
||||
Returns:
|
||||
str: The filtered CSS, with URLs proxied through Whoogle
|
||||
"""
|
||||
sheet = cssutils.parseString(css)
|
||||
urls = cssutils.getUrls(sheet)
|
||||
|
||||
for url in urls:
|
||||
abs_url = get_abs_url(url, page_url)
|
||||
if abs_url.startswith('data:'):
|
||||
continue
|
||||
css = css.replace(
|
||||
url,
|
||||
f'/element?type=image/png&url={abs_url}'
|
||||
)
|
||||
|
||||
return css
|
||||
|
||||
|
||||
class Filter:
|
||||
# Limit used for determining if a result is a "regular" result or a list
|
||||
# type result (such as "people also asked", "related searches", etc)
|
||||
RESULT_CHILD_LIMIT = 7
|
||||
|
||||
def __init__(self, user_key: str, config: Config, mobile=False) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
user_key: str,
|
||||
config: Config,
|
||||
root_url='',
|
||||
page_url='',
|
||||
mobile=False) -> None:
|
||||
self.config = config
|
||||
self.mobile = mobile
|
||||
self.user_key = user_key
|
||||
self.root_url = root_url
|
||||
self.page_url = page_url
|
||||
self.main_divs = ResultSet('')
|
||||
self._elements = 0
|
||||
self._av = set()
|
||||
|
||||
def __getitem__(self, name):
|
||||
return getattr(self, name)
|
||||
|
@ -89,6 +123,7 @@ class Filter:
|
|||
self.remove_block_titles()
|
||||
self.remove_block_url()
|
||||
self.collapse_sections()
|
||||
self.update_css(soup)
|
||||
self.update_styling(soup)
|
||||
self.remove_block_tabs(soup)
|
||||
|
||||
|
@ -264,7 +299,7 @@ class Filter:
|
|||
# enabled
|
||||
parent.decompose()
|
||||
|
||||
def update_element_src(self, element: Tag, mime: str) -> None:
|
||||
def update_element_src(self, element: Tag, mime: str, attr='src') -> None:
|
||||
"""Encrypts the original src of an element and rewrites the element src
|
||||
to use the "/element?src=" pass-through.
|
||||
|
||||
|
@ -272,10 +307,12 @@ class Filter:
|
|||
None (The soup element is modified directly)
|
||||
|
||||
"""
|
||||
src = element['src']
|
||||
src = element[attr].split(' ')[0]
|
||||
|
||||
if src.startswith('//'):
|
||||
src = 'https:' + src
|
||||
elif src.startswith('data:'):
|
||||
return
|
||||
|
||||
if src.startswith(LOGO_URL):
|
||||
# Re-brand with Whoogle logo
|
||||
|
@ -287,9 +324,29 @@ class Filter:
|
|||
element['src'] = BLANK_B64
|
||||
return
|
||||
|
||||
element['src'] = f'{Endpoint.element}?url=' + self.encrypt_path(
|
||||
src,
|
||||
is_element=True) + '&type=' + urlparse.quote(mime)
|
||||
element[attr] = f'{self.root_url}/{Endpoint.element}?url=' + (
|
||||
self.encrypt_path(
|
||||
src,
|
||||
is_element=True
|
||||
) + '&type=' + urlparse.quote(mime)
|
||||
)
|
||||
|
||||
def update_css(self, soup) -> None:
|
||||
"""Updates URLs used in inline styles to be proxied by Whoogle
|
||||
using the /element endpoint.
|
||||
|
||||
Returns:
|
||||
None (The soup element is modified directly)
|
||||
|
||||
"""
|
||||
# Filter all <style> tags
|
||||
for style in soup.find_all('style'):
|
||||
style.string = clean_css(style.string, self.page_url)
|
||||
|
||||
# TODO: Convert remote stylesheets to style tags and proxy all
|
||||
# remote requests
|
||||
# for link in soup.find_all('link', attrs={'rel': 'stylesheet'}):
|
||||
# print(link)
|
||||
|
||||
def update_styling(self, soup) -> None:
|
||||
# Remove unnecessary button(s)
|
||||
|
@ -384,9 +441,12 @@ class Filter:
|
|||
# Strip unneeded arguments
|
||||
link['href'] = filter_link_args(q)
|
||||
|
||||
# Add no-js option
|
||||
if self.config.nojs:
|
||||
append_nojs(link)
|
||||
# Add alternate viewing options for results,
|
||||
# if the result doesn't already have an AV link
|
||||
netloc = urlparse.urlparse(link['href']).netloc
|
||||
if self.config.anon_view and netloc not in self._av:
|
||||
self._av.add(netloc)
|
||||
append_anon_view(link, self.config)
|
||||
|
||||
if self.config.new_tab:
|
||||
link['target'] = '_blank'
|
||||
|
|
|
@ -28,6 +28,7 @@ class Config:
|
|||
self.new_tab = read_config_bool('WHOOGLE_CONFIG_NEW_TAB')
|
||||
self.view_image = read_config_bool('WHOOGLE_CONFIG_VIEW_IMAGE')
|
||||
self.get_only = read_config_bool('WHOOGLE_CONFIG_GET_ONLY')
|
||||
self.anon_view = read_config_bool('WHOOGLE_CONFIG_ANON_VIEW')
|
||||
self.accept_language = False
|
||||
|
||||
self.safe_keys = [
|
||||
|
@ -39,7 +40,9 @@ class Config:
|
|||
'new_tab',
|
||||
'view_image',
|
||||
'block',
|
||||
'safe'
|
||||
'safe',
|
||||
'nojs',
|
||||
'anon_view'
|
||||
]
|
||||
|
||||
# Skip setting custom config if there isn't one
|
||||
|
|
|
@ -16,6 +16,7 @@ from app.models.config import Config
|
|||
from app.models.endpoint import Endpoint
|
||||
from app.request import Request, TorError
|
||||
from app.utils.bangs import resolve_bang
|
||||
from app.filter import Filter
|
||||
from app.utils.misc import read_config_bool, get_client_ip, get_request_url, \
|
||||
check_for_update
|
||||
from app.utils.results import add_ip_card, bold_search_terms,\
|
||||
|
@ -457,8 +458,11 @@ def imgres():
|
|||
@session_required
|
||||
@auth_required
|
||||
def element():
|
||||
cipher_suite = Fernet(g.session_key)
|
||||
src_url = cipher_suite.decrypt(request.args.get('url').encode()).decode()
|
||||
element_url = src_url = request.args.get('url')
|
||||
if element_url.startswith('gAAAAA'):
|
||||
cipher_suite = Fernet(g.session_key)
|
||||
src_url = cipher_suite.decrypt(element_url.encode()).decode()
|
||||
|
||||
src_type = request.args.get('type')
|
||||
|
||||
try:
|
||||
|
@ -477,18 +481,62 @@ def element():
|
|||
|
||||
|
||||
@app.route(f'/{Endpoint.window}')
|
||||
@session_required
|
||||
@auth_required
|
||||
def window():
|
||||
get_body = g.user_request.send(base_url=request.args.get('location')).text
|
||||
get_body = get_body.replace('src="/',
|
||||
'src="' + request.args.get('location') + '"')
|
||||
get_body = get_body.replace('href="/',
|
||||
'href="' + request.args.get('location') + '"')
|
||||
target_url = request.args.get('location')
|
||||
if target_url.startswith('gAAAAA'):
|
||||
cipher_suite = Fernet(g.session_key)
|
||||
target_url = cipher_suite.decrypt(target_url.encode()).decode()
|
||||
|
||||
content_filter = Filter(
|
||||
g.session_key,
|
||||
root_url=request.url_root,
|
||||
config=g.user_config)
|
||||
target = urlparse.urlparse(target_url)
|
||||
host_url = f'{target.scheme}://{target.netloc}'
|
||||
|
||||
get_body = g.user_request.send(base_url=target_url).text
|
||||
|
||||
results = bsoup(get_body, 'html.parser')
|
||||
src_attrs = ['src', 'href', 'srcset', 'data-srcset', 'data-src']
|
||||
|
||||
for script in results('script'):
|
||||
script.decompose()
|
||||
# Parse HTML response and replace relative links w/ absolute
|
||||
for element in results.find_all():
|
||||
for attr in src_attrs:
|
||||
if not element.has_attr(attr) or not element[attr].startswith('/'):
|
||||
continue
|
||||
|
||||
element[attr] = host_url + element[attr]
|
||||
|
||||
# Replace or remove javascript sources
|
||||
for script in results.find_all('script', {'src': True}):
|
||||
if 'nojs' in request.args:
|
||||
script.decompose()
|
||||
else:
|
||||
content_filter.update_element_src(script, 'application/javascript')
|
||||
|
||||
# Replace all possible image attributes
|
||||
img_sources = ['src', 'data-src', 'data-srcset', 'srcset']
|
||||
for img in results.find_all('img'):
|
||||
_ = [
|
||||
content_filter.update_element_src(img, 'image/png', attr=_)
|
||||
for _ in img_sources if img.has_attr(_)
|
||||
]
|
||||
|
||||
# Replace all stylesheet sources
|
||||
for link in results.find_all('link', {'href': True}):
|
||||
content_filter.update_element_src(link, 'text/css', attr='href')
|
||||
|
||||
# Use anonymous view for all links on page
|
||||
for a in results.find_all('a', {'href': True}):
|
||||
a['href'] = '/window?location=' + a['href'] + (
|
||||
'&nojs=1' if 'nojs' in request.args else '')
|
||||
|
||||
# Remove all iframes -- these are commonly used inside of <noscript> tags
|
||||
# to enforce loading Google Analytics
|
||||
for iframe in results.find_all('iframe'):
|
||||
iframe.decompose()
|
||||
|
||||
return render_template(
|
||||
'display.html',
|
||||
|
|
|
@ -22,6 +22,11 @@ li {
|
|||
color: var(--whoogle-dark-text) !important;
|
||||
}
|
||||
|
||||
.anon-view {
|
||||
color: var(--whoogle-dark-text) !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: var(--whoogle-dark-page-bg) !important;
|
||||
color: var(--whoogle-dark-text) !important;
|
||||
|
|
|
@ -22,6 +22,11 @@ li {
|
|||
color: var(--whoogle-text) !important;
|
||||
}
|
||||
|
||||
.anon-view {
|
||||
color: var(--whoogle-text) !important;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: var(--whoogle-page-bg) !important;
|
||||
color: var(--whoogle-text) !important;
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
"config-block-url": "Block by URL",
|
||||
"config-block-url-help": "Use regex",
|
||||
"config-theme": "Theme",
|
||||
"config-nojs": "Show NoJS Links",
|
||||
"config-nojs": "Remove Javascript in Anonymous View",
|
||||
"config-anon-view": "Show Anonymous View Links",
|
||||
"config-dark": "Dark Mode",
|
||||
"config-safe": "Safe Search",
|
||||
"config-alts": "Replace Social Media Links",
|
||||
|
@ -41,7 +42,8 @@
|
|||
"maps": "Maps",
|
||||
"videos": "Videos",
|
||||
"news": "News",
|
||||
"books": "Books"
|
||||
"books": "Books",
|
||||
"anon-view": "Anonymous View"
|
||||
},
|
||||
"lang_nl": {
|
||||
"search": "Zoeken",
|
||||
|
@ -58,7 +60,8 @@
|
|||
"config-block-url": "Blokkeren op URL",
|
||||
"config-block-url-help": "Gebruik regex",
|
||||
"config-theme": "Thema",
|
||||
"config-nojs": "Laat NoJS links zien",
|
||||
"config-nojs": "Javascript verwijderen in anonieme weergave",
|
||||
"config-anon-view": "Toon anonieme links bekijken",
|
||||
"config-dark": "Donkere Modus",
|
||||
"config-safe": "Veilig zoeken",
|
||||
"config-alts": "Social Media Links Vervangen",
|
||||
|
@ -85,7 +88,8 @@
|
|||
"maps": "Maps",
|
||||
"videos": "Videos",
|
||||
"news": "Nieuws",
|
||||
"books": "Boeken"
|
||||
"books": "Boeken",
|
||||
"anon-view": "Anonieme Weergave"
|
||||
},
|
||||
"lang_de": {
|
||||
"search": "Suchen",
|
||||
|
@ -102,7 +106,8 @@
|
|||
"config-block-url": "Nach URL blockieren",
|
||||
"config-block-url-help": "Regex verwenden",
|
||||
"config-theme": "Thema",
|
||||
"config-nojs": "NoJS-Links anzeigen",
|
||||
"config-nojs": "Entfernen Sie Javascript in der anonymen Ansicht",
|
||||
"config-anon-view": "Anonyme Ansichtslinks anzeigen",
|
||||
"config-dark": "Dark Mode",
|
||||
"config-safe": "Sicheres Suchen",
|
||||
"config-alts": "Social-Media-Links ersetzen",
|
||||
|
@ -129,7 +134,8 @@
|
|||
"maps": "Maps",
|
||||
"videos": "Videos",
|
||||
"news": "Nieuws",
|
||||
"books": "Bücher"
|
||||
"books": "Bücher",
|
||||
"anon-view": "Anonyme Ansicht"
|
||||
},
|
||||
"lang_es": {
|
||||
"search": "Buscar",
|
||||
|
@ -146,7 +152,8 @@
|
|||
"config-block-url": "Bloquear por URL",
|
||||
"config-block-url-help": "Usar expresiones regulares",
|
||||
"config-theme": "Tema",
|
||||
"config-nojs": "Mostrar Enlaces NoJS",
|
||||
"config-nojs": "Eliminar Javascript en vista anónima",
|
||||
"config-anon-view": "Mostrar enlaces de vista anónima",
|
||||
"config-dark": "Modo Oscuro",
|
||||
"config-safe": "Búsqueda Segura",
|
||||
"config-alts": "Reemplazar Enlaces de Redes Sociales",
|
||||
|
@ -173,7 +180,8 @@
|
|||
"maps": "Maps",
|
||||
"videos": "Vídeos",
|
||||
"news": "Noticias",
|
||||
"books": "Libros"
|
||||
"books": "Libros",
|
||||
"anon-view": "Vista Anónima"
|
||||
},
|
||||
"lang_it": {
|
||||
"search": "Cerca",
|
||||
|
@ -190,7 +198,8 @@
|
|||
"config-block-url": "Blocca per url",
|
||||
"config-block-url-help": "Usa regex",
|
||||
"config-theme": "Tema",
|
||||
"config-nojs": "Mostra link NoJS",
|
||||
"config-nojs": "Rimuovere Javascript in visualizzazione anonima",
|
||||
"config-anon-view": "Mostra collegamenti di visualizzazione anonimi",
|
||||
"config-dark": "Modalità Notte",
|
||||
"config-safe": "Ricerca Sicura",
|
||||
"config-alts": "Sostituisci link dei social",
|
||||
|
@ -217,7 +226,8 @@
|
|||
"maps": "Maps",
|
||||
"videos": "Video",
|
||||
"news": "Notizie",
|
||||
"books": "Libri"
|
||||
"books": "Libri",
|
||||
"anon-view": "Vista Anonima"
|
||||
},
|
||||
"lang_pt": {
|
||||
"search": "Pesquisar",
|
||||
|
@ -234,7 +244,8 @@
|
|||
"config-block-url": "Bloquear por url",
|
||||
"config-block-url-help": "Use regex",
|
||||
"config-theme": "Tema",
|
||||
"config-nojs": "Mostrar Links NoJS",
|
||||
"config-nojs": "Remover Javascript na visualização anônima",
|
||||
"config-anon-view": "Mostrar links de visualização anônimos",
|
||||
"config-dark": "Modo Escuro",
|
||||
"config-safe": "Pesquisa Segura",
|
||||
"config-alts": "Substituir Links de Redes Sociais",
|
||||
|
@ -261,7 +272,8 @@
|
|||
"maps": "Maps",
|
||||
"videos": "Vídeos",
|
||||
"news": "Notícias",
|
||||
"books": "Livros"
|
||||
"books": "Livros",
|
||||
"anon-view": "Visualização Anônima"
|
||||
},
|
||||
"lang_ru": {
|
||||
"search": "Поиск",
|
||||
|
@ -278,7 +290,8 @@
|
|||
"config-block-url": "Блокировать по URL-адресу",
|
||||
"config-block-url-help": "Используйте regex",
|
||||
"config-theme": "Оформление",
|
||||
"config-nojs": "Показывать ссылки NoJS",
|
||||
"config-nojs": "Удалить Javascript в анонимном просмотре",
|
||||
"config-anon-view": "показать ссылки для анонимного просмотра",
|
||||
"config-dark": "Темный режим",
|
||||
"config-safe": "Безопасный поиск",
|
||||
"config-alts": "Заменить ссылки на социальные сети",
|
||||
|
@ -305,7 +318,8 @@
|
|||
"maps": "Карты",
|
||||
"videos": "Видео",
|
||||
"news": "Новости",
|
||||
"books": "Книги"
|
||||
"books": "Книги",
|
||||
"anon-view": "Анонимный просмотр"
|
||||
},
|
||||
"lang_zh-CN": {
|
||||
"search": "搜索",
|
||||
|
@ -322,7 +336,8 @@
|
|||
"config-block-url": "按网站链接屏蔽",
|
||||
"config-block-url-help": "使用正则表达式",
|
||||
"config-theme": "主题",
|
||||
"config-nojs": "显示 NoJS 链接",
|
||||
"config-nojs": "在匿名视图中删除 Javascript",
|
||||
"config-anon-view": "显示匿名查看链接",
|
||||
"config-dark": "深色模式",
|
||||
"config-safe": "安全搜索",
|
||||
"config-alts": "替换社交媒体链接",
|
||||
|
@ -349,7 +364,8 @@
|
|||
"maps": "地圖",
|
||||
"videos": "影片",
|
||||
"news": "新聞",
|
||||
"books": "書籍"
|
||||
"books": "書籍",
|
||||
"anon-view": "匿名视图"
|
||||
},
|
||||
"lang_si": {
|
||||
"search": "සොයන්න",
|
||||
|
@ -366,7 +382,8 @@
|
|||
"config-block-url": "ඒ.ස.නි. මඟින් අවහිර කරන්න",
|
||||
"config-block-url-help": "රෙජෙක්ස් භාවිතා කරන්න",
|
||||
"config-theme": "තේමාව",
|
||||
"config-nojs": "නෝජේඑස් සබැඳි පෙන්වන්න",
|
||||
"config-nojs": "Anonymous View හි Javascript ඉවත් කරන්න",
|
||||
"config-anon-view": "නිර්නාමික බලන්න සබැඳි පෙන්වන්න",
|
||||
"config-dark": "අඳුරු ආකාරය",
|
||||
"config-safe": "ආරක්ෂිත සෙවුම",
|
||||
"config-alts": "සමාජ මාධ්ය සබැඳි ප්රතිස්ථාපනය කරන්න",
|
||||
|
@ -393,7 +410,8 @@
|
|||
"maps": "සිතියම්",
|
||||
"videos": "වීඩියෝ",
|
||||
"news": "අනුරූප",
|
||||
"books": "පොත්"
|
||||
"books": "පොත්",
|
||||
"anon-view": "නිර්නාමික දසුන"
|
||||
},
|
||||
"lang_fr": {
|
||||
"search": "Chercher",
|
||||
|
@ -410,7 +428,8 @@
|
|||
"config-block-url": "Bloquer par URL",
|
||||
"config-block-url-help": "Utiliser l'expression régulière",
|
||||
"config-theme": "Theme",
|
||||
"config-nojs": "Montrer les liens NoJS",
|
||||
"config-nojs": "Supprimer Javascript dans la vue anonyme",
|
||||
"config-anon-view": "Afficher les liens de vue anonymes",
|
||||
"config-dark": "Mode Sombre",
|
||||
"config-safe": "Recherche sécurisée",
|
||||
"config-alts": "Remplacer les liens des réseaux sociaux",
|
||||
|
@ -437,7 +456,8 @@
|
|||
"maps": "Maps",
|
||||
"videos": "Vidéos",
|
||||
"news": "Actualités",
|
||||
"books": "Livres"
|
||||
"books": "Livres",
|
||||
"anon-view": "Vue anonyme"
|
||||
},
|
||||
"lang_fa": {
|
||||
"search": "جستجو",
|
||||
|
@ -454,7 +474,8 @@
|
|||
"config-block-url": "بلوک بر اساس URL",
|
||||
"config-block-url-help": "از عبارت منظم استفاده کنید",
|
||||
"config-theme": "پوسته",
|
||||
"config-nojs": "نمایش پیوندهای بدون جاوا اسکیریپت",
|
||||
"config-nojs": "جاوا اسکریپت را در نمای ناشناس حذف کنید",
|
||||
"config-anon-view": "نمایش پیوندهای مشاهده ناشناس",
|
||||
"config-dark": "حالت تاریک",
|
||||
"config-safe": "جستجوی امن",
|
||||
"config-alts": "جایگزینی پیوندهای شبکههای اجتماعی",
|
||||
|
@ -481,7 +502,8 @@
|
|||
"maps": "نقشهها",
|
||||
"videos": "ویدئوها",
|
||||
"news": "اخبار",
|
||||
"books": "کتابها"
|
||||
"books": "کتابها",
|
||||
"anon-view": "نمای ناشناس"
|
||||
},
|
||||
"lang_cs": {
|
||||
"search": "Hledat",
|
||||
|
@ -498,7 +520,8 @@
|
|||
"config-block-url": "Blokovat podle adresy URL",
|
||||
"config-block-url-help": "Použijte regulární výraz",
|
||||
"config-theme": "Motiv",
|
||||
"config-nojs": "Zobrazit NoJS odkazy",
|
||||
"config-nojs": "Odeberte Javascript v anonymním zobrazení",
|
||||
"config-anon-view": "Zobrazit odkazy anonymního zobrazení",
|
||||
"config-dark": "Tmavý motiv",
|
||||
"config-safe": "Bezpečné vyhledávání",
|
||||
"config-alts": "Nahradit odkazy na sociální média",
|
||||
|
@ -525,7 +548,8 @@
|
|||
"maps": "Mapy",
|
||||
"videos": "Videa",
|
||||
"news": "Zprávy",
|
||||
"books": "Knihy"
|
||||
"books": "Knihy",
|
||||
"anon-view": "Anonymní pohled"
|
||||
},
|
||||
"lang_zh-TW": {
|
||||
"search": "搜尋",
|
||||
|
@ -542,7 +566,8 @@
|
|||
"config-block-url": "按網址屏蔽",
|
||||
"config-block-url-help": "使用正則表達式",
|
||||
"config-theme": "主題",
|
||||
"config-nojs": "顯示 NoJS 連結",
|
||||
"config-nojs": "在匿名視圖中刪除 Javascript",
|
||||
"config-anon-view": "顯示匿名查看鏈接",
|
||||
"config-dark": "深色模式",
|
||||
"config-safe": "安全搜尋",
|
||||
"config-alts": "將社群網站連結換掉",
|
||||
|
@ -569,7 +594,8 @@
|
|||
"maps": "地圖",
|
||||
"videos": "影片",
|
||||
"news": "新聞",
|
||||
"books": "書籍"
|
||||
"books": "書籍",
|
||||
"anon-view": "匿名視圖"
|
||||
},
|
||||
"lang_bg": {
|
||||
"search": "Търсене",
|
||||
|
@ -586,7 +612,8 @@
|
|||
"config-block-url": "Блокиране по url",
|
||||
"config-block-url-help": "Използвайте регулярно изражение",
|
||||
"config-theme": "Стил",
|
||||
"config-nojs": "Показване на връзки без JS",
|
||||
"config-nojs": "Премахнете Javascript в анонимен изглед",
|
||||
"config-anon-view": "Показване на анонимни връзки за преглед",
|
||||
"config-dark": "Тъмен режим",
|
||||
"config-safe": "Безопасно търсене",
|
||||
"config-alts": "Заменете връзките към социалните медии",
|
||||
|
@ -613,7 +640,8 @@
|
|||
"maps": "Видеоклипове",
|
||||
"videos": "Новини",
|
||||
"news": "Карти",
|
||||
"books": "Книги"
|
||||
"books": "Книги",
|
||||
"anon-view": "Анонимен изглед"
|
||||
},
|
||||
"lang_hi": {
|
||||
"search": "खोज",
|
||||
|
@ -630,7 +658,8 @@
|
|||
"config-block-url": "url द्वारा अवरोधित करें",
|
||||
"config-block-url-help": "रेगेक्स का प्रयोग करें",
|
||||
"config-theme": "विषय",
|
||||
"config-nojs": "NoJS लिंक दिखाएं",
|
||||
"config-nojs": "अनाम दृश्य में जावास्क्रिप्ट निकालें",
|
||||
"config-anon-view": "बेनामी देखें लिंक दिखाएं",
|
||||
"config-dark": "डार्क मोड",
|
||||
"config-safe": "सुरक्षित खोज",
|
||||
"config-alts": "सोशल मीडिया लिंक बदलें",
|
||||
|
@ -657,7 +686,8 @@
|
|||
"maps": "वीडियो",
|
||||
"videos": "मैप",
|
||||
"news": "समाचार",
|
||||
"books": "किताबें"
|
||||
"books": "किताबें",
|
||||
"anon-view": "अनाम दृश्य"
|
||||
},
|
||||
"lang_ja": {
|
||||
"search": "検索",
|
||||
|
@ -674,7 +704,8 @@
|
|||
"config-block-url": "でブロック",
|
||||
"config-block-url-help": "正規表現を使用",
|
||||
"config-theme": "テーマ",
|
||||
"config-nojs": "非JSリンクを表示",
|
||||
"config-nojs": "匿名ビューでJavascriptを削除する",
|
||||
"config-anon-view": "匿名のビューリンクを表示する",
|
||||
"config-dark": "ダークモード",
|
||||
"config-safe": "セーフサーチ",
|
||||
"config-alts": "ソーシャルメディアのリンクを置き換え",
|
||||
|
@ -701,7 +732,8 @@
|
|||
"maps": "地図",
|
||||
"videos": "動画",
|
||||
"news": "ニュース",
|
||||
"books": "書籍"
|
||||
"books": "書籍",
|
||||
"anon-view": "匿名ビュー"
|
||||
},
|
||||
"lang_ko": {
|
||||
"search": "검색",
|
||||
|
@ -718,7 +750,8 @@
|
|||
"config-block-url": "URL로 차단",
|
||||
"config-block-url-help": "정규 표현식 사용",
|
||||
"config-theme": "테마",
|
||||
"config-nojs": "Show NoJS Links",
|
||||
"config-nojs": "익명 보기에서 Javascript 제거",
|
||||
"config-anon-view": "익명 보기 링크 표시",
|
||||
"config-dark": "다크 모드",
|
||||
"config-safe": "세이프서치",
|
||||
"config-alts": "소설 미디어 주소 수정",
|
||||
|
@ -745,6 +778,7 @@
|
|||
"maps": "지도",
|
||||
"videos": "동영상",
|
||||
"news": "뉴스",
|
||||
"books": "도서"
|
||||
"books": "도서",
|
||||
"anon-view": "익명 보기"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,6 +148,10 @@
|
|||
<input type="text" name="block_url" id="config-block"
|
||||
placeholder="{{ translation['config-block-url-help'] }}" value="{{ config.block_url }}">
|
||||
</div>
|
||||
<div class="config-div config-div-anon-view">
|
||||
<label for="config-anon-view">{{ translation['config-anon-view'] }}: </label>
|
||||
<input type="checkbox" name="anon_view" id="config-anon-view" {{ 'checked' if config.anon_view else '' }}>
|
||||
</div>
|
||||
<div class="config-div config-div-nojs">
|
||||
<label for="config-nojs">{{ translation['config-nojs'] }}: </label>
|
||||
<input type="checkbox" name="nojs" id="config-nojs" {{ 'checked' if config.nojs else '' }}>
|
||||
|
|
|
@ -3,6 +3,7 @@ from flask import Request
|
|||
import hashlib
|
||||
import os
|
||||
from requests import exceptions, get
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
def gen_file_hash(path: str, static_file: str) -> str:
|
||||
|
@ -47,3 +48,14 @@ def check_for_update(version_url: str, current: str) -> int:
|
|||
has_update = ''
|
||||
|
||||
return has_update
|
||||
|
||||
|
||||
def get_abs_url(url, page_url):
|
||||
# Creates a valid absolute URL using a partial or relative URL
|
||||
if url.startswith('//'):
|
||||
return f'https:{url}'
|
||||
elif url.startswith('/'):
|
||||
return f'{urlparse(page_url).netloc}{url}'
|
||||
elif url.startswith('./'):
|
||||
return f'{page_url}{url[2:]}'
|
||||
return url
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from app.models.config import Config
|
||||
from app.models.endpoint import Endpoint
|
||||
from bs4 import BeautifulSoup, NavigableString
|
||||
import copy
|
||||
from flask import current_app
|
||||
import html
|
||||
import os
|
||||
import urllib.parse as urlparse
|
||||
|
@ -183,11 +185,35 @@ def append_nojs(result: BeautifulSoup) -> None:
|
|||
|
||||
"""
|
||||
nojs_link = BeautifulSoup(features='html.parser').new_tag('a')
|
||||
nojs_link['href'] = f'/{Endpoint.window}?location=' + result['href']
|
||||
nojs_link['href'] = f'/{Endpoint.window}?nojs=1&location=' + result['href']
|
||||
nojs_link.string = ' NoJS Link'
|
||||
result.append(nojs_link)
|
||||
|
||||
|
||||
def append_anon_view(result: BeautifulSoup, config: Config) -> None:
|
||||
"""Appends an 'anonymous view' for a search result, where all site
|
||||
contents are viewed through Whoogle as a proxy.
|
||||
|
||||
Args:
|
||||
result: The search result to append an anon view link to
|
||||
nojs: Remove Javascript from Anonymous View
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
av_link = BeautifulSoup(features='html.parser').new_tag('a')
|
||||
nojs = 'nojs=1' if config.nojs else 'nojs=0'
|
||||
location = f'location={result["href"]}'
|
||||
av_link['href'] = f'/{Endpoint.window}?{nojs}&{location}'
|
||||
translation = current_app.config['TRANSLATIONS'][
|
||||
config.get_localization_lang()
|
||||
]
|
||||
av_link.string = f'{translation["anon-view"]}'
|
||||
av_link['class'] = 'anon-view'
|
||||
result.append(av_link)
|
||||
|
||||
|
||||
def add_ip_card(html_soup: BeautifulSoup, ip: str) -> BeautifulSoup:
|
||||
"""Adds the client's IP address to the search results
|
||||
if query contains keywords
|
||||
|
|
|
@ -56,6 +56,7 @@ class Search:
|
|||
"""
|
||||
def __init__(self, request, config, session_key, cookies_disabled=False):
|
||||
method = request.method
|
||||
self.request = request
|
||||
self.request_params = request.args if method == 'GET' else request.form
|
||||
self.user_agent = request.headers.get('User-Agent')
|
||||
self.feeling_lucky = False
|
||||
|
@ -115,6 +116,7 @@ class Search:
|
|||
mobile = 'Android' in self.user_agent or 'iPhone' in self.user_agent
|
||||
|
||||
content_filter = Filter(self.session_key,
|
||||
root_url=self.request.url_root,
|
||||
mobile=mobile,
|
||||
config=self.config)
|
||||
full_query = gen_query(self.query,
|
||||
|
|
|
@ -6,6 +6,7 @@ cffi==1.15.0
|
|||
chardet==3.0.4
|
||||
click==8.0.3
|
||||
cryptography==3.3.2
|
||||
cssutils==2.4.0
|
||||
defusedxml==0.7.1
|
||||
Flask==1.1.1
|
||||
Flask-Session==0.4.0
|
||||
|
|
Loading…
Reference in New Issue