Compare commits

..

13 Commits

Author SHA1 Message Date
779dab4f77 add bs4 in requirements 2025-07-13 07:17:49 -04:00
d872836624 fix: uri style page 2025-07-11 15:46:46 +02:00
df182fa7bb fix: readme 2025-07-11 15:44:24 +02:00
7d739ed777 add: doc photodown 2025-07-09 02:39:32 +02:00
6af8781aa2 add: bozodown doc 2025-07-09 02:20:58 +02:00
00bc97465f clean 2025-07-09 01:58:34 +02:00
74e99b96f0 clean: remove bulk_suffix
bulk_page -> page
bulk_category -> category
page -> collection
2025-07-09 01:57:52 +02:00
cbab99cc82 add: css to page 2025-07-09 00:03:03 +02:00
001a8c692e add: pictures menu in photo markdown 2025-07-08 22:08:24 +02:00
e1f08f0312 fix: indent and remove empty <p> 2025-07-08 22:05:41 +02:00
cf9cd77bcf add: use photodown to gen page 2025-07-08 18:35:58 +02:00
1563b3618a clean: create bozodown module 2025-07-08 16:31:53 +02:00
8c378492a3 bozodown: implement block 2025-07-08 15:10:17 +02:00
39 changed files with 763 additions and 435 deletions

20
doc/bozodown/block.md Normal file
View File

@ -0,0 +1,20 @@
# Block
## Usage
```
-----
style
---
content
-----
```
| name | usage |
| --- | --- |
| style | Your custom css, multiline supported |
| content | Your bozodown |
## Effect
Create a div with custom css

13
doc/bozodown/checkbox.md Normal file
View File

@ -0,0 +1,13 @@
# Checkbox
## Usage
```
- [ ] your text
or
- [x] your text
```
## Effect
![](./checkbox.png)

BIN
doc/bozodown/checkbox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

17
doc/bozodown/code.md Normal file
View File

@ -0,0 +1,17 @@
# Quote
## Usage
```
\```
your text
\```
```
## Effect
your text
to
```
your text
```

11
doc/bozodown/image.md Normal file
View File

@ -0,0 +1,11 @@
# Image
## Usage
```
![url]
```
## Effect
display the image

10
doc/bozodown/quote.md Normal file
View File

@ -0,0 +1,10 @@
# Quote
## Usage
```
`your text`
```
## Effect
your text -> `your text`

19
doc/bozodown/readme.md Normal file
View File

@ -0,0 +1,19 @@
# Bozodown
A markdown like, written in python, modulable
## Feature
### Text Formater
- [title](./title.md)
- [subtitle](./subtitle.md)
- [subsubtitle](./subsubtitle.md)
- [subsubsubtitle](./subsubsubtitle.md)
- [code](./code.md)
- [image](./image.md)
- [block](./block.md)
- [checkbox](./checkbox.md)
### Text Modifier
- [strong](./strong.md)
- [quote](./quote.md)

11
doc/bozodown/strong.md Normal file
View File

@ -0,0 +1,11 @@
# Strong
## Usage
```
**your text**
```
## Effect
your text -> **your text**

View File

@ -0,0 +1,13 @@
# Subsubsubtitle
## Usage
```
#### your text
```
## Effect
your text
to
#### your text

View File

@ -0,0 +1,13 @@
# Subsubtitle
## Usage
```
### your text
```
## Effect
your text
to
### your text

13
doc/bozodown/subtitle.md Normal file
View File

@ -0,0 +1,13 @@
# Subtitle
## Usage
```
## your text
```
## Effect
your text
to
## your text

13
doc/bozodown/title.md Normal file
View File

@ -0,0 +1,13 @@
# Title
## Usage
```
# your text
```
## Effect
your text
to
# your text

53
doc/photodown/gallery.md Normal file
View File

@ -0,0 +1,53 @@
# Block
## Usage
```
+++++
config
---
style
---
content
+++++
```
### Config
| type |
| --- |
| image-list |
| direction |
| --- |
| down |
| up |
| left |
| right |
### Style
Just css
### Content
```
![img1]
description 1
--
![img2]
description 2
--
...
```
| name | usage |
| --- | --- |
| style | Your custom css, multiline supported |
| content | Your bozodown |
## Effect
Create a group of image

16
doc/photodown/image.md Normal file
View File

@ -0,0 +1,16 @@
# Image
Permit you to use your own image without think about small, thumb, large, reference the bulk page. Always permit you to use external image with the url
## Usage
```
![./bulk/path_to_image/image_large_name_without_ext]
```
## Example
On my server I got this image `bulk/01613/01613.png`, so I will used put
```
![./bulk/01613/01613]
```
And the python will display an image clickable who link to the bulk page

11
doc/photodown/readme.md Normal file
View File

@ -0,0 +1,11 @@
# PhotoDown
Just a simple extension of [BozoDown](../bozodown/readme.md)
## Add
- [Pictures gallery]()
## Modify
- [image](./image.md)

6
doc/readme.md Normal file
View File

@ -0,0 +1,6 @@
# PhotoHUB
## Tools
### Generate page
To let user create his own page this project use [BozoDown](./bozodown/readme.md) with the [PhotoDown](./photodown/readme.md) extension

View File

@ -1,3 +1,5 @@
beautifulsoup4==4.13.4
bs4==0.0.2
exif==1.6.1
imageio==2.37.0
Jinja2==3.1.6
@ -8,3 +10,5 @@ pillow==11.1.0
plum-py==0.8.7
progress==1.6
rawpy==0.24.0
soupsieve==2.7
typing_extensions==4.14.1

View File

@ -1,139 +0,0 @@
from collections.abc import Callable
from typing import Any
def _photohub_render(id: str, text: str) -> str:
if (id == "img"):
return f"<img src='{text[2:-1]}'>"
def _bozodown_render(id: str, text: str) -> str:
if (id == "list"):
print("error: list not supported")
return ""
_specific_case_namespaces: dict[str, Callable[[str, str], str]] = {
"bozodown": _bozodown_render,
"photohub": _photohub_render,
}
_converters: list[dict[str, str]] = [
{
"from_prefix": "# ",
"from_suffix": "\n",
"to_prefix": "<h1>",
"to_suffix": "</h1>",
},
{
"from_prefix": "## ",
"from_suffix": "\n",
"to_prefix": "<h2>",
"to_suffix": "</h2>",
},
{
"from_prefix": "### ",
"from_suffix": "\n",
"to_prefix": "<h3>",
"to_suffix": "</h3>",
},
{
"from_prefix": "#### ",
"from_suffix": "\n",
"to_prefix": "<h4>",
"to_suffix": "</h4>",
},
{
"from_prefix": "`",
"from_suffix": "`",
"to_prefix": "<p><code>",
"to_suffix": "</code></p>",
},
{
"from_prefix": "**",
"from_suffix": "**",
"to_prefix": "<strong>",
"to_suffix": "</strong>",
},
{
"from_prefix": "- ",
"from_suffix": "\n",
"code": "bozodown:list",
},
{
"from_prefix": "```",
"from_suffix": "```",
"to_prefix": "<pre><code>",
"to_suffix": "</code></pre>",
},
{
"from_prefix": "![",
"from_suffix": "]",
"code": "photohub:img",
},
{
"from_prefix": "- [ ] ",
"from_suffix": "\n",
"to_prefix": "<li><input type='checkbox' disabled> ",
"to_suffix": "</li>",
},
{
"from_prefix": "- [x] ",
"from_suffix": "\n",
"to_prefix": "<li><input type='checkbox' checked disabled> ",
"to_suffix": "</li>",
},
{
"from_prefix": "<br>",
"from_suffix": "",
"to_prefix": "<br>",
},
]
_default_converter: dict[str, str] = {
"from_prefix": "",
"to_prefix": "<p>",
"to_suffix": "</p>",
}
def _render_element(text: str, converter: dict[str, str]) -> str:
code: str = converter.get("code")
if (code is not None):
namespace, id = code.split(":")
print(namespace, id)
func = _specific_case_namespaces[namespace]
return func(id, text)
print(converter)
start: int = len(converter["from_prefix"])
stop: int = len(text) - len(converter.get("from_suffix", ""))
return f"{converter['to_prefix']}{text[start:stop]}{converter['to_suffix']}"
def _get_first_converter(text: str) -> tuple[str, dict] | None:
first_converter_found: dict[str, str | Callable[[str, list[str]], Any]] | None = None
start: int | None = None
for converter in _converters:
matching_patern_pos: str = text.find(converter['from_prefix'])
if (matching_patern_pos != -1):
if (first_converter_found is None or matching_patern_pos < start):
first_converter_found = converter
start = matching_patern_pos
if (first_converter_found is None):
return text, _default_converter
if (start != 0):
return text[:start], _default_converter
suffix: int = first_converter_found.get("from_suffix", "")
prefix: int = first_converter_found['from_prefix']
stop: int = text.find(suffix, start)
if (stop == -1):
print(f"error: '{prefix}' was never finished by a '{suffix}'")
return
stop += len(suffix)
return text[start:stop], first_converter_found
def render(raw_content: str) -> str:
content: str = ""
to_parse: str = raw_content
while len(to_parse) > 0:
text, converter = _get_first_converter(to_parse)
content += _render_element(text, converter)
to_parse = to_parse[len(text):]
return content

1
src/bozodown/__init__.py Normal file
View File

@ -0,0 +1 @@
__all__ = ["Bozodown"]

69
src/bozodown/bozodown.py Normal file
View File

@ -0,0 +1,69 @@
from collections.abc import Callable
from . import default_converters
from bs4 import BeautifulSoup
class Bozodown():
def __init__(self):
self._converters: dict[str, dict[str, str]] = default_converters.converters.copy()
self._text_converter: dict[str, str] = default_converters.text_converter.copy()
self._specific_case_namespaces: dict[str, Callable[[object, str, str], str]] = {
"bozodown": self._bozodown_render,
}
def render(self, to_parse: str):
content: str = ""
while len(to_parse) > 0:
text, converter = self._get_first_converter(to_parse)
content += self._render_element(text, converter)
to_parse = to_parse[len(text):]
return BeautifulSoup(content, 'html.parser').prettify()
def _render_element(self, text: str, converter: dict[str, str]) -> str:
code: str = converter.get("code")
if (code is not None):
namespace, id = code.split(":")
func = self._specific_case_namespaces[namespace]
return func(id, text)
start: int = len(converter["from_prefix"])
stop: int = len(text) - len(converter.get("from_suffix", ""))
if (text[start:stop] == "\n" and converter is self._text_converter):
return ""
return f"{converter['to_prefix']}{text[start:stop]}{converter['to_suffix']}"
def _bozodown_render(self, id: str, text: str) -> str:
if (id == "list"):
print("error: list not supported")
return ""
if (id == "block"):
style, to_parse = text[5:-5].split("---")
style = style.replace("\n", "")
content: str = self.render(to_parse)
return f"<div style='{style}'>{content}</div>"
def _get_first_converter(self, text: str) -> tuple[str, dict] | None:
first_converter_found: dict[str, str] | None = None
start: int | None = None
for converter in self._converters.values():
matching_patern_pos: str = text.find(converter['from_prefix'])
if (matching_patern_pos != -1):
if (first_converter_found is None or matching_patern_pos < start):
first_converter_found = converter
start = matching_patern_pos
if (first_converter_found is None):
return text, self._text_converter
if (start != 0):
return text[:start], self._text_converter
suffix: int = first_converter_found.get("from_suffix", "")
prefix: int = first_converter_found['from_prefix']
stop: int = text.find(suffix, start + len(prefix))
if (stop == -1):
print(f"error: '{prefix}' was never finished by a '{suffix}'")
return
stop += len(suffix)
return text[start:stop], first_converter_found

View File

@ -0,0 +1,78 @@
converters: list[str, dict[str, str]] = {
"title": {
"from_prefix": "# ",
"from_suffix": "\n",
"to_prefix": "<h1>",
"to_suffix": "</h1>",
},
"subtitle": {
"from_prefix": "## ",
"from_suffix": "\n",
"to_prefix": "<h2>",
"to_suffix": "</h2>",
},
"subsubtitle": {
"from_prefix": "### ",
"from_suffix": "\n",
"to_prefix": "<h3>",
"to_suffix": "</h3>",
},
"subsubsubtitle": {
"from_prefix": "#### ",
"from_suffix": "\n",
"to_prefix": "<h4>",
"to_suffix": "</h4>",
},
"quote": {
"from_prefix": "`",
"from_suffix": "`",
"to_prefix": "<p><code>",
"to_suffix": "</code></p>",
},
"bold": {
"from_prefix": "**",
"from_suffix": "**",
"to_prefix": "<strong>",
"to_suffix": "</strong>",
},
"code": {
"from_prefix": "```",
"from_suffix": "```",
"to_prefix": "<pre><code>",
"to_suffix": "</code></pre>",
},
"image": {
"from_prefix": "![",
"from_suffix": "]",
"to_prefix": "<img src='",
"to_suffix": "'>",
},
"block": {
"from_prefix": "-----",
"from_suffix": "-----",
"code": "bozodown:block",
},
"checkbox_clear": {
"from_prefix": "- [ ] ",
"from_suffix": "\n",
"to_prefix": "<li><input type='checkbox' disabled> ",
"to_suffix": "</li>",
},
"checkbox_full": {
"from_prefix": "- [x] ",
"from_suffix": "\n",
"to_prefix": "<li><input type='checkbox' checked disabled> ",
"to_suffix": "</li>",
},
"newline": {
"from_prefix": "<br>",
"from_suffix": "",
"to_prefix": "<br>",
},
}
text_converter: dict[str, str] = {
"from_prefix": "",
"to_prefix": "<p>",
"to_suffix": "</p>",
}

View File

@ -1,78 +0,0 @@
from __future__ import annotations
from jinja2 import Environment, FileSystemLoader
import markdown
import os
from path import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bulk_page import Page
from picture import Picture
env = Environment(loader=FileSystemLoader('src/templates'))
page_template = env.get_template('bulk_page.jinja')
class BulkPage():
def __init__(self, path: Path, name: str, pictures: list[Picture] = None, prev: BulkPage|None = None, next: BulkPage|None = None):
self.name: str = name
self._path: Path = path
self._pictures: list[Picture] = pictures or []
self._exif: Path = Path(self._path, "exif.txt")
self._readme: Path = Path(self._path, "readme.md")
self._raw: Path = Path(self._path, name + ".NEF")
self.html: Path = Path(self._path, "page.html")
self.prev: Page = prev
self.next: Page = next
if (not self._readme.exist()):
self._readme.touch()
def add_picture(self, picture: Picture) -> None:
self._pictures.append(picture)
def get_pictures(self) -> list[Picture]:
return self._pictures
def _to_html(self) -> str|None:
html_rendered = page_template.render(page=self)
return html_rendered
def create(self) -> Path:
with open(self.html.get_absolute_path(), "w") as f:
f.write(self._to_html())
def render_readme(self) -> str|None:
if not self._readme.exist():
return None
with open(self._readme.get_absolute_path(), 'r') as f:
text = f.read()
if len(text) == 0:
return None
html = markdown.markdown(text)
return html
def _gen_exif(self):
if self._raw.exist():
if os.system(f"exiftool {self._raw.get_absolute_path()} > {self._exif.get_absolute_path()} 2>/dev/null") == 0:
return 0
os.remove(self._exif.get_absolute_path())
return 1
def get_exif(self) -> Path | None:
if not self._exif.exist():
if (self._gen_exif()):
return None
return self._exif
def render_exif(self):
if not self.get_exif():
return None
with open(self._exif.get_absolute_path()) as f:
return f.read()
def get_raw(self):
return self._raw

View File

@ -10,9 +10,9 @@ if TYPE_CHECKING:
from path import Path
env = Environment(loader=FileSystemLoader('src/templates'))
category_template = env.get_template('bulk_category.jinja')
category_template = env.get_template('category.jinja')
class BulkCategory():
class Category():
def __init__(self, name: str, categorys_path: Path, pictures: list[Picture] = None, is_repertoried: bool = False):
self.name: str = name

35
src/collection.py Normal file
View File

@ -0,0 +1,35 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import config
from path import Path
if TYPE_CHECKING:
from photodown import Photodown
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('src/templates'))
page_template = env.get_template('collection.jinja')
class Collection:
def __init__(self, path: Path, markdown: Photodown):
self._path: Path = path
self._html: Path = Path(self._path.get_absolute_path()[:-len(config.COLLECTION_EXT)] + ".html")
self._markdown: Photodown = markdown
with open(path.get_absolute_path(), "r") as f:
self._raw_content: str = f.read()
def _get_content(self):
content = self._markdown.render(self._raw_content)
return content
def _to_html(self) -> str:
html: str = page_template.render(content=self._get_content())
return html
def create(self) -> Path:
with open(self._html.get_absolute_path(), "w") as f:
f.write(self._to_html())

View File

@ -3,4 +3,4 @@
CREATE_GENERAL_CATEGORY: bool = True
THUMB_DIMENSION: tuple[int, int] = (200, 200)
MAX_THREADS: int = 50
PAGE_EXT: str = ".ph"
COLLECTION_EXT: str = ".ph"

View File

@ -1,12 +1,11 @@
import sys
import os
from progress.bar import Bar
from path import Path
from picture import Picture
from bulk_page import BulkPage
from bulk_category import BulkCategory
from page import Page
from category import Category
from collection import Collection
from photodown import Photodown
import argparse
import config
@ -23,14 +22,14 @@ def argument_parsing():
args = parser.parse_args()
return args
def scan_bulk_pages(folders: list[Path]) -> list[BulkPage]:
pages: list[BulkPage] = []
prev: BulkPage = None
def scan_pages(folders: list[Path]) -> list[Page]:
pages: list[Page] = []
prev: Page = None
with Bar("Scanning Pages...", max=len(folders)) as bar:
for folder in folders:
files: list[Path] = folder.get_files()
page: BulkPage = BulkPage(folder, folder.get_name(), prev=prev)
page: Page = Page(folder, folder.get_name(), prev=prev)
raw: Path = Path(folder, folder.get_name() + ".NEF")
id: int = 0
@ -53,62 +52,69 @@ def scan_bulk_pages(folders: list[Path]) -> list[BulkPage]:
pages[0].prev = pages[-1]
return pages
def create_pages(pages: list[BulkPage]) -> None:
def create_pages(pages: list[Page]) -> None:
with Bar("Generating Pages...", max=len(pages)) as bar:
for page in pages:
page.create()
bar.next()
def scan_categories(pages: list[BulkPage], categories_path: Path) -> list[BulkCategory]:
categories: dict[str, BulkCategory] = {}
def scan_categories(pages: list[Page], categories_path: Path) -> list[Category]:
categories: dict[str, Category] = {}
with Bar("Scanning pages...", max=len(pages)) as bar:
for page in pages:
for picture in page.get_pictures():
for category_name in picture.get_categories_name():
category: BulkCategory | None = categories.get(category_name)
category: Category | None = categories.get(category_name)
if (category is None):
category = BulkCategory(category_name, categories_path)
category = Category(category_name, categories_path)
categories.update({category_name: category})
picture.categories.append(category)
category.add_picture(picture)
bar.next()
return (categories.values())
def create_categories(categories: list[BulkCategory]) -> None:
def create_categories(categories: list[Category]) -> None:
with Bar("Generating categories...", max=len(categories)) as bar:
for category in categories:
category.create()
bar.next()
def gen_bulk(bulk_path: Path):
def gen_bulk(bulk_path: Path, markdown: Photodown):
category_path: Path = Path(bulk_path, "categories")
pages: list[BulkPage] = scan_bulk_pages(bulk_path.get_dirs())
categories: list[BulkCategory] = scan_categories(pages, category_path)
pages: list[Page] = scan_pages(bulk_path.get_dirs())
categories: list[Category] = scan_categories(pages, category_path)
bulk: list[Picture] = []
for page in pages:
for picture in page.get_pictures():
bulk.append(picture)
markdown.add_bulk(bulk)
if config.CREATE_GENERAL_CATEGORY:
for category in categories:
if (category.name == "general"):
category.path = Path(bulk_path, "index.html")
Path("./src/templates/bulk_page.css").copy_to(Path(bulk_path, "bulk_page.css"))
Path("./src/templates/page.css").copy_to(Path(bulk_path, "page.css"))
create_pages(pages)
Path("./src/templates/bulk_category.css").copy_to(Path(bulk_path, "bulk_category.css"))
Path("./src/templates/category.css").copy_to(Path(bulk_path, "category.css"))
if (not category_path.exist()):
category_path.create()
create_categories(categories)
def scan_pages(site_path: Path):
for path in [f for f in site_path.get_files() if f.get_name().endswith(config.PAGE_EXT)]:
page: Page = Page(path)
page.create()
def scan_collections(site_path: Path, markdown: Photodown):
for path in [f for f in site_path.get_files() if f.get_name().endswith(config.COLLECTION_EXT)]:
collection: Collection = Collection(path, markdown)
collection.create()
def gen_pages(site_path: Path):
scan_pages(site_path)
def gen_collections(site_path: Path, markdown: Photodown):
scan_collections(site_path, markdown)
Path("./src/templates/collection.css").copy_to(Path(site_path, "collection.css"))
def regen(bulk_path: Path, is_thumb: bool, is_small: bool):
pages: list[BulkPage] = scan_bulk_pages(bulk_path.get_dirs())
def regen(_path: Path, is_thumb: bool, is_small: bool):
pages: list[Page] = scan_pages(_path.get_dirs())
with Bar("Regenerating assets...", max=len(pages)) as bar:
for page in pages:
for picture in page.get_pictures():
@ -132,8 +138,9 @@ def main():
bulk_path: Path = Path(site_path, "bulk/")
if args.regen is not None:
regen(bulk_path, 't' in args.regen, 's' in args.regen)
gen_bulk(bulk_path)
gen_pages(site_path)
markdown = Photodown()
gen_bulk(bulk_path, markdown)
gen_collections(site_path, markdown)
gen_home(site_path)

