Add "view image" functionality (#268)
* add view image option * prevent whoogle links from opening in a new tab. * remove view image template on mobile requests * change loop values to be more robust to the number of images * Update app/templates/imageresults.html * fix "Basically the .cvifge class needs width: 100%; in order to expand the search input to fit the form width." * Update app/templates/imageresults.html * remove hardcoded string from template * Add view image config var to app.json * Add view image config var to whoogle.env Co-authored-by: jacr13 <ramos.joao@protonmail.com> Co-authored-by: Ben Busby <benbusby@protonmail.com>main
parent
fcfa3783e3
commit
448efb8f2a
17
README.md
17
README.md
|
@ -103,17 +103,25 @@ Sandboxed temporary instance:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ whoogle-search --help
|
$ whoogle-search --help
|
||||||
usage: whoogle-search [-h] [--port <port number>] [--host <ip address>] [--debug]
|
usage: whoogle-search [-h] [--port <port number>] [--host <ip address>] [--debug] [--https-only] [--userpass <username:password>]
|
||||||
[--https-only]
|
[--proxyauth <username:password>] [--proxytype <socks4|socks5|http>] [--proxyloc <location:port>]
|
||||||
|
|
||||||
Whoogle Search console runner
|
Whoogle Search console runner
|
||||||
|
|
||||||
optional arguments:
|
optional arguments:
|
||||||
-h, --help show this help message and exit
|
-h, --help Show this help message and exit
|
||||||
--port <port number> Specifies a port to run on (default 5000)
|
--port <port number> Specifies a port to run on (default 5000)
|
||||||
--host <ip address> Specifies the host address to use (default 127.0.0.1)
|
--host <ip address> Specifies the host address to use (default 127.0.0.1)
|
||||||
--debug Activates debug mode for the server (default False)
|
--debug Activates debug mode for the server (default False)
|
||||||
--https-only Enforces HTTPS redirects for all requests (default False)
|
--https-only Enforces HTTPS redirects for all requests
|
||||||
|
--userpass <username:password>
|
||||||
|
Sets a username/password basic auth combo (default None)
|
||||||
|
--proxyauth <username:password>
|
||||||
|
Sets a username/password for a HTTP/SOCKS proxy (default None)
|
||||||
|
--proxytype <socks4|socks5|http>
|
||||||
|
Sets a proxy type for all connections (default None)
|
||||||
|
--proxyloc <location:port>
|
||||||
|
Sets a proxy location for all connections (default None)
|
||||||
```
|
```
|
||||||
See the [available environment variables](#environment-variables) for additional configuration.
|
See the [available environment variables](#environment-variables) for additional configuration.
|
||||||
|
|
||||||
|
@ -286,6 +294,7 @@ These environment variables allow setting default config values, but can be over
|
||||||
| WHOOGLE_CONFIG_ALTS | Use social media site alternatives (nitter, invidious, etc) |
|
| WHOOGLE_CONFIG_ALTS | Use social media site alternatives (nitter, invidious, etc) |
|
||||||
| WHOOGLE_CONFIG_TOR | Use Tor routing (if available) |
|
| WHOOGLE_CONFIG_TOR | Use Tor routing (if available) |
|
||||||
| WHOOGLE_CONFIG_NEW_TAB | Always open results in new tab |
|
| WHOOGLE_CONFIG_NEW_TAB | Always open results in new tab |
|
||||||
|
| WHOOGLE_CONFIG_VIEW_IMAGE | Enable View Image option |
|
||||||
| WHOOGLE_CONFIG_GET_ONLY | Search using GET requests only |
|
| WHOOGLE_CONFIG_GET_ONLY | Search using GET requests only |
|
||||||
| WHOOGLE_CONFIG_URL | The root url of the instance (`https://<your url>/`) |
|
| WHOOGLE_CONFIG_URL | The root url of the instance (`https://<your url>/`) |
|
||||||
| WHOOGLE_CONFIG_STYLE | The custom CSS to use for styling (should be single line) |
|
| WHOOGLE_CONFIG_STYLE | The custom CSS to use for styling (should be single line) |
|
||||||
|
|
5
app.json
5
app.json
|
@ -115,6 +115,11 @@
|
||||||
"value": "",
|
"value": "",
|
||||||
"required": false
|
"required": false
|
||||||
},
|
},
|
||||||
|
"WHOOGLE_CONFIG_VIEW_IMAGE": {
|
||||||
|
"description": "[CONFIG] Enable View Image option (set to 1 or leave blank)",
|
||||||
|
"value": "",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
"WHOOGLE_CONFIG_GET_ONLY": {
|
"WHOOGLE_CONFIG_GET_ONLY": {
|
||||||
"description": "[CONFIG] Search using GET requests only (set to 1 or leave blank)",
|
"description": "[CONFIG] Search using GET requests only (set to 1 or leave blank)",
|
||||||
"value": "",
|
"value": "",
|
||||||
|
|
|
@ -254,3 +254,65 @@ class Filter:
|
||||||
|
|
||||||
# Replace link destination
|
# Replace link destination
|
||||||
link_desc[0].replace_with(get_site_alt(link_desc[0]))
|
link_desc[0].replace_with(get_site_alt(link_desc[0]))
|
||||||
|
|
||||||
|
def view_image(self, soup) -> BeautifulSoup:
|
||||||
|
"""Replaces the soup with a new one that handles mobile results and
|
||||||
|
adds the link of the image full res to the results.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
soup: A BeautifulSoup object containing the image mobile results.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
BeautifulSoup: The new BeautifulSoup object
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 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"})
|
||||||
|
next_pages = soup.find_all('table', attrs={'class': "uZgmoc"})[0]
|
||||||
|
information = soup.find_all('div', attrs={'class': "TuS8Ad"})[0]
|
||||||
|
|
||||||
|
results = []
|
||||||
|
# find results div
|
||||||
|
results_div = soup.find_all('div', attrs={'class': "nQvrDb"})[0]
|
||||||
|
# find all the results
|
||||||
|
results_all = results_div.find_all('div', attrs={'class': "lIMUZd"})
|
||||||
|
|
||||||
|
for item in results_all:
|
||||||
|
urls = item.find('a')['href'].split('&imgrefurl=')
|
||||||
|
|
||||||
|
img_url = urlparse.unquote(urls[0].replace('/imgres?imgurl=', ''))
|
||||||
|
webpage = urlparse.unquote(urls[1].split('&')[0])
|
||||||
|
img_tbn = urlparse.unquote(item.find('a').find('img')['src'])
|
||||||
|
results.append({
|
||||||
|
'domain': urlparse.urlparse(webpage).netloc,
|
||||||
|
'img_url': img_url,
|
||||||
|
'webpage': webpage,
|
||||||
|
'img_tbn': img_tbn
|
||||||
|
})
|
||||||
|
|
||||||
|
soup = BeautifulSoup(render_template('imageresults.html',
|
||||||
|
length=len(results),
|
||||||
|
results=results,
|
||||||
|
view_label="View Image"),
|
||||||
|
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
|
||||||
|
if len(cor_suggested):
|
||||||
|
soup.find_all(
|
||||||
|
'table',
|
||||||
|
attrs={'class': "By0U9"}
|
||||||
|
)[0].replaceWith(cor_suggested[0])
|
||||||
|
# replace next page object at the bottom of the page
|
||||||
|
soup.find_all('table',
|
||||||
|
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
|
||||||
|
|
|
@ -27,7 +27,9 @@ class Config:
|
||||||
self.tor = read_config_bool('WHOOGLE_CONFIG_TOR')
|
self.tor = read_config_bool('WHOOGLE_CONFIG_TOR')
|
||||||
self.near = os.getenv('WHOOGLE_CONFIG_NEAR', '')
|
self.near = os.getenv('WHOOGLE_CONFIG_NEAR', '')
|
||||||
self.new_tab = read_config_bool('WHOOGLE_CONFIG_NEW_TAB')
|
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.get_only = read_config_bool('WHOOGLE_CONFIG_GET_ONLY')
|
||||||
|
|
||||||
self.safe_keys = [
|
self.safe_keys = [
|
||||||
'lang_search',
|
'lang_search',
|
||||||
'lang_interface',
|
'lang_interface',
|
||||||
|
|
|
@ -151,6 +151,8 @@ class Request:
|
||||||
self.language = config.lang_search
|
self.language = config.lang_search
|
||||||
self.mobile = 'Android' in normal_ua or 'iPhone' in normal_ua
|
self.mobile = 'Android' in normal_ua or 'iPhone' in normal_ua
|
||||||
self.modified_user_agent = gen_user_agent(self.mobile)
|
self.modified_user_agent = gen_user_agent(self.mobile)
|
||||||
|
if not self.mobile:
|
||||||
|
self.modified_user_agent_mobile = gen_user_agent(True)
|
||||||
|
|
||||||
# Set up proxy, if previously configured
|
# Set up proxy, if previously configured
|
||||||
if os.environ.get('WHOOGLE_PROXY_LOC'):
|
if os.environ.get('WHOOGLE_PROXY_LOC'):
|
||||||
|
@ -197,7 +199,8 @@ class Request:
|
||||||
return [_.attrib['data'] for _ in
|
return [_.attrib['data'] for _ in
|
||||||
root.findall('.//suggestion/[@data]')]
|
root.findall('.//suggestion/[@data]')]
|
||||||
|
|
||||||
def send(self, base_url=SEARCH_URL, query='', attempt=0) -> Response:
|
def send(self, base_url=SEARCH_URL, query='', attempt=0,
|
||||||
|
force_mobile=False) -> Response:
|
||||||
"""Sends an outbound request to a URL. Optionally sends the request
|
"""Sends an outbound request to a URL. Optionally sends the request
|
||||||
using Tor, if enabled by the user.
|
using Tor, if enabled by the user.
|
||||||
|
|
||||||
|
@ -211,8 +214,13 @@ class Request:
|
||||||
Response: The Response object returned by the requests call
|
Response: The Response object returned by the requests call
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
if force_mobile and not self.mobile:
|
||||||
|
modified_user_agent = self.modified_user_agent_mobile
|
||||||
|
else:
|
||||||
|
modified_user_agent = self.modified_user_agent
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'User-Agent': self.modified_user_agent
|
'User-Agent': modified_user_agent
|
||||||
}
|
}
|
||||||
|
|
||||||
# FIXME: Should investigate this further to ensure the consent
|
# FIXME: Should investigate this further to ensure the consent
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta content="application/xhtml+xml; charset=utf-8" http-equiv="Content-Type"/>
|
||||||
|
<meta content="no-cache" name="Cache-Control"/>
|
||||||
|
<title>
|
||||||
|
</title>
|
||||||
|
<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%}
|
||||||
|
</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&gbv=1&output=images&ie=UTF-8&tbm=isch&sa=X&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 class="lIMUZd">
|
||||||
|
<table class="By0U9">
|
||||||
|
<!-- correction suggested -->
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class="GpQGbf">
|
||||||
|
{% for i in range((length // 4) + 1) %}
|
||||||
|
<tr>
|
||||||
|
{% for j in range([length - (i*4), 4]|min) %}
|
||||||
|
<td align="center" class="e3goi">
|
||||||
|
<div class="svla5d">
|
||||||
|
<div>
|
||||||
|
<div class="lIMUZd">
|
||||||
|
<div>
|
||||||
|
<table class="TxbwNb">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ results[(i*4)+j].webpage }}">
|
||||||
|
<div class="RAyV4b">
|
||||||
|
<img alt="" class="t0fcAb" src="{{ results[(i*4)+j].img_tbn }}"/>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ results[(i*4)+j].webpage }}">
|
||||||
|
<div class="Tor4Ec">
|
||||||
|
<span class="qXLe6d x3G5ab">
|
||||||
|
<span class="fYyStc">
|
||||||
|
{{ results[(i*4)+j].domain }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a href="{{ results[(i*4)+j].img_url }}">
|
||||||
|
<div class="Tor4Ec">
|
||||||
|
<span class="qXLe6d F9iS2e">
|
||||||
|
<span class="fYyStc">
|
||||||
|
{{ view_label }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<table class="uZgmoc">
|
||||||
|
<!-- next page object -->
|
||||||
|
</table>
|
||||||
|
<br/>
|
||||||
|
<div class="TuS8Ad">
|
||||||
|
<!-- information about user connection -->
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,152 +1,158 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="apple-touch-icon" sizes="57x57" href="static/img/favicon/apple-icon-57x57.png">
|
<link rel="apple-touch-icon" sizes="57x57" href="static/img/favicon/apple-icon-57x57.png">
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="static/img/favicon/apple-icon-60x60.png">
|
<link rel="apple-touch-icon" sizes="60x60" href="static/img/favicon/apple-icon-60x60.png">
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="static/img/favicon/apple-icon-72x72.png">
|
<link rel="apple-touch-icon" sizes="72x72" href="static/img/favicon/apple-icon-72x72.png">
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="static/img/favicon/apple-icon-76x76.png">
|
<link rel="apple-touch-icon" sizes="76x76" href="static/img/favicon/apple-icon-76x76.png">
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="static/img/favicon/apple-icon-114x114.png">
|
<link rel="apple-touch-icon" sizes="114x114" href="static/img/favicon/apple-icon-114x114.png">
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="static/img/favicon/apple-icon-120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" href="static/img/favicon/apple-icon-120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="static/img/favicon/apple-icon-144x144.png">
|
<link rel="apple-touch-icon" sizes="144x144" href="static/img/favicon/apple-icon-144x144.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="static/img/favicon/apple-icon-152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" href="static/img/favicon/apple-icon-152x152.png">
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="static/img/favicon/apple-icon-180x180.png">
|
<link rel="apple-touch-icon" sizes="180x180" href="static/img/favicon/apple-icon-180x180.png">
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="static/img/favicon/android-icon-192x192.png">
|
<link rel="icon" type="image/png" sizes="192x192" href="static/img/favicon/android-icon-192x192.png">
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon/favicon-32x32.png">
|
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="static/img/favicon/favicon-96x96.png">
|
<link rel="icon" type="image/png" sizes="96x96" href="static/img/favicon/favicon-96x96.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon/favicon-16x16.png">
|
||||||
<link rel="manifest" href="static/img/favicon/manifest.json">
|
<link rel="manifest" href="static/img/favicon/manifest.json">
|
||||||
<meta name="referrer" content="no-referrer">
|
<meta name="referrer" content="no-referrer">
|
||||||
<meta name="msapplication-TileColor" content="#ffffff">
|
<meta name="msapplication-TileColor" content="#ffffff">
|
||||||
<meta name="msapplication-TileImage" content="static/img/favicon/ms-icon-144x144.png">
|
<meta name="msapplication-TileImage" content="static/img/favicon/ms-icon-144x144.png">
|
||||||
<script type="text/javascript" src="static/js/autocomplete.js"></script>
|
<script type="text/javascript" src="static/js/autocomplete.js"></script>
|
||||||
<script type="text/javascript" src="static/js/controller.js"></script>
|
<script type="text/javascript" src="static/js/controller.js"></script>
|
||||||
<link rel="search" href="opensearch.xml" type="application/opensearchdescription+xml" title="Whoogle Search">
|
<link rel="search" href="opensearch.xml" type="application/opensearchdescription+xml" title="Whoogle Search">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="static/css/variables.css">
|
<link rel="stylesheet" href="static/css/variables.css">
|
||||||
<link rel="stylesheet" href="static/css/main.css">
|
<link rel="stylesheet" href="static/css/main.css">
|
||||||
<link rel="stylesheet" href="static/css/{{ 'dark' if config.dark else 'light' }}-theme.css"/>
|
<link rel="stylesheet" href="static/css/{{ 'dark' if config.dark else 'light' }}-theme.css"/>
|
||||||
<noscript>
|
<noscript>
|
||||||
<style>
|
<style>
|
||||||
#main { display: inherit !important; }
|
#main { display: inherit !important; }
|
||||||
.content { max-height: 720px; padding: 18px; border-radius: 10px; }
|
.content { max-height: 720px; padding: 18px; border-radius: 10px; }
|
||||||
.collapsible { display: none; }
|
.collapsible { display: none; }
|
||||||
</style>
|
</style>
|
||||||
</noscript>
|
</noscript>
|
||||||
<style>{{ config.style }}</style>
|
<style>{{ config.style }}</style>
|
||||||
<title>Whoogle Search</title>
|
<title>Whoogle Search</title>
|
||||||
</head>
|
</head>
|
||||||
<body id="main" style="display: none; background-color: {{ '#000' if config.dark else '#fff' }}">
|
<body id="main" style="display: none; background-color: {{ '#000' if config.dark else '#fff' }}">
|
||||||
<div class="search-container">
|
<div class="search-container">
|
||||||
<div class="logo-container">
|
<div class="logo-container">
|
||||||
{{ logo|safe }}
|
{{ logo|safe }}
|
||||||
</div>
|
</div>
|
||||||
<form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}">
|
<form id="search-form" action="search" method="{{ 'get' if config.get_only else 'post' }}">
|
||||||
<div class="search-fields">
|
<div class="search-fields">
|
||||||
<div class="autocomplete">
|
<div class="autocomplete">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="q"
|
name="q"
|
||||||
id="search-bar"
|
id="search-bar"
|
||||||
class="home-search"
|
class="home-search"
|
||||||
autofocus="autofocus"
|
autofocus="autofocus"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
autocomplete="off">
|
autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" id="search-submit" value="Search">
|
<input type="submit" id="search-submit" value="Search">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% if not config_disabled %}
|
{% if not config_disabled %}
|
||||||
<br/>
|
<br/>
|
||||||
<button id="config-collapsible" class="collapsible">Configuration</button>
|
<button id="config-collapsible" class="collapsible">Configuration</button>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="config-fields">
|
<div class="config-fields">
|
||||||
<form id="config-form" action="config" method="post">
|
<form id="config-form" action="config" method="post">
|
||||||
<div class="config-div config-div-ctry">
|
<div class="config-div config-div-ctry">
|
||||||
<label for="config-ctry">Filter Results by Country: </label>
|
<label for="config-ctry">Filter Results by Country: </label>
|
||||||
<select name="ctry" id="config-ctry">
|
<select name="ctry" id="config-ctry">
|
||||||
{% for ctry in countries %}
|
{% for ctry in countries %}
|
||||||
<option value="{{ ctry.value }}"
|
<option value="{{ ctry.value }}"
|
||||||
{% if ctry.value in config.ctry %}
|
{% if ctry.value in config.ctry %}
|
||||||
selected
|
selected
|
||||||
{% endif %}>
|
{% endif %}>
|
||||||
{{ ctry.name }}
|
{{ ctry.name }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
<div><span class="info-text"> — Note: If enabled, a website will only appear in the results if it is *hosted* in the selected country.</span></div>
|
<div><span class="info-text"> — Note: If enabled, a website will only appear in the results if it is *hosted* in the selected country.</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-lang">
|
<div class="config-div config-div-lang">
|
||||||
<label for="config-lang-interface">Interface Language: </label>
|
<label for="config-lang-interface">Interface Language: </label>
|
||||||
<select name="lang_interface" id="config-lang-interface">
|
<select name="lang_interface" id="config-lang-interface">
|
||||||
{% for lang in languages %}
|
{% for lang in languages %}
|
||||||
<option value="{{ lang.value }}"
|
<option value="{{ lang.value }}"
|
||||||
{% if lang.value in config.lang_interface %}
|
{% if lang.value in config.lang_interface %}
|
||||||
selected
|
selected
|
||||||
{% endif %}>
|
{% endif %}>
|
||||||
{{ lang.name }}
|
{{ lang.name }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-search-lang">
|
<div class="config-div config-div-search-lang">
|
||||||
<label for="config-lang-search">Search Language: </label>
|
<label for="config-lang-search">Search Language: </label>
|
||||||
<select name="lang_search" id="config-lang-search">
|
<select name="lang_search" id="config-lang-search">
|
||||||
{% for lang in languages %}
|
{% for lang in languages %}
|
||||||
<option value="{{ lang.value }}"
|
<option value="{{ lang.value }}"
|
||||||
{% if lang.value in config.lang_search %}
|
{% if lang.value in config.lang_search %}
|
||||||
selected
|
selected
|
||||||
{% endif %}>
|
{% endif %}>
|
||||||
{{ lang.name }}
|
{{ lang.name }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-near">
|
<div class="config-div config-div-near">
|
||||||
<label for="config-near">Near: </label>
|
<label for="config-near">Near: </label>
|
||||||
<input type="text" name="near" id="config-near" placeholder="City Name" value="{{ config.near }}">
|
<input type="text" name="near" id="config-near" placeholder="City Name" value="{{ config.near }}">
|
||||||
|
</div>
|
||||||
|
<div class="config-div config-div-block">
|
||||||
|
<label for="config-block">Block: </label>
|
||||||
|
<input type="text" name="block" id="config-block" placeholder="Comma-separated site list" value="{{ config.block }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-block">
|
|
||||||
<label for="config-block">Block: </label>
|
|
||||||
<input type="text" name="block" id="config-block" placeholder="Comma-separated site list" value="{{ config.block }}">
|
|
||||||
</div>
|
|
||||||
<div class="config-div config-div-nojs">
|
<div class="config-div config-div-nojs">
|
||||||
<label for="config-nojs">Show NoJS Links: </label>
|
<label for="config-nojs">Show NoJS Links: </label>
|
||||||
<input type="checkbox" name="nojs" id="config-nojs" {{ 'checked' if config.nojs else '' }}>
|
<input type="checkbox" name="nojs" id="config-nojs" {{ 'checked' if config.nojs else '' }}>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-dark">
|
<div class="config-div config-div-dark">
|
||||||
<label for="config-dark">Dark Mode: </label>
|
<label for="config-dark">Dark Mode: </label>
|
||||||
<input type="checkbox" name="dark" id="config-dark" {{ 'checked' if config.dark else '' }}>
|
<input type="checkbox" name="dark" id="config-dark" {{ 'checked' if config.dark else '' }}>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-safe">
|
<div class="config-div config-div-safe">
|
||||||
<label for="config-safe">Safe Search: </label>
|
<label for="config-safe">Safe Search: </label>
|
||||||
<input type="checkbox" name="safe" id="config-safe" {{ 'checked' if config.safe else '' }}>
|
<input type="checkbox" name="safe" id="config-safe" {{ 'checked' if config.safe else '' }}>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-alts">
|
<div class="config-div config-div-alts">
|
||||||
<label class="tooltip" for="config-alts">Replace Social Media Links: </label>
|
<label class="tooltip" for="config-alts">Replace Social Media Links: </label>
|
||||||
<input type="checkbox" name="alts" id="config-alts" {{ 'checked' if config.alts else '' }}>
|
<input type="checkbox" name="alts" id="config-alts" {{ 'checked' if config.alts else '' }}>
|
||||||
<div><span class="info-text"> — Replaces Twitter/YouTube/Instagram/Reddit links
|
<div><span class="info-text"> — Replaces Twitter/YouTube/Instagram/Reddit links
|
||||||
with Nitter/Invidious/Bibliogram/Libreddit links.</span></div>
|
with Nitter/Invidious/Bibliogram/Libreddit links.</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-new-tab">
|
<div class="config-div config-div-new-tab">
|
||||||
<label for="config-new-tab">Open Links in New Tab: </label>
|
<label for="config-new-tab">Open Links in New Tab: </label>
|
||||||
<input type="checkbox" name="new_tab" id="config-new-tab" {{ 'checked' if config.new_tab else '' }}>
|
<input type="checkbox" name="new_tab" id="config-new-tab" {{ 'checked' if config.new_tab else '' }}>
|
||||||
|
</div>
|
||||||
|
<div class="config-div config-div-view-image">
|
||||||
|
<label for="config-view-image">Full Size Image Search: </label>
|
||||||
|
<input type="checkbox" name="view_image" id="config-view-image" {{ 'checked' if config.view_image else '' }}>
|
||||||
|
<div><span class="info-text"> — (Experimental) Adds the "View Image" option on desktop to view full size images in search results.
|
||||||
|
This will cause image result thumbnails to be lower resolution.</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-tor">
|
<div class="config-div config-div-tor">
|
||||||
<label for="config-tor">Use Tor: {{ '' if tor_available else 'Unavailable' }}</label>
|
<label for="config-tor">Use Tor: {{ '' if tor_available else 'Unavailable' }}</label>
|
||||||
<input type="checkbox" name="tor" id="config-tor" {{ '' if tor_available else 'hidden' }} {{ 'checked' if config.tor else '' }}>
|
<input type="checkbox" name="tor" id="config-tor" {{ '' if tor_available else 'hidden' }} {{ 'checked' if config.tor else '' }}>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-get-only">
|
<div class="config-div config-div-get-only">
|
||||||
<label for="config-get-only">GET Requests Only: </label>
|
<label for="config-get-only">GET Requests Only: </label>
|
||||||
<input type="checkbox" name="get_only" id="config-get-only" {{ 'checked' if config.get_only else '' }}>
|
<input type="checkbox" name="get_only" id="config-get-only" {{ 'checked' if config.get_only else '' }}>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-root-url">
|
<div class="config-div config-div-root-url">
|
||||||
<label for="config-url">Root URL: </label>
|
<label for="config-url">Root URL: </label>
|
||||||
<input type="text" name="url" id="config-url" value="{{ config.url }}">
|
<input type="text" name="url" id="config-url" value="{{ config.url }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div config-div-custom-css">
|
<div class="config-div config-div-custom-css">
|
||||||
<label for="config-style">Custom CSS:</label>
|
<label for="config-style">Custom CSS:</label>
|
||||||
<textarea
|
<textarea
|
||||||
name="style"
|
name="style"
|
||||||
id="config-style"
|
id="config-style"
|
||||||
autocapitalize="off"
|
autocapitalize="off"
|
||||||
|
@ -155,23 +161,23 @@
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
value="">
|
value="">
|
||||||
{{ config.style }}
|
{{ config.style }}
|
||||||
</textarea>
|
</textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="config-div">
|
<div class="config-div">
|
||||||
<input type="submit" id="config-load" value="Load">
|
<input type="submit" id="config-load" value="Load">
|
||||||
<input type="submit" id="config-submit" value="Apply">
|
<input type="submit" id="config-submit" value="Apply">
|
||||||
<input type="submit" id="config-save" value="Save As...">
|
<input type="submit" id="config-save" value="Save As...">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<p style="color: {{ 'var(--whoogle-dark-text)' if config.dark else 'var(--whoogle-text)' }};">
|
<p style="color: {{ 'var(--whoogle-dark-text)' if config.dark else 'var(--whoogle-text)' }};">
|
||||||
Whoogle Search v{{ version_number }} ||
|
Whoogle Search v{{ version_number }} ||
|
||||||
<a id="gh-link" href="https://github.com/benbusby/whoogle-search">View on GitHub</a>
|
<a id="gh-link" href="https://github.com/benbusby/whoogle-search">View on GitHub</a>
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -119,11 +119,23 @@ class Search:
|
||||||
self.request_params,
|
self.request_params,
|
||||||
self.config,
|
self.config,
|
||||||
content_filter.near)
|
content_filter.near)
|
||||||
get_body = g.user_request.send(query=full_query)
|
|
||||||
|
# force mobile search when view image is true and
|
||||||
|
# the request is not already made by a mobile
|
||||||
|
view_image = ('tbm=isch' in full_query
|
||||||
|
and self.config.view_image
|
||||||
|
and not g.user_request.mobile)
|
||||||
|
|
||||||
|
get_body = g.user_request.send(query=full_query,
|
||||||
|
force_mobile=view_image)
|
||||||
|
|
||||||
# Produce cleanable html soup from response
|
# Produce cleanable html soup from response
|
||||||
html_soup = bsoup(content_filter.reskin(get_body.text), 'html.parser')
|
html_soup = bsoup(content_filter.reskin(get_body.text), 'html.parser')
|
||||||
|
|
||||||
|
# Replace current soup if view_image is active
|
||||||
|
if view_image:
|
||||||
|
html_soup = content_filter.view_image(html_soup)
|
||||||
|
|
||||||
# Indicate whether or not a Tor connection is active
|
# Indicate whether or not a Tor connection is active
|
||||||
tor_banner = bsoup('', 'html.parser')
|
tor_banner = bsoup('', 'html.parser')
|
||||||
if g.user_request.tor_valid:
|
if g.user_request.tor_valid:
|
||||||
|
|
|
@ -46,6 +46,9 @@
|
||||||
# Open results in new tab
|
# Open results in new tab
|
||||||
#WHOOGLE_CONFIG_NEW_TAB=1
|
#WHOOGLE_CONFIG_NEW_TAB=1
|
||||||
|
|
||||||
|
# Enable View Image option
|
||||||
|
#WHOOGLE_CONFIG_VIEW_IMAGE=1
|
||||||
|
|
||||||
# Search using GET requests only (exposes query in logs)
|
# Search using GET requests only (exposes query in logs)
|
||||||
#WHOOGLE_CONFIG_GET_ONLY=1
|
#WHOOGLE_CONFIG_GET_ONLY=1
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue