7 Commits
v0.1.1 ... v0.2

Author SHA1 Message Date
eneller
c5ef95d69b fix: table headers 2025-03-20 21:53:26 +01:00
eneller
c728f65e73 fix: search 2025-03-20 21:46:53 +01:00
eneller
c1f813d70c frontend tweaks 2025-03-20 20:53:19 +01:00
eneller
aa66c06f88 feat: CTRL + K search, css 2025-03-20 20:15:51 +01:00
eneller
518a8a1744 minor tweaks 2025-03-20 15:07:06 +01:00
eneller
b65d878981 build: add watchman 2025-03-19 22:25:23 +01:00
eneller
3d10081846 feat: error handling 2025-03-19 22:04:23 +01:00
10 changed files with 121 additions and 52 deletions

1
.gitignore vendored
View File

@@ -372,3 +372,4 @@ pyrightconfig.json
# End of https://www.toptal.com/developers/gitignore/api/python,django,visualstudiocode,intellij+all
staticfiles/

View File

@@ -1,2 +1,6 @@
# epub2go-web
A simple Website to provide a `NNI (Non-Nerd Interface)` to [epub2go.py](https://github.com/eneller/epub2go.py), a web to epub converter.
A simple Website to provide a `NNI (Non-Nerd Interface)` to [epub2go.py](https://github.com/eneller/epub2go.py), a web to epub converter.
## Development
This project uses [watchman](https://facebook.github.io/watchman/) for file watching and reloading.
Follow the [official instructions](https://facebook.github.io/watchman/docs/install.html) for your system to install, django will default to its standard watcher otherwise.

View File

@@ -8,6 +8,8 @@ dependencies = [
"celery>=5.4.0",
"django>=5.1.6",
"epub2go",
"python-dotenv>=1.0.1",
"pywatchman>=2.0.0",
]
[tool.uv.sources]

4
src/.watchmanconfig Normal file
View File

@@ -0,0 +1,4 @@
{
"ignore_dirs": ["node_modules"]
}

View File

@@ -1,5 +1,5 @@
from django.shortcuts import render
from django.http import HttpRequest, HttpResponse, FileResponse
from django.http import HttpRequest, HttpResponse, FileResponse, HttpResponseBadRequest
from django.conf import settings
from celery import shared_task
@@ -12,7 +12,7 @@ import logging
logger = logging.getLogger(__name__) #TODO configure logging
converter = GBConvert(downloaddir=settings.MEDIA_ROOT)
books = get_all_books()# TODO get from pickle
books = sorted(get_all_books(), key= lambda b: b.title)# TODO get from pickle
gbnetloc = urlparse(allbooks_url).netloc
def index(request: HttpRequest):
@@ -21,22 +21,26 @@ def index(request: HttpRequest):
'http_host': request.META['HTTP_HOST'],
'books': books,
'book_count': len(books),
'allbooks_url': allbooks_url,
}
targetParam = request.GET.get('t', None)
if validateUrl(targetParam):
fpath = getEpub(targetParam)
fname = os.path.basename(fpath)
file = open(fpath, 'rb')
response = FileResponse(file)
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = f'attachment; filename="{fname}"'
return response
return render(request, 'index.html', context)
if targetParam:
if validateUrl(targetParam):
# download file
fpath = getEpub(targetParam)
fname = os.path.basename(fpath)
file = open(fpath, 'rb')
response = FileResponse(file)
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = f'attachment; filename="{fname}"'
return response
else: return HttpResponseBadRequest('Input URL invalid.')
else:
# return base view
return render(request, 'index.html', context)
def validateUrl(param)->bool :
if not param: return False
netloc = urlparse(param).netloc
if(netloc == gbnetloc): return True

View File

@@ -26,7 +26,7 @@ SECRET_KEY = "django-insecure-^@m5bl*8x+=@c^b0lhkgb-%_#9#&oad=v15jq=!0$g#x17zjf8
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['*']
# Application definition
@@ -123,6 +123,7 @@ STATIC_URL = "static/"
STATICFILES_DIRS = [
PROJ_DIR / "static/",
]
STATIC_ROOT = PROJ_DIR/ "staticfiles"
MEDIA_URL = "media/"
MEDIA_ROOT = PROJ_DIR / "media/"

View File

@@ -2,12 +2,19 @@
const params = new URLSearchParams(window.location.search);
const searchInput = document.getElementById('searchInput');
const table = document.getElementById('table');
const table_r = Array.from(table.getElementsByTagName('tr'));
const table_r = Array.from(table.getElementsByClassName('table-entry'));
// allow search from url parameter
document.addEventListener('keydown', (event)=>{
if (event.ctrlKey && event.key === 'k'){
event.preventDefault();
searchInput.select();
}
});
// search from url parameter
let searchParam = params.get('s');
if (searchParam){
searchInput.value = searchParam;
console.log(searchParam);
search(searchParam);
}
@@ -16,9 +23,10 @@ function submitSearch(event){
search();
}
function search(searchStr = searchInput.value){
searchStr= searchStr.toLowerCase();
function showMatch(tr){
// match search with list
let searchSuccess = Array.from(tr.getElementsByClassName('table-data')).map(e => e.textContent)
let searchSuccess = Array.from(tr.getElementsByClassName('table-data')).map(e => e.textContent.toLowerCase())
.join(' ')
.indexOf(searchStr) > -1;
if (searchSuccess) tr.style.display = "";

View File

@@ -1,57 +1,78 @@
/* this is part of the http://bettermotherfuckingwebsite.com/ */
/*TODO also style svg icons accordingly */
:root{
--bg:#faf0e673;
--bg-acc:#EEEEEE;
--bg-hover: #DDDDDD;
--fg:#444;
--fg-deemph: #777;
}
html{
font-family: serif;
}
body{
background-color: var(--bg);
margin:40px auto;
max-width:650px;
line-height:1.6;
max-width:800px;
line-height:1.4;
font-size:18px;
color:#444;
padding:0
10px}
color:var(--fg);
padding:0 10px;
}
h1,h2,h3{
line-height:1.2
}
/* custom styles here */
:root{
--white:#faf0e673;
}
body{
background-color: var(--white)
line-height:1.2;
letter-spacing: -2%;
}
header{
text-align: center;
justify-content: center;
margin-bottom: 4%;
}
small{
color: #777;
color: var(--fg-deemph);
}
.searchbar{
width: fit-content;
width: 100%;
}
#searchInput {
background-color: var(--bg);
width: 100%;
padding: .2em;
font-size: larger;
border-radius: 10px;
border-color: var(--bg-acc);
box-shadow: var(--bg-acc) 2px 2px;
}
table, tr{
/* make table not resize when elements are hidden by searching */
width: 100%;
}
th{
text-align: left;
}
tr:nth-child(even){
background-color: #EEEEEE;
background-color: var(--bg-acc);
}
tr:hover{
background-color: #DDDDDD;
background-color: var(--bg-hover);
transition: all 2ms;
}
.inline-icon{
.inline-icon, .header-icon{
vertical-align: middle;
height: 1em;
}
.header-icon{
vertical-align: middle;
height: 1em;
padding: .5em;
fill: var(--fg-deemph);
}
a:hover, a:any-link{
text-decoration: none;
color: inherit;
}
.table-link{
/* TODO fix links with no title/content being almost unclickable */
display: block;
width: 100%;
height: 100%;
padding: 3px;
padding: 1px;
}

View File

@@ -10,19 +10,21 @@
<body>
<header>
<div>
<h1>{{ title }}</h1>
<a href="javascript:void(window.open('http://{{ http_host }}/?t=' + encodeURIComponent(window.location.toString())))" title="Als Lesezeichen speichern"><!--TODO fix domain part as variable-->
<img src="{% static 'bookmark.svg' %}" alt="Bookmarklet" class="header-icon">
</a>
<a href="https://github.com/eneller/epub2go-web">
<img src="{% static 'github.svg' %}" alt="GitHub" class="header-icon">
</a></div>
<h1>{{ title }}</h1>
<h2>1 Click Download für Literatur</h2>
</div>
<search>
<form onsubmit="submitSearch(event)" class="searchbar">
<input type="search" id="searchInput" placeholder="Suche nach Titel" minlength="3">
</form>
</search>
<small>Im Moment finden sich hier {{ book_count }} Bücher. </small>
<small>Im Moment finden sich hier <a href="{{ allbooks_url }}">{{ book_count }} Bücher.</a> </small>
<a href="javascript:void(window.open('http://{{ http_host }}/?t=' + encodeURIComponent(window.location.toString())))" title="Als Lesezeichen speichern"><!--TODO fix domain part as variable-->
<img src="{% static 'bookmark.svg' %}" alt="Bookmarklet" class="header-icon">
</a>
<a href="https://github.com/eneller/epub2go-web">
<img src="{% static 'github.svg' %}" alt="GitHub" class="header-icon">
</a>
</header>
<main>
<!-- NOTE use dl here?-->
@@ -30,13 +32,13 @@
<thead>
<tr>
<th></th>
<th>Title</th>
<th>Author</th>
<th>Titel</th>
<th>Autor</th>
</tr>
</thead>
<tbody>
{% for item in books %}
<tr>
<tr class="table-entry">
<td>
<a href= {{ item.url }} target="_blank" rel="noopener noreferrer" class="table-link">
<img src="{% static 'open-link.svg' %}" alt="Open Link" class="inline-icon">

22
uv.lock generated
View File

@@ -201,6 +201,8 @@ dependencies = [
{ name = "celery" },
{ name = "django" },
{ name = "epub2go" },
{ name = "python-dotenv" },
{ name = "pywatchman" },
]
[package.metadata]
@@ -208,6 +210,8 @@ requires-dist = [
{ name = "celery", specifier = ">=5.4.0" },
{ name = "django", specifier = ">=5.1.6" },
{ name = "epub2go", git = "https://github.com/eneller/epub2go.py" },
{ name = "python-dotenv", specifier = ">=1.0.1" },
{ name = "pywatchman", specifier = ">=2.0.0" },
]
[[package]]
@@ -266,6 +270,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
]
[[package]]
name = "python-dotenv"
version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
]
[[package]]
name = "pywatchman"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cf/39/fc10dd952ac72a3a293936cd66a4551fdeb9012d2db99234a376100641ce/pywatchman-2.0.0.tar.gz", hash = "sha256:25354d9e3647f94411a4c13e510c83a1ceecc17977b0525ba41b16e7019c7b0c", size = 40570 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/51/7f/5d68d803489770cffa5d2b44be99b978c866f8a4d8e835f9da850415ed8a/pywatchman-2.0.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:51c2b4c72bea6b9fd90caf20759f5bc47febf0fd27bf2f247b87c66e2f6bab02", size = 52557 },
]
[[package]]
name = "requests"
version = "2.32.3"