about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
authorNguyễn Gia Phong <mcsinyx@disroot.org>2022-06-01 02:09:01 +0900
committerNguyễn Gia Phong <mcsinyx@disroot.org>2022-06-01 02:09:01 +0900
commitf23c6ed4f84055551b459d235c4a675faf017cea (patch)
treeb8acbb342591aa959948a7888c15b282c201db02
parentc2cb66ad1830d1394c5d6c1dfa2e75ef7e23072b (diff)
downloadphylactery-f23c6ed4f84055551b459d235c4a675faf017cea.tar.gz
Draft basic CBZ rendering
-rw-r--r--main.go81
-rw-r--r--static/favicon.svg53
-rw-r--r--static/style.css250
-rw-r--r--templates/archive.html19
-rw-r--r--templates/index.html1
5 files changed, 153 insertions, 251 deletions
diff --git a/main.go b/main.go
index 656b816..4fa807b 100644
--- a/main.go
+++ b/main.go
@@ -1,11 +1,34 @@
+// Server entry point
+// Copyright (C) 2022  Nguyễn Gia Phong
+//
+// This file is part of Phylactery.
+//
+// Phylactery is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Phylactery is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Phylactery.  If not, see <https://www.gnu.org/licenses/>.
+
 package main
 
 import (
+	"archive/zip"
 	"embed"
 	"html/template"
 	"log"
+	"io"
 	"net/http"
 	"os"
+	"path"
+	"strconv"
+	"strings"
 )
 
 //go:embed static/*
@@ -14,14 +37,70 @@ var static embed.FS
 //go:embed templates/*.html
 var templates embed.FS
 
+type Page struct {
+	Index int
+	Name string
+}
+
+type Archive struct {
+	Path string
+	Title string
+	Entries []Page
+}
+
 func main() {
 	http.Handle("/static/", http.FileServer(http.FS(static)))
 	t, err := template.ParseFS(templates, "templates/*.html")
 	if err != nil {
 		log.Fatal(err)
 	}
+	lib := os.Getenv("PHYLACTERY_LIBRARY")
 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-		t.ExecuteTemplate(w, "index.html", "Phylactery")
+		p := path.Clean(lib + r.URL.Path)
+		stat, err := os.Stat(p)
+		if err != nil {
+			http.NotFound(w, r)
+			return
+		}
+
+		if stat.IsDir() {
+			// TODO
+		}
+
+		// TODO: LRU caching
+		cbz, err := zip.OpenReader(p)
+		if err != nil {
+			http.Error(w, "invalid cbz", 406)
+			return
+		}
+		defer cbz.Close()
+
+		r.ParseForm()
+		if entry, isImage := r.Form["entry"]; isImage {
+			i, err := strconv.Atoi(entry[0])
+			if err != nil || i < 0 || i >= len(cbz.File) {
+				http.NotFound(w, r)
+				return
+			}
+			image, _ := cbz.File[i].Open()
+			defer image.Close()
+			io.Copy(w, image)
+			return
+		}
+
+		var pages []Page
+		for i, f := range cbz.File {
+			image, _ := cbz.File[i].Open()
+			defer image.Close()
+			buf := make([]byte, 512)
+			n, _ := image.Read(buf)
+			mime := http.DetectContentType(buf[:n])
+			if strings.HasPrefix(mime, "image/") {
+				pages = append(pages, Page{ i, f.Name })
+			}
+		}
+		archive := Archive{ r.URL.Path, stat.Name(), pages }
+		t.ExecuteTemplate(w, "archive.html", archive)
 	})
 
 	addr := os.Getenv("PHYLACTERY_ADDRESS")