View File

@ -1,28 +1,77 @@
from __future__ import annotations
from jinja2 import Environment, FileSystemLoader
import markdown
import os
from path import Path
from typing import TYPE_CHECKING
import config
import bozodown
from path import Path
if TYPE_CHECKING:
from picture import Picture
env = Environment(loader=FileSystemLoader('src/templates'))
page_template = env.get_template('page.jinja')
class Page:
def __init__(self, path: Path):
class Page():
def __init__(self, path: Path, name: str, pictures: list[Picture] = None, prev: Page|None = None, next: Page|None = None):
self.name: str = name
self._path: Path = path
self._html: Path = Path(self._path.get_absolute_path()[:-len(config.PAGE_EXT)] + ".html")
with open(path.get_absolute_path(), "r") as f:
self._raw_content: str = f.read()
def _get_content(self):
content = bozodown.render(self._raw_content)
return content
self._pictures: list[Picture] = pictures or []
self._exif: Path = Path(self._path, "exif.txt")
self._readme: Path = Path(self._path, "readme.md")
self._raw: Path = Path(self._path, name + ".NEF")
self.html: Path = Path(self._path, "page.html")
self.prev: Page = prev
self.next: Page = next
if (not self._readme.exist()):
self._readme.touch()
def _to_html(self) -> str:
html: str = self._get_content()
def add_picture(self, picture: Picture) -> None:
self._pictures.append(picture)
def get_pictures(self) -> list[Picture]:
return self._pictures
def _to_html(self) -> str|None:
html_rendered = page_template.render(page=self)
return html_rendered
def create(self) -> Path:
with open(self.html.get_absolute_path(), "w") as f:
f.write(self._to_html())
def render_readme(self) -> str|None:
if not self._readme.exist():
return None
with open(self._readme.get_absolute_path(), 'r') as f:
text = f.read()
if len(text) == 0:
return None
html = markdown.markdown(text)
return html
def create(self) -> Path:
with open(self._html.get_absolute_path(), "w") as f:
f.write(self._to_html())
def _gen_exif(self):
if self._raw.exist():
if os.system(f"exiftool {self._raw.get_absolute_path()} > {self._exif.get_absolute_path()} 2>/dev/null") == 0:
return 0
os.remove(self._exif.get_absolute_path())
return 1
def get_exif(self) -> Path | None:
if not self._exif.exist():
if (self._gen_exif()):
return None
return self._exif
def render_exif(self):
if not self.get_exif():
return None
with open(self._exif.get_absolute_path()) as f:
return f.read()
def get_raw(self):
return self._raw

51
src/photodown.py Normal file
View File

@ -0,0 +1,51 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from bozodown.bozodown import Bozodown
if TYPE_CHECKING:
from picture import Picture
class Photodown(Bozodown):
def __init__(self):
super().__init__()
self._specific_case_namespaces.update({"photohub": self._photohub_render})
image_converter = self._converters.get("image")
image_converter.update({"code": "photohub:image"})
self._converters.update({"pictures": {"from_prefix": "+++++","from_suffix": "+++++","code": "photohub:pictures",}})
self._bulk: list[Picture] = []
def add_bulk(self, bulk: list[Picture]):
self._bulk = bulk.copy()
def _render_image(self, url: str) -> str:
if (url.startswith("./")):
for picture in self._bulk:
if url == "." + picture.get_large().get_url()[:-4]:
return f"""<a href={picture.get_page().html.get_url()}><img src='{picture.get_small().get_url()}'></a>"""
return f"<img src='{url}'>"
def _render_pictures(self, config: dict[str, str | int], to_parse: str):
segments: list[str] = to_parse.split("--")
content: str = ""
for segment in segments:
content += f"<div class='pictures-item'>{self.render(segment)}</div>"
return f"<div class='pictures-menu'>{content}</div>"
def _photohub_render(self, id: str, text: str) -> str:
if (id == "image"):
picture_url = f"{text[2:-1]}"
return self._render_image(picture_url)
if (id == "pictures"):
config, style, to_parse = text[5:-5].split("---")
style = style.replace("\n", "")
config = {
k: v for line in config.strip().splitlines()
if ": " in line
for k, v in [line.split(": ", 1)]
}
content: str = self._render_pictures(config, to_parse)
return f"<div style='{style}'>{content}</div>"

