đŸ Archived View for data.konfusator.de âș 2023-05-31_pelican_reader.gmi captured on 2024-07-08 at 23:21:43. Gemini links have been rewritten to link to archived content
âŹ ïž Previous capture (2023-09-08)
-=-=-=-=-=-=-
Manchmal ist eine Gemini-Kapsel doch sehr unsichtbar und man wĂŒnscht sich den Text auch auf dem Pelican-Blog, ohne dass man viel Ă€ndern muss. Mit Pelican ist man es ja schon gewöhnt eine einfache Auszeichnungssprache zu benutzen (markdown oder rst, wobei ich wegen Sphinx lieber rst benutze), so dass der Gedanke gemini als Input zu benutzen nicht sehr fern liegt. NatĂŒrlich muss man wegen der Struktur von Gemini-Text mit EinschrĂ€nkungen leben â Links können nicht inline sein. Die Link-Section unten sieht in HTML ungewohnt aus.
Wie in der Dokumentation beschrieben wird ist das fĂŒr das Einlesen von Text ein Reader zustĂ€ndig. Auch ein Beispiel findet dort. Ein Reader wird einem Dateityp zugeordnet (also einem oder mehreren Suffixen). Die RĂŒckgabe eines Readers ist ein Tupel aus einem HTML-String und Metadaten. Da gemini-Text zeilenorientiert ist lĂ€sst sich die Umwandlung mit einem regulĂ€ren Ausdruck pro Zeile und einem Mrker fĂŒr vorformatierte Blöcke erledigen.
Bei den Metadaten war eine Entscheidung nötig: Sollen sie am Anfang der gmi-Datei stehen oder kommen sie in eine eigene Datei? Ich habe mich fĂŒr letzteres entschieden, denn damit kann ich einen Beitrag nur fĂŒr die Gemini-Kapsel schreiben ohne die Gemini-Datei mit ĂŒberflĂŒssigen Daten zu belasten. Im Verzeichnis des Pelican-Blogs kann ich einfach eine Datei mit den Metadaten und einen Link zu Gemini-Datei anlegen.
Etwas fehleranfÀllig war beim Erzeugen des HTML waren die reservierten Zeichen < und >, die ersetzt werden mussten, wenn sie nicht gerade Bedeutung im Gemini-Text haben (bei Link- und Blockquote-Zeilen).
Alles in allem hat der Reader Stand heute 98 Zeilen, ist also nicht wirklich komplex. Das sehe ich als Zeichen dass das Versprechen âGemtext is carefully designed to be very, very easy to parse and render.â eingehalten wird; und auch Pelican macht da einen guten Job.
Pelican internals - Reader, Writer & Co. Mit Beispiel.
https://docs.getpelican.com/en/latest/internals.html
Als Inspiration genutzt, aber letztlich komplett anders gemacht.
https://github.com/khoulihan/pelican-gemini
Inspiration fĂŒr das Konvertieren. Viel Nacharbeit nötig.
https://github.com/huntingb/gemtext-html-converter
from pelican import signals from pelican.readers import BaseReader import logging import re class GeminiReader(BaseReader): enabled = True logger = logging.getLogger(__name__) file_extensions = ["gmi", "gemini"] def read(self, filename): metadata = {} content = "" # Metadaten sind in Dateiname.pelican_meta with open(filename + ".pelican_meta", mode="r") as f: while current := f.readline(): current = current.strip() split = current.split(": ", 1) metadata[split[0].lower()] = split[1] with open(filename, mode="r") as f: # After the first blank line, there is the title current = f.readline() if match := re.match(r"^#\s*(.*)$", current): # use if no title, otherwise ignore if not metadata.get("title"): self.logger.info(f"No title, using {current}") metadata["title"] = match.groups()[0] else: content = current # The rest is content. content = content + f.read() parsed = {} for key, value in metadata.items(): parsed[key] = self.process_metadata(key, value) return self._convert2html(content), parsed def _convert2html(self, content): result = "" preformat = False gemini_footer_delim = self.settings.get("GEMINI_FOOTER_DELIM") for line in content.splitlines(): if line.startswith("```"): preformat = not preformat result += "<pre>\n" if preformat else "</pre>\n" continue # Meine Art von gemini-Footer â Alles danach ignorieren if ( gemini_footer_delim and line.startswith(gemini_footer_delim) and not preformat ): return result result += self._parse_gmi_line(line, preformat) return result def _parse_gmi_line(self, gmi_line, preformat): tags_dict = { r"^#\s*([^#].*)": "h1", r"^##\s*([^#].*)": "h2", r"^###\s*([^#].*)": "h3", r"^\* (.*)": "li", r"^> (.*)": "blockquote", r"^=>\s+(\S+)(\s+.*)?": "a", } # HTML special chars gmi_line = gmi_line.replace("&", "&") gmi_line = gmi_line.replace("<", "<") # keep > for blockqoute or link if not (gmi_line.startswith(">") or gmi_line.startswith("=>")): gmi_line = gmi_line.replace(">", ">") if preformat: return gmi_line + "\n" for pattern in tags_dict.keys(): if match := re.match(pattern, gmi_line): tag = tags_dict[pattern] groups = match.groups() if tag == "a": href = groups[0] text = groups[1].strip() if len(groups) > 1 and groups[1] else href # text might be empty after strip - not by spec, but in practice text = href if not text else text return f"<p><a href='{href}'>{text}</a></p>\n" else: text = groups[0].strip() return f"<{tag}>{text}</{tag}>\n" return f"<p>{gmi_line}</p>\n" def add_reader(readers): for ext in GeminiReader.file_extensions: readers.reader_classes[ext] = GeminiReader def register(): signals.readers_init.connect(add_reader)
ââââââââââââââââââââââââ
2023-05-31T00:53+02:00