mirror of
https://github.com/th3r00t/pyShelf.git
synced 2026-04-28 01:59:35 -04:00
Merge branch 'newui' of github.com:th3r00t/pyShelf into newui
testing
This commit is contained in:
2
config.json
vendored
2
config.json
vendored
@@ -1 +1 @@
|
|||||||
{"TITLE": "pyShelf E-Book Server", "VERSION": "0.5.0", "BOOKPATH": "", "DB_HOST": "localhost", "DB_PORT": "5432", "DATABASE": "pyshelf", "USER": "pyshelf", "PASSWORD": "pyshelf", "BOOKSHELF": "data/shelf.json", "ALLOWED_HOSTS": "*", "hostname": "localhost", "webport": "8000", "wsgiport": "8001"}
|
{"TITLE": "pyShelf E-Book Server", "VERSION": "0.5.0", "BOOKPATH": "/home/raelon/Books", "DB_HOST": "localhost", "DB_PORT": "5432", "DATABASE": "pyshelf", "USER": "pyshelf", "PASSWORD": "pyshelf", "BOOKSHELF": "data/shelf.json", "ALLOWED_HOSTS": "*", "hostname": "localhost", "webport": "8000", "wsgiport": "8001"}
|
||||||
|
|||||||
2
importBooks
vendored
2
importBooks
vendored
@@ -1,4 +1,4 @@
|
|||||||
#!python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
0
importBooks.pstat
vendored
Normal file
0
importBooks.pstat
vendored
Normal file
2
installer
vendored
2
installer
vendored
@@ -1,4 +1,4 @@
|
|||||||
#!python
|
#!/usr/bin/ env python
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|||||||
2
makeCollections
vendored
2
makeCollections
vendored
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
BIN
preview_060_newui_collections_wip.png
vendored
Normal file
BIN
preview_060_newui_collections_wip.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
BIN
preview_060_newui_wip.png
vendored
Normal file
BIN
preview_060_newui_wip.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
2
pyproject.toml
vendored
2
pyproject.toml
vendored
@@ -7,4 +7,4 @@ use_parentheses = true
|
|||||||
# NOTE: the known_third_party setting is managed by
|
# NOTE: the known_third_party setting is managed by
|
||||||
# seed-isort-config and should not be modified directly.
|
# seed-isort-config and should not be modified directly.
|
||||||
# Any changes made to this setting will be overwritten.
|
# Any changes made to this setting will be overwritten.
|
||||||
known_third_party = ["backend", "bs4", "django", "interface", "prompt_toolkit", "psycopg2", "pyfiglet", "requests"]
|
known_third_party = ["backend", "bs4", "django", "interface", "mobi", "prompt_toolkit", "psycopg2", "pyfiglet", "requests"]
|
||||||
|
|||||||
1
requirements.txt
vendored
1
requirements.txt
vendored
@@ -17,3 +17,4 @@ psycopg2-binary
|
|||||||
prompt_toolkit
|
prompt_toolkit
|
||||||
psutil
|
psutil
|
||||||
pyfiglet
|
pyfiglet
|
||||||
|
mobi-python
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/env python
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
@@ -7,6 +7,8 @@ import zipfile
|
|||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from mobi import Mobi
|
||||||
|
|
||||||
from .api_hooks import DuckDuckGo
|
from .api_hooks import DuckDuckGo
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .storage import Storage
|
from .storage import Storage
|
||||||
@@ -26,8 +28,6 @@ class Catalogue:
|
|||||||
self.html_regx = re.compile(r"\.html")
|
self.html_regx = re.compile(r"\.html")
|
||||||
self.root_dir = config.root
|
self.root_dir = config.root
|
||||||
self.book_folder = config.book_path
|
self.book_folder = config.book_path
|
||||||
self.book_shelf = config.book_shelf
|
|
||||||
self._book_list_expanded = None
|
|
||||||
self.books = None
|
self.books = None
|
||||||
self.db_pointer = config.catalogue_db
|
self.db_pointer = config.catalogue_db
|
||||||
self.config = config
|
self.config = config
|
||||||
@@ -57,23 +57,29 @@ class Catalogue:
|
|||||||
:returns self._book_list_expanded: json string containing all book metadata
|
:returns self._book_list_expanded: json string containing all book metadata
|
||||||
"""
|
"""
|
||||||
self.scan_folder() # Populate file list
|
self.scan_folder() # Populate file list
|
||||||
regx = re.compile(r"\.epub")
|
regx = re.compile(r"\.epub|\.mobi")
|
||||||
try:
|
try:
|
||||||
self.books = list(filter(regx.search, filter(None, self.file_list)))
|
self.books = list(filter(regx.search, filter(None, self.file_list)))
|
||||||
except TypeError as e:
|
except TypeError as e:
|
||||||
print(e)
|
print(e)
|
||||||
self._book_list_expanded = {}
|
"""
|
||||||
with open(self.book_shelf, "w") as f:
|
|
||||||
for book in self.books:
|
for book in self.books:
|
||||||
self._book_list_expanded[book] = self.process_book(book)
|
self._book_list_expanded[book] = self.process_by_filetype(book)
|
||||||
json.dump(self._book_list_expanded, f)
|
|
||||||
return self._book_list_expanded
|
return self._book_list_expanded
|
||||||
|
"""
|
||||||
|
|
||||||
|
def process_by_filetype(self, book):
|
||||||
|
if book.endswith(".epub"):
|
||||||
|
epub = self.process_epub(book)
|
||||||
|
return self.extract_metadata_epub(epub)
|
||||||
|
elif book.endswith(".mobi"):
|
||||||
|
return self.extract_metadata_mobi(book)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_book(book):
|
def process_epub(book):
|
||||||
"""Return dictionary of epub file contents"""
|
"""Return dictionary of epub file contents"""
|
||||||
book = zipfile.ZipFile(book, "r")
|
|
||||||
details = {}
|
details = {}
|
||||||
|
book = zipfile.ZipFile(book, "r")
|
||||||
with book as book_zip:
|
with book as book_zip:
|
||||||
details["files"] = []
|
details["files"] = []
|
||||||
details["path"] = book.filename
|
details["path"] = book.filename
|
||||||
@@ -86,7 +92,7 @@ class Catalogue:
|
|||||||
details["files"].append(match.string)
|
details["files"].append(match.string)
|
||||||
return details
|
return details
|
||||||
|
|
||||||
def extract_metadata(self, book):
|
def extract_metadata_epub(self, book):
|
||||||
"""
|
"""
|
||||||
Return extracted metadata and cover picture
|
Return extracted metadata and cover picture
|
||||||
book['path'] == Full path to ebook file
|
book['path'] == Full path to ebook file
|
||||||
@@ -94,7 +100,7 @@ class Catalogue:
|
|||||||
"""
|
"""
|
||||||
book_zip = zipfile.ZipFile(book["path"], "r")
|
book_zip = zipfile.ZipFile(book["path"], "r")
|
||||||
with book_zip as f:
|
with book_zip as f:
|
||||||
content = self.extract_content(book_zip, book)
|
content = self.extract_content(f, book)
|
||||||
soup = BeautifulSoup(content, "lxml")
|
soup = BeautifulSoup(content, "lxml")
|
||||||
title = soup.find("dc:title")
|
title = soup.find("dc:title")
|
||||||
if title is None:
|
if title is None:
|
||||||
@@ -105,13 +111,89 @@ class Catalogue:
|
|||||||
if author is not None:
|
if author is not None:
|
||||||
author = author.contents[0]
|
author = author.contents[0]
|
||||||
try:
|
try:
|
||||||
cover = self.extract_cover_image(book_zip, book)
|
cover = self.extract_cover_image(f, book)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
# cover = self.extract_cover_html(book_zip, book)
|
# cover = self.extract_cover_html(book_zip, book)
|
||||||
cover = DuckDuckGo().image_result(title)
|
cover = DuckDuckGo().image_result(title)
|
||||||
book_details = [title, author, cover, book["path"]]
|
try:
|
||||||
|
description = self.stripTags(soup.find("dc:description").text)
|
||||||
|
except AttributeError:
|
||||||
|
description = None
|
||||||
|
try:
|
||||||
|
identifier = self.stripTags(soup.find("dc:identifier").text)
|
||||||
|
except AttributeError:
|
||||||
|
identifier = None
|
||||||
|
try:
|
||||||
|
publisher = self.stripTags(soup.find("dc:publisher").text)
|
||||||
|
except AttributeError:
|
||||||
|
publisher = None
|
||||||
|
try:
|
||||||
|
date = self.stripTags(soup.find("dc:date").text)
|
||||||
|
except AttributeError:
|
||||||
|
date = None
|
||||||
|
try:
|
||||||
|
rights = self.stripTags(soup.find("dc:rights").text)
|
||||||
|
except AttributeError:
|
||||||
|
rights = None
|
||||||
|
try:
|
||||||
|
tags = soup.find_all("dc:subject")
|
||||||
|
except AttributeError:
|
||||||
|
tags = None
|
||||||
|
ftags = None
|
||||||
|
if tags is not None:
|
||||||
|
for tag in tags:
|
||||||
|
if ftags is None:
|
||||||
|
ftags = tag.text
|
||||||
|
else:
|
||||||
|
ftags = ftags + "," + tag.text
|
||||||
|
book_details = [
|
||||||
|
title,
|
||||||
|
author,
|
||||||
|
cover,
|
||||||
|
book["path"],
|
||||||
|
description,
|
||||||
|
identifier,
|
||||||
|
publisher,
|
||||||
|
date,
|
||||||
|
rights,
|
||||||
|
ftags,
|
||||||
|
]
|
||||||
return book_details
|
return book_details
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def stripTags(source):
|
||||||
|
p = re.compile(r"<.*?>")
|
||||||
|
return p.sub("", source)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extract_metadata_mobi(book):
|
||||||
|
book = Mobi(book)
|
||||||
|
book.parse()
|
||||||
|
try:
|
||||||
|
cover_image = book.readImageRecord(0)
|
||||||
|
except KeyError:
|
||||||
|
cover_image = None
|
||||||
|
title = book.title().decode("utf-8")
|
||||||
|
author = book.author().decode("utf-8")
|
||||||
|
description = None
|
||||||
|
identifier = None
|
||||||
|
publisher = None
|
||||||
|
date = None
|
||||||
|
rights = None
|
||||||
|
ftags = None
|
||||||
|
return [
|
||||||
|
title,
|
||||||
|
author,
|
||||||
|
cover_image,
|
||||||
|
book.f.name,
|
||||||
|
description,
|
||||||
|
identifier,
|
||||||
|
publisher,
|
||||||
|
date,
|
||||||
|
rights,
|
||||||
|
ftags,
|
||||||
|
]
|
||||||
|
|
||||||
def extract_content(self, book_zip, book):
|
def extract_content(self, book_zip, book):
|
||||||
"""
|
"""
|
||||||
Opens epub as zip file filters then stores as list any files matching opf_regx
|
Opens epub as zip file filters then stores as list any files matching opf_regx
|
||||||
@@ -161,12 +243,13 @@ class Catalogue:
|
|||||||
Gets a list of new files via compare_shelf_current.
|
Gets a list of new files via compare_shelf_current.
|
||||||
Iterates over list and inserts new books into database.
|
Iterates over list and inserts new books into database.
|
||||||
"""
|
"""
|
||||||
|
# TODO Refactor metadata extraction into process_book \
|
||||||
|
# call to more easily handle additional formats
|
||||||
book_list = self.compare_shelf_current()
|
book_list = self.compare_shelf_current()
|
||||||
db = Storage(self.config)
|
db = Storage(self.config)
|
||||||
for book in book_list:
|
for book in book_list:
|
||||||
book = self.process_book(book)
|
book = self.process_by_filetype(book)
|
||||||
extracted = self.extract_metadata(book)
|
db.insert_book(book)
|
||||||
db.insert_book(extracted)
|
|
||||||
inserted = db.commit()
|
inserted = db.commit()
|
||||||
if inserted is not True:
|
if inserted is not True:
|
||||||
print(inserted)
|
print(inserted)
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class Storage:
|
|||||||
Insert book in database
|
Insert book in database
|
||||||
:returns: True if succeeds False if not
|
:returns: True if succeeds False if not
|
||||||
"""
|
"""
|
||||||
q = "INSERT INTO books (title, author, cover, progress, file_name, pages) values (%s, %s, %s, 0, %s, 0);"
|
q = "INSERT INTO books (title, author, cover, progress, file_name, pages, description, identifier, publisher, date, rights, tags) values (%s, %s, %s, 0, %s, 0, %s, %s, %s, %s, %s, %s);"
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
cover_image = book[2].data
|
cover_image = book[2].data
|
||||||
@@ -61,9 +61,26 @@ class Storage:
|
|||||||
cover_image = book[2]
|
cover_image = book[2]
|
||||||
if not book[2]: # If cover image is missing unset entry
|
if not book[2]: # If cover image is missing unset entry
|
||||||
cover_image = None
|
cover_image = None
|
||||||
self.cursor.execute(q, (book[0], book[1], cover_image, book[3]))
|
self.cursor.execute(
|
||||||
|
q,
|
||||||
|
(
|
||||||
|
book[0], # title
|
||||||
|
book[1], # author
|
||||||
|
cover_image,
|
||||||
|
book[3], # file
|
||||||
|
book[4], # descr
|
||||||
|
book[5], # ident
|
||||||
|
book[6], # publisher
|
||||||
|
book[7], # date
|
||||||
|
book[8], # rights
|
||||||
|
book[9], # tags
|
||||||
|
),
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
if int(e.pgcode) == 22007:
|
||||||
|
book[7] = psycopg2.Date(int(book[7]), 1, 1)
|
||||||
|
self.insert_book(book)
|
||||||
print(e)
|
print(e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/env python
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/env python
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|||||||
35
src/interface/migrations/0005_navigation.py
Normal file
35
src/interface/migrations/0005_navigation.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Generated by Django 3.0.4 on 2020-06-10 05:07
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("interface", "0004_collections"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="Navigation",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("title", models.CharField(max_length=255)),
|
||||||
|
("link", models.CharField(max_length=255, null=True)),
|
||||||
|
("category", models.CharField(max_length=255, null=True)),
|
||||||
|
("parent_id", models.IntegerField(null=True)),
|
||||||
|
("alt", models.CharField(max_length=255, null=True)),
|
||||||
|
("type", models.IntegerField(null=True)),
|
||||||
|
("socket", models.CharField(max_length=255)),
|
||||||
|
],
|
||||||
|
options={"db_table": "navigation",},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -29,6 +29,12 @@ class Books(models.Model):
|
|||||||
pages = models.IntegerField(null=True)
|
pages = models.IntegerField(null=True)
|
||||||
progress = models.IntegerField(null=True)
|
progress = models.IntegerField(null=True)
|
||||||
file_name = models.CharField(max_length=255, null=False)
|
file_name = models.CharField(max_length=255, null=False)
|
||||||
|
description = models.TextField(null=True)
|
||||||
|
identifier = models.CharField(max_length=255, null=True)
|
||||||
|
publisher = models.CharField(max_length=266, null=True)
|
||||||
|
date = models.DateField(null=True)
|
||||||
|
rights = models.CharField(max_length=255, null=True)
|
||||||
|
tags = models.CharField(max_length=255, null=True)
|
||||||
|
|
||||||
def generic_search(self, query):
|
def generic_search(self, query):
|
||||||
try:
|
try:
|
||||||
@@ -62,3 +68,100 @@ class Collections(models.Model):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise
|
raise
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
class Navigation(models.Model):
|
||||||
|
"""
|
||||||
|
pyShelfs Navigation Database class
|
||||||
|
:param title: Link Text
|
||||||
|
:param link: Link link :)
|
||||||
|
:param category: Where in the nav tree do I belong
|
||||||
|
:param parent_id: This link is a sub link of link with id of me
|
||||||
|
:param alt: Alternate text of link
|
||||||
|
:param type: Web link, or Socket link which will be expected to act on \
|
||||||
|
the link, and the action defined in socket
|
||||||
|
:param socket: if a Socket link define socket here
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "navigation"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
title = models.CharField(max_length=255)
|
||||||
|
link = models.CharField(max_length=255, null=True)
|
||||||
|
category = models.CharField(max_length=255, null=True)
|
||||||
|
parent_id = models.IntegerField(null=True, editable=True)
|
||||||
|
alt = models.CharField(max_length=255, null=True)
|
||||||
|
type = models.IntegerField(null=True)
|
||||||
|
socket = models.CharField(max_length=255, null=False)
|
||||||
|
|
||||||
|
def generic_search(self, query):
|
||||||
|
try:
|
||||||
|
results = Navigation.objects.annotate(
|
||||||
|
search=SearchVector("title", "parent_id", "category"),
|
||||||
|
).filter(search=query)
|
||||||
|
except Exception as e:
|
||||||
|
raise
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
class Users(models.Model):
|
||||||
|
"""
|
||||||
|
pyShelfs User Database class
|
||||||
|
:param uname: User Name
|
||||||
|
:param fname: First Name
|
||||||
|
:param lname: Last Name
|
||||||
|
:param email: User Email Address
|
||||||
|
:param password: User Password
|
||||||
|
:param ulvl: User Level
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "users"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
uname = models.CharField(max_length=255)
|
||||||
|
fname = models.CharField(max_length=255, null=True)
|
||||||
|
lname = models.CharField(max_length=255, null=True)
|
||||||
|
email = models.CharField(max_length=255, null=True, editable=True)
|
||||||
|
password = models.CharField(max_length=255, null=True)
|
||||||
|
ulvl = models.IntegerField(null=True)
|
||||||
|
|
||||||
|
def generic_search(self, query):
|
||||||
|
try:
|
||||||
|
results = Users.objects.annotate(
|
||||||
|
search=SearchVector("uname", "email", "lname"),
|
||||||
|
).filter(search=query)
|
||||||
|
except Exception as e:
|
||||||
|
raise
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
class Favorites(models.Model):
|
||||||
|
"""
|
||||||
|
pyShelfs User Database class
|
||||||
|
:param uname: User Name
|
||||||
|
:param fname: First Name
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "favorites"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
favorite = models.ManyToManyField(Books)
|
||||||
|
uname = models.ManyToManyField(Users)
|
||||||
|
|
||||||
|
def generic_search(self, query):
|
||||||
|
try:
|
||||||
|
results = Favorites.objects.annotate(search=SearchVector("uname"),).filter(
|
||||||
|
search=query
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise
|
||||||
|
return results
|
||||||
|
|||||||
118
src/interface/static/css/main.css
vendored
118
src/interface/static/css/main.css
vendored
@@ -1,18 +1,17 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
background-color: #DCDCDD;
|
background-color: #FFF;
|
||||||
color: #fff;
|
color: #000;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-areas: "app_header"
|
grid-template-areas:
|
||||||
"app_body"
|
"app_body";
|
||||||
"app_footer";
|
grid-template-rows: auto;
|
||||||
grid-template-rows: auto auto auto;
|
/*! max-height: 100%; */
|
||||||
/*max-height: 100%;*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.clear {
|
.clear {
|
||||||
@@ -24,43 +23,44 @@ body {
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"title slogan subhdr"
|
"title nav_left_top nav_center_top nav_right_top";
|
||||||
"nav_left_top nav_center_top nav_right_top";
|
grid-template-columns: 225px auto auto;
|
||||||
align-items: center;
|
|
||||||
background-color: #2b2b2b;
|
|
||||||
padding: 4px 0px 4px;
|
padding: 4px 0px 4px;
|
||||||
grid-auto-columns: auto;
|
background-color: #2d2d2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav_left_top {
|
.nav_left_top {
|
||||||
grid-area: nav_left_top;
|
grid-area: nav_left_top;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: left;
|
justify-content: left;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav_center_top {
|
.nav_center_top {
|
||||||
grid-area: nav_center_top;
|
grid-area: nav_center_top;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav_right_top {
|
.nav_right_top {
|
||||||
grid-area: nav_right_top;
|
grid-area: nav_right_top;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app_hdr {
|
.app_hdr {
|
||||||
grid-area: title;
|
grid-area: title;
|
||||||
margin: 0px 0px 5px 0px;
|
margin: 0px 0px 0px 0px;
|
||||||
font-family: 'Gruppo', cursive;
|
font-family: 'Gruppo', cursive;
|
||||||
font-size: 36px;
|
font-size: 20px;
|
||||||
text-align: start;
|
text-align: start;
|
||||||
padding: 0px 0px 0px 5px;
|
padding: 0px 0px 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shadow {
|
.shadow {
|
||||||
text-shadow: #4c5c68 -5px 3px 5px;
|
text-shadow: #fff -1px 0px 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app_subhdr {
|
.app_subhdr {
|
||||||
@@ -84,24 +84,25 @@ body {
|
|||||||
grid-area: app_body;
|
grid-area: app_body;
|
||||||
grid-template-rows: auto;
|
grid-template-rows: auto;
|
||||||
grid-template-areas: "nav_l shelf";
|
grid-template-areas: "nav_l shelf";
|
||||||
grid-template-columns: 15vw 85vw;
|
grid-template-columns: 160px auto;
|
||||||
background-color: dimgray;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav_l {
|
.nav_l {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-area: nav_l;
|
grid-area: nav_l;
|
||||||
font-family: 'Gruppo', cursive;
|
font-family: 'Gruppo', cursive;
|
||||||
font-size: 20px;
|
font-size: 15px;
|
||||||
max-height: 500px;
|
/*! max-height: 500px; */
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
padding: 0px 10px;
|
/*! padding: 0px 10px; */
|
||||||
|
margin: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popover{
|
.popover{
|
||||||
display: none;
|
display: none;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
background-color: #000;
|
background-color: #cecece;
|
||||||
/*min-width: 200px;*/
|
/*min-width: 200px;*/
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -113,31 +114,35 @@ body {
|
|||||||
padding: 0px 10px;
|
padding: 0px 10px;
|
||||||
}
|
}
|
||||||
.nav_l_hdr {
|
.nav_l_hdr {
|
||||||
text-align: center;
|
/*! text-align: center; */
|
||||||
padding: 5px;
|
padding: 0px 5px;
|
||||||
background-color: #292f35;
|
background-color: #9e9e9e;
|
||||||
border-bottom: 2px solid #000;
|
border-bottom: 2px solid #dadada;
|
||||||
|
color: black;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav_l_0 {
|
.nav_l_0 {
|
||||||
background-color: #2b2b2b;
|
background-color: #dadada;
|
||||||
padding: 5px;
|
/*! padding: 5px; */
|
||||||
text-align: center;
|
/*! text-align: center; */
|
||||||
border-bottom: 1px solid #000;
|
border-bottom: 1px solid #dadada;
|
||||||
|
padding: 0px 0px 0px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav_l_1 {
|
.nav_l_1 {
|
||||||
padding: 5px;
|
/*! padding: 5px; */
|
||||||
text-align: center;
|
/*! text-align: center; */
|
||||||
border-bottom: 1px solid #000;
|
border-bottom: 1px solid #dadada;
|
||||||
|
padding: 0px 0px 0px 10px;
|
||||||
}
|
}
|
||||||
.nav_link {}
|
.nav_link {}
|
||||||
#vert-nav {
|
#vert-nav {
|
||||||
list-style: None;
|
list-style: None;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
margin: 10px 0px;
|
margin: 0px;
|
||||||
border-left: 5px solid #292f35;
|
/*! border-left: 5px solid #292f35; */
|
||||||
border-right: 5px solid #292f35;
|
/*! border-right: 5px solid #292f35; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.vert-nav-item {}
|
.vert-nav-item {}
|
||||||
@@ -221,16 +226,18 @@ body {
|
|||||||
background-color: darkgray;
|
background-color: darkgray;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px solid #999;
|
border: 1px solid #999;
|
||||||
min-width: 110px;
|
/*! min-width: 110px; */
|
||||||
margin: 0px 5px 0px 0px;
|
margin: 0px 5px 0px 0px;
|
||||||
padding-top: 2px;
|
/*! padding-top: 2px; */
|
||||||
padding-bottom: 2px;
|
/*! padding-bottom: 2px; */
|
||||||
|
/*! max-height: 20px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav_search {
|
.nav_search {
|
||||||
margin: 0px 5px 0px 0px;
|
margin: 0px 5px 0px 0px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px solid #999;
|
border: 1px solid #999;
|
||||||
|
max-height: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search {}
|
.search {}
|
||||||
@@ -273,11 +280,9 @@ p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#book_shelf {
|
#book_shelf {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 21% 21% 21% 21%;
|
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
font-family: 'Audiowide', cursive;
|
font-family: 'Audiowide', cursive;
|
||||||
font-size: 25px;
|
font-size: 15px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
min-width: 99vw;
|
min-width: 99vw;
|
||||||
@@ -285,23 +290,34 @@ p {
|
|||||||
|
|
||||||
.shelf_item {
|
.shelf_item {
|
||||||
display: grid;
|
display: grid;
|
||||||
background-color: burlywood;
|
grid-template-columns: 100px auto;
|
||||||
|
grid-template-areas: "thumb details";
|
||||||
margin: 0 10px 10px 10px;
|
margin: 0 10px 10px 10px;
|
||||||
max-width: 20vw;
|
|
||||||
max-height: 70vh;
|
|
||||||
text-align: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.book_thumb {
|
.book_thumb {
|
||||||
width: 20vw;
|
grid-area: thumb;
|
||||||
height: 70vh;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.book_link {
|
a.book_link {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
.book_details {}
|
||||||
|
.book_details_list {grid-area: details;list-style-type: none;}
|
||||||
|
.book_title {}
|
||||||
|
.book_author {}
|
||||||
|
.book_controls {}
|
||||||
a.nav_link {
|
a.nav_link {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #fff;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
.hidden{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.vert-nav{
|
||||||
|
list-style: None;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px 0px;
|
||||||
|
}
|
||||||
|
.btn {cursor:pointer}
|
||||||
|
|||||||
9
src/interface/static/js/pyshelf_ux.js
vendored
9
src/interface/static/js/pyshelf_ux.js
vendored
@@ -30,9 +30,9 @@ $(document).ready(function(){
|
|||||||
$(this).removeAttr("disabled");
|
$(this).removeAttr("disabled");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$('#app').css("height", max_height);
|
// $('#app').css("height", max_height);
|
||||||
$('.nav_l').css("max-height", max_height);
|
// $('.nav_l').css("max-height", max_height);
|
||||||
$('div.shelf').css("max-height", max_height);
|
// $('div.shelf').css("max-height", max_height);
|
||||||
$('.nav_link').on('mouseover', function (e){
|
$('.nav_link').on('mouseover', function (e){
|
||||||
var popover_str = $(this).attr('alt');
|
var popover_str = $(this).attr('alt');
|
||||||
x = $(this).offset().left
|
x = $(this).offset().left
|
||||||
@@ -51,4 +51,7 @@ $(document).ready(function(){
|
|||||||
$('.popover').css('top', y);
|
$('.popover').css('top', y);
|
||||||
$('.popover').css('display','none');
|
$('.popover').css('display','none');
|
||||||
});
|
});
|
||||||
|
$('#btn_collections').on('click', function (e){
|
||||||
|
$('.hidden.vert-nav.collections').toggle()
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
97
src/interface/templates/index.html
vendored
97
src/interface/templates/index.html
vendored
@@ -17,76 +17,75 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
|
||||||
<div class="app_header">
|
|
||||||
<div class="app_hdr">
|
|
||||||
<h1 class="app_hdr shadow">pyShelf {{Version}}</h1>
|
|
||||||
</div>
|
|
||||||
<div class="app_slogan">
|
|
||||||
<h3 class="app_slogan shadow">"An elegant tool... for a more civilized age."</h3>
|
|
||||||
</div>
|
|
||||||
<div class="app_subhdr">
|
|
||||||
<a class="discord-button" href="https://discord.gg/H9TbNJS" alt="https://discord.gg/H9TbNJS">
|
|
||||||
<img src="/static/img/discord_logo_color.png" class="discord-button" />
|
|
||||||
</a>
|
|
||||||
<!-- Place this tag where you want the button to render. -->
|
|
||||||
<a class="github-button" href="https://github.com/th3r00t/pyShelf/fork" data-color-scheme="no-preference: dark; light: light; dark: dark;" data-icon="octicon-repo-forked" data-size="large" aria-label="Fork th3r00t/pyShelf on GitHub">Fork</a>
|
|
||||||
<!-- Place this tag where you want the button to render. -->
|
|
||||||
<a class="github-button" href="https://github.com/th3r00t/pyShelf/issues" data-color-scheme="no-preference: dark; light: light; dark: dark;" data-icon="octicon-issue-opened" data-size="large" aria-label="Issue th3r00t/pyShelf on GitHub">Issue</a>
|
|
||||||
<!--
|
|
||||||
<img src="/static/img/open-source-175x29.png" class="hdr_badge" /><br />
|
|
||||||
<img src="/static/img/gpl-125x28.png" class="hdr_badge" />
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
<input type="hidden" id="_set" name="_set" value="{{ Set }}" />
|
<input type="hidden" id="_set" name="_set" value="{{ Set }}" />
|
||||||
<div class="nav_left_top">
|
<div id="app">
|
||||||
<input class="nav_button prev_page" type="button" value="<< Prev Page" onclick="window.location.href = '/prev_page/{{ Set }}'">
|
|
||||||
</div>
|
|
||||||
<div class="nav_center_top">
|
|
||||||
<input class="nav_search" type="text" size="25" value=""><input class="nav_button search_submit" type="submit" value="Search">
|
|
||||||
</div>
|
|
||||||
<div class="nav_right_top">
|
|
||||||
<input class="nav_button next_page" type="button" value="Next Page >>" onclick="window.location.href = '/next_page/{{ Set }}'">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="app_body">
|
<div class="app_body">
|
||||||
<div class="nav_l">
|
<div class="nav_l">
|
||||||
<p class="popover"></p>
|
<p class="popover"></p>
|
||||||
<ul id="vert-nav">
|
<ul id="vert-nav">
|
||||||
<li class="vert-nav-item nav_l_hdr">Collections</li>
|
<li class="vert-nav-item nav_l_hdr">
|
||||||
{% for row in LeftNav %}
|
<h1 class="app_hdr shadow">pyShelf {{Version}}</h1>
|
||||||
|
</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr">
|
||||||
|
<input class="nav_search" type="text" size="19" value="">
|
||||||
|
</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr">
|
||||||
|
<input class="nav_button prev_page" type="button" value="<<" onclick="window.location.href = '/prev_page/{{ Set }}'">
|
||||||
|
<input class="nav_button search_submit" type="submit" value="Search">
|
||||||
|
<input class="nav_button next_page" type="button" value=">>" onclick="window.location.href = '/next_page/{{ Set }}'">
|
||||||
|
</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_collections">Collections</li>
|
||||||
|
<ul class="hidden vert-nav collections">
|
||||||
|
{% for row in LeftNavCollections %}
|
||||||
<a href="{% url 'show_collection' _collection=row.link _colset=Set%}" class="nav_link" alt="{{row.link}}">
|
<a href="{% url 'show_collection' _collection=row.link _colset=Set%}" class="nav_link" alt="{{row.link}}">
|
||||||
<li class="nav_l_{{row.class}}">
|
<li class="nav_l_{{row.class}} btn">
|
||||||
{{ row.string }}
|
{{ row.string }}
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul class="vert-nav dynnav">
|
||||||
|
{% if LeftNavMenu0 %}
|
||||||
|
{% for row in LeftNavMenu0 %}
|
||||||
|
<a href="{{ row.link }}"><li class="vert-nav-item nav_l_hdr btn">{{ row.title }}</li></a>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_irc">irc</li>
|
||||||
|
<a href="irc://freenode.net/pyshelf" class="web_footer_link"><li class="vert-nav-item nav_l_hdr" id="btn_discord">discord</li></a>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_github">github</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_irc">irc</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_python">
|
||||||
|
<img src="/static/img/py.png" id="python_logo" />
|
||||||
|
</li>
|
||||||
|
<a href="https://www.gnu.org/licenses/gpl-3.0.txt" class="web_footer_link">
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_gpl">
|
||||||
|
<img src="/static/img/gplv3-or-later.png" alt="GPLv3 or later" />
|
||||||
|
</li>
|
||||||
|
</a>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="shelf">
|
<div class="shelf">
|
||||||
<div class="shelf_contents">
|
<div class="shelf_contents">
|
||||||
<ul id="book_shelf">
|
<ul id="book_shelf">
|
||||||
{% for book in Books %}
|
{% for book in Books %}
|
||||||
<a href="{% url 'download' pk=book.pk %}" class="book_link">
|
<li class="shelf_item">
|
||||||
<li class="shelf_item"><img alt="{{ book.title }}" src="data:image;base64,{{ book.cover | bin_2_img }}" class="book_thumb" /> </li>
|
<img alt="cover missing" src="data:image;base64,{{ book.cover | bin_2_img }}" class="book_thumb" />
|
||||||
</a>
|
<ul class="book_details_list">
|
||||||
|
<li class="book_title">{{ book.title }}</li>
|
||||||
|
<li class="book_author">Author: {{ book.author }}</li>
|
||||||
|
<li class="book_controls">
|
||||||
|
fav
|
||||||
|
<a href="{% url 'download' pk=book.pk %}" class="book_link">download</a>
|
||||||
|
share
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="app_footer">
|
|
||||||
<div class="python_logo">
|
|
||||||
<img src="/static/img/py.png" id="python_logo" />
|
|
||||||
</div>
|
|
||||||
<div class="copyright">
|
|
||||||
<a href="https://www.gnu.org/licenses/gpl-3.0.txt" class="web_footer_link"><img src="/static/img/gplv3-or-later.png" alt="GPLv3 or later" /></a>
|
|
||||||
</div>
|
|
||||||
<div class="footer_contact">
|
|
||||||
|
|
||||||
<p>irc: <a href="irc://freenode.net/pyshelf" class="web_footer_link">#pyshelf</a> on freenode | discord: <a href="https://discord.gg/H9TbNJS" class="web_footer_link">discord.gg/H9TbNJS</a> | web: <a href=http://pyshelf.com class="web_footer_link" alt="pyshelf.com">http://pyshelf.com</a> | source: <a href="https://github.com/th3r00t/pyShelf" class="web_footer_link">github.com/th3r00t/pyShelf</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
73
src/interface/templates/search.html
vendored
73
src/interface/templates/search.html
vendored
@@ -16,49 +16,45 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div class="app_header" >
|
|
||||||
<div class="app_hdr">
|
|
||||||
<h1 class="app_hdr shadow">pyShelf {{Version}}</h1>
|
|
||||||
</div>
|
|
||||||
<div class="app_slogan">
|
|
||||||
<h3 class="app_slogan shadow">"An elegant tool... for a more civilized age."</h3>
|
|
||||||
</div>
|
|
||||||
<div class="app_subhdr">
|
|
||||||
<a class="discord-button" href="https://discord.gg/H9TbNJS" alt="https://discord.gg/H9TbNJS">
|
|
||||||
<img src="/static/img/discord_logo_color.png" class="discord-button"/>
|
|
||||||
</a>
|
|
||||||
<!-- Place this tag where you want the button to render. -->
|
|
||||||
<a class="github-button" href="https://github.com/th3r00t/pyShelf/fork" data-color-scheme="no-preference: dark; light: light; dark: dark;" data-icon="octicon-repo-forked" data-size="large" aria-label="Fork th3r00t/pyShelf on GitHub">Fork</a>
|
|
||||||
<!-- Place this tag where you want the button to render. -->
|
|
||||||
<a class="github-button" href="https://github.com/th3r00t/pyShelf/issues" data-color-scheme="no-preference: dark; light: light; dark: dark;" data-icon="octicon-issue-opened" data-size="large" aria-label="Issue th3r00t/pyShelf on GitHub">Issue</a>
|
|
||||||
<!--
|
|
||||||
<img src="/static/img/open-source-175x29.png" class="hdr_badge" /><br />
|
|
||||||
<img src="/static/img/gpl-125x28.png" class="hdr_badge" />
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
<input type="hidden" id="_set" name="_set" value="{{ Set }}" />
|
<input type="hidden" id="_set" name="_set" value="{{ Set }}" />
|
||||||
<div class="nav_left_top">
|
|
||||||
<input class="nav_button prev_page" type="button" value="<< Prev Page" onclick="window.location.href = '/search/{{ Query }}/{{ Set|add:"-1" }}'">
|
|
||||||
</div>
|
|
||||||
<div class="nav_center_top">
|
|
||||||
<input class="nav_search" type="text" size="25" value=""><input class="nav_button search_submit" type="submit" value="Search">
|
|
||||||
</div>
|
|
||||||
<div class="nav_right_top">
|
|
||||||
<input class="nav_button next_page" type="button" value="Next Page >>" onclick="window.location.href = '/search/{{ Query }}/{{ Set|add:"1" }}'">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="app_body" >
|
<div class="app_body" >
|
||||||
<div class="nav_l">
|
<div class="nav_l">
|
||||||
|
<p class="popover"></p>
|
||||||
<ul id="vert-nav">
|
<ul id="vert-nav">
|
||||||
<li class="vert-nav-item nav_l_hdr">Collections</li>
|
<li class="vert-nav-item nav_l_hdr">
|
||||||
{% for row in LeftNav %}
|
<h1 class="app_hdr shadow">pyShelf {{Version}}</h1>
|
||||||
|
</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr">
|
||||||
|
<input class="nav_search" type="text" size="19" value="">
|
||||||
|
</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr">
|
||||||
|
<input class="nav_button prev_page" type="button" value="<<" onclick="window.location.href = '/prev_page/{{ Set }}'">
|
||||||
|
<input class="nav_button search_submit" type="submit" value="Search">
|
||||||
|
<input class="nav_button next_page" type="button" value=">>" onclick="window.location.href = '/next_page/{{ Set }}'">
|
||||||
|
</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr" id="btn_collections">Collections</li>
|
||||||
|
<ul class="hidden vert-nav collections">
|
||||||
|
{% for row in LeftNavCollections %}
|
||||||
<a href="{% url 'show_collection' _collection=row.link _colset=Set%}" class="nav_link" alt="{{row.link}}">
|
<a href="{% url 'show_collection' _collection=row.link _colset=Set%}" class="nav_link" alt="{{row.link}}">
|
||||||
<li class="nav_l_{{row.class}}">
|
<li class="nav_l_{{row.class}} btn">
|
||||||
{{ row.string }}
|
{{ row.string }}
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_irc">irc</li>
|
||||||
|
<a href="irc://freenode.net/pyshelf" class="web_footer_link"><li class="vert-nav-item nav_l_hdr" id="btn_discord">discord</li></a>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_github">github</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_irc">irc</li>
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_python">
|
||||||
|
<img src="/static/img/py.png" id="python_logo" />
|
||||||
|
</li>
|
||||||
|
<a href="https://www.gnu.org/licenses/gpl-3.0.txt" class="web_footer_link">
|
||||||
|
<li class="vert-nav-item nav_l_hdr btn" id="btn_gpl">
|
||||||
|
<img src="/static/img/gplv3-or-later.png" alt="GPLv3 or later" />
|
||||||
|
</li>
|
||||||
|
</a>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="shelf">
|
<div class="shelf">
|
||||||
<div class="search_details">
|
<div class="search_details">
|
||||||
@@ -73,17 +69,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="app_footer" >
|
|
||||||
<div class="python_logo" >
|
|
||||||
<img src="/static/img/py.png" id="python_logo" />
|
|
||||||
</div>
|
|
||||||
<div class="copyright">
|
|
||||||
<a href="https://www.gnu.org/licenses/gpl-3.0.txt" class="web_footer_link"><img src="/static/img/gplv3-or-later.png" alt="GPLv3 or later" /></a>
|
|
||||||
</div>
|
|
||||||
<div class="footer_contact">
|
|
||||||
<p>irc: <a href="irc://freenode.net/pyshelf" class="web_footer_link">#pyshelf</a> on freenode | discord: <a href="https://discord.gg/H9TbNJS" class="web_footer_link">discord.gg/H9TbNJS</a> | web: <a href=http://pyshelf.com class="web_footer_link" alt="pyshelf.com">http://pyshelf.com</a> | source: <a href="https://github.com/th3r00t/pyShelf" class="web_footer_link">github.com/th3r00t/pyShelf</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from django.http import JsonResponse
|
|||||||
from django.shortcuts import HttpResponse, render # render_to_response
|
from django.shortcuts import HttpResponse, render # render_to_response
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
from .models import Books, Collections
|
from .models import Books, Collections, Navigation
|
||||||
|
|
||||||
config = Config(Path("../"))
|
config = Config(Path("../"))
|
||||||
|
|
||||||
@@ -26,7 +26,8 @@ def index(request):
|
|||||||
"Books": book_set(20, _set),
|
"Books": book_set(20, _set),
|
||||||
"Set": str(_set),
|
"Set": str(_set),
|
||||||
"Version": config.VERSION,
|
"Version": config.VERSION,
|
||||||
"LeftNav": menu("collections"),
|
"LeftNavCollections": menu("collections"),
|
||||||
|
"LeftNavMenu0": menu("nav_l_0"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ def show_collection(request, _collection, _colset):
|
|||||||
"Books": collection(_collection, _set),
|
"Books": collection(_collection, _set),
|
||||||
"Set": str(_set),
|
"Set": str(_set),
|
||||||
"Version": config.VERSION,
|
"Version": config.VERSION,
|
||||||
|
"LeftNavCollections": menu("collections"),
|
||||||
"LeftNav": menu("collections"),
|
"LeftNav": menu("collections"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -63,6 +65,7 @@ def next_page(request, bookset):
|
|||||||
"Books": book_set(None, _set),
|
"Books": book_set(None, _set),
|
||||||
"Set": str(_set),
|
"Set": str(_set),
|
||||||
"Version": config.VERSION,
|
"Version": config.VERSION,
|
||||||
|
"LeftNavCollections": menu("collections"),
|
||||||
"LeftNav": menu("collections"),
|
"LeftNav": menu("collections"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -87,6 +90,7 @@ def prev_page(request, bookset):
|
|||||||
"Books": book_set(None, _set),
|
"Books": book_set(None, _set),
|
||||||
"Set": str(_set),
|
"Set": str(_set),
|
||||||
"Version": config.VERSION,
|
"Version": config.VERSION,
|
||||||
|
"LeftNavCollections": menu("collections"),
|
||||||
"LeftNav": menu("collections"),
|
"LeftNav": menu("collections"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -117,6 +121,7 @@ def search(request, query=None, _set=1, _limit=None):
|
|||||||
"Set": _set,
|
"Set": _set,
|
||||||
"len_results": search_len,
|
"len_results": search_len,
|
||||||
"Version": config.VERSION,
|
"Version": config.VERSION,
|
||||||
|
"LeftNavCollections": menu("collections"),
|
||||||
"LeftNav": menu("collections"),
|
"LeftNav": menu("collections"),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -190,7 +195,21 @@ def hr_name(book):
|
|||||||
return "{0}{1}".format(slugify(book.title), os.path.splitext(book.file_name)[1])
|
return "{0}{1}".format(slugify(book.title), os.path.splitext(book.file_name)[1])
|
||||||
|
|
||||||
|
|
||||||
def menu(which, _set=1):
|
def format_list(list_in):
|
||||||
|
formated_list, formated_list_key, x = [], [], 0
|
||||||
|
for i in list_in:
|
||||||
|
if i.id not in formated_list_key:
|
||||||
|
if x % 2 == 0:
|
||||||
|
c = 0
|
||||||
|
else:
|
||||||
|
c = 1
|
||||||
|
if x <= 10:
|
||||||
|
x += 1
|
||||||
|
else:
|
||||||
|
x = 0
|
||||||
|
|
||||||
|
|
||||||
|
def menu(which, _set=1, parent=None):
|
||||||
if which == "collections":
|
if which == "collections":
|
||||||
collection_list = Collections.objects.all()
|
collection_list = Collections.objects.all()
|
||||||
collections, collection_key, x = [], [], 0
|
collections, collection_key, x = [], [], 0
|
||||||
@@ -217,3 +236,7 @@ def menu(which, _set=1):
|
|||||||
)
|
)
|
||||||
collection_key.append(i.collection)
|
collection_key.append(i.collection)
|
||||||
return collections
|
return collections
|
||||||
|
elif which == "nav_lvl_0":
|
||||||
|
navigation_list = Navigation.objects.all()
|
||||||
|
breakpoint()
|
||||||
|
return navigation_list
|
||||||
|
|||||||
Reference in New Issue
Block a user