View File

@ -10,11 +10,11 @@ from typing import TYPE_CHECKING
import config
if TYPE_CHECKING:
from bulk_page import BulkPage
from bulk_category import BulkCategory
from page import Page
from category import Category
class Picture():
def __init__(self, picture_path: Path, id: int, page: BulkPage = None, raw: Path|None = None, is_repertoried: bool = True):
def __init__(self, picture_path: Path, id: int, page: Page = None, raw: Path|None = None, is_repertoried: bool = True):
self._large: Path = picture_path
self._small: Path = Path(picture_path.get_absolute_path()[:-4] + "_small.jpg")
self._thumb: Path = Path(picture_path.get_absolute_path()[:-4] + "_thumb.jpg")
@ -22,9 +22,9 @@ class Picture():
self._categories_file: Path = Path(picture_path.get_absolute_path()[:-4] + "_categories.txt")
self._raw: Path|None = raw
self.id: int = id
self._page: BulkPage = page
self._page: Page = page
self._categories_name: list[str] = []
self.categories: list[BulkCategory] = []
self.categories: list[Category] = []
if self._categories_file.exist():
with open(self._categories_file.get_absolute_path(), "r+") as f:
categories_name: list[str] = f.read()

View File

@ -1,87 +0,0 @@
:root {
--bg1: #002b36;
--bg2: #073642;
--content1: #586e75;
--content2: #657b83;
--content3: #839496;
--content4: #93a1a1;
--lbg1: #eee8d5;
--lbg2: #fdf6e3;
}
body {
background-color: var(--bg1);
display: flex;
flex-direction: column;
align-items: center;
margin: 15% 15%;
margin-top: 5%;
height: auto;
}
.readme {
background-color: var(--bg2);
}
.picture-container {
background-color: var(--bg2);
padding: 10px;
}
.picture {
padding: 5px;
background-color: var(--content1);
}
.albums_container {
margin-right: 0;
}
img {
max-width: 100%;
height: auto;
}
.meta-picture, .navigation {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
}
.profile {
text-align: right;
}
a {
color: white;
text-decoration: none;
font-size: 1.5rem;
}
@media screen and (max-width:767px) {
body {
margin: 0% 0%;
}
}
.exif, .readme {
width: 100%;
display: block;
}
.download {
width: 100%;
background-color: var(--bg2);
text-align: center;
}
.exif summary {
text-align: center;
}
.exif details {
display: flex;
justify-content: center;
background-color: var(--bg2);
}

View File

@ -1,70 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/bulk/bulk_page.css">
</head>
<body>
<div class="navigation">
{% if page.prev %}
<a href="{{ page.prev.html.get_url() }}">Prev</a>
{% endif %}
{% if page.next %}
<a class="next" href="{{ page.next.html.get_url() }}">Next</a>
{% endif %}
</div>
{% if page.render_readme() %}
<div class="readme">
<details>
<summary>
README
</summary>
<div class="readme-content">
{{page.render_readme()}}
</div>
</details>
</div>
{% endif %}
<div class="picture-container">
{% for picture in page._pictures %}
<div class="picture-container-item" id="{{ picture.id}}">
<div class="picture">
<a href="{{ picture.get_small().get_url() }}">
<img src='{{ picture.get_small().get_url() }}'>
</a>
<div class="meta-picture">
<a href="{{ picture.get_large().get_url() }}">Large</a>
{% if picture._profile_file.exist() %}
<a class="profile" href="{{ picture.get_profile_file().get_url() }}">Raw therapee profile</a>
{% endif %}
</div>
</div>
<div class="categories_container">
<h1>Categories:</h1>
{% for category in picture.categories %}
<a href="{{ category.path.get_url() }}" class="category_item">{{ al }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% if page.get_raw().exist() %}
<div class="download">
<h1>Download</h1>
<a href="{{ page.get_raw().get_url() }}">RAW</a>
</div>
{% endif %}
{% if page.get_exif() %}
<div class="exif">
<details>
<summary>Exif</summary>
<pre>
{{ page.render_exif() }}
</pre>
</details>
</div>
{% endif %}
</body>
</html>

