From 1d70dac8a39a7ac340a94a8d736319c5d86186f2 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Sun, 26 Jun 2022 21:58:23 -0700 Subject: [PATCH] Add /photography. --- .gitignore | 1 + voussoir.net/index.html | 16 +- voussoir.net/photography/dark.css | 302 ++++++++++++++++++++++ voussoir.net/photography/generate_site.py | 278 ++++++++++++++++++++ 4 files changed, 595 insertions(+), 2 deletions(-) create mode 100644 voussoir.net/photography/dark.css create mode 100644 voussoir.net/photography/generate_site.py diff --git a/.gitignore b/.gitignore index 7c5c65b..7498b97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +voussoir.net/cv/*.jpg voussoir.net/cv/*.png voussoir.net/writing/**/*.fcstd voussoir.net/writing/**/*.flac diff --git a/voussoir.net/index.html b/voussoir.net/index.html index 462a116..74e31e4 100644 --- a/voussoir.net/index.html +++ b/voussoir.net/index.html @@ -395,7 +395,7 @@ body.start_eating_that_trashcan .cvitem_details
-

Projects

+

Software

@@ -578,12 +578,24 @@ body.start_eating_that_trashcan .cvitem_details

Writing

-

I recently decided to start writing from time to time. My thoughts are mostly about technology in culture, learning, and other choses sérieuses. I disable spellcheck because I'm a stellar speller.

+

I recently decided to start writing from time to time. My thoughts are mostly about technology in culture, learning, and other choses sérieuses. I disable spellcheck because I'm a stellar speller.

+

https://voussoir.net/writing

+
+
+

Photography

+
+

To be administered by intravitreal injection.

+

https://voussoir.net/photography

+
+ +
+
+

Contact

diff --git a/voussoir.net/photography/dark.css b/voussoir.net/photography/dark.css new file mode 100644 index 0000000..db6d58e --- /dev/null +++ b/voussoir.net/photography/dark.css @@ -0,0 +1,302 @@ +:root +{ + --color_bodybg: #272822; + --color_codebg: rgba(255, 255, 255, 0.05); + --color_codeborder: rgba(255, 255, 255, 0.2); + --color_h1bg: #284142; + --color_htmlbg: #1b1c18; + --color_blockquotebg: rgba(0, 0, 0, 0.2); + --color_blockquoteedge: rgba(255, 255, 255, 0.2); + --color_inlinecodebg: rgba(255, 255, 255, 0.1); + --color_link: #ae81ff; + --color_maintext: #ddd; +} + +*, *:before, *:after +{ + box-sizing: inherit; +} + +html +{ + box-sizing: border-box; + + background-color: var(--color_htmlbg); + color: var(--color_maintext); + + font-family: Verdana, sans-serif; + font-size: 10pt; + margin: 0; +} + +body +{ + width: fit-content; + margin-left: auto; + margin-right: auto; + margin-top: 0; + margin-bottom: 0; +} + +header +{ + width: 100%; + max-width: 120em; + margin-left: auto; + margin-right: auto; + margin-top: 20px; + margin-bottom: 20px; + text-align: end; +} +header > * +{ + padding: 16px; + background-color: var(--color_bodybg); +} +.photograph +{ + margin-left: auto; + margin-right: auto; + margin-top: 40px; + margin-bottom: 40px; + padding: 16px; + background-color: var(--color_bodybg); +} +article .photograph:first-of-type +{ + margin-top: 0; +} +article .photograph:last-of-type +{ + margin-bottom: 0; +} +.photograph img +{ + max-height: 92vh; +} +article .morelink +{ + font-size: 2em; + text-align: center; + margin-top: 0; +} +@media not print +{ + .photograph + { + box-shadow: #000 0px 0px 40px -10px; + } +} + +@media screen and (min-width: 600px) +{ + article + { + width: fit-content; + } +} + +@media screen and (max-width: 600px) +{ + .photograph + { + box-shadow: none; + } +} + +h1, h2, h3, h4, h5 +{ + margin-bottom: 0; + padding: 8px; +} +h1:first-child, h2:first-child, h3:first-child, h4:first-child, h5:first-child +{ + margin-top: 0; +} +h2, h3, h4, h5 +{ + border-bottom: 1px solid var(--color_maintext); + /*background-color: var(--color_h1bg);*/ +} +h1 + p +{ + margin-top: 0; +} +p:last-child +{ + margin-bottom: 0; +} +h1 {font-size: 2.5em;} h1 * {font-size: inherit;} +h2 {font-size: 2.0em;} h2 * {font-size: inherit;} +h3 {font-size: 1.5em;} h3 * {font-size: inherit;} +h4 {font-size: 1.2em;} h4 * {font-size: inherit;} +h5 {font-size: 1.0em;} h5 * {font-size: inherit;} + +.header_anchor_link {display: none; font-size: 1.0em; text-decoration: none} +h1:hover > .header_anchor_link {display: initial;} +h2:hover > .header_anchor_link {display: initial;} +h3:hover > .header_anchor_link {display: initial;} +h4:hover > .header_anchor_link {display: initial;} +h5:hover > .header_anchor_link {display: initial;} + +a +{ + color: var(--color_link); + cursor: pointer; +} + +article * +{ + max-width: 100%; + word-wrap: break-word; +} + +#table_of_contents +{ + border: 1px solid var(--color_blockquoteedge); + padding-top: 8px; + padding-bottom: 8px; + border-radius: 8px; +} + +blockquote +{ + background-color: var(--color_blockquotebg); + margin-inline-start: 0; + margin-inline-end: 0; + border-left: 4px solid var(--color_blockquoteedge); + + padding: 8px; + padding-inline-start: 20px; + padding-inline-end: 20px; +} + +table +{ + border-collapse: collapse; + font-size: 1em; +} +table, table th, table td +{ + border: 1px solid var(--color_maintext); +} +table th, table td +{ + padding: 4px; +} + +ol ol, ul ul, ol ul, ul ol +{ + padding-inline-start: 20px; +} + +*:not(pre) > code +{ + background-color: var(--color_inlinecodebg); + border-radius: 4px; + line-height: 1.5; + padding-left: 4px; + padding-right: 4px; +} + +pre +{ + padding: 8px; + border: 1px solid var(--color_codeborder); + background-color: var(--color_codebg); + overflow-x: auto; +} + +code, +pre, +.highlight * +{ + font-family: monospace; +} + +/* +Thank you richleland for pre-building this Monokai style. +https://github.com/richleland/pygments-css +*/ +:root +{ + --color_monokai_bg: #272822; + --color_monokai_purple: #ae81ff; + --color_monokai_green: #a6e22e; + --color_monokai_pink: #f92672; + --color_monokai_white: #f8f8f2; + --color_monokai_orange: #fd971f; + --color_monokai_yellow: #e6db74; + --color_monokai_blue: #66d9ef; +} +.highlight .hll { background-color: #49483e } +.highlight { background-color: var(--color_monokai_bg); color: var(--color_monokai_white) } +.highlight .c { color: #75715e } /* Comment */ +.highlight .err { color: #960050; background-color: #1e0010 } /* Error */ +.highlight .k { color: var(--color_monokai_pink) } /* Keyword */ +.highlight .l { color: var(--color_monokai_purple) } /* Literal */ +.highlight .n { color: var(--color_monokai_white) } /* Name */ +.highlight .o { color: var(--color_monokai_pink) } /* Operator */ +.highlight .p { color: var(--color_monokai_white) } /* Punctuation */ +.highlight .ch { color: #75715e } /* Comment.Hashbang */ +.highlight .cm { color: #75715e } /* Comment.Multiline */ +.highlight .cp { color: #75715e } /* Comment.Preproc */ +.highlight .cpf { color: #75715e } /* Comment.PreprocFile */ +.highlight .c1 { color: #75715e } /* Comment.Single */ +.highlight .cs { color: #75715e } /* Comment.Special */ +.highlight .gd { color: var(--color_monokai_pink) } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gi { color: var(--color_monokai_green) } /* Generic.Inserted */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #75715e } /* Generic.Subheading */ +.highlight .kc { color: var(--color_monokai_purple) } /* Keyword.Constant */ +.highlight .kd { color: var(--color_monokai_blue) } /* Keyword.Declaration */ +.highlight .kn { color: var(--color_monokai_pink) } /* Keyword.Namespace */ +.highlight .kp { color: var(--color_monokai_blue) } /* Keyword.Pseudo */ +.highlight .kr { color: var(--color_monokai_blue) } /* Keyword.Reserved */ +.highlight .kt { color: var(--color_monokai_blue) } /* Keyword.Type */ +.highlight .ld { color: var(--color_monokai_yellow) } /* Literal.Date */ +.highlight .m { color: var(--color_monokai_purple) } /* Literal.Number */ +.highlight .s { color: var(--color_monokai_yellow) } /* Literal.String */ +.highlight .na { color: var(--color_monokai_white) } /* Name.Attribute */ +.highlight .narg {color: var(--color_monokai_orange) } /* Custom Name.Argument */ +.highlight .nb { color: var(--color_monokai_blue) } /* Name.Builtin */ +.highlight .nc { color: var(--color_monokai_white) } /* Name.Class */ +.highlight .no { color: var(--color_monokai_blue) } /* Name.Constant */ +.highlight .nd { color: var(--color_monokai_green) } /* Name.Decorator */ +.highlight .ni { color: var(--color_monokai_white) } /* Name.Entity */ +.highlight .ne { color: var(--color_monokai_blue) } /* Name.Exception */ +.highlight .nf { color: var(--color_monokai_green) } /* Name.Function */ +.highlight .nl { color: var(--color_monokai_white) } /* Name.Label */ +.highlight .nn { color: var(--color_monokai_white) } /* Name.Namespace */ +.highlight .nx { color: var(--color_monokai_green) } /* Name.Other */ +.highlight .py { color: var(--color_monokai_white) } /* Name.Property */ +.highlight .nt { color: var(--color_monokai_pink) } /* Name.Tag */ +.highlight .nv { color: var(--color_monokai_white) } /* Name.Variable */ +.highlight .ow { color: var(--color_monokai_pink) } /* Operator.Word */ +.highlight .w { color: var(--color_monokai_white) } /* Text.Whitespace */ +.highlight .mb { color: var(--color_monokai_purple) } /* Literal.Number.Bin */ +.highlight .mf { color: var(--color_monokai_purple) } /* Literal.Number.Float */ +.highlight .mh { color: var(--color_monokai_purple) } /* Literal.Number.Hex */ +.highlight .mi { color: var(--color_monokai_purple) } /* Literal.Number.Integer */ +.highlight .mo { color: var(--color_monokai_purple) } /* Literal.Number.Oct */ +.highlight .sa { color: var(--color_monokai_white) } /* Literal.String.Affix */ +.highlight .sb { color: var(--color_monokai_yellow) } /* Literal.String.Backtick */ +.highlight .sc { color: var(--color_monokai_yellow) } /* Literal.String.Char */ +.highlight .dl { color: var(--color_monokai_yellow) } /* Literal.String.Delimiter */ +.highlight .sd { color: var(--color_monokai_yellow) } /* Literal.String.Doc */ +.highlight .s2 { color: var(--color_monokai_yellow) } /* Literal.String.Double */ +.highlight .se { color: var(--color_monokai_purple) } /* Literal.String.Escape */ +.highlight .sh { color: var(--color_monokai_yellow) } /* Literal.String.Heredoc */ +.highlight .si { color: var(--color_monokai_yellow) } /* Literal.String.Interpol */ +.highlight .sx { color: var(--color_monokai_yellow) } /* Literal.String.Other */ +.highlight .sr { color: var(--color_monokai_yellow) } /* Literal.String.Regex */ +.highlight .s1 { color: var(--color_monokai_yellow) } /* Literal.String.Single */ +.highlight .ss { color: var(--color_monokai_yellow) } /* Literal.String.Symbol */ +.highlight .bp { color: var(--color_monokai_white) } /* Name.Builtin.Pseudo */ +.highlight .fm { color: var(--color_monokai_blue) } /* Name.Function.Magic */ +.highlight .vc { color: var(--color_monokai_white) } /* Name.Variable.Class */ +.highlight .vg { color: var(--color_monokai_white) } /* Name.Variable.Global */ +.highlight .vi { color: var(--color_monokai_white) } /* Name.Variable.Instance */ +.highlight .vm { color: var(--color_monokai_white) } /* Name.Variable.Magic */ +.highlight .il { color: var(--color_monokai_purple) } /* Literal.Number.Integer.Long */ diff --git a/voussoir.net/photography/generate_site.py b/voussoir.net/photography/generate_site.py new file mode 100644 index 0000000..ffe8e65 --- /dev/null +++ b/voussoir.net/photography/generate_site.py @@ -0,0 +1,278 @@ +import PIL.Image +import jinja2 + +from voussoirkit import dotdict +from voussoirkit import imagetools +from voussoirkit import pathclass +from voussoirkit import spinal + +PHOTOGRAPHY_ROOTDIR = pathclass.Path(__file__).parent +DOMAIN_ROOTDIR = PHOTOGRAPHY_ROOTDIR.parent +CSS_CONTENT = PHOTOGRAPHY_ROOTDIR.with_child('dark.css').read('r', encoding='utf-8') + +def write(path, content): + ''' + open() and write the file, with validation that it is in the writing dir. + ''' + path = pathclass.Path(path) + if path not in PHOTOGRAPHY_ROOTDIR: + raise ValueError(path) + print(path.absolute_path) + f = path.open('w', encoding='utf-8') + f.write(content) + f.close() + +def render_photo(photo, d): + small_name = make_thumbnail(photo) + basename = '/' + photo.relative_to(DOMAIN_ROOTDIR, simple=True) + thumb = '/' + small_name.relative_to(DOMAIN_ROOTDIR, simple=True) + article_id = photo.replace_extension('').basename + + return f''' +
+ +
+ ''' + +def render_album_preview(directory): + photos = list(spinal.walk( + directory, + glob_filenames={'*.jpg'}, + exclude_filenames={'*_small*'}, + recurse=False, + yield_directories=False, + )) + article_id = directory.basename + photos.sort(key=lambda file: file.basename) + firsts = photos[:5] + remaining = photos[5:] + if remaining: + next_after_more = remaining[0].replace_extension('').basename + else: + next_after_more = None + firsts = [render_photo(photo, pathclass.cwd()) for photo in firsts] + + return jinja2.Template(''' + + ''').render( + article_id=article_id, + directory=directory, + album_path='/photography/' + directory.relative_to(PHOTOGRAPHY_ROOTDIR, simple=True), + next_after_more=next_after_more, + firsts=firsts, + remaining=len(remaining), + ) + +def write_directory_index(directory): + do_rss = directory == PHOTOGRAPHY_ROOTDIR + do_back = directory != PHOTOGRAPHY_ROOTDIR + sort_reverse = directory == PHOTOGRAPHY_ROOTDIR + + items = list(spinal.walk( + directory, + glob_filenames={'*.jpg'}, + exclude_filenames={'*_small*'}, + recurse=False, + yield_directories=False, + )) + list(directory.listdir_directories()) + items.sort(key=lambda p: p.basename, reverse=sort_reverse) + + items2 = [] + for item in items: + article_id = item.replace_extension('').basename + if item.is_file: + ren = render_photo(item, directory) + link = f'#{article_id}' + published = imagetools.get_exif_datetime(item) + else: + ren = render_album_preview(item) + link = f'/{article_id}' + published = imagetools.get_exif_datetime(sorted(item.listdir_files())[0]) + if published is None: + print(f'{item} lacks exif date') + item = dotdict.DotDict( + article_id=article_id, + rendered=ren, + link=link, + published=published.isoformat(), + ) + items2.append(item) + items = items2 + + page = jinja2.Template(''' + + + + + + {{directory.basename}} + + + + + +
+ hint: / + {% if do_rss %} + Atom + {% endif %} + + {% if do_back %} + Back + {% endif %} +
+ + {% for item in items %} + {{item.rendered}} + {% endfor %} + + + + + ''').render( + css_content=CSS_CONTENT, + directory=directory, + do_rss=do_rss, + do_back=do_back, + items=items, + ) + write(directory.with_child('index.html'), page) + + if do_rss: + write_atom(items) + +def write_atom(items): + atom = jinja2.Template(''' + + + voussoir.net/photography + + voussoir.net/photography + + {% for item in items %} + + {{item.article_id}} + {{item.article_id|e}} + + {{item.published}} + + + + + {% endfor %} + + '''.strip()).render(items=items) + write(PHOTOGRAPHY_ROOTDIR.with_child('photography.atom'), atom) + +def make_thumbnail(photo): + small_name = photo.replace_extension('').basename + '_small' + small_name = photo.parent.with_child(small_name).add_extension(photo.extension) + if small_name.is_file: + return small_name + image = PIL.Image.open(photo.absolute_path) + (image_width, image_height) = image.size + (width, height) = imagetools.fit_into_bounds(image_width, image_height, 1440, 1440) + image = image.resize((width, height), PIL.Image.ANTIALIAS) + image.save(small_name.absolute_path, quality=90) + print(small_name) + return small_name + +write_directory_index(PHOTOGRAPHY_ROOTDIR) +for directory in PHOTOGRAPHY_ROOTDIR.walk_directories(): + write_directory_index(directory)