Compare commits
No commits in common. 'main' and 'v0.5.4' have entirely different histories.
@ -1,38 +0,0 @@
|
||||
---
|
||||
name: New theme
|
||||
about: Create a new theme for Whoogle
|
||||
title: "[THEME] <your theme name>"
|
||||
labels: theme
|
||||
assignees: benbusby
|
||||
|
||||
---
|
||||
|
||||
Use the following template to design your theme, replacing the blank spaces with the colors of your choice.
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* LIGHT THEME COLORS */
|
||||
--whoogle-logo: #______;
|
||||
--whoogle-page-bg: #______;
|
||||
--whoogle-element-bg: #______;
|
||||
--whoogle-text: #______;
|
||||
--whoogle-contrast-text: #______;
|
||||
--whoogle-secondary-text: #______;
|
||||
--whoogle-result-bg: #______;
|
||||
--whoogle-result-title: #______;
|
||||
--whoogle-result-url: #______;
|
||||
--whoogle-result-visited: #______;
|
||||
|
||||
/* DARK THEME COLORS */
|
||||
--whoogle-dark-logo: #______;
|
||||
--whoogle-dark-page-bg: #______;
|
||||
--whoogle-dark-element-bg: #______;
|
||||
--whoogle-dark-text: #______;
|
||||
--whoogle-dark-contrast-text: #______;
|
||||
--whoogle-dark-secondary-text: #______;
|
||||
--whoogle-dark-result-bg: #______;
|
||||
--whoogle-dark-result-title: #______;
|
||||
--whoogle-dark-result-url: #______;
|
||||
--whoogle-dark-result-visited: #______;
|
||||
}
|
||||
```
|
@ -1,28 +0,0 @@
|
||||
name: docker_main
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["tests"]
|
||||
branches: [main]
|
||||
types:
|
||||
- completed
|
||||
|
||||
# TODO: Needs refactoring to use reusable workflows and share w/ docker_tests
|
||||
jobs:
|
||||
on-success:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: build and test (docker)
|
||||
run: |
|
||||
docker build --tag whoogle-search:test .
|
||||
docker run --publish 5000:5000 --detach --name whoogle-search-nocompose whoogle-search:test
|
||||
sleep 15
|
||||
docker exec whoogle-search-nocompose curl -f http://localhost:5000/healthz || exit 1
|
||||
- name: build and test (docker-compose)
|
||||
run: |
|
||||
docker rm -f whoogle-search-nocompose
|
||||
WHOOGLE_IMAGE="whoogle-search:test" docker-compose up --detach
|
||||
sleep 15
|
||||
docker exec whoogle-search curl -f http://localhost:5000/healthz || exit 1
|
@ -1,26 +0,0 @@
|
||||
name: docker_tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
pull_request:
|
||||
branches: main
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: build and test (docker)
|
||||
run: |
|
||||
docker build --tag whoogle-search:test .
|
||||
docker run --publish 5000:5000 --detach --name whoogle-search-nocompose whoogle-search:test
|
||||
sleep 15
|
||||
docker exec whoogle-search-nocompose curl -f http://localhost:5000/healthz || exit 1
|
||||
- name: build and test (docker-compose)
|
||||
run: |
|
||||
docker rm -f whoogle-search-nocompose
|
||||
WHOOGLE_IMAGE="whoogle-search:test" docker-compose up --detach
|
||||
sleep 15
|
||||
docker exec whoogle-search curl -f http://localhost:5000/healthz || exit 1
|
@ -0,0 +1,22 @@
|
||||
name: pep8
|
||||
|
||||
on:
|
||||
push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pycodestyle
|
||||
- name: Run pycodestyle
|
||||
run: |
|
||||
pycodestyle --show-source --show-pep8 app/*
|
||||
pycodestyle --show-source --show-pep8 test/*
|
@ -1,68 +0,0 @@
|
||||
name: pypi
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: main
|
||||
tags: v*
|
||||
|
||||
jobs:
|
||||
publish-test:
|
||||
name: Build and publish to TestPyPI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install pypa/build
|
||||
run: >-
|
||||
python -m
|
||||
pip install
|
||||
build
|
||||
setuptools
|
||||
--user
|
||||
- name: Set dev timestamp
|
||||
run: echo "DEV_BUILD=$(date +%s)" >> $GITHUB_ENV
|
||||
- name: Build binary wheel and source tarball
|
||||
run: >-
|
||||
python -m
|
||||
build
|
||||
--sdist
|
||||
--wheel
|
||||
--outdir dist/
|
||||
.
|
||||
- name: Publish distribution to TestPyPI
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
||||
repository_url: https://test.pypi.org/legacy/
|
||||
publish:
|
||||
name: Build and publish to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.9
|
||||
- name: Install pypa/build
|
||||
run: >-
|
||||
python -m
|
||||
pip install
|
||||
build
|
||||
--user
|
||||
- name: Build binary wheel and source tarball
|
||||
run: >-
|
||||
python -m
|
||||
build
|
||||
--sdist
|
||||
--wheel
|
||||
--outdir dist/
|
||||
.
|
||||
- name: Publish distribution to PyPI
|
||||
if: startsWith(github.ref, 'refs/tags')
|
||||
uses: pypa/gh-action-pypi-publish@master
|
||||
with:
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
@ -1,19 +0,0 @@
|
||||
name: scan
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
scan:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build the container image
|
||||
run: |
|
||||
docker build --tag whoogle-search:test .
|
||||
- name: Initiate grype scan
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b .
|
||||
chmod +x ./grype
|
||||
./grype whoogle-search:test --only-fixed
|
@ -1,17 +0,0 @@
|
||||
name: tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: pip install --upgrade pip && pip install -r requirements.txt
|
||||
- name: Run tests
|
||||
run: ./run test
|
@ -1 +1,3 @@
|
||||
entrypoint = "misc/replit.py"
|
||||
language = "python3"
|
||||
run = "./run"
|
||||
onBoot = "./run"
|
||||
|
@ -0,0 +1,15 @@
|
||||
language: python
|
||||
python: 3.6
|
||||
before_install:
|
||||
- sudo apt-get -y install libgnutls28-dev
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
script:
|
||||
- "./run test"
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: __token__
|
||||
password:
|
||||
secure: WNEH2Gg84MZF/AZEberFDGPPWb4cYyHAeD/XV8En94QRSI9Aznz6qiDKOvV4eVgjMAIEW5uB3TL1LHf6KU+Hrg6SmhF7JquqP1gsBOCDNFPTljO+k2Hc53uDdSnhi/HLgY7cnFNX4lc2nNrbyxZxMHuSA2oNz/tosyNGBEeyU+JA5va7uX0albGsLiNjimO4aeau83fsI0Hn2eN6ag68pewUMXNxzpyTeO2bRcCd5d5iILs07jMVwFoC2j7W11oNqrVuSWAs8CPe4+kwvNvXWxljUGiBGppNZ7RAsKNLwi6U6kGGUTWjQm09rY/2JBpJ2WEGmIWGIrno75iiFRbjnRp3mnXPvtVTyWhh+hQIUd7bJOVKM34i9eHotYTrkMJObgW1gnRzvI9VYldtgL/iP/Isn2Pv2EeMX8V+C9/8pxv0jkQkZMnFhE6gGlzpz37zTl04B2J7xyV5znM35Lx2Pn3zxdcmdCvD3yT8I4MuBbKqq2/v4emYCfPfOmfwnS0BEVSqr9lbx4xfUZV76tcvLcj4n86DJbx77pA2Ch8FRprpOOBcf0WuqTbZp8c3mb8prFp2EupUknXu7+C2VQ6sqrnzNuDeTGm/nyjjRQ81rlvlD4tqkwsEGEDDO44FF2eUTc5D2MvoHs4cnz095FWjy63gn5IxUjhMi31b5tGRz2Q=
|
||||
on:
|
||||
tags: true
|
@ -1,22 +0,0 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Endpoint(Enum):
|
||||
autocomplete = 'autocomplete'
|
||||
home = 'home'
|
||||
healthz = 'healthz'
|
||||
config = 'config'
|
||||
opensearch = 'opensearch.xml'
|
||||
search = 'search'
|
||||
search_html = 'search.html'
|
||||
url = 'url'
|
||||
imgres = 'imgres'
|
||||
element = 'element'
|
||||
window = 'window'
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
def in_path(self, path: str) -> bool:
|
||||
return path.startswith(self.value) or \
|
||||
path.startswith(f'/{self.value}')
|
@ -1,47 +0,0 @@
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
class GClasses:
|
||||
"""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'
|
||||
footer = 'TuS8Ad'
|
||||
result_class_a = 'ZINbbc'
|
||||
result_class_b = 'luh4td'
|
||||
scroller_class = 'idg8be'
|
||||
|
||||
result_classes = {
|
||||
result_class_a: ['Gx5Zad'],
|
||||
result_class_b: ['fP1Qef']
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def replace_css_classes(cls, soup: BeautifulSoup) -> BeautifulSoup:
|
||||
"""Replace updated Google classes with the original class names that
|
||||
Whoogle relies on for styling.
|
||||
|
||||
Args:
|
||||
soup: The result page as a BeautifulSoup object
|
||||
|
||||
Returns:
|
||||
BeautifulSoup: The new BeautifulSoup
|
||||
"""
|
||||
result_divs = soup.find_all('div', {
|
||||
'class': [_ for c in cls.result_classes.values() for _ in c]
|
||||
})
|
||||
|
||||
for div in result_divs:
|
||||
new_class = ' '.join(div['class'])
|
||||
for key, val in cls.result_classes.items():
|
||||
new_class = ' '.join(new_class.replace(_, key) for _ in val)
|
||||
div['class'] = new_class.split(' ')
|
||||
return soup
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"!i": {
|
||||
"url": "search?q={}&tbm=isch",
|
||||
"suggestion": "!i (Whoogle Images)"
|
||||
},
|
||||
"!v": {
|
||||
"url": "search?q={}&tbm=vid",
|
||||
"suggestion": "!v (Whoogle Videos)"
|
||||
},
|
||||
"!n": {
|
||||
"url": "search?q={}&tbm=nws",
|
||||
"suggestion": "!n (Whoogle News)"
|
||||
}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
@ -1,9 +0,0 @@
|
||||
html {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
html {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
const convert = (n1, n2, conversionFactor) => {
|
||||
// id's for currency input boxes
|
||||
let id1 = "cb" + n1;
|
||||
let id2 = "cb" + n2;
|
||||
// getting the value of the input box that just got filled
|
||||
let inputBox = document.getElementById(id1).value;
|
||||
// updating the other input box after conversion
|
||||
document.getElementById(id2).value = ((inputBox * conversionFactor).toFixed(2));
|
||||
}
|
@ -1,67 +1,11 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const advSearchToggle = document.getElementById("adv-search-toggle");
|
||||
const advSearchDiv = document.getElementById("adv-search-div");
|
||||
const searchBar = document.getElementById("search-bar");
|
||||
const countrySelect = document.getElementById("result-country");
|
||||
const timePeriodSelect = document.getElementById("result-time-period");
|
||||
const arrowKeys = [37, 38, 39, 40];
|
||||
let searchValue = searchBar.value;
|
||||
|
||||
countrySelect.onchange = () => {
|
||||
let str = window.location.href;
|
||||
n = str.lastIndexOf("/search");
|
||||
if (n > 0) {
|
||||
str = str.substring(0, n) + `/search?q=${searchBar.value}`;
|
||||
str = tackOnParams(str);
|
||||
window.location.href = str;
|
||||
}
|
||||
}
|
||||
|
||||
timePeriodSelect.onchange = () => {
|
||||
let str = window.location.href;
|
||||
n = str.lastIndexOf("/search");
|
||||
if (n > 0) {
|
||||
str = str.substring(0, n) + `/search?q=${searchBar.value}`;
|
||||
str = tackOnParams(str);
|
||||
window.location.href = str;
|
||||
}
|
||||
}
|
||||
|
||||
function tackOnParams(str) {
|
||||
if (timePeriodSelect.value != "") {
|
||||
str = str + `&tbs=${timePeriodSelect.value}`;
|
||||
}
|
||||
if (countrySelect.value != "") {
|
||||
str = str + `&country=${countrySelect.value}`;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const toggleAdvancedSearch = on => {
|
||||
if (on) {
|
||||
advSearchDiv.style.maxHeight = "70px";
|
||||
searchBar.addEventListener("keyup", function (event) {
|
||||
if (event.keyCode !== 13) {
|
||||
handleUserInput(searchBar);
|
||||
} else {
|
||||
advSearchDiv.style.maxHeight = "0px";
|
||||
}
|
||||
localStorage.advSearchToggled = on;
|
||||
}
|
||||
|
||||
try {
|
||||
toggleAdvancedSearch(JSON.parse(localStorage.advSearchToggled));
|
||||
} catch (error) {
|
||||
console.warn("Did not recover advanced search toggle state");
|
||||
}
|
||||
|
||||
advSearchToggle.onclick = () => {
|
||||
toggleAdvancedSearch(advSearchToggle.checked);
|
||||
}
|
||||
|
||||
searchBar.addEventListener("keyup", function(event) {
|
||||
if (event.keyCode === 13) {
|
||||
document.getElementById("search-form").submit();
|
||||
} else if (searchBar.value !== searchValue && !arrowKeys.includes(event.keyCode)) {
|
||||
searchValue = searchBar.value;
|
||||
handleUserInput();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1,247 +1,248 @@
|
||||
[
|
||||
{"name": "-------", "value": ""},
|
||||
{"name": "Afghanistan", "value": "AF"},
|
||||
{"name": "Albania", "value": "AL"},
|
||||
{"name": "Algeria", "value": "DZ"},
|
||||
{"name": "American Samoa", "value": "AS"},
|
||||
{"name": "Andorra", "value": "AD"},
|
||||
{"name": "Angola", "value": "AO"},
|
||||
{"name": "Anguilla", "value": "AI"},
|
||||
{"name": "Antarctica", "value": "AQ"},
|
||||
{"name": "Antigua and Barbuda", "value": "AG"},
|
||||
{"name": "Argentina", "value": "AR"},
|
||||
{"name": "Armenia", "value": "AM"},
|
||||
{"name": "Aruba", "value": "AW"},
|
||||
{"name": "Australia", "value": "AU"},
|
||||
{"name": "Austria", "value": "AT"},
|
||||
{"name": "Azerbaijan", "value": "AZ"},
|
||||
{"name": "Bahamas", "value": "BS"},
|
||||
{"name": "Bahrain", "value": "BH"},
|
||||
{"name": "Bangladesh", "value": "BD"},
|
||||
{"name": "Barbados", "value": "BB"},
|
||||
{"name": "Belarus", "value": "BY"},
|
||||
{"name": "Belgium", "value": "BE"},
|
||||
{"name": "Belize", "value": "BZ"},
|
||||
{"name": "Benin", "value": "BJ"},
|
||||
{"name": "Bermuda", "value": "BM"},
|
||||
{"name": "Bhutan", "value": "BT"},
|
||||
{"name": "Bolivia", "value": "BO"},
|
||||
{"name": "Bosnia and Herzegovina", "value": "BA"},
|
||||
{"name": "Botswana", "value": "BW"},
|
||||
{"name": "Bouvet Island", "value": "BV"},
|
||||
{"name": "Brazil", "value": "BR"},
|
||||
{"name": "British Indian Ocean Territory", "value": "IO"},
|
||||
{"name": "Brunei Darussalam", "value": "BN"},
|
||||
{"name": "Bulgaria", "value": "BG"},
|
||||
{"name": "Burkina Faso", "value": "BF"},
|
||||
{"name": "Burundi", "value": "BI"},
|
||||
{"name": "Cambodia", "value": "KH"},
|
||||
{"name": "Cameroon", "value": "CM"},
|
||||
{"name": "Canada", "value": "CA"},
|
||||
{"name": "Cape Verde", "value": "CV"},
|
||||
{"name": "Cayman Islands", "value": "KY"},
|
||||
{"name": "Central African Republic", "value": "CF"},
|
||||
{"name": "Chad", "value": "TD"},
|
||||
{"name": "Chile", "value": "CL"},
|
||||
{"name": "China", "value": "CN"},
|
||||
{"name": "Christmas Island", "value": "CX"},
|
||||
{"name": "Cocos (Keeling) Islands", "value": "CC"},
|
||||
{"name": "Colombia", "value": "CO"},
|
||||
{"name": "Comoros", "value": "KM"},
|
||||
{"name": "Congo", "value": "CG"},
|
||||
{"name": "Congo, Democratic Republic of the", "value": "CD"},
|
||||
{"name": "Cook Islands", "value": "CK"},
|
||||
{"name": "Costa Rica", "value": "CR"},
|
||||
{"name": "Cote D'ivoire", "value": "CI"},
|
||||
{"name": "Croatia (Hrvatska)", "value": "HR"},
|
||||
{"name": "Cuba", "value": "CU"},
|
||||
{"name": "Cyprus", "value": "CY"},
|
||||
{"name": "Czech Republic", "value": "CZ"},
|
||||
{"name": "Denmark", "value": "DK"},
|
||||
{"name": "Djibouti", "value": "DJ"},
|
||||
{"name": "Dominica", "value": "DM"},
|
||||
{"name": "Dominican Republic", "value": "DO"},
|
||||
{"name": "East Timor", "value": "TP"},
|
||||
{"name": "Ecuador", "value": "EC"},
|
||||
{"name": "Egypt", "value": "EG"},
|
||||
{"name": "El Salvador", "value": "SV"},
|
||||
{"name": "Equatorial Guinea", "value": "GQ"},
|
||||
{"name": "Eritrea", "value": "ER"},
|
||||
{"name": "Estonia", "value": "EE"},
|
||||
{"name": "Ethiopia", "value": "ET"},
|
||||
{"name": "European Union", "value": "EU"},
|
||||
{"name": "Falkland Islands (Malvinas)", "value": "FK"},
|
||||
{"name": "Faroe Islands", "value": "FO"},
|
||||
{"name": "Fiji", "value": "FJ"},
|
||||
{"name": "Finland", "value": "FI"},
|
||||
{"name": "France", "value": "FR"},
|
||||
{"name": "France, Metropolitan", "value": "FX"},
|
||||
{"name": "French Guiana", "value": "GF"},
|
||||
{"name": "French Polynesia", "value": "PF"},
|
||||
{"name": "French Southern Territories", "value": "TF"},
|
||||
{"name": "Gabon", "value": "GA"},
|
||||
{"name": "Gambia", "value": "GM"},
|
||||
{"name": "Georgia", "value": "GE"},
|
||||
{"name": "Germany", "value": "DE"},
|
||||
{"name": "Ghana", "value": "GH"},
|
||||
{"name": "Gibraltar", "value": "GI"},
|
||||
{"name": "Greece", "value": "GR"},
|
||||
{"name": "Greenland", "value": "GL"},
|
||||
{"name": "Grenada", "value": "GD"},
|
||||
{"name": "Guadeloupe", "value": "GP"},
|
||||
{"name": "Guam", "value": "GU"},
|
||||
{"name": "Guatemala", "value": "GT"},
|
||||
{"name": "Guinea", "value": "GN"},
|
||||
{"name": "Guinea-Bissau", "value": "GW"},
|
||||
{"name": "Guyana", "value": "GY"},
|
||||
{"name": "Haiti", "value": "HT"},
|
||||
{"name": "Heard Island and Mcdonald Islands", "value": "HM"},
|
||||
{"name": "Holy See (Vatican City State)", "value": "VA"},
|
||||
{"name": "Honduras", "value": "HN"},
|
||||
{"name": "Hong Kong", "value": "HK"},
|
||||
{"name": "Hungary", "value": "HU"},
|
||||
{"name": "Iceland", "value": "IS"},
|
||||
{"name": "India", "value": "IN"},
|
||||
{"name": "Indonesia", "value": "ID"},
|
||||
{"name": "Iran, Islamic Republic of", "value": "IR"},
|
||||
{"name": "Iraq", "value": "IQ"},
|
||||
{"name": "Ireland", "value": "IE"},
|
||||
{"name": "Israel", "value": "IL"},
|
||||
{"name": "Italy", "value": "IT"},
|
||||
{"name": "Jamaica", "value": "JM"},
|
||||
{"name": "Japan", "value": "JP"},
|
||||
{"name": "Jordan", "value": "JO"},
|
||||
{"name": "Kazakhstan", "value": "KZ"},
|
||||
{"name": "Kenya", "value": "KE"},
|
||||
{"name": "Kiribati", "value": "KI"},
|
||||
{"name": "Korea, Democratic People's Republic of", "value": "KP"},
|
||||
{"name": "Korea, Republic of", "value": "KR"},
|
||||
{"name": "Kuwait", "value": "KW"},
|
||||
{"name": "Kyrgyzstan", "value": "KG"},
|
||||
{"name": "Lao People's Democratic Republic", "value": "LA"},
|
||||
{"name": "Latvia", "value": "LV"},
|
||||
{"name": "Lebanon", "value": "LB"},
|
||||
{"name": "Lesotho", "value": "LS"},
|
||||
{"name": "Liberia", "value": "LR"},
|
||||
{"name": "Libyan Arab Jamahiriya", "value": "LY"},
|
||||
{"name": "Liechtenstein", "value": "LI"},
|
||||
{"name": "Lithuania", "value": "LT"},
|
||||
{"name": "Luxembourg", "value": "LU"},
|
||||
{"name": "Macao", "value": "MO"},
|
||||
{"name": "Afghanistan", "value": "countryAF"},
|
||||
{"name": "Albania", "value": "countryAL"},
|
||||
{"name": "Algeria", "value": "countryDZ"},
|
||||
{"name": "American Samoa", "value": "countryAS"},
|
||||
{"name": "Andorra", "value": "countryAD"},
|
||||
{"name": "Angola", "value": "countryAO"},
|
||||
{"name": "Anguilla", "value": "countryAI"},
|
||||
{"name": "Antarctica", "value": "countryAQ"},
|
||||
{"name": "Antigua and Barbuda", "value": "countryAG"},
|
||||
{"name": "Argentina", "value": "countryAR"},
|
||||
{"name": "Armenia", "value": "countryAM"},
|
||||
{"name": "Aruba", "value": "countryAW"},
|
||||
{"name": "Australia", "value": "countryAU"},
|
||||
{"name": "Austria", "value": "countryAT"},
|
||||
{"name": "Azerbaijan", "value": "countryAZ"},
|
||||
{"name": "Bahamas", "value": "countryBS"},
|
||||
{"name": "Bahrain", "value": "countryBH"},
|
||||
{"name": "Bangladesh", "value": "countryBD"},
|
||||
{"name": "Barbados", "value": "countryBB"},
|
||||
{"name": "Belarus", "value": "countryBY"},
|
||||
{"name": "Belgium", "value": "countryBE"},
|
||||
{"name": "Belize", "value": "countryBZ"},
|
||||
{"name": "Benin", "value": "countryBJ"},
|
||||
{"name": "Bermuda", "value": "countryBM"},
|
||||
{"name": "Bhutan", "value": "countryBT"},
|
||||
{"name": "Bolivia", "value": "countryBO"},
|
||||
{"name": "Bosnia and Herzegovina", "value": "countryBA"},
|
||||
{"name": "Botswana", "value": "countryBW"},
|
||||
{"name": "Bouvet Island", "value": "countryBV"},
|
||||
{"name": "Brazil", "value": "countryBR"},
|
||||
{"name": "British Indian Ocean Territory", "value": "countryIO"},
|
||||
{"name": "Brunei Darussalam", "value": "countryBN"},
|
||||
{"name": "Bulgaria", "value": "countryBG"},
|
||||
{"name": "Burkina Faso", "value": "countryBF"},
|
||||
{"name": "Burundi", "value": "countryBI"},
|
||||
{"name": "Cambodia", "value": "countryKH"},
|
||||
{"name": "Cameroon", "value": "countryCM"},
|
||||
{"name": "Canada", "value": "countryCA"},
|
||||
{"name": "Cape Verde", "value": "countryCV"},
|
||||
{"name": "Cayman Islands", "value": "countryKY"},
|
||||
{"name": "Central African Republic", "value": "countryCF"},
|
||||
{"name": "Chad", "value": "countryTD"},
|
||||
{"name": "Chile", "value": "countryCL"},
|
||||
{"name": "China", "value": "countryCN"},
|
||||
{"name": "Christmas Island", "value": "countryCX"},
|
||||
{"name": "Cocos (Keeling) Islands", "value": "countryCC"},
|
||||
{"name": "Colombia", "value": "countryCO"},
|
||||
{"name": "Comoros", "value": "countryKM"},
|
||||
{"name": "Congo", "value": "countryCG"},
|
||||
{"name": "Congo, Democratic Republic of the", "value": "countryCD"},
|
||||
{"name": "Cook Islands", "value": "countryCK"},
|
||||
{"name": "Costa Rica", "value": "countryCR"},
|
||||
{"name": "Cote D\"ivoire", "value": "countryCI"},
|
||||
{"name": "Croatia (Hrvatska)", "value": "countryHR"},
|
||||
{"name": "Cuba", "value": "countryCU"},
|
||||
{"name": "Cyprus", "value": "countryCY"},
|
||||
{"name": "Czech Republic", "value": "countryCZ"},
|
||||
{"name": "Denmark", "value": "countryDK"},
|
||||
{"name": "Djibouti", "value": "countryDJ"},
|
||||
{"name": "Dominica", "value": "countryDM"},
|
||||
{"name": "Dominican Republic", "value": "countryDO"},
|
||||
{"name": "East Timor", "value": "countryTP"},
|
||||
{"name": "Ecuador", "value": "countryEC"},
|
||||
{"name": "Egypt", "value": "countryEG"},
|
||||
{"name": "El Salvador", "value": "countrySV"},
|
||||
{"name": "Equatorial Guinea", "value": "countryGQ"},
|
||||
{"name": "Eritrea", "value": "countryER"},
|
||||
{"name": "Estonia", "value": "countryEE"},
|
||||
{"name": "Ethiopia", "value": "countryET"},
|
||||
{"name": "European Union", "value": "countryEU"},
|
||||
{"name": "Falkland Islands (Malvinas)", "value": "countryFK"},
|
||||
{"name": "Faroe Islands", "value": "countryFO"},
|
||||
{"name": "Fiji", "value": "countryFJ"},
|
||||
{"name": "Finland", "value": "countryFI"},
|
||||
{"name": "France", "value": "countryFR"},
|
||||
{"name": "France, Metropolitan", "value": "countryFX"},
|
||||
{"name": "French Guiana", "value": "countryGF"},
|
||||
{"name": "French Polynesia", "value": "countryPF"},
|
||||
{"name": "French Southern Territories", "value": "countryTF"},
|
||||
{"name": "Gabon", "value": "countryGA"},
|
||||
{"name": "Gambia", "value": "countryGM"},
|
||||
{"name": "Georgia", "value": "countryGE"},
|
||||
{"name": "Germany", "value": "countryDE"},
|
||||
{"name": "Ghana", "value": "countryGH"},
|
||||
{"name": "Gibraltar", "value": "countryGI"},
|
||||
{"name": "Greece", "value": "countryGR"},
|
||||
{"name": "Greenland", "value": "countryGL"},
|
||||
{"name": "Grenada", "value": "countryGD"},
|
||||
{"name": "Guadeloupe", "value": "countryGP"},
|
||||
{"name": "Guam", "value": "countryGU"},
|
||||
{"name": "Guatemala", "value": "countryGT"},
|
||||
{"name": "Guinea", "value": "countryGN"},
|
||||
{"name": "Guinea-Bissau", "value": "countryGW"},
|
||||
{"name": "Guyana", "value": "countryGY"},
|
||||
{"name": "Haiti", "value": "countryHT"},
|
||||
{"name": "Heard Island and Mcdonald Islands", "value": "countryHM"},
|
||||
{"name": "Holy See (Vatican City State)", "value": "countryVA"},
|
||||
{"name": "Honduras", "value": "countryHN"},
|
||||
{"name": "Hong Kong", "value": "countryHK"},
|
||||
{"name": "Hungary", "value": "countryHU"},
|
||||
{"name": "Iceland", "value": "countryIS"},
|
||||
{"name": "India", "value": "countryIN"},
|
||||
{"name": "Indonesia", "value": "countryID"},
|
||||
{"name": "Iran, Islamic Republic of", "value": "countryIR"},
|
||||
{"name": "Iraq", "value": "countryIQ"},
|
||||
{"name": "Ireland", "value": "countryIE"},
|
||||
{"name": "Israel", "value": "countryIL"},
|
||||
{"name": "Italy", "value": "countryIT"},
|
||||
{"name": "Jamaica", "value": "countryJM"},
|
||||
{"name": "Japan", "value": "countryJP"},
|
||||
{"name": "Jordan", "value": "countryJO"},
|
||||
{"name": "Kazakhstan", "value": "countryKZ"},
|
||||
{"name": "Kenya", "value": "countryKE"},
|
||||
{"name": "Kiribati", "value": "countryKI"},
|
||||
{"name": "Korea, Democratic People\"s Republic of",
|
||||
"value": "countryKP"},
|
||||
{"name": "Korea, Republic of", "value": "countryKR"},
|
||||
{"name": "Kuwait", "value": "countryKW"},
|
||||
{"name": "Kyrgyzstan", "value": "countryKG"},
|
||||
{"name": "Lao People\"s Democratic Republic", "value": "countryLA"},
|
||||
{"name": "Latvia", "value": "countryLV"},
|
||||
{"name": "Lebanon", "value": "countryLB"},
|
||||
{"name": "Lesotho", "value": "countryLS"},
|
||||
{"name": "Liberia", "value": "countryLR"},
|
||||
{"name": "Libyan Arab Jamahiriya", "value": "countryLY"},
|
||||
{"name": "Liechtenstein", "value": "countryLI"},
|
||||
{"name": "Lithuania", "value": "countryLT"},
|
||||
{"name": "Luxembourg", "value": "countryLU"},
|
||||
{"name": "Macao", "value": "countryMO"},
|
||||
{"name": "Macedonia, the Former Yugosalv Republic of",
|
||||
"value": "MK"},
|
||||
{"name": "Madagascar", "value": "MG"},
|
||||
{"name": "Malawi", "value": "MW"},
|
||||
{"name": "Malaysia", "value": "MY"},
|
||||
{"name": "Maldives", "value": "MV"},
|
||||
{"name": "Mali", "value": "ML"},
|
||||
{"name": "Malta", "value": "MT"},
|
||||
{"name": "Marshall Islands", "value": "MH"},
|
||||
{"name": "Martinique", "value": "MQ"},
|
||||
{"name": "Mauritania", "value": "MR"},
|
||||
{"name": "Mauritius", "value": "MU"},
|
||||
{"name": "Mayotte", "value": "YT"},
|
||||
{"name": "Mexico", "value": "MX"},
|
||||
{"name": "Micronesia, Federated States of", "value": "FM"},
|
||||
{"name": "Moldova, Republic of", "value": "MD"},
|
||||
{"name": "Monaco", "value": "MC"},
|
||||
{"name": "Mongolia", "value": "MN"},
|
||||
{"name": "Montserrat", "value": "MS"},
|
||||
{"name": "Morocco", "value": "MA"},
|
||||
{"name": "Mozambique", "value": "MZ"},
|
||||
{"name": "Myanmar", "value": "MM"},
|
||||
{"name": "Namibia", "value": "NA"},
|
||||
{"name": "Nauru", "value": "NR"},
|
||||
{"name": "Nepal", "value": "NP"},
|
||||
{"name": "Netherlands", "value": "NL"},
|
||||
{"name": "Netherlands Antilles", "value": "AN"},
|
||||
{"name": "New Caledonia", "value": "NC"},
|
||||
{"name": "New Zealand", "value": "NZ"},
|
||||
{"name": "Nicaragua", "value": "NI"},
|
||||
{"name": "Niger", "value": "NE"},
|
||||
{"name": "Nigeria", "value": "NG"},
|
||||
{"name": "Niue", "value": "NU"},
|
||||
{"name": "Norfolk Island", "value": "NF"},
|
||||
{"name": "Northern Mariana Islands", "value": "MP"},
|
||||
{"name": "Norway", "value": "NO"},
|
||||
{"name": "Oman", "value": "OM"},
|
||||
{"name": "Pakistan", "value": "PK"},
|
||||
{"name": "Palau", "value": "PW"},
|
||||
{"name": "Palestinian Territory", "value": "PS"},
|
||||
{"name": "Panama", "value": "PA"},
|
||||
{"name": "Papua New Guinea", "value": "PG"},
|
||||
{"name": "Paraguay", "value": "PY"},
|
||||
{"name": "Peru", "value": "PE"},
|
||||
{"name": "Philippines", "value": "PH"},
|
||||
{"name": "Pitcairn", "value": "PN"},
|
||||
{"name": "Poland", "value": "PL"},
|
||||
{"name": "Portugal", "value": "PT"},
|
||||
{"name": "Puerto Rico", "value": "PR"},
|
||||
{"name": "Qatar", "value": "QA"},
|
||||
{"name": "Reunion", "value": "RE"},
|
||||
{"name": "Romania", "value": "RO"},
|
||||
{"name": "Russian Federation", "value": "RU"},
|
||||
{"name": "Rwanda", "value": "RW"},
|
||||
{"name": "Saint Helena", "value": "SH"},
|
||||
{"name": "Saint Kitts and Nevis", "value": "KN"},
|
||||
{"name": "Saint Lucia", "value": "LC"},
|
||||
{"name": "Saint Pierre and Miquelon", "value": "PM"},
|
||||
{"name": "Saint Vincent and the Grenadines", "value": "VC"},
|
||||
{"name": "Samoa", "value": "WS"},
|
||||
{"name": "San Marino", "value": "SM"},
|
||||
{"name": "Sao Tome and Principe", "value": "ST"},
|
||||
{"name": "Saudi Arabia", "value": "SA"},
|
||||
{"name": "Senegal", "value": "SN"},
|
||||
{"name": "Serbia and Montenegro", "value": "CS"},
|
||||
{"name": "Seychelles", "value": "SC"},
|
||||
{"name": "Sierra Leone", "value": "SL"},
|
||||
{"name": "Singapore", "value": "SG"},
|
||||
{"name": "Slovakia", "value": "SK"},
|
||||
{"name": "Slovenia", "value": "SI"},
|
||||
{"name": "Solomon Islands", "value": "SB"},
|
||||
{"name": "Somalia", "value": "SO"},
|
||||
{"name": "South Africa", "value": "ZA"},
|
||||
"value": "countryMK"},
|
||||
{"name": "Madagascar", "value": "countryMG"},
|
||||
{"name": "Malawi", "value": "countryMW"},
|
||||
{"name": "Malaysia", "value": "countryMY"},
|
||||
{"name": "Maldives", "value": "countryMV"},
|
||||
{"name": "Mali", "value": "countryML"},
|
||||
{"name": "Malta", "value": "countryMT"},
|
||||
{"name": "Marshall Islands", "value": "countryMH"},
|
||||
{"name": "Martinique", "value": "countryMQ"},
|
||||
{"name": "Mauritania", "value": "countryMR"},
|
||||
{"name": "Mauritius", "value": "countryMU"},
|
||||
{"name": "Mayotte", "value": "countryYT"},
|
||||
{"name": "Mexico", "value": "countryMX"},
|
||||
{"name": "Micronesia, Federated States of", "value": "countryFM"},
|
||||
{"name": "Moldova, Republic of", "value": "countryMD"},
|
||||
{"name": "Monaco", "value": "countryMC"},
|
||||
{"name": "Mongolia", "value": "countryMN"},
|
||||
{"name": "Montserrat", "value": "countryMS"},
|
||||
{"name": "Morocco", "value": "countryMA"},
|
||||
{"name": "Mozambique", "value": "countryMZ"},
|
||||
{"name": "Myanmar", "value": "countryMM"},
|
||||
{"name": "Namibia", "value": "countryNA"},
|
||||
{"name": "Nauru", "value": "countryNR"},
|
||||
{"name": "Nepal", "value": "countryNP"},
|
||||
{"name": "Netherlands", "value": "countryNL"},
|
||||
{"name": "Netherlands Antilles", "value": "countryAN"},
|
||||
{"name": "New Caledonia", "value": "countryNC"},
|
||||
{"name": "New Zealand", "value": "countryNZ"},
|
||||
{"name": "Nicaragua", "value": "countryNI"},
|
||||
{"name": "Niger", "value": "countryNE"},
|
||||
{"name": "Nigeria", "value": "countryNG"},
|
||||
{"name": "Niue", "value": "countryNU"},
|
||||
{"name": "Norfolk Island", "value": "countryNF"},
|
||||
{"name": "Northern Mariana Islands", "value": "countryMP"},
|
||||
{"name": "Norway", "value": "countryNO"},
|
||||
{"name": "Oman", "value": "countryOM"},
|
||||
{"name": "Pakistan", "value": "countryPK"},
|
||||
{"name": "Palau", "value": "countryPW"},
|
||||
{"name": "Palestinian Territory", "value": "countryPS"},
|
||||
{"name": "Panama", "value": "countryPA"},
|
||||
{"name": "Papua New Guinea", "value": "countryPG"},
|
||||
{"name": "Paraguay", "value": "countryPY"},
|
||||
{"name": "Peru", "value": "countryPE"},
|
||||
{"name": "Philippines", "value": "countryPH"},
|
||||
{"name": "Pitcairn", "value": "countryPN"},
|
||||
{"name": "Poland", "value": "countryPL"},
|
||||
{"name": "Portugal", "value": "countryPT"},
|
||||
{"name": "Puerto Rico", "value": "countryPR"},
|
||||
{"name": "Qatar", "value": "countryQA"},
|
||||
{"name": "Reunion", "value": "countryRE"},
|
||||
{"name": "Romania", "value": "countryRO"},
|
||||
{"name": "Russian Federation", "value": "countryRU"},
|
||||
{"name": "Rwanda", "value": "countryRW"},
|
||||
{"name": "Saint Helena", "value": "countrySH"},
|
||||
{"name": "Saint Kitts and Nevis", "value": "countryKN"},
|
||||
{"name": "Saint Lucia", "value": "countryLC"},
|
||||
{"name": "Saint Pierre and Miquelon", "value": "countryPM"},
|
||||
{"name": "Saint Vincent and the Grenadines", "value": "countryVC"},
|
||||
{"name": "Samoa", "value": "countryWS"},
|
||||
{"name": "San Marino", "value": "countrySM"},
|
||||
{"name": "Sao Tome and Principe", "value": "countryST"},
|
||||
{"name": "Saudi Arabia", "value": "countrySA"},
|
||||
{"name": "Senegal", "value": "countrySN"},
|
||||
{"name": "Serbia and Montenegro", "value": "countryCS"},
|
||||
{"name": "Seychelles", "value": "countrySC"},
|
||||
{"name": "Sierra Leone", "value": "countrySL"},
|
||||
{"name": "Singapore", "value": "countrySG"},
|
||||
{"name": "Slovakia", "value": "countrySK"},
|
||||
{"name": "Slovenia", "value": "countrySI"},
|
||||
{"name": "Solomon Islands", "value": "countrySB"},
|
||||
{"name": "Somalia", "value": "countrySO"},
|
||||
{"name": "South Africa", "value": "countryZA"},
|
||||
{"name": "South Georgia and the South Sandwich Islands",
|
||||
"value": "GS"},
|
||||
{"name": "Spain", "value": "ES"},
|
||||
{"name": "Sri Lanka", "value": "LK"},
|
||||
{"name": "Sudan", "value": "SD"},
|
||||
{"name": "Suriname", "value": "SR"},
|
||||
{"name": "Svalbard and Jan Mayen", "value": "SJ"},
|
||||
{"name": "Swaziland", "value": "SZ"},
|
||||
{"name": "Sweden", "value": "SE"},
|
||||
{"name": "Switzerland", "value": "CH"},
|
||||
{"name": "Syrian Arab Republic", "value": "SY"},
|
||||
{"name": "Taiwan", "value": "TW"},
|
||||
{"name": "Tajikistan", "value": "TJ"},
|
||||
{"name": "Tanzania, United Republic of", "value": "TZ"},
|
||||
{"name": "Thailand", "value": "TH"},
|
||||
{"name": "Togo", "value": "TG"},
|
||||
{"name": "Tokelau", "value": "TK"},
|
||||
{"name": "Tonga", "value": "TO"},
|
||||
{"name": "Trinidad and Tobago", "value": "TT"},
|
||||
{"name": "Tunisia", "value": "TN"},
|
||||
{"name": "Turkey", "value": "TR"},
|
||||
{"name": "Turkmenistan", "value": "TM"},
|
||||
{"name": "Turks and Caicos Islands", "value": "TC"},
|
||||
{"name": "Tuvalu", "value": "TV"},
|
||||
{"name": "Uganda", "value": "UG"},
|
||||
{"name": "Ukraine", "value": "UA"},
|
||||
{"name": "United Arab Emirates", "value": "AE"},
|
||||
{"name": "United Kingdom", "value": "UK"},
|
||||
{"name": "United States", "value": "US"},
|
||||
{"name": "United States Minor Outlying Islands", "value": "UM"},
|
||||
{"name": "Uruguay", "value": "UY"},
|
||||
{"name": "Uzbekistan", "value": "UZ"},
|
||||
{"name": "Vanuatu", "value": "VU"},
|
||||
{"name": "Venezuela", "value": "VE"},
|
||||
{"name": "Vietnam", "value": "VN"},
|
||||
{"name": "Virgin Islands, British", "value": "VG"},
|
||||
{"name": "Virgin Islands, U.S.", "value": "VI"},
|
||||
{"name": "Wallis and Futuna", "value": "WF"},
|
||||
{"name": "Western Sahara", "value": "EH"},
|
||||
{"name": "Yemen", "value": "YE"},
|
||||
{"name": "Yugoslavia", "value": "YU"},
|
||||
{"name": "Zambia", "value": "ZM"},
|
||||
{"name": "Zimbabwe", "value": "ZW"}
|
||||
"value": "countryGS"},
|
||||
{"name": "Spain", "value": "countryES"},
|
||||
{"name": "Sri Lanka", "value": "countryLK"},
|
||||
{"name": "Sudan", "value": "countrySD"},
|
||||
{"name": "Suriname", "value": "countrySR"},
|
||||
{"name": "Svalbard and Jan Mayen", "value": "countrySJ"},
|
||||
{"name": "Swaziland", "value": "countrySZ"},
|
||||
{"name": "Sweden", "value": "countrySE"},
|
||||
{"name": "Switzerland", "value": "countryCH"},
|
||||
{"name": "Syrian Arab Republic", "value": "countrySY"},
|
||||
{"name": "Taiwan, Province of China", "value": "countryTW"},
|
||||
{"name": "Tajikistan", "value": "countryTJ"},
|
||||
{"name": "Tanzania, United Republic of", "value": "countryTZ"},
|
||||
{"name": "Thailand", "value": "countryTH"},
|
||||
{"name": "Togo", "value": "countryTG"},
|
||||
{"name": "Tokelau", "value": "countryTK"},
|
||||
{"name": "Tonga", "value": "countryTO"},
|
||||
{"name": "Trinidad and Tobago", "value": "countryTT"},
|
||||
{"name": "Tunisia", "value": "countryTN"},
|
||||
{"name": "Turkey", "value": "countryTR"},
|
||||
{"name": "Turkmenistan", "value": "countryTM"},
|
||||
{"name": "Turks and Caicos Islands", "value": "countryTC"},
|
||||
{"name": "Tuvalu", "value": "countryTV"},
|
||||
{"name": "Uganda", "value": "countryUG"},
|
||||
{"name": "Ukraine", "value": "countryUA"},
|
||||
{"name": "United Arab Emirates", "value": "countryAE"},
|
||||
{"name": "United Kingdom", "value": "countryUK"},
|
||||
{"name": "United States", "value": "countryUS"},
|
||||
{"name": "United States Minor Outlying Islands", "value": "countryUM"},
|
||||
{"name": "Uruguay", "value": "countryUY"},
|
||||
{"name": "Uzbekistan", "value": "countryUZ"},
|
||||
{"name": "Vanuatu", "value": "countryVU"},
|
||||
{"name": "Venezuela", "value": "countryVE"},
|
||||
{"name": "Vietnam", "value": "countryVN"},
|
||||
{"name": "Virgin Islands, British", "value": "countryVG"},
|
||||
{"name": "Virgin Islands, U.S.", "value": "countryVI"},
|
||||
{"name": "Wallis and Futuna", "value": "countryWF"},
|
||||
{"name": "Western Sahara", "value": "countryEH"},
|
||||
{"name": "Yemen", "value": "countryYE"},
|
||||
{"name": "Yugoslavia", "value": "countryYU"},
|
||||
{"name": "Zambia", "value": "countryZM"},
|
||||
{"name": "Zimbabwe", "value": "countryZW"}
|
||||
]
|
||||
|
@ -1,32 +0,0 @@
|
||||
{
|
||||
"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={map_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
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
[
|
||||
"light",
|
||||
"dark",
|
||||
"system"
|
||||
]
|
@ -1,8 +0,0 @@
|
||||
[
|
||||
{"name": "Any time", "value": ""},
|
||||
{"name": "Past hour", "value": "qdr:h"},
|
||||
{"name": "Past 24 hours", "value": "qdr:d"},
|
||||
{"name": "Past week", "value": "qdr:w"},
|
||||
{"name": "Past month", "value": "qdr:m"},
|
||||
{"name": "Past year", "value": "qdr:y"}
|
||||
]
|
File diff suppressed because it is too large
Load Diff
@ -1,260 +0,0 @@
|
||||
<!--
|
||||
Calculator widget.
|
||||
This file should contain all required
|
||||
CSS, HTML, and JS for it.
|
||||
-->
|
||||
|
||||
<style>
|
||||
#calc-text {
|
||||
background: var(--whoogle-dark-page-bg);
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
text-align: right;
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
color: var(--whoogle-dark-text);
|
||||
}
|
||||
#prev-equation {
|
||||
text-align: right;
|
||||
}
|
||||
.error-border {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
#calc-btns {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-template-rows: repeat(5, 1fr);
|
||||
gap: 5px;
|
||||
}
|
||||
#calc-btns button {
|
||||
background: #313141;
|
||||
color: var(--whoogle-dark-text);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
#calc-btns button:hover {
|
||||
background: #414151;
|
||||
}
|
||||
#calc-btns .common {
|
||||
background: #51516a;
|
||||
}
|
||||
#calc-btns .common:hover {
|
||||
background: #61617a;
|
||||
}
|
||||
#calc-btn-0 { grid-row: 5; grid-column: 3; }
|
||||
#calc-btn-1 { grid-row: 4; grid-column: 3; }
|
||||
#calc-btn-2 { grid-row: 4; grid-column: 4; }
|
||||
#calc-btn-3 { grid-row: 4; grid-column: 5; }
|
||||
#calc-btn-4 { grid-row: 3; grid-column: 3; }
|
||||
#calc-btn-5 { grid-row: 3; grid-column: 4; }
|
||||
#calc-btn-6 { grid-row: 3; grid-column: 5; }
|
||||
#calc-btn-7 { grid-row: 2; grid-column: 3; }
|
||||
#calc-btn-8 { grid-row: 2; grid-column: 4; }
|
||||
#calc-btn-9 { grid-row: 2; grid-column: 5; }
|
||||
#calc-btn-EQ { grid-row: 5; grid-column: 5; }
|
||||
#calc-btn-PT { grid-row: 5; grid-column: 4; }
|
||||
#calc-btn-BCK { grid-row: 5; grid-column: 6; }
|
||||
#calc-btn-ADD { grid-row: 4; grid-column: 6; }
|
||||
#calc-btn-SUB { grid-row: 3; grid-column: 6; }
|
||||
#calc-btn-MLT { grid-row: 2; grid-column: 6; }
|
||||
#calc-btn-DIV { grid-row: 1; grid-column: 6; }
|
||||
#calc-btn-CLR { grid-row: 1; grid-column: 5; }
|
||||
#calc-btn-PRC{ grid-row: 1; grid-column: 4; }
|
||||
#calc-btn-RP { grid-row: 1; grid-column: 3; }
|
||||
#calc-btn-LP { grid-row: 1; grid-column: 2; }
|
||||
#calc-btn-ABS { grid-row: 1; grid-column: 1; }
|
||||
#calc-btn-SIN { grid-row: 2; grid-column: 2; }
|
||||
#calc-btn-COS { grid-row: 3; grid-column: 2; }
|
||||
#calc-btn-TAN { grid-row: 4; grid-column: 2; }
|
||||
#calc-btn-SQR { grid-row: 5; grid-column: 2; }
|
||||
#calc-btn-EXP { grid-row: 2; grid-column: 1; }
|
||||
#calc-btn-E { grid-row: 3; grid-column: 1; }
|
||||
#calc-btn-PI { grid-row: 4; grid-column: 1; }
|
||||
#calc-btn-LOG { grid-row: 5; grid-column: 1; }
|
||||
</style>
|
||||
<p id="prev-equation"></p>
|
||||
<div id="calculator-widget">
|
||||
<p id="calc-text">0</p>
|
||||
<div id="calc-btns">
|
||||
<button id="calc-btn-0" class="common">0</button>
|
||||
<button id="calc-btn-1" class="common">1</button>
|
||||
<button id="calc-btn-2" class="common">2</button>
|
||||
<button id="calc-btn-3" class="common">3</button>
|
||||
<button id="calc-btn-4" class="common">4</button>
|
||||
<button id="calc-btn-5" class="common">5</button>
|
||||
<button id="calc-btn-6" class="common">6</button>
|
||||
<button id="calc-btn-7" class="common">7</button>
|
||||
<button id="calc-btn-8" class="common">8</button>
|
||||
<button id="calc-btn-9" class="common">9</button>
|
||||
<button id="calc-btn-EQ" class="common">=</button>
|
||||
<button id="calc-btn-PT" class="common">.</button>
|
||||
<button id="calc-btn-BCK">⬅</button>
|
||||
<button id="calc-btn-ADD">+</button>
|
||||
<button id="calc-btn-SUB">-</button>
|
||||
<button id="calc-btn-MLT">x</button>
|
||||
<button id="calc-btn-DIV">/</button>
|
||||
<button id="calc-btn-CLR">C</button>
|
||||
<button id="calc-btn-PRC">%</button>
|
||||
<button id="calc-btn-RP">)</button>
|
||||
<button id="calc-btn-LP">(</button>
|
||||
<button id="calc-btn-ABS">|x|</button>
|
||||
<button id="calc-btn-SIN">sin</button>
|
||||
<button id="calc-btn-COS">cos</button>
|
||||
<button id="calc-btn-TAN">tan</button>
|
||||
<button id="calc-btn-SQR">√</button>
|
||||
<button id="calc-btn-EXP">^</button>
|
||||
<button id="calc-btn-E">ℇ</button>
|
||||
<button id="calc-btn-PI">π</button>
|
||||
<button id="calc-btn-LOG">log</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// JS does not have this by default.
|
||||
// from https://www.freecodecamp.org/news/how-to-factorialize-a-number-in-javascript-9263c89a4b38/
|
||||
function factorial(num) {
|
||||
if (num < 0)
|
||||
return -1;
|
||||
else if (num === 0)
|
||||
return 1;
|
||||
else {
|
||||
return (num * factorial(num - 1));
|
||||
}
|
||||
}
|
||||
// returns true if the user is currently focused on the calculator widget
|
||||
function usingCalculator() {
|
||||
let activeElement = document.activeElement;
|
||||
while (true) {
|
||||
if (!activeElement) return false;
|
||||
if (activeElement.id === "calculator-wrapper") return true;
|
||||
activeElement = activeElement.parentElement;
|
||||
}
|
||||
}
|
||||
const $ = q => document.querySelectorAll(q);
|
||||
// key bindings for commonly used buttons
|
||||
const keybindings = {
|
||||
"0": "0",
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9",
|
||||
"Enter": "EQ",
|
||||
".": "PT",
|
||||
"+": "ADD",
|
||||
"-": "SUB",
|
||||
"*": "MLT",
|
||||
"/": "DIV",
|
||||
"%": "PRC",
|
||||
"c": "CLR",
|
||||
"(": "LP",
|
||||
")": "RP",
|
||||
"Backspace": "BCK",
|
||||
}
|
||||
window.addEventListener("keydown", event => {
|
||||
if (!usingCalculator()) return;
|
||||
if (event.key === "Enter" && document.activeElement.id !== "search-bar")
|
||||
event.preventDefault();
|
||||
if (keybindings[event.key])
|
||||
document.getElementById("calc-btn-" + keybindings[event.key]).click();
|
||||
})
|
||||
// calculates the string
|
||||
const calc = () => {
|
||||
var mathtext = document.getElementById("calc-text");
|
||||
var statement = mathtext.innerHTML
|
||||
// remove empty ()
|
||||
.replace("()", "")
|
||||
// special constants
|
||||
.replace("π", "(Math.PI)")
|
||||
.replace("ℇ", "(Math.E)")
|
||||
// turns 3(1+2) into 3*(1+2) (for example)
|
||||
.replace(/(?<=[0-9\)])(?<=[^+\-x*\/%^])\(/, "x(")
|
||||
// same except reversed
|
||||
.replace(/\)(?=[0-9\(])(?=[^+\-x*\/%^])/, ")x")
|
||||
// replace human friendly x with JS *
|
||||
.replace("x", "*")
|
||||
// trig & misc functions
|
||||
.replace("sin", "Math.sin")
|
||||
.replace("cos", "Math.cos")
|
||||
.replace("tan", "Math.tan")
|
||||
.replace("√", "Math.sqrt")
|
||||
.replace("^", "**")
|
||||
.replace("abs", "Math.abs")
|
||||
.replace("log", "Math.log")
|
||||
;
|
||||
// add any missing )s to the end
|
||||
while(true) if (
|
||||
(statement.match(/\(/g) || []).length >
|
||||
(statement.match(/\)/g) || []).length
|
||||
) statement += ")"; else break;
|
||||
// evaluate the expression.
|
||||
console.log("calculating [" + statement + "]");
|
||||
try {
|
||||
var result = eval(statement);
|
||||
document.getElementById("prev-equation").innerHTML = mathtext.innerHTML + " = ";
|
||||
mathtext.innerHTML = result;
|
||||
mathtext.classList.remove("error-border");
|
||||
} catch (e) {
|
||||
mathtext.classList.add("error-border");
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
const updateCalc = (e) => {
|
||||
// character(s) recieved from button
|
||||
var c = event.target.innerHTML;
|
||||
var mathtext = document.getElementById("calc-text");
|
||||
if (mathtext.innerHTML === "0") mathtext.innerHTML = "";
|
||||
// special cases
|
||||
switch (c) {
|
||||
case "C":
|
||||
// Clear
|
||||
mathtext.innerHTML = "0";
|
||||
break;
|
||||
case "⬅":
|
||||
// Delete
|
||||
mathtext.innerHTML = mathtext.innerHTML.slice(0, -1);
|
||||
if (mathtext.innerHTML.length === 0) {
|
||||
mathtext.innerHTML = "0";
|
||||
}
|
||||
break;
|
||||
case "=":
|
||||
calc()
|
||||
break;
|
||||
case "sin":
|
||||
case "cos":
|
||||
case "tan":
|
||||
case "log":
|
||||
case "√":
|
||||
mathtext.innerHTML += `${c}(`;
|
||||
break;
|
||||
case "|x|":
|
||||
mathtext.innerHTML += "abs("
|
||||
break;
|
||||
case "+":
|
||||
case "-":
|
||||
case "x":
|
||||
case "/":
|
||||
case "%":
|
||||
case "^":
|
||||
if (mathtext.innerHTML.length === 0) mathtext.innerHTML = "0";
|
||||
// prevent typing 2 operators in a row
|
||||
if (mathtext.innerHTML.match(/[+\-x\/%^] $/))
|
||||
mathtext.innerHTML = mathtext.innerHTML.slice(0, -3);
|
||||
mathtext.innerHTML += ` ${c} `;
|
||||
break;
|
||||
default:
|
||||
mathtext.innerHTML += c;
|
||||
}
|
||||
}
|
||||
for (let i of $("#calc-btns button")) {
|
||||
i.addEventListener('click', event => {
|
||||
updateCalc(event);
|
||||
})
|
||||
}
|
||||
</script>
|
@ -1,48 +1,29 @@
|
||||
<html>
|
||||
<head>
|
||||
<head>
|
||||
<link rel="shortcut icon" href="static/img/favicon.ico" type="image/x-icon">
|
||||
<link rel="icon" href="static/img/favicon.ico" type="image/x-icon">
|
||||
{% if not search_type %}
|
||||
<link rel="search" href="opensearch.xml" type="application/opensearchdescription+xml" title="Whoogle Search">
|
||||
{% else %}
|
||||
<link rel="search" href="opensearch.xml?tbm={{ search_type }}" type="application/opensearchdescription+xml" title="Whoogle Search ({{ search_name }})">
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<link rel="stylesheet" href="{{ cb_url('logo.css') }}">
|
||||
<link rel="stylesheet" href="{{ cb_url('input.css') }}">
|
||||
<link rel="stylesheet" href="{{ cb_url('search.css') }}">
|
||||
<link rel="stylesheet" href="{{ cb_url('header.css') }}">
|
||||
{% if config.theme %}
|
||||
{% if config.theme == 'system' %}
|
||||
<style>
|
||||
@import "{{ cb_url('light-theme.css') }}" screen;
|
||||
@import "{{ cb_url('dark-theme.css') }}" screen and (prefers-color-scheme: dark);
|
||||
</style>
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ cb_url(config.theme + '-theme.css') }}"/>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ cb_url(('dark' if config.dark else 'light') + '-theme.css') }}"/>
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="static/css/input.css">
|
||||
<link rel="stylesheet" href="static/css/search.css">
|
||||
<link rel="stylesheet" href="static/css/variables.css">
|
||||
<link rel="stylesheet" href="static/css/header.css">
|
||||
<link rel="stylesheet" href="static/css/{{ 'dark' if config.dark else 'light' }}-theme.css"/>
|
||||
<style>{{ config.style }}</style>
|
||||
<title>{{ clean_query(query) }} - Whoogle Search</title>
|
||||
</head>
|
||||
<body>
|
||||
{{ search_header|safe }}
|
||||
{% if is_translation %}
|
||||
<iframe
|
||||
id="lingva-iframe"
|
||||
src="{{ lingva_url }}/auto/{{ translate_to }}/{{ translate_str }}">
|
||||
</iframe>
|
||||
{% endif %}
|
||||
{{ response|safe }}
|
||||
</body>
|
||||
{% include 'footer.html' %}
|
||||
{% if autocomplete_enabled == '1' %}
|
||||
<script src="{{ cb_url('autocomplete.js') }}"></script>
|
||||
{% endif %}
|
||||
<script src="{{ cb_url('utils.js') }}"></script>
|
||||
<script src="{{ cb_url('keyboard.js') }}"></script>
|
||||
<script src="{{ cb_url('currency.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
{{ search_header|safe }}
|
||||
{{ response|safe }}
|
||||
</body>
|
||||
<footer>
|
||||
<p style="color: {{ 'var(--whoogle-dark-text)' if config.dark else 'var(--whoogle-text)' }};">
|
||||
Whoogle Search v{{ version_number }} ||
|
||||
<a id="gh-link" href="https://github.com/benbusby/whoogle-search">{{ translation['github-link'] }}</a>
|
||||
</p>
|
||||
</footer>
|
||||
<script src="static/js/autocomplete.js"></script>
|
||||
<script src="static/js/utils.js"></script>
|
||||
<script src="static/js/keyboard.js"></script>
|
||||
</html>
|
||||
|
@ -1,106 +1,6 @@
|
||||
{% if config.theme %}
|
||||
{% if config.theme == 'system' %}
|
||||
<style>
|
||||
@import "{{ cb_url('light-theme.css') }}" screen;
|
||||
@import "{{ cb_url('dark-theme.css') }}" screen and (prefers-color-scheme: dark);
|
||||
</style>
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ cb_url(config.theme + '-theme.css') }}"/>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<link rel="stylesheet" href="{{ cb_url(('dark' if config.dark else 'light') + '-theme.css') }}"/>
|
||||
{% endif %}
|
||||
<link rel="stylesheet" href="{{ cb_url('main.css') }}">
|
||||
<link rel="stylesheet" href="{{ cb_url('error.css') }}">
|
||||
<style>{{ config.style }}</style>
|
||||
<div>
|
||||
<h1>Error</h1>
|
||||
<p>
|
||||
{{ error_message }}
|
||||
</p>
|
||||
<hr>
|
||||
{% if query and translation %}
|
||||
<p>
|
||||
<h4><a class="link" href="https://farside.link">{{ translation['continue-search'] }}</a></h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/benbusby/whoogle-search">Whoogle</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a class="link-color" href="{{farside}}/whoogle/search?q={{query}}{{params}}">
|
||||
{{farside}}/whoogle/search?q={{query}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/searxng/searxng">SearXNG</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a class="link-color" href="{{farside}}/searxng/search?q={{query}}">
|
||||
{{farside}}/searxng/search?q={{query}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h4>Other options:</h4>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://kagi.com">Kagi</a>
|
||||
<ul>
|
||||
<li>Requires account</li>
|
||||
<li>
|
||||
<a class="link-color" href="https://kagi.com/search?q={{query}}">
|
||||
kagi.com/search?q={{query}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://duckduckgo.com">DuckDuckGo</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a class="link-color" href="https://duckduckgo.com/search?q={{query}}">
|
||||
duckduckgo.com/search?q={{query}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://search.brave.com">Brave Search</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a class="link-color" href="https://search.brave.com/search?q={{query}}">
|
||||
search.brave.com/search?q={{query}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://ecosia.com">Ecosia</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a class="link-color" href="https://ecosia.com/search?q={{query}}">
|
||||
ecosia.com/search?q={{query}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://google.com">Google</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a class="link-color" href="https://google.com/search?q={{query}}">
|
||||
google.com/search?q={{query}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
</p>
|
||||
{% endif %}
|
||||
<a class="link" href="home">Return Home</a>
|
||||
</div>
|
||||
<h1>Error</h1>
|
||||
<hr>
|
||||
<p>
|
||||
Error: "{{ error_message|safe }}"
|
||||
</p>
|
||||
<a href="/">Return Home</a>
|
||||
|
@ -1,9 +0,0 @@
|
||||
<footer>
|
||||
<p class="footer">
|
||||
Whoogle Search v{{ version_number }} ||
|
||||
<a class="link" href="https://github.com/benbusby/whoogle-search">{{ translation['github-link'] }}</a>
|
||||
{% if has_update %}
|
||||
|| <span class="update_available">Update Available 🟢</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</footer>
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.6 KiB |
File diff suppressed because one or more lines are too long
@ -1,139 +0,0 @@
|
||||
import base64
|
||||
from bs4 import BeautifulSoup as bsoup
|
||||
from cryptography.fernet import Fernet
|
||||
from flask import Request
|
||||
import hashlib
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
from requests import exceptions, get
|
||||
from urllib.parse import urlparse
|
||||
|
||||
ddg_favicon_site = 'http://icons.duckduckgo.com/ip2'
|
||||
|
||||
empty_gif = base64.b64decode(
|
||||
'R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==')
|
||||
|
||||
placeholder_img = base64.b64decode(
|
||||
'iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAABF0lEQVRIS8XWPw9EMBQA8Eok' \
|
||||
'JBKrMFqMBt//GzAYLTZ/VomExPDu6uLiaPteqVynBn0/75W2Vp7nEIYhe6p1XcespmmAd7Is' \
|
||||
'M+4URcGiKPogvMMvmIS2eN9MOMKbKWgf54SYgI4vKkTuQKJKSJErkKzUSkQHUs0lilAg7GMh' \
|
||||
'ISoIA/hYMiKCKIA2soeowCWEMkfHtUmrXLcyGYYBfN9HF8djiaglWzNZlgVs21YisoAUaEXG' \
|
||||
'cQTP86QIFgi7vyLzPIPjOEIEC7ANQv/4aZrAdd0TUtc1i+MYnSsMWjPp+x6CIPgJVlUVS5KE' \
|
||||
'DKig/+wnVzM4pnzaGeHd+ENlWbI0TbVLJBtw2uMfP63wc9d2kDCWxi5Q27bsBerSJ9afJbeL' \
|
||||
'AAAAAElFTkSuQmCC'
|
||||
)
|
||||
|
||||
|
||||
def fetch_favicon(url: str) -> bytes:
|
||||
"""Fetches a favicon using DuckDuckGo's favicon retriever
|
||||
|
||||
Args:
|
||||
url: The url to fetch the favicon from
|
||||
Returns:
|
||||
bytes - the favicon bytes, or a placeholder image if one
|
||||
was not returned
|
||||
"""
|
||||
domain = urlparse(url).netloc
|
||||
|
||||
response = get(f'{ddg_favicon_site}/{domain}.ico')
|
||||
|
||||
if response.status_code == 200 and len(response.content) > 0:
|
||||
tmp_mem = io.BytesIO()
|
||||
tmp_mem.write(response.content)
|
||||
tmp_mem.seek(0)
|
||||
|
||||
return tmp_mem.read()
|
||||
else:
|
||||
return placeholder_img
|
||||
|
||||
|
||||
def gen_file_hash(path: str, static_file: str) -> str:
|
||||
file_contents = open(os.path.join(path, static_file), 'rb').read()
|
||||
file_hash = hashlib.md5(file_contents).hexdigest()[:8]
|
||||
filename_split = os.path.splitext(static_file)
|
||||
|
||||
return filename_split[0] + '.' + file_hash + filename_split[-1]
|
||||
|
||||
|
||||
def read_config_bool(var: str, default: bool=False) -> bool:
|
||||
val = os.getenv(var, '1' if default else '0')
|
||||
# user can specify one of the following values as 'true' inputs (all
|
||||
# variants with upper case letters will also work):
|
||||
# ('true', 't', '1', 'yes', 'y')
|
||||
val = val.lower() in ('true', 't', '1', 'yes', 'y')
|
||||
return val
|
||||
|
||||
|
||||
def get_client_ip(r: Request) -> str:
|
||||
if r.environ.get('HTTP_X_FORWARDED_FOR') is None:
|
||||
return r.environ['REMOTE_ADDR']
|
||||
else:
|
||||
return r.environ['HTTP_X_FORWARDED_FOR']
|
||||
|
||||
|
||||
def get_request_url(url: str) -> str:
|
||||
if os.getenv('HTTPS_ONLY', False):
|
||||
return url.replace('http://', 'https://', 1)
|
||||
|
||||
return url
|
||||
|
||||
|
||||
def get_proxy_host_url(r: Request, default: str, root=False) -> str:
|
||||
scheme = r.headers.get('X-Forwarded-Proto', 'https')
|
||||
http_host = r.headers.get('X-Forwarded-Host')
|
||||
|
||||
full_path = r.full_path if not root else ''
|
||||
if full_path.startswith('/'):
|
||||
full_path = f'/{full_path}'
|
||||
|
||||
if http_host:
|
||||
prefix = os.environ.get('WHOOGLE_URL_PREFIX', '')
|
||||
if prefix:
|
||||
prefix = f'/{re.sub("[^0-9a-zA-Z]+", "", prefix)}'
|
||||
return f'{scheme}://{http_host}{prefix}{full_path}'
|
||||
|
||||
return default
|
||||
|
||||
|
||||
def check_for_update(version_url: str, current: str) -> int:
|
||||
# Check for the latest version of Whoogle
|
||||
try:
|
||||
update = bsoup(get(version_url).text, 'html.parser')
|
||||
latest = update.select_one('[class="Link--primary"]').string[1:]
|
||||
current = int(''.join(filter(str.isdigit, current)))
|
||||
latest = int(''.join(filter(str.isdigit, latest)))
|
||||
has_update = '' if current >= latest else latest
|
||||
except (exceptions.ConnectionError, AttributeError):
|
||||
# Ignore failures, assume current version is up to date
|
||||
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
|
||||
|
||||
|
||||
def list_to_dict(lst: list) -> dict:
|
||||
if len(lst) < 2:
|
||||
return {}
|
||||
return {lst[i].replace(' ', ''): lst[i+1].replace(' ', '')
|
||||
for i in range(0, len(lst), 2)}
|
||||
|
||||
|
||||
def encrypt_string(key: bytes, string: str) -> str:
|
||||
cipher_suite = Fernet(key)
|
||||
return cipher_suite.encrypt(string.encode()).decode()
|
||||
|
||||
|
||||
def decrypt_string(key: bytes, string: str) -> str:
|
||||
cipher_suite = Fernet(g.session_key)
|
||||
return cipher_suite.decrypt(string.encode()).decode()
|
@ -1,71 +0,0 @@
|
||||
from pathlib import Path
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
# root
|
||||
BASE_DIR = Path(__file__).parent.parent.parent
|
||||
|
||||
def add_ip_card(html_soup: BeautifulSoup, ip: str) -> BeautifulSoup:
|
||||
"""Adds the client's IP address to the search results
|
||||
if query contains keywords
|
||||
|
||||
Args:
|
||||
html_soup: The parsed search result containing the keywords
|
||||
ip: ip address of the client
|
||||
|
||||
Returns:
|
||||
BeautifulSoup
|
||||
|
||||
"""
|
||||
main_div = html_soup.select_one('#main')
|
||||
if main_div:
|
||||
# HTML IP card tag
|
||||
ip_tag = html_soup.new_tag('div')
|
||||
ip_tag['class'] = 'ZINbbc xpd O9g5cc uUPGi'
|
||||
|
||||
# For IP Address html tag
|
||||
ip_address = html_soup.new_tag('div')
|
||||
ip_address['class'] = 'kCrYT ip-address-div'
|
||||
ip_address.string = ip
|
||||
|
||||
# Text below the IP address
|
||||
ip_text = html_soup.new_tag('div')
|
||||
ip_text.string = 'Your public IP address'
|
||||
ip_text['class'] = 'kCrYT ip-text-div'
|
||||
|
||||
# Adding all the above html tags to the IP card
|
||||
ip_tag.append(ip_address)
|
||||
ip_tag.append(ip_text)
|
||||
|
||||
# Insert the element at the top of the result list
|
||||
main_div.insert_before(ip_tag)
|
||||
return html_soup
|
||||
|
||||
def add_calculator_card(html_soup: BeautifulSoup) -> BeautifulSoup:
|
||||
"""Adds the a calculator widget to the search results
|
||||
if query contains keywords
|
||||
|
||||
Args:
|
||||
html_soup: The parsed search result containing the keywords
|
||||
|
||||
Returns:
|
||||
BeautifulSoup
|
||||
"""
|
||||
main_div = html_soup.select_one('#main')
|
||||
if main_div:
|
||||
# absolute path
|
||||
widget_file = open(BASE_DIR / 'app/static/widgets/calculator.html', encoding="utf8")
|
||||
widget_tag = html_soup.new_tag('div')
|
||||
widget_tag['class'] = 'ZINbbc xpd O9g5cc uUPGi'
|
||||
widget_tag['id'] = 'calculator-wrapper'
|
||||
calculator_text = html_soup.new_tag('div')
|
||||
calculator_text['class'] = 'kCrYT ip-address-div'
|
||||
calculator_text.string = 'Calculator'
|
||||
calculator_widget = html_soup.new_tag('div')
|
||||
calculator_widget.append(BeautifulSoup(widget_file, 'html.parser'))
|
||||
calculator_widget['class'] = 'kCrYT ip-text-div'
|
||||
widget_tag.append(calculator_text)
|
||||
widget_tag.append(calculator_widget)
|
||||
main_div.insert_before(widget_tag)
|
||||
widget_file.close()
|
||||
return html_soup
|
@ -1,7 +0,0 @@
|
||||
import os
|
||||
|
||||
optional_dev_tag = ''
|
||||
if os.getenv('DEV_BUILD'):
|
||||
optional_dev_tag = '.dev' + os.getenv('DEV_BUILD')
|
||||
|
||||
__version__ = '0.8.4' + optional_dev_tag
|
@ -1,23 +0,0 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
@ -1,23 +0,0 @@
|
||||
apiVersion: v2
|
||||
name: whoogle
|
||||
description: A self hosted search engine on Kubernetes
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: 0.8.4
|
||||
|
||||
icon: https://github.com/benbusby/whoogle-search/raw/main/app/static/img/favicon/favicon-96x96.png
|
||||
|
||||
sources:
|
||||
- https://github.com/benbusby/whoogle-search
|
||||
- https://gitlab.com/benbusby/whoogle-search
|
||||
- https://gogs.benbusby.com/benbusby/whoogle-search
|
||||
|
||||
keywords:
|
||||
- whoogle
|
||||
- degoogle
|
||||
- search
|
||||
- google
|
||||
- search-engine
|
||||
- privacy
|
||||
- tor
|
||||
- python
|
@ -1,22 +0,0 @@
|
||||
1. Get the application URL by running these commands:
|
||||
{{- if .Values.ingress.enabled }}
|
||||
{{- range $host := .Values.ingress.hosts }}
|
||||
{{- range .paths }}
|
||||
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else if contains "NodePort" .Values.service.type }}
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "whoogle.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo http://$NODE_IP:$NODE_PORT
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
|
||||
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "whoogle.fullname" . }}'
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "whoogle.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
|
||||
echo http://$SERVICE_IP:{{ .Values.service.port }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "whoogle.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
|
||||
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
|
||||
echo "Visit http://127.0.0.1:8080 to use your application"
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
|
||||
{{- end }}
|
@ -1,62 +0,0 @@
|
||||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "whoogle.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "whoogle.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "whoogle.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "whoogle.labels" -}}
|
||||
helm.sh/chart: {{ include "whoogle.chart" . }}
|
||||
{{ include "whoogle.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "whoogle.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "whoogle.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "whoogle.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "whoogle.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,82 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "whoogle.fullname" . }}
|
||||
labels:
|
||||
{{- include "whoogle.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if not .Values.autoscaling.enabled }}
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "whoogle.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "whoogle.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.image.pullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- range .}}
|
||||
- name: {{ . }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "whoogle.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: whoogle
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
{{- with .Values.conf }}
|
||||
env:
|
||||
{{- range $k,$v := . }}
|
||||
{{- if $v }}
|
||||
- name: {{ $k }}
|
||||
value: {{ tpl (toString $v) $ | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ default 5000 .Values.conf.EXPOSE_PORT }}
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
{{- if and .Values.conf.WHOOGLE_USER .Values.conf.WHOOGLE_PASS }}
|
||||
httpHeaders:
|
||||
- name: Authorization
|
||||
value: Basic {{ b64enc (printf "%s:%s" .Values.conf.WHOOGLE_USER .Values.conf.WHOOGLE_PASS) }}
|
||||
{{- end }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: http
|
||||
{{- if and .Values.conf.WHOOGLE_USER .Values.conf.WHOOGLE_PASS }}
|
||||
httpHeaders:
|
||||
- name: Authorization
|
||||
value: Basic {{ b64enc (printf "%s:%s" .Values.conf.WHOOGLE_USER .Values.conf.WHOOGLE_PASS) }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
@ -1,28 +0,0 @@
|
||||
{{- if .Values.autoscaling.enabled }}
|
||||
apiVersion: autoscaling/v2beta1
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "whoogle.fullname" . }}
|
||||
labels:
|
||||
{{- include "whoogle.labels" . | nindent 4 }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "whoogle.fullname" . }}
|
||||
minReplicas: {{ .Values.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,61 +0,0 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- $fullName := include "whoogle.fullname" . -}}
|
||||
{{- $svcPort := .Values.service.port -}}
|
||||
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
|
||||
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
|
||||
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
{{- else -}}
|
||||
apiVersion: extensions/v1beta1
|
||||
{{- end }}
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $fullName }}
|
||||
labels:
|
||||
{{- include "whoogle.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.tls }}
|
||||
tls:
|
||||
{{- range .Values.ingress.tls }}
|
||||
- hosts:
|
||||
{{- range .hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
secretName: {{ .secretName }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range .Values.ingress.hosts }}
|
||||
- host: {{ .host | quote }}
|
||||
http:
|
||||
paths:
|
||||
{{- range .paths }}
|
||||
- path: {{ .path }}
|
||||
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
|
||||
pathType: {{ .pathType }}
|
||||
{{- end }}
|
||||
backend:
|
||||
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
|
||||
service:
|
||||
name: {{ $fullName }}
|
||||
port:
|
||||
number: {{ $svcPort }}
|
||||
{{- else }}
|
||||
serviceName: {{ $fullName }}
|
||||
servicePort: {{ $svcPort }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,15 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "whoogle.fullname" . }}
|
||||
labels:
|
||||
{{- include "whoogle.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "whoogle.selectorLabels" . | nindent 4 }}
|
@ -1,12 +0,0 @@
|
||||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "whoogle.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "whoogle.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
@ -1,15 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: "{{ include "whoogle.fullname" . }}-test-connection"
|
||||
labels:
|
||||
{{- include "whoogle.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
"helm.sh/hook": test
|
||||
spec:
|
||||
containers:
|
||||
- name: wget
|
||||
image: busybox
|
||||
command: ['wget']
|
||||
args: ['{{ include "whoogle.fullname" . }}:{{ .Values.service.port }}']
|
||||
restartPolicy: Never
|
@ -1,114 +0,0 @@
|
||||
# Default values for whoogle.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
replicaCount: 1
|
||||
image:
|
||||
repository: benbusby/whoogle-search
|
||||
pullPolicy: IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
pullSecrets: []
|
||||
# - my-image-pull-secret
|
||||
|
||||
serviceAccount:
|
||||
# Specifies whether a service account should be created
|
||||
create: true
|
||||
# Annotations to add to the service account
|
||||
annotations: {}
|
||||
# The name of the service account to use.
|
||||
# If not set and create is true, a name is generated using the fullname template
|
||||
name: ""
|
||||
|
||||
conf: {}
|
||||
# WHOOGLE_URL_PREFIX: "" # The URL prefix to use for the whoogle instance (i.e. "/whoogle")
|
||||
# WHOOGLE_DOTENV: "" # Load environment variables in whoogle.env
|
||||
# WHOOGLE_USER: "" # The username for basic auth. WHOOGLE_PASS must also be set if used.
|
||||
# WHOOGLE_PASS: "" # The password for basic auth. WHOOGLE_USER must also be set if used.
|
||||
# WHOOGLE_PROXY_USER: "" # The username of the proxy server.
|
||||
# WHOOGLE_PROXY_PASS: "" # The password of the proxy server.
|
||||
# WHOOGLE_PROXY_TYPE: "" # The type of the proxy server. Can be "socks5", "socks4", or "http".
|
||||
# WHOOGLE_PROXY_LOC: "" # The location of the proxy server (host or ip).
|
||||
# EXPOSE_PORT: "" # The port where Whoogle will be exposed. (default 5000)
|
||||
# HTTPS_ONLY: "" # Enforce HTTPS. (See https://github.com/benbusby/whoogle-search#https-enforcement)
|
||||
# WHOOGLE_ALT_TW: "" # The twitter.com alternative to use when site alternatives are enabled in the config.
|
||||
# WHOOGLE_ALT_YT: "" # The youtube.com alternative to use when site alternatives are enabled in the config.
|
||||
# WHOOGLE_ALT_RD: "" # The reddit.com alternative to use when site alternatives are enabled in the config.
|
||||
# WHOOGLE_ALT_TL: "" # The Google Translate alternative to use. This is used for all "translate ____" searches.
|
||||
# WHOOGLE_ALT_MD: "" # The medium.com alternative to use when site alternatives are enabled in the config.
|
||||
# WHOOGLE_ALT_IMG: "" # The imgur.com alternative to use when site alternatives are enabled in the config.
|
||||
# WHOOGLE_ALT_WIKI: "" # The wikipedia.com alternative to use when site alternatives are enabled in the config.
|
||||
# WHOOGLE_ALT_IMDB: "" # The imdb.com alternative to use. Set to "" to continue using imdb.com when site alternatives are enabled.
|
||||
# WHOOGLE_ALT_QUORA: "" # The quora.com alternative to use. Set to "" to continue using quora.com when site alternatives are enabled.
|
||||
# WHOOGLE_AUTOCOMPLETE: "" # Controls visibility of autocomplete/search suggestions. Default on -- use '0' to disable
|
||||
# WHOOGLE_MINIMAL: "" # Remove everything except basic result cards from all search queries.
|
||||
|
||||
# WHOOGLE_CONFIG_DISABLE: "" # Hide config from UI and disallow changes to config by client
|
||||
# WHOOGLE_CONFIG_COUNTRY: "" # Filter results by hosting country
|
||||
# WHOOGLE_CONFIG_LANGUAGE: "" # Set interface language
|
||||
# WHOOGLE_CONFIG_SEARCH_LANGUAGE: "" # Set search result language
|
||||
# WHOOGLE_CONFIG_BLOCK: "" # Block websites from search results (use comma-separated list)
|
||||
# WHOOGLE_CONFIG_THEME: "" # Set theme mode (light, dark, or system)
|
||||
# WHOOGLE_CONFIG_SAFE: "" # Enable safe searches
|
||||
# WHOOGLE_CONFIG_ALTS: "" # Use social media site alternatives (nitter, invidious, etc)
|
||||
# WHOOGLE_CONFIG_NEAR: "" # Restrict results to only those near a particular city
|
||||
# WHOOGLE_CONFIG_TOR: "" # Use Tor routing (if available)
|
||||
# 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_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_PREFERENCES_ENCRYPTED: "" # Encrypt preferences token, requires key
|
||||
# WHOOGLE_CONFIG_PREFERENCES_KEY: "" # Key to encrypt preferences in URL (REQUIRED to show url)
|
||||
|
||||
podAnnotations: {}
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 5000
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ""
|
||||
annotations: {}
|
||||
# kubernetes.io/ingress.class: nginx
|
||||
# kubernetes.io/tls-acme: "true"
|
||||
hosts:
|
||||
- host: whoogle.example.com
|
||||
paths:
|
||||
- path: /
|
||||
pathType: ImplementationSpecific
|
||||
tls: []
|
||||
# - secretName: chart-example-tls
|
||||
# hosts:
|
||||
# - whoogle.example.com
|
||||
|
||||
resources: {}
|
||||
# requests:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
minReplicas: 1
|
||||
maxReplicas: 100
|
||||
targetCPUUtilizationPercentage: 80
|
||||
# targetMemoryUtilizationPercentage: 80
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
@ -1,80 +0,0 @@
|
||||
# can't use mem_limit in a 3.x docker-compose file in non swarm mode
|
||||
# see https://github.com/docker/compose/issues/4513
|
||||
version: "2.4"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:v2.7"
|
||||
container_name: "traefik"
|
||||
command:
|
||||
#- "--log.level=DEBUG"
|
||||
- "--api.insecure=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
|
||||
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
- "--certificatesresolvers.myresolver.acme.email=change@domain.name"
|
||||
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
|
||||
ports:
|
||||
- "443:443"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- "./letsencrypt:/letsencrypt"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
|
||||
whoogle-search:
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`change.host.name`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=websecure"
|
||||
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
|
||||
- "traefik.http.services.whoogle-search.loadbalancer.server.port=5000"
|
||||
image: ${WHOOGLE_IMAGE:-benbusby/whoogle-search}
|
||||
container_name: whoogle-search
|
||||
restart: unless-stopped
|
||||
pids_limit: 50
|
||||
mem_limit: 256mb
|
||||
memswap_limit: 256mb
|
||||
# user debian-tor from tor package
|
||||
user: whoogle
|
||||
security_opt:
|
||||
- no-new-privileges
|
||||
cap_drop:
|
||||
- ALL
|
||||
tmpfs:
|
||||
- /config/:size=10M,uid=927,gid=927,mode=1700
|
||||
- /var/lib/tor/:size=15M,uid=927,gid=927,mode=1700
|
||||
- /run/tor/:size=1M,uid=927,gid=927,mode=1700
|
||||
environment: # Uncomment to configure environment variables
|
||||
# Basic auth configuration, uncomment to enable
|
||||
#- WHOOGLE_USER=<auth username>
|
||||
#- WHOOGLE_PASS=<auth password>
|
||||
# Proxy configuration, uncomment to enable
|
||||
#- WHOOGLE_PROXY_USER=<proxy username>
|
||||
#- WHOOGLE_PROXY_PASS=<proxy password>
|
||||
#- WHOOGLE_PROXY_TYPE=<proxy type (http|https|socks4|socks5)
|
||||
#- WHOOGLE_PROXY_LOC=<proxy host/ip>
|
||||
# Site alternative configurations, uncomment to enable
|
||||
# Note: If not set, the feature will still be available
|
||||
# with default values.
|
||||
#- WHOOGLE_ALT_TW=farside.link/nitter
|
||||
#- WHOOGLE_ALT_YT=farside.link/invidious
|
||||
#- WHOOGLE_ALT_IG=farside.link/bibliogram/u
|
||||
#- WHOOGLE_ALT_RD=farside.link/libreddit
|
||||
#- WHOOGLE_ALT_MD=farside.link/scribe
|
||||
#- WHOOGLE_ALT_TL=farside.link/lingva
|
||||
#- WHOOGLE_ALT_IMG=farside.link/rimgo
|
||||
#- WHOOGLE_ALT_WIKI=farside.link/wikiless
|
||||
#- WHOOGLE_ALT_IMDB=farside.link/libremdb
|
||||
#- WHOOGLE_ALT_QUORA=farside.link/quetre
|
||||
# - WHOOGLE_CONFIG_DISABLE=1
|
||||
# - WHOOGLE_CONFIG_SEARCH_LANGUAGE=lang_en
|
||||
# - WHOOGLE_CONFIG_GET_ONLY=1
|
||||
# - WHOOGLE_CONFIG_COUNTRY=FR
|
||||
# - WHOOGLE_CONFIG_PREFERENCES_ENCRYPTED=1
|
||||
# - WHOOGLE_CONFIG_PREFERENCES_KEY="NEEDS_TO_BE_MODIFIED"
|
||||
#env_file: # Alternatively, load variables from whoogle.env
|
||||
#- whoogle.env
|
||||
ports:
|
||||
- 8000:5000
|
Binary file not shown.
After Width: | Height: | Size: 215 KiB |
Binary file not shown.
Before Width: | Height: | Size: 214 KiB |
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
Binary file not shown.
Before Width: | Height: | Size: 61 KiB |
@ -1,24 +0,0 @@
|
||||
https://search.albony.xyz
|
||||
https://search.garudalinux.org
|
||||
https://search.dr460nf1r3.org
|
||||
https://search.nezumi.party
|
||||
https://s.tokhmi.xyz
|
||||
https://search.sethforprivacy.com
|
||||
https://whoogle.dcs0.hu
|
||||
https://whoogle.lunar.icu
|
||||
https://gowogle.voring.me
|
||||
https://whoogle.privacydev.net
|
||||
https://whoogle.hostux.net
|
||||
https://wg.vern.cc
|
||||
https://whoogle.hxvy0.gq
|
||||
https://whoogle.ungovernable.men
|
||||
https://whoogle2.ungovernable.men
|
||||
https://whoogle3.ungovernable.men
|
||||
https://wgl.frail.duckdns.org
|
||||
https://whoogle.no-logs.com
|
||||
https://whoogle.ftw.lol
|
||||
https://whoogle-search--replitcomreside.repl.co
|
||||
https://search.notrustverify.ch
|
||||
https://whoogle.datura.network
|
||||
https://whoogle.yepserver.xyz
|
||||
https://search.snine.nl
|
@ -1,5 +0,0 @@
|
||||
import subprocess
|
||||
|
||||
# A plague upon Replit and all who have built it
|
||||
replit_cmd = "killall -q python3 > /dev/null 2>&1; pip install -r requirements.txt && ./run"
|
||||
subprocess.run(replit_cmd, shell=True)
|
@ -1 +0,0 @@
|
||||
# Place password here. Keep this safe.
|
@ -1,33 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
FF_STRING="FascistFirewall 1"
|
||||
|
||||
if [ "$WHOOGLE_TOR_SERVICE" == "0" ]; then
|
||||
echo "Skipping Tor startup..."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$WHOOGLE_TOR_FF" == "1" ]; then
|
||||
if (grep -q "$FF_STRING" /etc/tor/torrc); then
|
||||
echo "FascistFirewall feature already enabled."
|
||||
else
|
||||
echo "$FF_STRING" >> /etc/tor/torrc
|
||||
|
||||
if [ "$?" -eq 0 ]; then
|
||||
echo "FascistFirewall added to /etc/tor/torrc"
|
||||
else
|
||||
echo "ERROR: Unable to modify /etc/tor/torrc with $FF_STRING."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
#!/bin/bash
|
||||
|
||||
if [ "$(whoami)" != "root" ]; then
|
||||
tor -f /etc/tor/torrc
|
||||
else
|
||||
if (grep alpine /etc/os-release >/dev/null); then
|
||||
rc-service tor start
|
||||
else
|
||||
service tor start
|
||||
fi
|
||||
fi
|
||||
|
@ -1,67 +0,0 @@
|
||||
import json
|
||||
import pathlib
|
||||
import requests
|
||||
|
||||
lingva = 'https://lingva.ml/api/v1/en'
|
||||
|
||||
|
||||
def format_lang(lang: str) -> str:
|
||||
# Chinese (traditional and simplified) require
|
||||
# a different format for lingva translations
|
||||
if 'zh-' in lang:
|
||||
if lang == 'zh-TW':
|
||||
return 'zh_HANT'
|
||||
return 'zh'
|
||||
|
||||
# Strip lang prefix to leave only the actual
|
||||
# language code (i.e. 'en', 'fr', etc)
|
||||
return lang.replace('lang_', '')
|
||||
|
||||
|
||||
def translate(v: str, lang: str) -> str:
|
||||
# Strip lang prefix to leave only the actual
|
||||
#language code (i.e. "es", "fr", etc)
|
||||
lang = format_lang(lang)
|
||||
|
||||
lingva_req = f'{lingva}/{lang}/{v}'
|
||||
|
||||
response = requests.get(lingva_req).json()
|
||||
|
||||
if 'translation' in response:
|
||||
return response['translation']
|
||||
return ''
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
file_path = pathlib.Path(__file__).parent.resolve()
|
||||
tl_path = 'app/static/settings/translations.json'
|
||||
|
||||
with open(f'{file_path}/../{tl_path}', 'r+', encoding='utf-8') as tl_file:
|
||||
tl_data = json.load(tl_file)
|
||||
|
||||
# If there are any english translations that don't
|
||||
# exist for other languages, extract them and translate
|
||||
# them now
|
||||
en_tl = tl_data['lang_en']
|
||||
for k, v in en_tl.items():
|
||||
for lang in tl_data:
|
||||
if lang == 'lang_en' or k in tl_data[lang]:
|
||||
continue
|
||||
|
||||
translation = ''
|
||||
if len(k) == 0:
|
||||
# Special case for placeholder text that gets used
|
||||
# for translations without any key present
|
||||
translation = v
|
||||
else:
|
||||
# Translate the string using lingva
|
||||
translation = translate(v, lang)
|
||||
|
||||
if len(translation) == 0:
|
||||
print(f'! Unable to translate {lang}[{k}]')
|
||||
continue
|
||||
print(f'{lang}[{k}] = {translation}')
|
||||
tl_data[lang][k] = translation
|
||||
|
||||
# Write out updated translations json
|
||||
print(json.dumps(tl_data, indent=4, ensure_ascii=False))
|
@ -1,3 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
@ -1,37 +1,34 @@
|
||||
attrs==22.2.0
|
||||
beautifulsoup4==4.11.2
|
||||
brotli==1.0.9
|
||||
cachelib==0.10.2
|
||||
certifi==2023.7.22
|
||||
cffi==1.15.1
|
||||
chardet==5.1.0
|
||||
click==8.1.3
|
||||
cryptography==3.3.2; platform_machine == 'armv7l'
|
||||
cryptography==42.0.4; platform_machine != 'armv7l'
|
||||
cssutils==2.6.0
|
||||
defusedxml==0.7.1
|
||||
Flask==2.3.2
|
||||
idna==3.4
|
||||
itsdangerous==2.1.2
|
||||
Jinja2==3.1.3
|
||||
MarkupSafe==2.1.2
|
||||
more-itertools==9.0.0
|
||||
packaging==23.0
|
||||
pluggy==1.0.0
|
||||
pycodestyle==2.10.0
|
||||
pycparser==2.21
|
||||
pyOpenSSL==19.1.0; platform_machine == 'armv7l'
|
||||
pyOpenSSL==24.0.0; platform_machine != 'armv7l'
|
||||
pyparsing==3.0.9
|
||||
attrs==19.3.0
|
||||
beautifulsoup4==4.8.2
|
||||
bs4==0.0.1
|
||||
cachelib==0.1
|
||||
certifi==2020.4.5.1
|
||||
cffi==1.13.2
|
||||
chardet==3.0.4
|
||||
Click==7.0
|
||||
cryptography==3.3.2
|
||||
Flask==1.1.1
|
||||
Flask-Session==0.3.2
|
||||
idna==2.9
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.3
|
||||
MarkupSafe==1.1.1
|
||||
more-itertools==8.3.0
|
||||
packaging==20.4
|
||||
pluggy==0.13.1
|
||||
py==1.10.0
|
||||
pycodestyle==2.6.0
|
||||
pycparser==2.19
|
||||
pyOpenSSL==19.1.0
|
||||
pyparsing==2.4.7
|
||||
PySocks==1.7.1
|
||||
pytest==7.2.1
|
||||
python-dateutil==2.8.2
|
||||
requests==2.31.0
|
||||
soupsieve==2.4
|
||||
stem==1.8.1
|
||||
urllib3==1.26.18
|
||||
validators==0.22.0
|
||||
waitress==2.1.2
|
||||
wcwidth==0.2.6
|
||||
Werkzeug==3.0.1
|
||||
python-dotenv==0.21.1
|
||||
pytest==5.4.1
|
||||
python-dateutil==2.8.1
|
||||
requests==2.25.1
|
||||
soupsieve==1.9.5
|
||||
stem==1.8.0
|
||||
urllib3==1.26.5
|
||||
waitress==1.4.3
|
||||
wcwidth==0.1.9
|
||||
Werkzeug==0.16.0
|
||||
python-dotenv==0.16.0
|
||||
|
@ -1,37 +1,26 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
# Usage:
|
||||
# ./run # Runs the full web app
|
||||
# ./run test # Runs the testing suite
|
||||
|
||||
set -e
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(CDPATH= command cd -- "$(dirname -- "$0")" && pwd -P)"
|
||||
SCRIPT_DIR="$(builtin cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
|
||||
# Set directory to serve static content from
|
||||
SUBDIR="${1:-app}"
|
||||
export APP_ROOT="$SCRIPT_DIR/$SUBDIR"
|
||||
export STATIC_FOLDER="$APP_ROOT/static"
|
||||
|
||||
# Clear out build directory
|
||||
rm -f "$SCRIPT_DIR"/app/static/build/*.js
|
||||
rm -f "$SCRIPT_DIR"/app/static/build/*.css
|
||||
|
||||
# Check for regular vs test run
|
||||
if [ "$SUBDIR" = "test" ]; then
|
||||
if [[ "$SUBDIR" == "test" ]]; then
|
||||
# Set up static files for testing
|
||||
rm -rf "$STATIC_FOLDER"
|
||||
ln -s "$SCRIPT_DIR/app/static" "$STATIC_FOLDER"
|
||||
pytest -sv
|
||||
else
|
||||
mkdir -p "$STATIC_FOLDER"
|
||||
|
||||
if [ ! -z "$UNIX_SOCKET" ]; then
|
||||
python3 -um app \
|
||||
--unix-socket "$UNIX_SOCKET"
|
||||
else
|
||||
echo "Running on http://${ADDRESS:-0.0.0.0}:${PORT:-"${EXPOSE_PORT:-5000}"}"
|
||||
python3 -um app \
|
||||
--host "${ADDRESS:-0.0.0.0}" \
|
||||
--port "${PORT:-"${EXPOSE_PORT:-5000}"}"
|
||||
fi
|
||||
fi
|
||||
|
@ -1,45 +0,0 @@
|
||||
[metadata]
|
||||
name = whoogle-search
|
||||
version = attr: app.version.__version__
|
||||
url = https://github.com/benbusby/whoogle-search
|
||||
description = Self-hosted, ad-free, privacy-respecting metasearch engine
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
keywords = search, metasearch, flask, adblock, degoogle, privacy
|
||||
author = Ben Busby
|
||||
author_email = contact@benbusby.com
|
||||
license = MIT
|
||||
classifiers =
|
||||
Programming Language :: Python :: 3
|
||||
License :: OSI Approved :: MIT License
|
||||
Operating System :: OS Independent
|
||||
|
||||
[options]
|
||||
packages = find:
|
||||
include_package_data = True
|
||||
install_requires=
|
||||
beautifulsoup4
|
||||
brotli
|
||||
cssutils
|
||||
cryptography
|
||||
defusedxml
|
||||
Flask
|
||||
python-dotenv
|
||||
requests
|
||||
stem
|
||||
validators
|
||||
waitress
|
||||
|
||||
[options.extras_require]
|
||||
test =
|
||||
pytest
|
||||
python-dateutil
|
||||
dev = pycodestyle
|
||||
|
||||
[options.packages.find]
|
||||
exclude =
|
||||
test*
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
whoogle-search = app.routes:run_app
|
@ -0,0 +1,29 @@
|
||||
import setuptools
|
||||
|
||||
long_description = open('README.md', 'r').read()
|
||||
|
||||
requirements = list(open('requirements.txt', 'r'))
|
||||
|
||||
setuptools.setup(
|
||||
author='Ben Busby',
|
||||
author_email='benbusby@protonmail.com',
|
||||
name='whoogle-search',
|
||||
version='0.5.4',
|
||||
include_package_data=True,
|
||||
install_requires=requirements,
|
||||
description='Self-hosted, ad-free, privacy-respecting metasearch engine',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/markdown',
|
||||
url='https://github.com/benbusby/whoogle-search',
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'whoogle-search=app.routes:run_app',
|
||||
]
|
||||
},
|
||||
packages=setuptools.find_packages(),
|
||||
classifiers=[
|
||||
'Programming Language :: Python :: 3',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
],
|
||||
)
|
@ -1,16 +1,12 @@
|
||||
from app.models.endpoint import Endpoint
|
||||
|
||||
|
||||
def test_autocomplete_get(client):
|
||||
rv = client.get(f'/{Endpoint.autocomplete}?q=green+eggs+and')
|
||||
rv = client.get('/autocomplete?q=green+eggs+and')
|
||||
assert rv._status_code == 200
|
||||
assert len(rv.data) >= 1
|
||||
assert b'green eggs and ham' in rv.data
|
||||
|
||||
|
||||
def test_autocomplete_post(client):
|
||||
rv = client.post(f'/{Endpoint.autocomplete}',
|
||||
data=dict(q='the+cat+in+the'))
|
||||
rv = client.post('/autocomplete', data=dict(q='the+cat+in+the'))
|
||||
assert rv._status_code == 200
|
||||
assert len(rv.data) >= 1
|
||||
assert b'the cat in the hat' in rv.data
|
||||
|
Loading…
Reference in New Issue