Use consistent header for all result types (#535)

Introduces a header for switching between result types (i.e. "All", "News",
etc) that is consistent between the different result types. Previously, image
results had a tab header that was formatted in a drastically different manner,
which was jarring when switching from a different result page to the Images
page.

Created a G class enum to reference class names returned in search
results. As noted in the class doc, this should only be used/updated as
a last resort, as class names change frequently. For some instances,
such as replacing the tbm tab, it's a lot easier to just replace by
header name than attempting to replace it based on how the element is
structured.

Also updated a few styles to revert the latest styling changes being
applied by Google.

Co-authored-by: jacr13 <ramos.joao@protonmail.com>
Co-authored-by: Ben Busby <contact@benbusby.com>
main
Joao A. Candido Ramos 2022-02-07 18:47:25 +01:00 committed by GitHub
parent 4aa94a5d75
commit 11099f7b1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 821 additions and 138 deletions

View File

@ -52,6 +52,9 @@ app.config['TRANSLATIONS'] = json.load(open(
app.config['THEMES'] = json.load(open( app.config['THEMES'] = json.load(open(
os.path.join(app.config['STATIC_FOLDER'], 'settings/themes.json'), os.path.join(app.config['STATIC_FOLDER'], 'settings/themes.json'),
encoding='utf-8')) encoding='utf-8'))
app.config['HEADER_TABS'] = json.load(open(
os.path.join(app.config['STATIC_FOLDER'], 'settings/header_tabs.json'),
encoding='utf-8'))
app.config['CONFIG_PATH'] = os.getenv( app.config['CONFIG_PATH'] = os.getenv(
'CONFIG_VOLUME', 'CONFIG_VOLUME',
os.path.join(app.config['STATIC_FOLDER'], 'config')) os.path.join(app.config['STATIC_FOLDER'], 'config'))

View File

@ -1,5 +1,6 @@
from app.models.config import Config from app.models.config import Config
from app.models.endpoint import Endpoint from app.models.endpoint import Endpoint
from app.models.g_classes import GClasses
from app.request import VALID_PARAMS, MAPS_URL from app.request import VALID_PARAMS, MAPS_URL
from app.utils.misc import read_config_bool from app.utils.misc import read_config_bool
from app.utils.results import * from app.utils.results import *
@ -13,6 +14,15 @@ from urllib.parse import parse_qs
import os import os
minimal_mode_sections = ['Top stories', 'Images', 'People also ask'] minimal_mode_sections = ['Top stories', 'Images', 'People also ask']
unsupported_g_pages = [
'support.google.com',
'accounts.google.com',
'policies.google.com',
'google.com/preferences',
'google.com/intl',
'advanced_search',
'tbm=shop'
]
def extract_q(q_str: str, href: str) -> str: def extract_q(q_str: str, href: str) -> str:
@ -80,6 +90,7 @@ class Filter:
self.remove_block_url() self.remove_block_url()
self.collapse_sections() self.collapse_sections()
self.update_styling(soup) self.update_styling(soup)
self.remove_block_tabs(soup)
for img in [_ for _ in soup.find_all('img') if 'src' in _.attrs]: for img in [_ for _ in soup.find_all('img') if 'src' in _.attrs]:
self.update_element_src(img, 'image/png') self.update_element_src(img, 'image/png')
@ -143,6 +154,21 @@ class Filter:
if block_url.search(_.attrs['href']) is not None] if block_url.search(_.attrs['href']) is not None]
_ = div.decompose() if len(block_divs) else None _ = div.decompose() if len(block_divs) else None
def remove_block_tabs(self, soup) -> None:
if self.main_divs:
for div in self.main_divs.find_all(
'div',
attrs={'class': f'{GClasses.main_tbm_tab}'}
):
_ = div.decompose()
else:
# when in images tab
for div in soup.find_all(
'div',
attrs={'class': f'{GClasses.images_tbm_tab}'}
):
_ = div.decompose()
def collapse_sections(self) -> None: def collapse_sections(self) -> None:
"""Collapses long result sections ("people also asked", "related """Collapses long result sections ("people also asked", "related
searches", etc) into "details" elements searches", etc) into "details" elements
@ -273,6 +299,26 @@ class Filter:
except AttributeError: except AttributeError:
pass pass
# Fix body max width on images tab
style = soup.find('style')
div = soup.find('div', attrs={'class': f'{GClasses.images_tbm_tab}'})
if style and div and not self.mobile:
css = style.string
css_html_tag = (
'html{'
'font-family: Roboto, Helvetica Neue, Arial, sans-serif;'
'font-size: 14px;'
'line-height: 20px;'
'text-size-adjust: 100%;'
'word-wrap: break-word;'
'}'
)
css = f"{css_html_tag}{css}"
css = re.sub('body{(.*?)}',
'body{padding:0 8px;margin:0 auto;max-width:736px;}',
css)
style.string = css
def update_link(self, link: Tag) -> None: def update_link(self, link: Tag) -> None:
"""Update internal link paths with encrypted path, otherwise remove """Update internal link paths with encrypted path, otherwise remove
unnecessary redirects and/or marketing params from the url unnecessary redirects and/or marketing params from the url
@ -284,14 +330,15 @@ class Filter:
None (the tag is updated directly) None (the tag is updated directly)
""" """
# Replace href with only the intended destination (no "utm" type tags) # Remove any elements that direct to unsupported Google pages
href = link['href'].replace('https://www.google.com', '') if any(url in link['href'] for url in unsupported_g_pages):
if 'advanced_search' in href or 'tbm=shop' in href:
# FIXME: The "Shopping" tab requires further filtering (see #136) # FIXME: The "Shopping" tab requires further filtering (see #136)
# Temporarily removing all links to that tab for now. # Temporarily removing all links to that tab for now.
link.decompose() link.decompose()
return return
# Replace href with only the intended destination (no "utm" type tags)
href = link['href'].replace('https://www.google.com', '')
result_link = urlparse.urlparse(href) result_link = urlparse.urlparse(href)
q = extract_q(result_link.query, href) q = extract_q(result_link.query, href)
@ -362,11 +409,8 @@ class Filter:
""" """
# get some tags that are unchanged between mobile and pc versions # get some tags that are unchanged between mobile and pc versions
search_input = soup.find_all('td', attrs={'class': "O4cRJf"})[0]
search_options = soup.find_all('div', attrs={'class': "M7pB2"})[0]
cor_suggested = soup.find_all('table', attrs={'class': "By0U9"}) cor_suggested = soup.find_all('table', attrs={'class': "By0U9"})
next_pages = soup.find_all('table', attrs={'class': "uZgmoc"})[0] next_pages = soup.find_all('table', attrs={'class': "uZgmoc"})[0]
information = soup.find_all('div', attrs={'class': "TuS8Ad"})[0]
results = [] results = []
# find results div # find results div
@ -404,12 +448,7 @@ class Filter:
results=results, results=results,
view_label="View Image"), view_label="View Image"),
features='html.parser') features='html.parser')
# replace search input object
soup.find_all('td',
attrs={'class': "O4cRJf"})[0].replaceWith(search_input)
# replace search options object (All, Images, Videos, etc.)
soup.find_all('div',
attrs={'class': "M7pB2"})[0].replaceWith(search_options)
# replace correction suggested by google object if exists # replace correction suggested by google object if exists
if len(cor_suggested): if len(cor_suggested):
soup.find_all( soup.find_all(
@ -419,7 +458,4 @@ class Filter:
# replace next page object at the bottom of the page # replace next page object at the bottom of the page
soup.find_all('table', soup.find_all('table',
attrs={'class': "uZgmoc"})[0].replaceWith(next_pages) attrs={'class': "uZgmoc"})[0].replaceWith(next_pages)
# replace information about user connection at the bottom of the page
soup.find_all('div',
attrs={'class': "TuS8Ad"})[0].replaceWith(information)
return soup return soup

16
app/models/g_classes.py Normal file
View File

@ -0,0 +1,16 @@
from enum import Enum
class GClasses(Enum):
"""A class for tracking obfuscated class names used in Google results that
are directly referenced in Whoogle's filtering code.
Note: Using these should be a last resort. It is always preferred to filter
results using structural cues instead of referencing class names, as these
are liable to change at any moment.
"""
main_tbm_tab = 'KP7LCb'
images_tbm_tab = 'n692Zd'
def __str__(self):
return self.value

View File

@ -1,6 +1,7 @@
import argparse import argparse
import base64 import base64
import io import io
import os
import json import json
import os import os
import pickle import pickle
@ -17,14 +18,15 @@ from app.request import Request, TorError
from app.utils.bangs import resolve_bang from app.utils.bangs import resolve_bang
from app.utils.misc import read_config_bool, get_client_ip, get_request_url from app.utils.misc import read_config_bool, get_client_ip, get_request_url
from app.utils.results import add_ip_card, bold_search_terms,\ from app.utils.results import add_ip_card, bold_search_terms,\
add_currency_card, check_currency add_currency_card, check_currency, get_tabs_content
from app.utils.search import * from app.utils.search import Search, needs_https, has_captcha
from app.utils.session import generate_user_key, valid_user_session from app.utils.session import generate_user_key, valid_user_session
from bs4 import BeautifulSoup as bsoup from bs4 import BeautifulSoup as bsoup
from flask import jsonify, make_response, request, redirect, render_template, \ from flask import jsonify, make_response, request, redirect, render_template, \
send_file, session, url_for send_file, session, url_for, g
from requests import exceptions, get from requests import exceptions, get
from requests.models import PreparedRequest from requests.models import PreparedRequest
from cryptography.fernet import Fernet
# Load DDG bang json files only on init # Load DDG bang json files only on init
bang_json = json.load(open(app.config['BANG_FILE'])) or {} bang_json = json.load(open(app.config['BANG_FILE'])) or {}
@ -347,6 +349,12 @@ def search():
html_soup = bsoup(str(response), 'html.parser') html_soup = bsoup(str(response), 'html.parser')
response = add_ip_card(html_soup, get_client_ip(request)) response = add_ip_card(html_soup, get_client_ip(request))
# Update tabs content
tabs = get_tabs_content(app.config['HEADER_TABS'],
search_util.full_query,
search_util.search_type,
translation)
# Feature to display currency_card # Feature to display currency_card
conversion = check_currency(str(response)) conversion = check_currency(str(response))
if conversion: if conversion:
@ -373,15 +381,14 @@ def search():
) and not search_util.search_type, # Standard search queries only ) and not search_util.search_type, # Standard search queries only
response=response, response=response,
version_number=app.config['VERSION_NUMBER'], version_number=app.config['VERSION_NUMBER'],
search_header=(render_template( search_header=render_template(
'header.html', 'header.html',
config=g.user_config, config=g.user_config,
logo=render_template('logo.html', dark=g.user_config.dark), logo=render_template('logo.html', dark=g.user_config.dark),
query=urlparse.unquote(query), query=urlparse.unquote(query),
search_type=search_util.search_type, search_type=search_util.search_type,
mobile=g.user_request.mobile) mobile=g.user_request.mobile,
if 'isch' not in tabs=tabs))
search_util.search_type else '')), 200
@app.route(f'/{Endpoint.config}', methods=['GET', 'POST', 'PUT']) @app.route(f'/{Endpoint.config}', methods=['GET', 'POST', 'PUT'])

View File

@ -74,6 +74,7 @@ select {
.ZINbbc.luh4tb { .ZINbbc.luh4tb {
background: var(--whoogle-dark-result-bg) !important; background: var(--whoogle-dark-result-bg) !important;
margin-bottom: 24px !important;
} }
.bRsWnc { .bRsWnc {
@ -204,3 +205,7 @@ path {
.cb:focus { .cb:focus {
color: var(--whoogle-dark-contrast-text) !important; color: var(--whoogle-dark-contrast-text) !important;
} }
.desktop-header, .mobile-header {
background-color: var(--whoogle-dark-result-bg) !important;
}

View File

@ -98,6 +98,11 @@ header {
border: 0px !important; border: 0px !important;
} }
.autocomplete-mobile{
display: -webkit-box;
width: 100%;
}
.desktop-header-logo { .desktop-header-logo {
height: 1.65em; height: 1.65em;
} }
@ -106,3 +111,113 @@ header {
width: 100%; width: 100%;
flex: 1 flex: 1
} }
a {
color: #1967D2;
text-decoration: none;
tap-highlight-color: rgba(0, 0, 0, .10);
}
.header-tab-div {
border-radius: 0 0 8px 8px;
box-shadow: 0 2px 3px rgba(32, 33, 36, 0.18);
margin-bottom: 20px;
overflow: hidden;
}
.header-tab-div-2 {
border-top: 1px solid #dadce0;
height: 39px;
overflow: hidden;
}
.header-tab-div-3 {
height: 51px;
overflow-x: auto;
overflow-y: hidden;
}
.desktop-header {
height: 39px;
display: box;
display: flex;
width: 100%;
}
.header-tab {
box-pack: justify;
font-size: 14px;
line-height: 37px;
justify-content: space-between;
}
.desktop-header a, .desktop-header span {
color: #70757a;
display: block;
flex: none;
padding: 0 16px;
text-align: center;
text-transform: uppercase;
}
span.header-tab-span {
border-bottom: 2px solid #4285f4;
color: #4285f4;
font-weight: bold;
}
.mobile-header {
height: 39px;
display: box;
display: flex;
overflow-x: scroll;
width: 100%;
padding-left: 12px;
}
.mobile-header a, .mobile-header span {
color: #70757a;
text-decoration: none;
display: inline-block;
/* padding: 8px 12px 8px 12px; */
}
span.mobile-tab-span {
border-bottom: 2px solid #202124;
color: #202124;
height: 26px;
/* margin: 0 12px; */
/* padding: 0; */
}
.desktop-header input {
margin: 2px 4px 2px 8px;
}
a.header-tab-a:visited {
color: #70757a;
}
.header-tab-div-end {
border-left: 1px solid rgba(0, 0, 0, 0.12);
}
.search-bar-input {
display: block;
font-size: 16px;
padding: 0 0 0 8px;
flex: 1;
height: 35px;
outline: none;
border: none;
width: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
overflow: hidden;
}
@media (max-width: 801px) {
.header-tab-div {
margin-bottom: 10px !important
}
}

View File

@ -43,6 +43,8 @@ select {
.ZINbbc.luh4tb { .ZINbbc.luh4tb {
background: var(--whoogle-result-bg) !important; background: var(--whoogle-result-bg) !important;
box-shadow: 0 1px 6px rgba(32,33,36,0.28) !important;
margin-bottom: 24px !important;
} }
.bRsWnc { .bRsWnc {
@ -190,3 +192,7 @@ path {
.cb:focus { .cb:focus {
color: var(--whoogle-text) !important; color: var(--whoogle-text) !important;
} }
.desktop-header, .mobile-header {
background-color: var(--whoogle-result-bg) !important;
}

View File

@ -31,8 +31,9 @@ body {
} }
details summary { details summary {
padding: 10px; margin-bottom: 20px;
font-weight: bold; font-weight: bold;
padding-left: 10px;
} }
details summary span { details summary span {
@ -53,8 +54,18 @@ details summary span {
padding-top: 0 !important; padding-top: 0 !important;
} }
.footer {
text-align: center;
}
@media (min-width: 801px) { @media (min-width: 801px) {
body { body {
min-width: 736px !important; min-width: 736px !important;
} }
} }
@media (max-width: 801px) {
details summary {
margin-bottom: 10px !important
}
}

View File

@ -0,0 +1,38 @@
{
"all": {
"tbm": null,
"href": "search?q={query}",
"name": "All",
"selected": true
},
"images": {
"tbm": "isch",
"href": "search?q={query}",
"name": "Images",
"selected": false
},
"maps": {
"tbm": null,
"href": "https://maps.google.com/maps?q={query}",
"name": "Maps",
"selected": false
},
"videos": {
"tbm": "vid",
"href": "search?q={query}",
"name": "Videos",
"selected": false
},
"news": {
"tbm": "nws",
"href": "search?q={query}",
"name": "News",
"selected": false
},
"books": {
"tbm": "bks",
"href": "search?q={query}",
"name": "Books",
"selected": false
}
}

View File

@ -35,7 +35,13 @@
"dark": "dark", "dark": "dark",
"system": "system", "system": "system",
"ratelimit": "Instance has been ratelimited", "ratelimit": "Instance has been ratelimited",
"continue-search": "Continue your search with Farside" "continue-search": "Continue your search with Farside",
"all": "All",
"images": "Images",
"maps": "Maps",
"videos": "Videos",
"news": "News",
"books": "Books"
}, },
"lang_nl": { "lang_nl": {
"search": "Zoeken", "search": "Zoeken",
@ -73,7 +79,13 @@
"dark": "donker", "dark": "donker",
"system": "systeeminstellingen", "system": "systeeminstellingen",
"ratelimit": "Instantie is beperkt in snelheid", "ratelimit": "Instantie is beperkt in snelheid",
"continue-search": "Ga verder met zoeken met Farside" "continue-search": "Ga verder met zoeken met Farside",
"all": "Alle",
"images": "Afbeeldingen",
"maps": "Maps",
"videos": "Videos",
"news": "Nieuws",
"books": "Boeken"
}, },
"lang_de": { "lang_de": {
"search": "Suchen", "search": "Suchen",
@ -111,7 +123,13 @@
"dark": "dunkel", "dark": "dunkel",
"system": "Systemeinstellung", "system": "Systemeinstellung",
"ratelimit": "Instanz wurde ratenbegrenzt", "ratelimit": "Instanz wurde ratenbegrenzt",
"continue-search": "Setzen Sie Ihre Suche fort mit Farside" "continue-search": "Setzen Sie Ihre Suche fort mit Farside",
"all": "Alle",
"images": "Bilder",
"maps": "Maps",
"videos": "Videos",
"news": "Nieuws",
"books": "Bücher"
}, },
"lang_es": { "lang_es": {
"search": "Buscar", "search": "Buscar",
@ -149,7 +167,13 @@
"dark": "oscuro", "dark": "oscuro",
"system": "configuración del sistema", "system": "configuración del sistema",
"ratelimit": "La instancia ha sido ratelimited", "ratelimit": "La instancia ha sido ratelimited",
"continue-search": "Continúe su búsqueda con Farside" "continue-search": "Continúe su búsqueda con Farside",
"all": "Todo",
"images": "Imágenes",
"maps": "Maps",
"videos": "Vídeos",
"news": "Noticias",
"books": "Libros"
}, },
"lang_it": { "lang_it": {
"search": "Cerca", "search": "Cerca",
@ -187,7 +211,13 @@
"dark": "notte", "dark": "notte",
"system": "impostazioni di sistema", "system": "impostazioni di sistema",
"ratelimit": "L'istanza è stata limitata alla velocità", "ratelimit": "L'istanza è stata limitata alla velocità",
"continue-search": "Continua la tua ricerca con Farside" "continue-search": "Continua la tua ricerca con Farside",
"all": "Tutti",
"images": "Immagini",
"maps": "Maps",
"videos": "Video",
"news": "Notizie",
"books": "Libri"
}, },
"lang_pt": { "lang_pt": {
"search": "Pesquisar", "search": "Pesquisar",
@ -225,7 +255,13 @@
"dark": "escuro", "dark": "escuro",
"system": "configuração de sistema", "system": "configuração de sistema",
"ratelimit": "A instância foi limitada pela taxa", "ratelimit": "A instância foi limitada pela taxa",
"continue-search": "Continue sua pesquisa com Farside" "continue-search": "Continue sua pesquisa com Farside",
"all": "Todas",
"images": "Imagens",
"maps": "Maps",
"videos": "Vídeos",
"news": "Notícias",
"books": "Livros"
}, },
"lang_ru": { "lang_ru": {
"search": "Поиск", "search": "Поиск",
@ -263,7 +299,13 @@
"dark": "темное", "dark": "темное",
"system": "системное", "system": "системное",
"ratelimit": "Число экземпляров ограничено", "ratelimit": "Число экземпляров ограничено",
"continue-search": "Продолжайте поиск с Farside" "continue-search": "Продолжайте поиск с Farside",
"all": "Все",
"images": "Картинки",
"maps": "Карты",
"videos": "Видео",
"news": "Новости",
"books": "Книги"
}, },
"lang_zh-CN": { "lang_zh-CN": {
"search": "搜索", "search": "搜索",
@ -301,7 +343,13 @@
"dark": "黑暗的", "dark": "黑暗的",
"system": "系统设置", "system": "系统设置",
"ratelimit": "实例已被限速", "ratelimit": "实例已被限速",
"continue-search": "继续搜索 Farside" "continue-search": "继续搜索 Farside",
"all": "全部",
"images": "圖片",
"maps": "影片",
"videos": "地圖",
"news": "新聞",
"books": "書籍"
}, },
"lang_si": { "lang_si": {
"search": "සොයන්න", "search": "සොයන්න",
@ -339,7 +387,13 @@
"dark": "අඳුරු", "dark": "අඳුරු",
"system": "පද්ධතිය", "system": "පද්ධතිය",
"ratelimit": "සේවාදායකය අනුපාතනය කර ඇත", "ratelimit": "සේවාදායකය අනුපාතනය කර ඇත",
"continue-search": "Farside සමඟ ඔබගේ සෙවුම කරගෙන යන්න" "continue-search": "Farside සමඟ ඔබගේ සෙවුම කරගෙන යන්න",
"all": "සියල්ල",
"images": "රූප",
"maps": "සිතියම්",
"videos": "වීඩියෝ",
"news": "අනුරූප",
"books": "පොත්"
}, },
"lang_fr": { "lang_fr": {
"search": "Chercher", "search": "Chercher",
@ -377,7 +431,13 @@
"dark": "sombre", "dark": "sombre",
"system": "système", "system": "système",
"ratelimit": "Le débit de l'instance a été limité", "ratelimit": "Le débit de l'instance a été limité",
"continue-search": "Continuez votre recherche avec Farside" "continue-search": "Continuez votre recherche avec Farside",
"all": "Tous",
"images": "Images",
"maps": "Maps",
"videos": "Vidéos",
"news": "Actualités",
"books": "Livres"
}, },
"lang_fa": { "lang_fa": {
"search": "جستجو", "search": "جستجو",
@ -415,7 +475,13 @@
"dark": "تیره", "dark": "تیره",
"system": "سیستم", "system": "سیستم",
"ratelimit": "نمونه با نرخ محدود شده است", "ratelimit": "نمونه با نرخ محدود شده است",
"continue-search": "Farside جستجوی خود را با " "continue-search": "Farside جستجوی خود را با ",
"all": "همه",
"images": "تصاویر",
"maps": "نقشه‌ها",
"videos": "ویدئوها",
"news": "اخبار",
"books": "کتاب‌ها"
}, },
"lang_cs": { "lang_cs": {
"search": "Hledat", "search": "Hledat",
@ -453,7 +519,13 @@
"dark": "Tmavý", "dark": "Tmavý",
"system": "Systémový", "system": "Systémový",
"ratelimit": "Instance byla omezena sazbou", "ratelimit": "Instance byla omezena sazbou",
"continue-search": "Pokračujte ve vyhledávání pomocí Farside" "continue-search": "Pokračujte ve vyhledávání pomocí Farside",
"all": "Vše",
"images": "Obrázky",
"maps": "Mapy",
"videos": "Videa",
"news": "Zprávy",
"books": "Knihy"
}, },
"lang_zh-TW": { "lang_zh-TW": {
"search": "搜尋", "search": "搜尋",
@ -491,7 +563,13 @@
"dark": "黑暗的", "dark": "黑暗的",
"system": "依系統", "system": "依系統",
"ratelimit": "實例已被限速", "ratelimit": "實例已被限速",
"continue-search": "繼續搜索 Farside" "continue-search": "繼續搜索 Farside",
"all": "全部",
"images": "圖片",
"maps": "影片",
"videos": "地圖",
"news": "新聞",
"books": "書籍"
}, },
"lang_bg": { "lang_bg": {
"search": "Търсене", "search": "Търсене",
@ -529,7 +607,13 @@
"dark": "тъмна", "dark": "тъмна",
"system": "системна", "system": "системна",
"ratelimit": "Екземплярът е с ограничена скорост", "ratelimit": "Екземплярът е с ограничена скорост",
"continue-search": "Продължете търсенето си с Farside" "continue-search": "Продължете търсенето си с Farside",
"all": "Всичкo",
"images": "Изображения",
"maps": "Видеоклипове",
"videos": "Новини",
"news": "Карти",
"books": "Книги"
}, },
"lang_hi": { "lang_hi": {
"search": "खोज", "search": "खोज",
@ -567,7 +651,13 @@
"dark": "अंधेरा", "dark": "अंधेरा",
"system": "प्रणाली", "system": "प्रणाली",
"ratelimit": "इंस्टेंस को सीमित कर दिया गया है", "ratelimit": "इंस्टेंस को सीमित कर दिया गया है",
"continue-search": "के साथ अपनी खोज जारी रखें Farside" "continue-search": "के साथ अपनी खोज जारी रखें Farside",
"all": "सभी",
"images": "इमेज",
"maps": "वीडियो",
"videos": "मैप",
"news": "समाचार",
"books": "किताबें"
}, },
"lang_ja": { "lang_ja": {
"search": "検索", "search": "検索",
@ -605,6 +695,12 @@
"dark": "ダーク", "dark": "ダーク",
"system": "自動", "system": "自動",
"ratelimit": "インスタンスはレート制限されています", "ratelimit": "インスタンスはレート制限されています",
"continue-search": "で検索を続ける Farside" "continue-search": "で検索を続ける Farside",
"all": "すべて",
"images": "画像",
"maps": "地図",
"videos": "動画",
"news": "ニュース",
"books": "書籍"
} }
} }

View File

@ -1,7 +1,7 @@
{% if mobile %} {% if mobile %}
<header> <header>
<div class="bz1lBb header-div"> <div class="header-div">
<form class="search-form Pg70bf" <form class="search-form header"
id="search-form" id="search-form"
method="{{ 'GET' if config.get_only else 'POST' }}"> method="{{ 'GET' if config.get_only else 'POST' }}">
<a class="logo-link mobile-logo" href="home"> <a class="logo-link mobile-logo" href="home">
@ -10,7 +10,7 @@
</div> </div>
</a> </a>
<div class="H0PQec mobile-input-div"> <div class="H0PQec mobile-input-div">
<div class="sbc esbc autocomplete"> <div class="autocomplete-mobile esbc autocomplete">
<input <input
id="search-bar" id="search-bar"
class="mobile-search-bar" class="mobile-search-bar"
@ -18,7 +18,7 @@
autocomplete="off" autocomplete="off"
autocorrect="off" autocorrect="off"
spellcheck="false" spellcheck="false"
class="noHIxc" class="search-bar-input"
name="q" name="q"
type="text" type="text"
value="{{ clean_query(query) }}" value="{{ clean_query(query) }}"
@ -31,6 +31,25 @@
</div> </div>
</form> </form>
</div> </div>
<div>
<div class="header-tab-div">
<div class="header-tab-div-2">
<div class="header-tab-div-3">
<div class="mobile-header header-tab">
{% for tab_id, tab_content in tabs.items() %}
{% if tab_content['selected'] %}
<span class="mobile-tab-span">{{ tab_content['name'] }}</span>
{% else %}
<a class="header-tab-a" href="{{ tab_content['href'] }}">{{ tab_content['name'] }}</a>
{% endif %}
{% endfor %}
<div class="header-tab-div-end"></div>
</div>
</div>
</div>
</div>
<div class="" id="s">
</div>
</header> </header>
{% else %} {% else %}
<header> <header>
@ -53,7 +72,7 @@
autocapitalize="none" autocapitalize="none"
autocomplete="off" autocomplete="off"
autocorrect="off" autocorrect="off"
class="search-bar-desktop noHIxc" class="search-bar-desktop search-bar-input"
name="q" name="q"
spellcheck="false" spellcheck="false"
type="text" type="text"
@ -67,6 +86,25 @@
</form> </form>
</div> </div>
</header> </header>
<div>
<div class="header-tab-div">
<div class="header-tab-div-2">
<div class="header-tab-div-3">
<div class="desktop-header header-tab">
{% for tab_id, tab_content in tabs.items() %}
{% if tab_content['selected'] %}
<span class="header-tab-span">{{ tab_content['name'] }}</span>
{% else %}
<a class="header-tab-a" href="{{ tab_content['href'] }}">{{ tab_content['name'] }}</a>
{% endif %}
{% endfor %}
<div class="header-tab-div-end"></div>
</div>
</div>
</div>
</div>
<div class="" id="s">
</div>
{% endif %} {% endif %}
<script type="text/javascript" src="{{ cb_url('header.js') }}"></script> <script type="text/javascript" src="{{ cb_url('header.js') }}"></script>

View File

@ -1,116 +1,390 @@
<!DOCTYPE html> <div>
<html>
<head>
<meta content="application/xhtml+xml; charset=utf-8" http-equiv="Content-Type"/>
<meta content="no-cache" name="Cache-Control"/>
<title>
</title>
<style> <style>
a{text-decoration:none;color:inherit}a:hover{text-decoration:underline}a img{border:0}body{font-family:Roboto,Helvetica,Arial,sans-serif;padding:8px;margin:0 auto;max-width:700px;min-width:240px;}.FbhRzb{border-left:thin solid #dadce0;border-right:thin solid #dadce0;border-top:thin solid #dadce0;height:40px;overflow:hidden}.n692Zd{margin-bottom:10px}.cvifge{height:40px;border-spacing:0;width:100%;}.QvGUP{height:40px;padding:0 8px 0 8px;vertical-align:top}.O4cRJf{height:40px;width:100%;padding:0;padding-right:16px}.O1ePr{height:40px;padding:0;vertical-align:top}.kgJEQe{height:36px;width:98px;vertical-align:top;margin-top:4px}.lXLRf{vertical-align:top}.MhzMZd{border:0;vertical-align:middle;font-size:14px;height:40px;padding:0;width:100%;padding-left:16px}.xB0fq{height:40px;border:none;font-size:14px;background-color:#4285f4;color:#fff;padding:0 16px;margin:0;vertical-align:top;cursor:pointer}.xB0fq:focus{border:1px solid #000}.M7pB2{border:thin solid #dadce0;margin:0 0 3px 0;font-size:13px;font-weight:500;height:40px}.euZec{width:100%;height:40px;text-align:center;border-spacing:0}table.euZec td{padding:0;width:25%}.QIqI7{display:inline-block;padding-top:4px;font-weight:bold;color:#4285f4}.EY24We{border-bottom:2px solid #4285f4}.CsQyDc{display:inline-block;color:#70757a}.TuS8Ad{font-size:14px}.HddGcc{padding:8px;color:#70757a}.dzp8ae{font-weight:bold;color:#3c4043}.rEM8G{color:#70757a}.bookcf{table-layout:fixed;width:100%;border-spacing:0}.InWNIe{text-align:center}.uZgmoc{border:thin solid #dadce0;color:#70757a;font-size:14px;text-align:center;table-layout:fixed;width:100%}.frGj1b{display:block;padding:12px 0 12px 0;width:100%}.BnJWBc{text-align:center;padding:6px 0 13px 0;height:35px}.e3goi{vertical-align:top;padding:0;height:180px}.GpQGbf{margin:auto;border-collapse:collapse;border-spacing:0;width:100%} html {
font-family: Roboto, Helvetica Neue, Arial, sans-serif;
font-size: 14px;
line-height: 20px;
text-size-adjust: 100%;
color: #3c4043;
word-wrap: break-word;
background-color: #fff;
}
body {
padding: 0 8px;
margin: 0 auto;
max-width: 736px;
}
a {
text-decoration: none;
color: inherit;
}
a:hover {
text-decoration: underline;
}
a img {
border: 0;
}
.FbhRzb {
border-left: thin solid #dadce0;
border-right: thin solid #dadce0;
border-top: thin solid #dadce0;
height: 40px;
overflow: hidden;
}
.n692Zd {
margin-bottom: 10px;
}
.cvifge {
height: 40px;
border-spacing: 0;
width: 100%;
}
.QvGUP {
height: 40px;
padding: 0 8px 0 8px;
vertical-align: top;
}
.O4cRJf {
height: 40px;
width: 100%;
padding: 0;
padding-right: 16px;
}
.O1ePr {
height: 40px;
padding: 0;
vertical-align: top;
}
.kgJEQe {
height: 36px;
width: 98px;
vertical-align: top;
margin-top: 4px;
}
.lXLRf {
vertical-align: top;
}
.MhzMZd {
border: 0;
vertical-align: middle;
font-size: 14px;
height: 40px;
padding: 0;
width: 100%;
padding-left: 16px;
}
.xB0fq {
height: 40px;
border: none;
font-size: 14px;
background-color: #4285f4;
color: #fff;
padding: 0 16px;
margin: 0;
vertical-align: top;
cursor: pointer;
}
.xB0fq:focus {
border: 1px solid #000;
}
.M7pB2 {
border: thin solid #dadce0;
margin: 0 0 3px 0;
font-size: 13px;
font-weight: 500;
height: 40px;
}
.euZec {
width: 100%;
height: 40px;
text-align: center;
border-spacing: 0;
}
table.euZec td {
padding: 0;
width: 25%;
}
.QIqI7 {
display: inline-block;
padding-top: 4px;
font-weight: bold;
color: #4285f4;
}
.EY24We {
border-bottom: 2px solid #4285f4;
}
.CsQyDc {
display: inline-block;
color: #70757a;
}
.TuS8Ad {
font-size: 14px;
}
.HddGcc {
padding: 8px;
color: #70757a;
}
.dzp8ae {
font-weight: bold;
color: #3c4043;
}
.rEM8G {
color: #70757a;
}
.bookcf {
table-layout: fixed;
width: 100%;
border-spacing: 0;
}
.InWNIe {
text-align: center;
}
.uZgmoc {
border: thin solid #dadce0;
color: #70757a;
font-size: 14px;
text-align: center;
table-layout: fixed;
width: 100%;
}
.frGj1b {
display: block;
padding: 12px 0 12px 0;
width: 100%;
}
.BnJWBc {
text-align: center;
padding: 6px 0 13px 0;
height: 35px;
}
.e3goi {
vertical-align: top;
padding: 0;
height: 180px;
}
.GpQGbf {
margin: auto;
border-collapse: collapse;
border-spacing: 0;
width: 100%;
}
.X6ZCif {
color: #202124;
font-size: 11px;
line-height: 16px;
display: inline-block;
padding-top: 2px;
overflow: hidden;
padding-bottom: 4px;
width: 100%;
}
.TwVfHd {
border-radius: 16px;
border: thin solid #dadce0;
display: inline-block;
padding: 8px 8px;
margin-right: 8px;
margin-bottom: 4px;
}
.yekiAe {
background-color: #dadce0;
}
.svla5d {
width: 100%;
}
.ezO2md {
border: thin solid #dadce0;
padding: 12px 16px 12px 16px;
margin-bottom: 10px;
font-family: Roboto, Helvetica, Arial, sans-serif;
}
.TxbwNb {
border-spacing: 0;
}
.K35ahc {
width: 100%;
}
.owohpf {
text-align: center;
}
.RAyV4b {
width: 162px;
height: 140px;
line-height: 140px;
overflow: "hidden";
text-align: center;
}
.t0fcAb {
text-align: center;
margin: auto;
vertical-align: middle;
width: 100%;
height: 100%;
object-fit: contain;
}
.Tor4Ec {
padding-top: 2px;
padding-bottom: 8px;
}
.fYyStc {
word-break: break-word;
}
.ynsChf {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.Fj3V3b {
color: #1967d2;
font-size: 14px;
line-height: 20px;
}
.FrIlee {
color: #202124;
font-size: 11px;
line-height: 16px;
}
.F9iS2e {
color: #70757a;
font-size: 11px;
line-height: 16px;
}
.WMQ2Le {
color: #70757a;
font-size: 12px;
line-height: 16px;
}
.x3G5ab {
color: #202124;
font-size: 12px;
line-height: 16px;
}
.fuLhoc {
color: #1967d2;
font-size: 18px;
line-height: 24px;
}
.epoveb {
font-size: 32px;
line-height: 40px;
font-weight: 400;
color: #202124;
}
.dXDvrc {
color: #0d652d;
font-size: 14px;
line-height: 20px;
word-wrap: break-word;
}
.dloBPe {
font-weight: bold;
}
.YVIcad {
color: #70757a;
}
.JkVVdd {
color: #ea4335;
}
.oXZRFd {
color: #ea4335;
}
.MQHtg {
color: #fbbc04;
}
.pyMRrb {
color: #1e8e3e;
}
.EtTZid {
color: #1e8e3e;
}
.M3vVJe {
color: #1967d2;
}
.qXLe6d {
display: block;
}
.NHQNef {
font-style: italic;
}
.Cb8Z7c {
white-space: pre;
}
a.ZWRArf {
text-decoration: none;
}
a .CVA68e:hover {
text-decoration: underline;
}
</style> </style>
</head>
<body>
<style>
.X6ZCif{color:#202124;font-size:11px;line-height:16px;display:inline-block;padding-top:2px;overflow:hidden;padding-bottom:4px;width:100%}.TwVfHd{border-radius:16px;border:thin solid #dadce0;display:inline-block;padding:8px 8px;margin-right:8px;margin-bottom:4px}.yekiAe{background-color:#dadce0}.svla5d{width:100%}.ezO2md{border:thin solid #dadce0;padding:12px 16px 12px 16px;margin-bottom:10px;font-family:Roboto,Helvetica,Arial,sans-serif}.lIMUZd{font-family:Roboto,Helvetica,Arial,sans-serif}.TxbwNb{border-spacing:0}.K35ahc{width:100%}.owohpf{text-align:center}.RAyV4b{width:162px;height:140px;line-height:140px;overflow:'hidden';text-align:center;}.t0fcAb{text-align:center;margin:auto;vertical-align:middle;width:100%;height:100%;object-fit: contain}.Tor4Ec{padding-top:2px;padding-bottom:8px;}.fYyStc{word-break:break-word}.ynsChf{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.Fj3V3b{color:#1967D2;font-size:14px;line-height:20px}.FrIlee{color:#202124;font-size:11px;line-height:16px}.F9iS2e{color:#70757a;font-size:11px;line-height:16px}.WMQ2Le{color:#70757a;font-size:12px;line-height:16px}.x3G5ab{color:#202124;font-size:12px;line-height:16px}.fuLhoc{color:#1967D2;font-size:18px;line-height:24px}.epoveb{font-size:32px;line-height:40px;font-weight:400;color:#202124}.dXDvrc{color:#0d652d;font-size:14px;line-height:20px;word-wrap:break-word}.dloBPe{font-weight:bold}.YVIcad{color:#70757a}.JkVVdd{color:#ea4335}.oXZRFd{color:#ea4335}.MQHtg{color:#fbbc04}.pyMRrb{color:#1e8e3e}.EtTZid{color:#1e8e3e}.M3vVJe{color:#1967D2}.qXLe6d{display:block}.NHQNef{font-style:italic}.Cb8Z7c{white-space:pre}a.ZWRArf{text-decoration:none}a .CVA68e:hover{text-decoration:underline}
</style>
<div class="n692Zd">
<div class="BnJWBc">
<a class="lXLRf" href="/?safe=off&amp;gbv=1&amp;output=images&amp;ie=UTF-8&amp;tbm=isch&amp;sa=X&amp;ved=0ahUKEwjhh7TZyd_vAhWShf0HHeYzCmsQPAgC">
<img alt="Google" class="kgJEQe" src="/images/branding/searchlogo/1x/googlelogo_desk_heirloom_color_150x55dp.gif"/>
</a>
</div>
<div class="FbhRzb">
<form action="/search">
<input name="safe" type="hidden" value="off"/>
<input name="gbv" type="hidden" value="1"/>
<input name="ie" type="hidden" value="ISO-8859-1"/>
<input name="tbm" type="hidden" value="isch"/>
<input name="oq" type="hidden"/>
<input name="aqs" type="hidden"/>
<table class="cvifge">
<tr>
<td class="O4cRJf">
<!-- search input -->
</td>
</tr>
</table>
</form>
</div>
<div class="M7pB2">
<!-- search options -->
</div>
</div>
<!-- <div class="X6ZCif"> Not present in mobile
</div> -->
<div> <div>
<div> <div>
<div> <div>
<div class="lIMUZd"> <div class="lIMUZd">
<table class="By0U9"> <table class="By0U9">
<!-- correction suggested --> <!-- correction suggested -->
</table> </table>
</div> </div>
</div> </div>
</div> </div>
<table class="GpQGbf"> <table class="GpQGbf">
{% for i in range((length // 4) + 1) %} {% for i in range((length // 4) + 1) %}
<tr> <tr>
{% for j in range([length - (i*4), 4]|min) %} {% for j in range([length - (i*4), 4]|min) %}
<td align="center" class="e3goi"> <td align="center" class="e3goi">
<div class="svla5d"> <div class="svla5d">
<div> <div>
<div class="lIMUZd"> <div class="lIMUZd">
<div> <div>
<table class="TxbwNb"> <table class="TxbwNb">
<tr> <tr>
<td> <td>
<a href="{{ results[(i*4)+j].web_page }}"> <a href="{{ results[(i*4)+j].web_page }}">
<div class="RAyV4b"> <div class="RAyV4b">
<img alt="" class="t0fcAb" src="{{ results[(i*4)+j].img_tbn }}"/> <img
</div> alt=""
</a> class="t0fcAb"
</td> src="{{ results[(i*4)+j].img_tbn }}"
</tr> />
<tr> </div>
<td> </a>
<a href="{{ results[(i*4)+j].web_page }}"> </td>
<div class="Tor4Ec"> </tr>
<span class="qXLe6d x3G5ab"> <tr>
<span class="fYyStc"> <td>
{{ results[(i*4)+j].domain }} <a href="{{ results[(i*4)+j].web_page }}">
</span> <div class="Tor4Ec">
</span> <span class="qXLe6d x3G5ab">
</div> <span class="fYyStc">
</a> {{ results[(i*4)+j].domain }}
<a href="{{ results[(i*4)+j].img_url }}"> </span>
<div class="Tor4Ec"> </span>
<span class="qXLe6d F9iS2e"> </div>
<span class="fYyStc"> </a>
{{ view_label }} <a href="{{ results[(i*4)+j].img_url }}">
</span> <div class="Tor4Ec">
</span> <span class="qXLe6d F9iS2e">
</div> <span class="fYyStc"> {{ view_label }} </span>
</a> </span>
</td> </div>
</tr> </a>
</table> </td>
</tr>
</table>
</div>
</div> </div>
</div> </div>
</div>
</div> </div>
</td> </td>
{% endfor %} {% endfor %}
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
<table class="uZgmoc"> <table class="uZgmoc">
<!-- next page object --> <!-- next page object -->
</table> </table>
<br/> <br />
<div class="TuS8Ad"> </div>
<!-- information about user connection -->
<div>
</div>
</body>
</html>

View File

@ -1,5 +1,6 @@
from app.models.endpoint import Endpoint from app.models.endpoint import Endpoint
from bs4 import BeautifulSoup, NavigableString from bs4 import BeautifulSoup, NavigableString
import copy
import html import html
import os import os
import urllib.parse as urlparse import urllib.parse as urlparse
@ -329,3 +330,39 @@ def add_currency_card(soup: BeautifulSoup,
element1.insert_before(conversion_box) element1.insert_before(conversion_box)
return soup return soup
def get_tabs_content(tabs: dict,
full_query: str,
search_type: str,
translation: dict) -> dict:
"""Takes the default tabs content and updates it according to the query.
Args:
tabs: The default content for the tabs
full_query: The original search query
search_type: The current search_type
translation: The translation to get the names of the tabs
Returns:
dict: contains the name, the href and if the tab is selected or not
"""
tabs = copy.deepcopy(tabs)
for tab_id, tab_content in tabs.items():
# update name to desired language
if tab_id in translation:
tab_content['name'] = translation[tab_id]
# update href with query
query = full_query.replace(f'&tbm={search_type}', '')
if tab_content['tbm'] is not None:
query = f"{query}&tbm={tab_content['tbm']}"
tab_content['href'] = tab_content['href'].format(query=query)
# update if selected tab (default all tab is selected)
if tab_content['tbm'] == search_type:
tabs['all']['selected'] = False
tab_content['selected'] = True
return tabs

View File

@ -120,6 +120,7 @@ class Search:
full_query = gen_query(self.query, full_query = gen_query(self.query,
self.request_params, self.request_params,
self.config) self.config)
self.full_query = full_query
# force mobile search when view image is true and # force mobile search when view image is true and
# the request is not already made by a mobile # the request is not already made by a mobile