First commit
Maxwell Jensen 85795372+maxwelljens@users.noreply.github.com
Sun, 26 Apr 2026 17:22:49 +0200
9 files changed,
381 insertions(+),
0 deletions(-)
A
LICENCE.txt
@@ -0,0 +1,121 @@
+Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work.
A
README.md
@@ -0,0 +1,30 @@
+# Newspaper + +Magazine-style PDF generator from RSS feeds using `llm_aggregator`. + +## Usage + +```bash +python3 generate_magazine.py +``` + +## Structure + +- `feeds/*.txt` — one RSS URL per line +- `generate_magazine.py` — generates the magazine +- `output/magazine.pdf` — final PDF + +## Customisation + +Edit `CATEGORIES` in `generate_magazine.py` to add/modify sections. Each entry has: +- `name` — section title +- `icon` — FontAwesome icon (e.g., `\faLinux`) +- `feed` — path to feeds file +- `prompt` — summarisation instructions + +Edit `STRIP_TAG_START`/`STRIP_TAG_END` to strip AI thinking tags from output. + +## Licence + +Released into the public domain under [CC0 1.0 +Universal](https://creativecommons.org/publicdomain/zero/1.0/).
A
feeds/ai_science.txt
@@ -0,0 +1,7 @@
+https://azuresilent.github.io/hf-paper-rss/feed.xml +https://www.theregister.com/software/ai_ml/headlines.atom +https://www.theregister.com/offbeat/science/headlines.atom +https://www.wired.com/feed/tag/ai/latest/rss +https://www.wired.com/feed/category/science/latest/rss +https://www.technologyreview.com/topic/artificial-intelligence/feed +https://www.france24.com/en/tag/artificial-intelligence/rss
A
feeds/esoteric.txt
@@ -0,0 +1,2 @@
+https://modernity.news/feed/ +https://nowakonfederacja.pl/feed/
A
feeds/free_software.txt
@@ -0,0 +1,9 @@
+https://api.substack.com/feed/podcast/462466.rss +https://lwn.net/headlines/newrss +https://opensource.com/feed +https://www.kernel.org/feeds/all.atom.xml +https://www.phoronix.com/rss.php +https://www.reddit.com/r/linux/.rss +https://www.theregister.com/software/applications/headlines.atom +https://www.theregister.com/software/oses/headlines.atom +https://feed.itsfoss.com/
A
feeds/poland.txt
@@ -0,0 +1,5 @@
+https://tvn24.pl/najwazniejsze.xml +https://www.bankier.pl/rss/finanse.xml +https://www.rmf24.pl/fakty/feed +https://wyborcza.pl/pub/rss/najnowsze_wyborcza.xml +https://www.rp.pl/rss_main
A
generate_magazine.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python3 +""" +Newspaper Magazine Generator +Proof of concept: generates a magazine-style PDF from RSS feeds +""" + +import re +import subprocess +from concurrent.futures import ThreadPoolExecutor +from pathlib import Path + +SCRIPT_DIR = Path(__file__).parent +LLM_AGGREGATOR = SCRIPT_DIR.parent / "llm_aggregator" / "llm_aggregator" +OUTPUT_DIR = SCRIPT_DIR / "output" +TEX_FILE = OUTPUT_DIR / "magazine.tex" +PDF_FILE = OUTPUT_DIR / "magazine.pdf" + +# Strip think tags +STRIP_TAG_START = "<think>" +STRIP_TAG_END = "</think>" +STRIP_TAG_REPLACEMENT = "" + +# Base prompt ending +PROMPT_ENDING = r"""Output in LaTeX format suitable for an editorial section. +Use asterisk section headers (\section*{...}) to prevent numbering. Use the +correct 1st January 2001 date format. Use English; not American dialect (color +-> colour, formalize -> formalise, etc.). Use ``...'' for quotation marks, +\emph{...} for italics and \textbf{...} for bold text. Do not attempt +citations.""" + +CATEGORIES = [ + { + "name": "LINUX", + "icon": r"\faLinux", + "feed": SCRIPT_DIR / "feeds" / "linux.txt", + "prompt": f"""Summarise the latest Linux kernel discussions and + technical developments from the LKML feed. Focus on significant + patches, kernel updates, and developer discussions. {PROMPT_ENDING}""", + }, + { + "name": "FREE SOFTWARE", + "icon": r"\faCogs", + "feed": SCRIPT_DIR / "feeds" / "free_software.txt", + "prompt": f"""Summarise the latest news from the free and open source + software world. Cover new releases, project updates, and community + news. {PROMPT_ENDING}""", + }, + { + "name": r"AI \& SCIENCE", + "icon": r"\faFlask", + "feed": SCRIPT_DIR / "feeds" / "ai_science.txt", + "prompt": f"""Summarise the latest news regarding science and AI. Pay + attention to esoteric and difficult topics, rather than what is likely + to be easy pop-science without substance. {PROMPT_ENDING}""", + }, + { + "name": "POLAND", + "icon": r"\faFlag", + "feed": SCRIPT_DIR / "feeds" / "poland.txt", + "prompt": f"""Summarise today's top news from Poland covering politics, + finance, and current events. Include major stories and their + significance. {PROMPT_ENDING}""", + }, + { + "name": "ESOTERIC", + "icon": r"\faUserSecret", + "feed": SCRIPT_DIR / "feeds" / "esoteric.txt", + "prompt": f"""Summarise news from sources that are not mainstream, + regarding topics not often discussed in the public square. The readers + enjoy this section, so maintain a supportive tone, rather than + accusative. Seek out topics that specifically challenge popular + preconceptions. Do not write disclaimers or observations. + {PROMPT_ENDING}""", + }, +] + + +def strip_tags(content: str) -> str: + """Strip content between (and including) two tags. Leaves placeholder.""" + if STRIP_TAG_START and STRIP_TAG_END: + pattern = f"{re.escape(STRIP_TAG_START)}.*?{re.escape(STRIP_TAG_END)}" + return re.sub(pattern, STRIP_TAG_REPLACEMENT, content, flags=re.DOTALL) + return content + + +def run_llm_aggregator(category: dict) -> dict: + """Run llm_aggregator for a category and return the result.""" + output_file = OUTPUT_DIR / f"{category['name'].lower()}.txt" + + cmd = [ + str(LLM_AGGREGATOR), + "-f", str(category["feed"]), + "-p", category["prompt"], + "-P", + "--output-file", str(output_file), + "-v", + ] + + print(f"📰 Processing: {category['name']}") + result = subprocess.run(cmd, capture_output=True, text=True) + + if result.returncode != 0: + print(f" ❌ Error: {result.stderr}") + raise RuntimeError(f"llm_aggregator failed for {category['name']}") + + content = output_file.read_text() + content = strip_tags(content) + + print(f" ✓ Done: {category['name']}") + return {"name": category["name"], "icon": category["icon"], "content": content} + + +def generate_latex(content_files: list[dict]) -> None: + """Generate the LaTeX document.""" + latex = r"""\documentclass[12pt,a4paper]{article} +\usepackage{fancyhdr, fontawesome, fontspec, graphicx, geometry, sectsty, setspace, parskip} +\usepackage[UKenglish]{isodate} +\setmainfont[Ligatures=TeX]{Liberation Sans} +\geometry{margin=2.5cm} +\sectionfont{\centering\bfseries\scshape} +\subsectionfont{\large\bfseries} +\pagestyle{fancy} +\fancyhf{} +\fancyhead[C]{\today} +\fancyfoot[C]{\thepage} + +\begin{document} +\onehalfspacing + +\vspace*{2cm} +{\LARGE\bfseries The Daily Digest}\\[0.5em] +{\Large \today}\\[2em] +\hrule +\vspace{2em} +""" + + for item in content_files: + name = item["name"] + icon = item["icon"] + content = item["content"] + + latex += f"{{\\centering\\fontsize{{64}}{{12}}{icon}\\par}}\n" + latex += f"\\section*{{{name}}}\n" + latex += content + latex += "\n\n\\vspace{1.5em}\n\n" + + latex += r"\end{document}" + + with open(TEX_FILE, "w") as f: + f.write(latex) + + print(f" Created: {TEX_FILE}") + + +def compile_pdf() -> bool: + """Compile LaTeX to PDF.""" + print() + print("🔄 Compiling PDF...") + + result = subprocess.run( + ["xelatex", "-interaction=batchmode", "magazine.tex"], + cwd=OUTPUT_DIR, + capture_output=True, + ) + + return PDF_FILE.exists() + + +def main(): + OUTPUT_DIR.mkdir(parents=True, exist_ok=True) + + print("🗞️ Newspaper Magazine Generator") + print("===================================") + print() + + # Run all feeds through llm_aggregator in parallel + print("📰 Processing feeds in parallel...") + print() + with ThreadPoolExecutor(max_workers=8) as executor: + content_files = list(executor.map(run_llm_aggregator, CATEGORIES)) + + print() + print("📄 Generating LaTeX magazine...") + print() + + generate_latex(content_files) + print() + + if compile_pdf(): + print("✅ Magazine generated successfully!") + print() + print(f" PDF: {PDF_FILE}") + print() + print("📖 Opening PDF viewer...") + subprocess.run(["xdg-open", str(PDF_FILE)]) + else: + print("❌ PDF compilation failed") + return 1 + + return 0 + + +if __name__ == "__main__": + exit(main())