diff --git a/static/favicon.svg b/static/favicon.svg
new file mode 100644
index 0000000..2305a95
--- /dev/null
+++ b/static/favicon.svg
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://web.resource.org/cc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg" xmlns:ns1="http://sozi.baierouge.fr" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg2" sodipodi:modified="true" sodipodi:docname="scroll.svg" viewBox="0 0 216.61 247.01" sodipodi:version="0.32" version="1.0" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.45pre1" sodipodi:docbase="C:\scratch\clipart">
+  <sodipodi:namedview id="base" bordercolor="#666666" inkscape:pageshadow="2" guidetolerance="10" pagecolor="#ffffff" gridtolerance="10000" inkscape:window-height="569" inkscape:zoom="1.5822796" objecttolerance="10" borderopacity="1.0" inkscape:current-layer="layer1" inkscape:cx="106.43052" inkscape:cy="121.74098" inkscape:window-y="1212" inkscape:window-x="1504" inkscape:window-width="744" showborder="true" inkscape:pageopacity="0.0" inkscape:document-units="px"/>
+  <g id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer" transform="translate(66.751 -781.12)">
+    <g id="g3159">
+      <path id="path4447" sodipodi:nodetypes="csssccssscc" style="fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:3;fill:#ffffff" d="m-41.494 1010.7c9.375 1.5 19.091-7.3 25.403-23.73 9.7605-25.4 10.036-62.77 1.938-90.25-8.099-27.48-7.823-64.85 1.937-90.25 6.1828-16.1 15.658-24.84 24.882-23.76l112.1 15.9c-9.43-1.63-19.24 7.15-25.58 23.66-9.761 25.4-10.037 62.77-1.938 90.25 8.098 27.47 7.828 64.84-1.937 90.28-6.161 16-15.592 24.7-24.726 23.7l-112.08-15.8z"/>
+      <path id="path4458" sodipodi:nodetypes="csccssc" style="fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:3;fill:#ffffff" d="m121.84 798.44c-8.48 0.44-16.96 9.03-22.652 23.84-7.279 18.95-9.248 44.53-6.438 67.72l36.41 5.16c4.82 0.6 9.95-3.72 13.43-11.41 9.03-19.91 6.84-52.78-2.25-70.66-5.29-10.42-11.9-14.99-18.5-14.65z"/>
+      <path id="path4496" sodipodi:nodetypes="cssc" style="stroke:#000000;stroke-linecap:round;stroke-width:3;fill:none" d="m129.91 895.22c-3.97-0.07-7.85-3.37-10.61-10.72-3.34-8.89-4.01-25.85 1.59-31.49 2.06-2.08 7.53 0.53 5.42 7.69"/>
+      <path id="path4498" sodipodi:nodetypes="cc" style="stroke:#000000;stroke-linecap:round;stroke-width:3;fill:none" d="m123.17 852.36l-30.261-4.29"/>
+      <path id="path4500" sodipodi:nodetypes="cc" style="stroke:#000000;stroke-linecap:round;stroke-width:3;fill:none" d="m126.31 860.7l-8.61-1.22"/>
+      <path id="path4504" sodipodi:nodetypes="ccssccssc" style="fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:3;fill:#ffffff" d="m70.622 1026.6l-112.09-15.9c-5.669-0.9-11.2-5.6-15.762-14.57-9.082-17.88-11.284-50.74-2.256-70.65 3.415-7.53 8.371-11.83 13.136-11.45l112.08 15.9c-4.966-0.88-10.234 3.46-13.81 11.35-9.029 19.91-6.826 52.77 2.256 70.62 4.748 9.4 10.544 14 16.446 14.7z"/>
+      <path id="path4523" sodipodi:nodetypes="csss" style="stroke:#000000;stroke-linecap:round;stroke-width:3;fill:none" d="m68.193 964.34c-2.108 7.16 3.359 9.76 5.424 7.68 5.597-5.63 4.923-22.59 1.585-31.48-2.673-7.11-6.383-10.43-10.219-10.71"/>
+      <path id="path4525" sodipodi:nodetypes="cc" style="stroke:#000000;stroke-linecap:round;stroke-width:3;fill:none" d="m71.484 972.69l-25.265-3.58"/>
+      <path id="path4527" sodipodi:nodetypes="cc" style="stroke:#000000;stroke-linecap:round;stroke-width:3;fill:none" d="m68.181 964.34l-21.409-3.03"/>
+    </g>
+  </g>
+  <metadata>
+    <rdf:RDF>
+      <cc:Work>
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+        <cc:license rdf:resource="http://creativecommons.org/licenses/publicdomain/"/>
+        <dc:publisher>
+          <cc:Agent rdf:about="http://openclipart.org/">
+            <dc:title>Openclipart</dc:title>
+          </cc:Agent>
+        </dc:publisher>
+        <dc:title>scroll</dc:title>
+        <dc:date>2007-11-08T21:26:16</dc:date>
+        <dc:description>Scroll of paper, white filled.</dc:description>
+        <dc:source>https://openclipart.org/detail/8032/scroll-by-kelan</dc:source>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>kelan</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>paper</rdf:li>
+            <rdf:li>scroll</rdf:li>
+            <rdf:li>white</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+      </cc:Work>
+      <cc:License rdf:about="http://creativecommons.org/licenses/publicdomain/">
+        <cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction"/>
+        <cc:permits rdf:resource="http://creativecommons.org/ns#Distribution"/>
+        <cc:permits rdf:resource="http://creativecommons.org/ns#DerivativeWorks"/>
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+</svg>
\ No newline at end of file
diff --git a/static/style.css b/static/style.css
deleted file mode 100644
index d7bea79..0000000
--- a/static/style.css
+++ /dev/null
@@ -1,250 +0,0 @@
-/* Variables */
-:root {
-    --black: #1c1b19;
-    --red: #ef2f27;
-    --green: #519f50;
-    --yellow: #fbb829;
-    --blue: #2c78bf;
-    --magenta: #e02c6d;
-    --cyan: #0aaeb3;
-    --white: #d0bfa1;
-    --bright-black: #918175;
-    --bright-red: #f75341;
-    --bright-green: #98bc37;
-    --bright-yellow: #fed06e;
-    --bright-blue: #68a8e4;
-    --bright-magenta: #ff5c8f;
-    --bright-cyan: #53fde9;
-    --bright-white: #fce8c3;
-
-    --block-bg: var(--black);
-    --fade-fg: var(--bright-black);
-    --link-fg: var(--green);
-    --overlay-bg: #8881;
-    --text-bg: #121212;
-    --text-fg: var(--bright-white);
-}
-
-@media (prefers-color-scheme: light) {
-    :root {
-        --block-bg: #eeeeec;
-        --fade-fg: #888a85;
-        --link-fg: #436e58;
-        --text-bg: #ffffff;
-        --text-fg: #2e3436;
-    }
-}
-
-/* Default font and layout */
-html {
-    background-color: var(--text-bg);
-    box-sizing: border-box;
-    color: var(--text-fg);
-    font-size: min(max(100%, 2vw), 150%);
-    margin: auto;
-    max-width: 36rem;
-    scrollbar-color: var(--fade-fg) var(--overlay-bg);
-}
-
-body { margin: 0 1rem }
-
-.franklin-content .row { display: block }
-
-/* Text geometry */
-p, details {
-    hyphens: auto;
-    line-height: 1.4rem;
-    text-align: justify;
-}
-
-/* Titles */
-.franklin-content h1 a,
-.franklin-content h2 a,
-.franklin-content h3 a,
-.franklin-content h4 a,
-.franklin-content h5 a,
-.franklin-content h6 a {
-    color: var(--text-fg);
-}
-
-.franklin-content h1 a:hover,
-.franklin-content h2 a:hover,
-.franklin-content h3 a:hover,
-.franklin-content h4 a:hover,
-.franklin-content h5 a:hover,
-.franklin-content h6 a:hover {
-    text-decoration: none;
-}
-
-.franklin-toc ol ol {
-    list-style-type: lower-alpha;
-}
-
-/* General formatting */
-.franklin-content li p { margin: 0 }
-
-.franklin-content a {
-    color: var(--link-fg);
-    text-decoration: none;
-}
-
-.franklin-content a:hover {
-    text-decoration: underline;
- }
-
-/* Hyperrefs and footnotes */
-.franklin-content .bibref a,
-.franklin-content .eqref a { color: var(--link-fg) }
-
-.franklin-content .fndef {
-    border: none;
-    margin: 1ex 0;
-}
-.franklin-content .fndef tr { text-align: left }
-.franklin-content .fndef td { padding: 0 }
-.franklin-content .fndef td, .franklin-content sup { font-size: 80% }
-.franklin-content .fndef td.fndef-backref { padding-left: 0 }
-.franklin-content .fndef td.fndef-content { padding-left: 1ch }
-
-/* Images */
-.franklin-content img {
-    display: block;
-    margin: auto;
-    max-width: 100%;
-}
-
-/* KaTeX */
-.katex { font-size: 1em !important }
-
-/* Boxes */
-.franklin-content blockquote, .note {
-    margin: 0 -1rem;
-    padding-bottom: 1ex;
-    padding-left: 0.75rem;
-    padding-right: 1rem;
-    padding-top: 1ex;
-}
-.franklin-content blockquote p, .note p { margin: 1ex 0 }
-.note p:first-child { font-weight: bold }
-.franklin-content blockquote {
-    background: var(--overlay-bg);
-    border-left: 0.25rem solid #8884;
-}
-.note {
-    background-color: #51affe25;
-    border-left: 0.25rem solid var(--blue);
-}
-
-/* Header */
-header {
-    margin: 1.5rem -0.5rem;
-    display: flex;
-    flex-wrap: wrap;
-    justify-content: space-between;
-    align-items: center;
-}
-
-header a {
-    border-bottom: solid;
-    color: var(--link-fg);
-    font-weight: bold;
-    margin: 0 0.5rem;
-    text-decoration: none;
-    transition: color 0.3s ease;
-}
-header a:hover { color: var(--text-fg) }
-
-nav, nav li { display: inline-block }
-nav ul { margin: 0 }
-
-footer, .tags, .right {
-    color: var(--fade-fg);
-    font-size: 80%;
-}
-
-footer {
-    margin-top: 1.5rem;
-    margin-bottom: 2rem;
-}
-
-footer a, .tags a {
-    color: var(--fade-fg)!important;
-    text-decoration: underline!important;
-}
-
-.nowrap { display: inline-block }
-.tags { float: left }
-.right {
-    float: right;
-    margin-left: auto;
-}
-
-/* Table */
-table {
-    border-bottom: 0.15em solid var(--text-fg);
-    border-collapse: collapse;
-    border-top: 0.15em solid var(--text-fg);
-    line-height: 1em;
-    margin-bottom: 1.5em;
-    margin-left: auto;
-    margin-right: auto;
-    text-align: center;
-}
-
-tr:first-of-type > th { border-bottom: 0.08em solid var(--text-fg) }
-tr, th, td { padding: 0.5em }
-
-/* highlight.js */
-code, .hljs {
-    background-color: var(--block-bg);
-    font-size: inherit;
-    padding: 0.1em 0.2em;
-}
-
-.hljs, .comment pre > code {
-    display: block;
-    line-height: 1.45em;
-    overflow-x: auto;
-}
-
-.hljs-built_in .hljs-name, .hljs-title, .hljs-type { color: var(--cyan) }
-.hljs-code, .hljs-selector-class { color: var(--bright-blue) }
-.hljs-comment, .hljs-deletion, .hljs-meta { color: var(--fade-fg) }
-.hljs-comment, .hljs-emphasis { font-style: italic; }
-.hljs-string { color: var(--bright-green) }
-.hljs-strong { font-weight: bold; }
-
-.hljs-bullet, .hljs-quote, .hljs-link, .hljs-number,
-.hljs-regexp, .hljs-literal { color: var(--bright-magenta) }
-
-.hljs-keyword, .hljs-selector-tag, .hljs-section,
-.hljs-attribute, .hljs-variable { color: var(--red) }
-
-.hljs-subst, .hljs-symbol, .hljs-selector-id, .hljs-selector-attr,
-.hljs-selector-pseudo, .hljs-template-tag, .hljs-template-variable,
-.hljs-addition { color: var(--red) }
-
-.comment {
-    background-color: var(--overlay-bg);
-    clear: both;
-    margin: 1ex 0;
-    overflow: hidden;
-    padding: 0 1rem;
-}
-
-.openring {
-    display: flex;
-    flex-wrap: wrap;
-    margin: -0.5rem;
-    margin-bottom: 0;
-}
-.openring h3 { margin: 0 0 1ex }
-.openring article {
-    background: var(--overlay-bg);
-    display: flex;
-    flex-direction: column;
-    flex: 1 1 0;
-    margin: 1ex;
-    min-width: 12rem;
-    padding: 1ex;
-}
diff --git a/templates/archive.html b/templates/archive.html
new file mode 100644
index 0000000..d3d8e0b
--- /dev/null
+++ b/templates/archive.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang=en>
+<meta charset=utf-8>
+<link rel=stylesheet href=/static/archive.css>
+<link rel=icon href=/static/favicon.svg>
+<title>{{.Title}}</title>
+<style>
+body { margin: 0 }
+img {
+    display: block;
+    margin: auto;
+    max-width: 100%;
+}
+</style>
+
+<body>{{range .Entries}}
+<img src="?entry={{.Index}}" alt="{{.Name}}">{{end}}
+</body>
+</html>
diff --git a/templates/index.html b/templates/index.html
index 39785aa..67f96fb 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -3,6 +3,7 @@
 <meta charset=utf-8>
 <meta name=viewport content='width=device-width, initial-scale=1'>
 <link rel=stylesheet href=/static/style.css>
+<link rel=icon href=/static/favicon.svg>
 <title>{{.}}</title>
 <body>
 </body>