View File

@ -2,7 +2,7 @@
<html>
<head>
<link rel="stylesheet" href="/bulk/bulk_category.css">
<link rel="stylesheet" href="/bulk/category.css">
</head>
<body>

View File

@ -0,0 +1,6 @@
img {
width: 1000px;
height: 1000px;
}

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/page.css">
</head>
<body>
{{ content }}
</body>
</html>

0
src/templates/home.css Normal file
View File

View File

@ -1,7 +1,13 @@
<!DOCTYPE html>
<html>
<body>
<a href="/bulk/"><h1>Site WIP go to BULK</h1></a>
<a href="/bulk/"><h1>See all pictures I take</h1></a>
<ul>
{% for collection in collections %}
<li>
<a href="{{collection.get_url() }}">{{ collection.get_name()}} </a>
</li>
{% endfor %}
</ul>
</body>
</html>

View File

@ -0,0 +1,87 @@
:root {
--bg1: #002b36;
--bg2: #073642;
--content1: #586e75;
--content2: #657b83;
--content3: #839496;
--content4: #93a1a1;
--lbg1: #eee8d5;
--lbg2: #fdf6e3;
}
body {
background-color: var(--bg1);
display: flex;
flex-direction: column;
align-items: center;
margin: 15% 15%;
margin-top: 5%;
height: auto;
}
.readme {
background-color: var(--bg2);
}
.picture-container {
background-color: var(--bg2);
padding: 10px;
}
.picture {
padding: 5px;
background-color: var(--content1);
}
.albums_container {
margin-right: 0;
}
img {
max-width: 100%;
height: auto;
}
.meta-picture, .navigation {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
}
.profile {
text-align: right;
}
a {
color: white;
text-decoration: none;
font-size: 1.5rem;
}
@media screen and (max-width:767px) {
body {
margin: 0% 0%;
}
}
.exif, .readme {
width: 100%;
display: block;
}
.download {
width: 100%;
background-color: var(--bg2);
text-align: center;
}
.exif summary {
text-align: center;
}
.exif details {
display: flex;
justify-content: center;
background-color: var(--bg2);
}

View File

@ -2,11 +2,69 @@
<html>
<head>
<link rel="stylesheet" href="/page.css">
<link rel="stylesheet" href="/bulk/page.css">
</head>
<body>
<h1>{{ category.name }}</h1>
<div class="navigation">
{% if page.prev %}
<a href="{{ page.prev.html.get_url() }}">Prev</a>
{% endif %}
{% if page.next %}
<a class="next" href="{{ page.next.html.get_url() }}">Next</a>
{% endif %}
</div>
{% if page.render_readme() %}
<div class="readme">
<details>
<summary>
README
</summary>
<div class="readme-content">
{{page.render_readme()}}
</div>
</details>
</div>
{% endif %}
<div class="picture-container">
{% for picture in page._pictures %}
<div class="picture-container-item" id="{{ picture.id}}">
<div class="picture">
<a href="{{ picture.get_small().get_url() }}">
<img src='{{ picture.get_small().get_url() }}'>
</a>
<div class="meta-picture">
<a href="{{ picture.get_large().get_url() }}">Large</a>
{% if picture._profile_file.exist() %}
<a class="profile" href="{{ picture.get_profile_file().get_url() }}">Raw therapee profile</a>
{% endif %}
</div>
</div>
<div class="categories_container">
<h1>Categories:</h1>
{% for category in picture.categories %}
<a href="{{ category.path.get_url() }}" class="category_item">{{ al }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
{% if page.get_raw().exist() %}
<div class="download">
<h1>Download</h1>
<a href="{{ page.get_raw().get_url() }}">RAW</a>
</div>
{% endif %}
{% if page.get_exif() %}
<div class="exif">
<details>
<summary>Exif</summary>
<pre>
{{ page.render_exif() }}
</pre>
</details>
</div>
{% endif %}
</body>
</html>