Added UI
This commit is contained in:
parent
6fde26affd
commit
52e8b92f1f
|
@ -0,0 +1,10 @@
|
||||||
|
FROM python:3-alpine
|
||||||
|
|
||||||
|
WORKDIR /usr/src/rural-dict
|
||||||
|
|
||||||
|
COPY requirements.txt ./
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD [ "python", "./main.py" ]
|
45
README.md
45
README.md
|
@ -1,3 +1,46 @@
|
||||||
# country-dictionary
|
# Country Dictionary
|
||||||
|
|
||||||
A privacy-respecting Urban Dictionary client in Flask - Mostly based on https://git.vern.cc/cobra/rural-dict
|
A privacy-respecting Urban Dictionary client in Flask - Mostly based on https://git.vern.cc/cobra/rural-dict
|
||||||
|
|
||||||
|
## Instances
|
||||||
|
|
||||||
|
| **URL** | **Country** | **Ownername** | **Owner Website** | |
|
||||||
|
| ---------------------------------- | ----------- | ------------- | ------------------------ | --- |
|
||||||
|
| https://country-dict.winscloud.net | TH | WinsDominoes | https://winsdominoes.net | |
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Country Dictionary scrapes _Urban Dictionary_ for data and then displays it in HTML & CSS.
|
||||||
|
|
||||||
|
### Supports
|
||||||
|
|
||||||
|
- Define a word with multiple entries
|
||||||
|
- Pagination
|
||||||
|
- Random list of words
|
||||||
|
- User pages
|
||||||
|
- Urban Dictionary home with words of the day
|
||||||
|
- Matches urban dictionary's endpoints for features listed above
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- bs4
|
||||||
|
- requests
|
||||||
|
- waitress
|
||||||
|
- Relatively new version of python
|
||||||
|
|
||||||
|
### Redirection
|
||||||
|
|
||||||
|
Simply replace an _Urban Dictionary_ URL with a Country Dictionary URL from the instance list above.
|
||||||
|
|
||||||
|
```
|
||||||
|
https://urbandictionary.com/define.php?term=eevee
|
||||||
|
|
||||||
|
https://country-dict.winscloud.net/define.php?term=eevee
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: More endpoints are supported
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
- https://git.vern.cc/cobra/rural-dict - the original Rural Dictionary project
|
||||||
|
- https://codeberg.org/zortazert - created the initial Urban Dictionary frontend using JavaScript and helped develop Rural Dictionary
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
rural-dict:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:8080:8080"
|
|
@ -0,0 +1,8 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"clearnet": "https://country-dict.winscloud.net",
|
||||||
|
"country": "TH",
|
||||||
|
"owner_name": "WinsDominoes",
|
||||||
|
"owner_website": "https://winsdominoes.net"
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from flask import Flask, render_template, request, redirect
|
||||||
|
import requests
|
||||||
|
import html
|
||||||
|
import re
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from urllib.parse import quote, unquote
|
||||||
|
|
||||||
|
def scrape(url):
|
||||||
|
data = requests.get(url)
|
||||||
|
|
||||||
|
our_path = re.sub(r".*://.*/", "/", request.url)
|
||||||
|
path = re.sub(r".*://.*/", "/", data.url)
|
||||||
|
if our_path != path and \
|
||||||
|
quote(unquote(re.sub("[?&=]", "", our_path))) != re.sub("[?&=]", "", path):
|
||||||
|
# this is bad ^
|
||||||
|
return f"REDIRECT {path}"
|
||||||
|
ret = []
|
||||||
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
defs = [(div, div.get('data-defid')) for div in soup.find_all("div") if div.get('data-defid')]
|
||||||
|
try:
|
||||||
|
thumbs_data = {
|
||||||
|
str(entry['defid']): entry
|
||||||
|
for entry
|
||||||
|
in requests.get(
|
||||||
|
'https://api.urbandictionary.com/v0/uncacheable?ids=' + ','.join(defid for (_, defid) in defs)
|
||||||
|
).json()['thumbs']
|
||||||
|
}
|
||||||
|
except:
|
||||||
|
thumbs_data = {}
|
||||||
|
|
||||||
|
for (definition, defid) in defs:
|
||||||
|
word = definition.select("div div h1 a, div div h2 a")[0].text
|
||||||
|
meaning = definition.find(attrs={"class" : ["break-words meaning mb-4"]}).decode_contents()
|
||||||
|
example = definition.find(attrs={"class" : ["break-words example italic mb-4"]}).decode_contents()
|
||||||
|
contributor = definition.find(attrs={"class" : ["contributor font-bold"]})
|
||||||
|
thumbs_up = thumbs_data.get(defid, {}).get('up')
|
||||||
|
thumbs_down = thumbs_data.get(defid, {}).get('down')
|
||||||
|
ret.append([defid, word, meaning, example, contributor, thumbs_up, thumbs_down])
|
||||||
|
pages = soup.find(attrs={"class" : ["pagination text-xl text-center"]})
|
||||||
|
if pages == None:
|
||||||
|
pages = ""
|
||||||
|
return (ret, pages)
|
||||||
|
|
||||||
|
app = Flask(__name__, template_folder="templates", static_folder="static")
|
||||||
|
|
||||||
|
@app.route('/', defaults={'path': ''})
|
||||||
|
@app.route('/<path:path>')
|
||||||
|
def catch_all(path):
|
||||||
|
scraped = scrape(f"https://urbandictionary.com/{re.sub(r'.*://.*/', '/', request.url)}")
|
||||||
|
if type(scraped) == str and scraped.startswith("REDIRECT"):
|
||||||
|
return redirect(scraped.replace("REDIRECT ", ""), 302)
|
||||||
|
return render_template('index.html', data=scraped)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from waitress import serve
|
||||||
|
serve(app, host="0.0.0.0", port=8089)
|
|
@ -0,0 +1,4 @@
|
||||||
|
beautifulsoup4
|
||||||
|
requests
|
||||||
|
flask
|
||||||
|
waitress
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,28 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: "Roboto";
|
||||||
|
src: url("../fonts/Roboto-Regular.ttf") format("truetype");
|
||||||
|
} /* Safari, Android, iOS */
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
font-family: "Roboto" !important;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
margin-right: 1ch;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination ul > li {
|
||||||
|
list-style: none;
|
||||||
|
display: inline-block;
|
||||||
|
padding-left: 1ch;
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
@ -0,0 +1,19 @@
|
||||||
|
(function () {
|
||||||
|
const htmlElement = document.querySelector("html");
|
||||||
|
if (htmlElement.getAttribute("data-bs-theme") === "auto") {
|
||||||
|
function updateTheme() {
|
||||||
|
document
|
||||||
|
.querySelector("html")
|
||||||
|
.setAttribute(
|
||||||
|
"data-bs-theme",
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
|
? "dark"
|
||||||
|
: "light"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
window
|
||||||
|
.matchMedia("(prefers-color-scheme: dark)")
|
||||||
|
.addEventListener("change", updateTheme);
|
||||||
|
updateTheme();
|
||||||
|
}
|
||||||
|
})();
|
|
@ -0,0 +1,71 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="mdui-theme-auto" data-bs-theme="auto">
|
||||||
|
<head>
|
||||||
|
<title>Country Dictionary</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}" />
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap-icons.min.css') }}">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}" />
|
||||||
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='img/favicon.png') }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div id="logoContainer" class="mt-5">
|
||||||
|
<a href="/" class="text-light-emphasis">
|
||||||
|
<h1 class="display-1 fw-bold">Country Dictionary</h1>
|
||||||
|
</a>
|
||||||
|
<p>A fork of <a href="https://git.vern.cc/cobra/rural-dict">Rural Dictionary</a></p>
|
||||||
|
</div>
|
||||||
|
<div class="field-text">
|
||||||
|
<form id="search" role="search" method="get" action="/define.php">
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<input type="search" id="term" name="term" placeholder="Search" class="form-control" autofocus>
|
||||||
|
<button class="btn btn-outline-secondary">Go</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="row row-cols-auto gap-2 mb-3 justify-content-center">
|
||||||
|
<a href=/random.php>
|
||||||
|
<button class="btn btn-danger">
|
||||||
|
Random
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<a href="https://git.winscloud.net/winsdominoes/country-dict">
|
||||||
|
<button class="btn btn-warning">
|
||||||
|
Source Code
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% for defid, word, definition, example, author, thumbs_up, thumbs_down in data[0] %}
|
||||||
|
<div class="card mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="{{ defid }}">
|
||||||
|
<a href="/define.php?term={{ word }}">
|
||||||
|
<h2>{{ word }}</h2>
|
||||||
|
</a>
|
||||||
|
<p>{{ definition|safe }}</p>
|
||||||
|
<p><i>{{ example|safe }}</i></p>
|
||||||
|
<p>{{ author|safe }}</p>
|
||||||
|
{% if thumbs_up and thumbs_down %}
|
||||||
|
<div class="d-grid gap-2 d-md-block">
|
||||||
|
<button class="btn btn-outline-secondary disabled">
|
||||||
|
<i class="bi bi-hand-thumbs-up-fill"></i>
|
||||||
|
{{ thumbs_up|safe }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary disabled">
|
||||||
|
<i class="bi bi-hand-thumbs-down-fill"></i>
|
||||||
|
{{ thumbs_down|safe }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
{{ data[1]|safe }}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue