<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://yusupov.cloud/index.php?action=history&amp;feed=atom&amp;title=Quidlibet</id>
	<title>Quidlibet - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://yusupov.cloud/index.php?action=history&amp;feed=atom&amp;title=Quidlibet"/>
	<link rel="alternate" type="text/html" href="https://yusupov.cloud/index.php?title=Quidlibet&amp;action=history"/>
	<updated>2026-05-16T11:28:05Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.44.0</generator>
	<entry>
		<id>https://yusupov.cloud/index.php?title=Quidlibet&amp;diff=402&amp;oldid=prev</id>
		<title>Mvuijlst at 13:50, 13 April 2026</title>
		<link rel="alternate" type="text/html" href="https://yusupov.cloud/index.php?title=Quidlibet&amp;diff=402&amp;oldid=prev"/>
		<updated>2026-04-13T13:50:01Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en-GB&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 13:50, 13 April 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l10&quot;&gt;Line 10:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 10:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&#039;&#039;&#039;Quidlibet&#039;&#039;&#039; (Latin for &quot;anything whatsoever&quot;) is a web application hosted at &amp;lt;code&amp;gt;quidlibet.yusupov.cloud&amp;lt;/code&amp;gt; that generates and catalogues entirely fictional books. Each book is a complete literary artefact: a plausible title, a named author with a biographical sketch and portrait, a Markdown-formatted synopsis, an AI-generated cover in genre-appropriate style, a publication date and page count, and between four and eight reader reviews with individually calibrated star ratings. The site presents itself as a browsable library catalogue under the heading &quot;Possible Books,&quot; inviting visitors to enter a title &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;— &lt;/del&gt;and optionally an author and genre &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;— &lt;/del&gt;for &quot;a book that you&#039;d like to read but that doesn&#039;t actually exist.&quot; In addition to on-demand generation, an automated pipeline publishes up to twelve new books per day on a cron schedule.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&#039;&#039;&#039;Quidlibet&#039;&#039;&#039; (Latin for &quot;anything whatsoever&quot;) is a web application hosted at &amp;lt;code&amp;gt;quidlibet.yusupov.cloud&amp;lt;/code&amp;gt; that generates and catalogues entirely fictional books. Each book is a complete literary artefact: a plausible title, a named author with a biographical sketch and portrait, a Markdown-formatted synopsis, an AI-generated cover in genre-appropriate style, a publication date and page count, and between four and eight reader reviews with individually calibrated star ratings. The site presents itself as a browsable library catalogue under the heading &quot;Possible Books,&quot; inviting visitors to enter a title and optionally an author and genre for &quot;a book that you&#039;d like to read but that doesn&#039;t actually exist.&quot; In addition to on-demand generation, an automated pipeline publishes up to twelve new books per day on a cron schedule.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Technology stack ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Technology stack ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key yusupov:diff:1.41:old-401:rev-402:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Mvuijlst</name></author>
	</entry>
	<entry>
		<id>https://yusupov.cloud/index.php?title=Quidlibet&amp;diff=401&amp;oldid=prev</id>
		<title>Mvuijlst at 13:49, 13 April 2026</title>
		<link rel="alternate" type="text/html" href="https://yusupov.cloud/index.php?title=Quidlibet&amp;diff=401&amp;oldid=prev"/>
		<updated>2026-04-13T13:49:12Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en-GB&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 13:49, 13 April 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l10&quot;&gt;Line 10:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 10:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&#039;&#039;&#039;Quidlibet&#039;&#039;&#039; (Latin for &quot;anything whatsoever&quot;) is a web application hosted at &amp;lt;code&amp;gt;quidlibet.yusupov.cloud&amp;lt;/code&amp;gt; that generates and catalogues entirely fictional books. Each book is a complete literary artefact: a plausible title, a named author with a biographical sketch and portrait, a Markdown-formatted synopsis, an AI-generated cover in genre-appropriate style, a publication date and page count, and between four and eight reader reviews with individually calibrated star ratings. The site presents itself as a browsable library catalogue under the heading &quot;Possible Books,&quot; inviting visitors to enter a title and optionally an author and genre for &quot;a book that you&#039;d like to read but that doesn&#039;t actually exist.&quot; In addition to on-demand generation, an automated pipeline publishes up to twelve new books per day on a cron schedule.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&#039;&#039;&#039;Quidlibet&#039;&#039;&#039; (Latin for &quot;anything whatsoever&quot;) is a web application hosted at &amp;lt;code&amp;gt;quidlibet.yusupov.cloud&amp;lt;/code&amp;gt; that generates and catalogues entirely fictional books. Each book is a complete literary artefact: a plausible title, a named author with a biographical sketch and portrait, a Markdown-formatted synopsis, an AI-generated cover in genre-appropriate style, a publication date and page count, and between four and eight reader reviews with individually calibrated star ratings. The site presents itself as a browsable library catalogue under the heading &quot;Possible Books,&quot; inviting visitors to enter a title &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;— &lt;/ins&gt;and optionally an author and genre &lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;— &lt;/ins&gt;for &quot;a book that you&#039;d like to read but that doesn&#039;t actually exist.&quot; In addition to on-demand generation, an automated pipeline publishes up to twelve new books per day on a cron schedule.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Technology stack ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Technology stack ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l40&quot;&gt;Line 40:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 40:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Book generation pipeline ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Book generation pipeline ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Book generation is performed in the main Flask application via a multi-step pipeline of OpenAI API calls. The default text model is GPT-5; the image model is &amp;lt;code&amp;gt;gpt-image-1&amp;lt;/code&amp;gt;.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Book generation is performed in the main Flask application via a multi-step pipeline of OpenAI API calls. The default text model is GPT-5; the image model is &amp;lt;code&amp;gt;gpt-image-1&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;.5&lt;/ins&gt;&amp;lt;/code&amp;gt;.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Title and metadata ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Title and metadata ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l49&quot;&gt;Line 49:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 49:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Before generating a new author, the pipeline queries the database for existing authors whose names are at least 80% similar (by sequence-matching across four name-format permutations). If a match is found — which happens for roughly a quarter of all generated books — the pipeline reuses the existing author record. It supplies the model with a context dossier listing the author&amp;#039;s existing books, genres, and publication dates, and instructs it to revise the biography so that it naturally covers the author&amp;#039;s expanded body of work. A hard constraint requires that any new publication date differ from the author&amp;#039;s existing dates by at least six months. Biographies are post-processed by a helper that detects and corrects any instance of the &amp;quot;Lastname, Firstname&amp;quot; storage format that may have leaked into the prose.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Before generating a new author, the pipeline queries the database for existing authors whose names are at least 80% similar (by sequence-matching across four name-format permutations). If a match is found — which happens for roughly a quarter of all generated books — the pipeline reuses the existing author record. It supplies the model with a context dossier listing the author&amp;#039;s existing books, genres, and publication dates, and instructs it to revise the biography so that it naturally covers the author&amp;#039;s expanded body of work. A hard constraint requires that any new publication date differ from the author&amp;#039;s existing dates by at least six months. Biographies are post-processed by a helper that detects and corrects any instance of the &amp;quot;Lastname, Firstname&amp;quot; storage format that may have leaked into the prose.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;When the model must invent a new author, the prompt includes a dynamically generated blocklist of first and last names that already appear two or more times in the database, preventing the library from accumulating clusters of identically named authors. The blocklist is rebuilt from the live database on every generation run.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Synopses and formatting ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Synopses and formatting ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l64&quot;&gt;Line 64:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 66:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Cover generation ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Cover generation ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Cover images are generated via OpenAI&#039;s &amp;lt;code&amp;gt;gpt-image-1&amp;lt;/code&amp;gt; model at 1024 × 1536 pixels (2:3 portrait aspect). The prompt is tailored to the book&#039;s genre through a weighted style-selection system: cookbooks receive an eightfold bias toward photographic covers; graphic novels toward vector/graphic styles; science fiction, fantasy, and horror toward illustration; biography and history toward photography; and romance toward an even mix. The prompt specifies the exact title and author name as cover typography and bans watermarks. Generated images are decoded from base64, converted to optimised progressive JPEG via Pillow (quality 92), and saved as &amp;lt;code&amp;gt;book_{id}.jpg&amp;lt;/code&amp;gt;. The pipeline allows two attempts; if both fail, the entire book-generation transaction is rolled back so that no coverless book can enter the database.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;Cover images are generated via OpenAI&#039;s &amp;lt;code&amp;gt;gpt-image-1&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;.5&lt;/ins&gt;&amp;lt;/code&amp;gt; model at 1024 × 1536 pixels (2:3 portrait aspect). The prompt is tailored to the book&#039;s genre through a weighted style-selection system: cookbooks receive an eightfold bias toward photographic covers; graphic novels toward vector/graphic styles; science fiction, fantasy, and horror toward illustration; biography and history toward photography; and romance toward an even mix. The prompt specifies the exact title and author name as cover typography and bans watermarks. Generated images are decoded from base64, converted to optimised progressive JPEG via Pillow (quality 92), and saved as &amp;lt;code&amp;gt;book_{id}.jpg&amp;lt;/code&amp;gt;. The pipeline allows two attempts; if both fail, the entire book-generation transaction is rolled back so that no coverless book can enter the database.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Author-photo generation ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Author-photo generation ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;After a successful cover, the pipeline checks whether the author already has a portrait. If not, it sends a prompt to &amp;lt;code&amp;gt;gpt-image-1&amp;lt;/code&amp;gt; for a photorealistic head-and-shoulders portrait at 1024 × 1024 pixels. The result is centre-cropped to a 512 × 512 square JPEG and saved as &amp;lt;code&amp;gt;author_{id}.jpg&amp;lt;/code&amp;gt;. Unlike cover generation, photo generation is non-blocking: failures are logged but do not abort the job. In the interface, authors without photos receive a CSS-generated avatar showing their initials.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;After a successful cover, the pipeline checks whether the author already has a portrait. If not, it sends a prompt to &amp;lt;code&amp;gt;gpt-image-1&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;.5&lt;/ins&gt;&amp;lt;/code&amp;gt; for a photorealistic head-and-shoulders portrait at 1024 × 1024 pixels. The result is centre-cropped to a 512 × 512 square JPEG and saved as &amp;lt;code&amp;gt;author_{id}.jpg&amp;lt;/code&amp;gt;. Unlike cover generation, photo generation is non-blocking: failures are logged but do not abort the job. In the interface, authors without photos receive a CSS-generated avatar showing their initials.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Error handling and transaction safety ===&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;=== Error handling and transaction safety ===&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l91&quot;&gt;Line 91:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 93:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# &amp;#039;&amp;#039;&amp;#039;Title memory.&amp;#039;&amp;#039;&amp;#039; Accepted titles are appended to the rolling memory file (capped at 2,000 entries, oldest dropped first) and checked against the database to prevent exact duplicates.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# &amp;#039;&amp;#039;&amp;#039;Title memory.&amp;#039;&amp;#039;&amp;#039; Accepted titles are appended to the rolling memory file (capped at 2,000 entries, oldest dropped first) and checked against the database to prevent exact duplicates.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# &amp;#039;&amp;#039;&amp;#039;Database deduplication.&amp;#039;&amp;#039;&amp;#039; Each final title is compared case-insensitively to every existing book in the database; duplicates are silently skipped.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;# &amp;#039;&amp;#039;&amp;#039;Database deduplication.&amp;#039;&amp;#039;&amp;#039; Each final title is compared case-insensitively to every existing book in the database; duplicates are silently skipped.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-side-deleted&quot;&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&lt;ins style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;A parallel system applies to author names. The prompt includes a dynamically built blocklist of first and last names that already appear two or more times across the library&#039;s author table, and requires that no two authors within a single batch share a first name. The local fallback generator (used when the OpenAI API is unavailable) enforces the same within-batch uniqueness constraint on first names.&lt;/ins&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;The output is written to &amp;lt;code&amp;gt;daily_books.json&amp;lt;/code&amp;gt;, a flat array of title/author/genre objects with generation timestamps.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;The output is written to &amp;lt;code&amp;gt;daily_books.json&amp;lt;/code&amp;gt;, a flat array of title/author/genre objects with generation timestamps.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key yusupov:diff:1.41:old-400:rev-401:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Mvuijlst</name></author>
	</entry>
	<entry>
		<id>https://yusupov.cloud/index.php?title=Quidlibet&amp;diff=400&amp;oldid=prev</id>
		<title>Mvuijlst at 13:24, 13 April 2026</title>
		<link rel="alternate" type="text/html" href="https://yusupov.cloud/index.php?title=Quidlibet&amp;diff=400&amp;oldid=prev"/>
		<updated>2026-04-13T13:24:04Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en-GB&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 13:24, 13 April 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot; id=&quot;mw-diff-left-l10&quot;&gt;Line 10:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 10:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}}&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;}}&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;−&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&#039;&#039;&#039;Quidlibet&#039;&#039;&#039; (Latin for &quot;anything whatsoever&quot;) is a web application hosted at &amp;lt;code&amp;gt;quidlibet.yusupov.cloud&amp;lt;/code&amp;gt; that generates and catalogues entirely fictional books. Each book is a complete literary artefact: a plausible title, a named author with a biographical sketch and portrait, a Markdown-formatted synopsis, an AI-generated cover in genre-appropriate style, a publication date and page count, and between four and eight reader reviews with individually calibrated star ratings. The site presents itself as a browsable library catalogue under the heading &quot;Possible Books,&quot; inviting visitors to enter a title &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;— &lt;/del&gt;and optionally an author and genre &lt;del style=&quot;font-weight: bold; text-decoration: none;&quot;&gt;— &lt;/del&gt;for &quot;a book that you&#039;d like to read but that doesn&#039;t actually exist.&quot; In addition to on-demand generation, an automated pipeline publishes up to twelve new books per day on a cron schedule.&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;&lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;&#039;&#039;&#039;Quidlibet&#039;&#039;&#039; (Latin for &quot;anything whatsoever&quot;) is a web application hosted at &amp;lt;code&amp;gt;quidlibet.yusupov.cloud&amp;lt;/code&amp;gt; that generates and catalogues entirely fictional books. Each book is a complete literary artefact: a plausible title, a named author with a biographical sketch and portrait, a Markdown-formatted synopsis, an AI-generated cover in genre-appropriate style, a publication date and page count, and between four and eight reader reviews with individually calibrated star ratings. The site presents itself as a browsable library catalogue under the heading &quot;Possible Books,&quot; inviting visitors to enter a title and optionally an author and genre for &quot;a book that you&#039;d like to read but that doesn&#039;t actually exist.&quot; In addition to on-demand generation, an automated pipeline publishes up to twelve new books per day on a cron schedule.&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Technology stack ==&lt;/div&gt;&lt;/td&gt;&lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;&lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;== Technology stack ==&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;

&lt;!-- diff cache key yusupov:diff:1.41:old-399:rev-400:php=table --&gt;
&lt;/table&gt;</summary>
		<author><name>Mvuijlst</name></author>
	</entry>
	<entry>
		<id>https://yusupov.cloud/index.php?title=Quidlibet&amp;diff=399&amp;oldid=prev</id>
		<title>Mvuijlst: Created page with &quot;{{Infobox | 01_name         = Quidlibet | 02_url          = https://quidlibet.yusupov.cloud | 03_developer    = Michel Vuijlsteke | 04_released     = 2025 | 05_genre        = AI-generated fictional book library | 06_language     = Python | 07_framework    = Flask 3.0 | 08_license      = Proprietary }}  &#039;&#039;&#039;Quidlibet&#039;&#039;&#039; (Latin for &quot;anything whatsoever&quot;) is a web application hosted at &lt;code&gt;quidlibet.yusupov.cloud&lt;/code&gt; that generates and catalogues entirely fictional...&quot;</title>
		<link rel="alternate" type="text/html" href="https://yusupov.cloud/index.php?title=Quidlibet&amp;diff=399&amp;oldid=prev"/>
		<updated>2026-04-13T13:15:04Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;{{Infobox | 01_name         = Quidlibet | 02_url          = https://quidlibet.yusupov.cloud | 03_developer    = Michel Vuijlsteke | 04_released     = 2025 | 05_genre        = AI-generated fictional book library | 06_language     = Python | 07_framework    = &lt;a href=&quot;/index.php?title=Flask&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;Flask (page does not exist)&quot;&gt;Flask&lt;/a&gt; 3.0 | 08_license      = Proprietary }}  &amp;#039;&amp;#039;&amp;#039;Quidlibet&amp;#039;&amp;#039;&amp;#039; (Latin for &amp;quot;anything whatsoever&amp;quot;) is a web application hosted at &amp;lt;code&amp;gt;quidlibet.yusupov.cloud&amp;lt;/code&amp;gt; that generates and catalogues entirely fictional...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Infobox&lt;br /&gt;
| 01_name         = Quidlibet&lt;br /&gt;
| 02_url          = https://quidlibet.yusupov.cloud&lt;br /&gt;
| 03_developer    = Michel Vuijlsteke&lt;br /&gt;
| 04_released     = 2025&lt;br /&gt;
| 05_genre        = AI-generated fictional book library&lt;br /&gt;
| 06_language     = Python&lt;br /&gt;
| 07_framework    = [[Flask]] 3.0&lt;br /&gt;
| 08_license      = Proprietary&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Quidlibet&amp;#039;&amp;#039;&amp;#039; (Latin for &amp;quot;anything whatsoever&amp;quot;) is a web application hosted at &amp;lt;code&amp;gt;quidlibet.yusupov.cloud&amp;lt;/code&amp;gt; that generates and catalogues entirely fictional books. Each book is a complete literary artefact: a plausible title, a named author with a biographical sketch and portrait, a Markdown-formatted synopsis, an AI-generated cover in genre-appropriate style, a publication date and page count, and between four and eight reader reviews with individually calibrated star ratings. The site presents itself as a browsable library catalogue under the heading &amp;quot;Possible Books,&amp;quot; inviting visitors to enter a title — and optionally an author and genre — for &amp;quot;a book that you&amp;#039;d like to read but that doesn&amp;#039;t actually exist.&amp;quot; In addition to on-demand generation, an automated pipeline publishes up to twelve new books per day on a cron schedule.&lt;br /&gt;
&lt;br /&gt;
== Technology stack ==&lt;br /&gt;
&lt;br /&gt;
The application is built on Flask 3.0 with Flask-SQLAlchemy as its ORM and Flask-Login for authentication.&amp;lt;ref name=&amp;quot;requirements&amp;quot;&amp;gt;requirements.txt in the project repository lists Flask 3.0.3, Flask-SQLAlchemy 3.1.1, Flask-Login 0.6.3, and Flask-Caching 2.3.0.&amp;lt;/ref&amp;gt; It uses [[SQLite]] as its database and is deployed behind [[Nginx]] on a Linux VPS. Additional dependencies include [[Pillow (imaging library)|Pillow]] for image processing, the Python &amp;lt;code&amp;gt;markdown&amp;lt;/code&amp;gt; library for rich-text rendering, [[Beautiful Soup (HTML parser)|Beautiful Soup]] for web scraping, &amp;lt;code&amp;gt;python-dotenv&amp;lt;/code&amp;gt; for configuration, and the [[OpenAI]] Python client for all language-model and image-generation calls. The front end uses Bootstrap 5.3 with a custom CSS layer, Google Fonts (Roboto Serif, Roboto Slab, Roboto), and Bootstrap Icons. The site is installable as a [[progressive web application]] via a Web App Manifest and a minimal service worker.&lt;br /&gt;
&lt;br /&gt;
== Data model ==&lt;br /&gt;
&lt;br /&gt;
=== Books ===&lt;br /&gt;
&lt;br /&gt;
Each book record carries a &amp;#039;&amp;#039;title&amp;#039;&amp;#039;, a foreign key to an &amp;#039;&amp;#039;Author&amp;#039;&amp;#039;, a foreign key to a &amp;#039;&amp;#039;Genre&amp;#039;&amp;#039; (with a legacy genre-name string for backward compatibility), a Markdown &amp;#039;&amp;#039;synopsis&amp;#039;&amp;#039;, a &amp;#039;&amp;#039;num_pages&amp;#039;&amp;#039; integer, an ISO 8601 &amp;#039;&amp;#039;publication_date&amp;#039;&amp;#039;, a &amp;#039;&amp;#039;cover_image&amp;#039;&amp;#039; path, an &amp;#039;&amp;#039;avg_rating&amp;#039;&amp;#039; and &amp;#039;&amp;#039;rating_count&amp;#039;&amp;#039; (derived from reviews), and the &amp;#039;&amp;#039;ip_address&amp;#039;&amp;#039; and timestamp of the generation request. Books are addressed by URL slug in the form &amp;lt;code&amp;gt;/book/{id}-{title-slug}&amp;lt;/code&amp;gt;, where the leading integer guarantees uniqueness and the slug provides readability.&lt;br /&gt;
&lt;br /&gt;
=== Authors ===&lt;br /&gt;
&lt;br /&gt;
Authors are stored with a &amp;#039;&amp;#039;name&amp;#039;&amp;#039; in &amp;quot;Lastname, Firstname&amp;quot; format (rendered as &amp;quot;Firstname Lastname&amp;quot; in the interface via a Jinja display-name filter), a Markdown-formatted &amp;#039;&amp;#039;bio&amp;#039;&amp;#039;, and an optional &amp;#039;&amp;#039;author_image&amp;#039;&amp;#039; path. Author names are matched case-insensitively using Python&amp;#039;s &amp;lt;code&amp;gt;SequenceMatcher&amp;lt;/code&amp;gt; with a similarity threshold, so that minor variations do not create duplicate records.&lt;br /&gt;
&lt;br /&gt;
=== Genres ===&lt;br /&gt;
&lt;br /&gt;
The &amp;#039;&amp;#039;Genre&amp;#039;&amp;#039; model supports hierarchical categorisation via a self-referential &amp;#039;&amp;#039;parent_id&amp;#039;&amp;#039; foreign key. Each genre has a &amp;#039;&amp;#039;name&amp;#039;&amp;#039;, an optional &amp;#039;&amp;#039;description&amp;#039;&amp;#039;, a hex &amp;#039;&amp;#039;color&amp;#039;&amp;#039; code for UI tags, an &amp;#039;&amp;#039;icon&amp;#039;&amp;#039; class, a &amp;#039;&amp;#039;display_order&amp;#039;&amp;#039;, and an &amp;#039;&amp;#039;is_active&amp;#039;&amp;#039; flag.&lt;br /&gt;
&lt;br /&gt;
=== Reviews ===&lt;br /&gt;
&lt;br /&gt;
Each review belongs to a book and carries a &amp;#039;&amp;#039;reviewer_name&amp;#039;&amp;#039;, a &amp;#039;&amp;#039;review_date&amp;#039;&amp;#039; (in-universe), Markdown &amp;#039;&amp;#039;review_text&amp;#039;&amp;#039;, and a &amp;#039;&amp;#039;stars&amp;#039;&amp;#039; rating between one and five. Reviews are cascade-deleted when their parent book is removed.&lt;br /&gt;
&lt;br /&gt;
=== Supporting models ===&lt;br /&gt;
&lt;br /&gt;
A &amp;#039;&amp;#039;GenerationJob&amp;#039;&amp;#039; tracks the asynchronous lifecycle of each generation request (pending → processing → completed or failed), with a &amp;#039;&amp;#039;progress_message&amp;#039;&amp;#039; field polled by the front end. A &amp;#039;&amp;#039;GenerationLog&amp;#039;&amp;#039; provides an audit trail of every generation attempt — successful or otherwise — including the model used, the API surface called, and any error message. A &amp;#039;&amp;#039;GenerationQueueItem&amp;#039;&amp;#039; implements a database-backed seed queue as an alternative to the file-based daily queue.&lt;br /&gt;
&lt;br /&gt;
== Book generation pipeline ==&lt;br /&gt;
&lt;br /&gt;
Book generation is performed in the main Flask application via a multi-step pipeline of OpenAI API calls. The default text model is GPT-5; the image model is &amp;lt;code&amp;gt;gpt-image-1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Title and metadata ===&lt;br /&gt;
&lt;br /&gt;
When a visitor submits a title (and optionally an author and genre), the application creates a &amp;#039;&amp;#039;GenerationJob&amp;#039;&amp;#039; and redirects to a status page that polls for progress. On the status page, while the visitor waits, a rotating display of faux-library-science progress messages — &amp;quot;Reticulating splines…,&amp;quot; &amp;quot;Reconciling author names against authority files…,&amp;quot; &amp;quot;De-duplicating near-identical editions and printings…,&amp;quot; &amp;quot;Calibrating star ratings to review sentiment…&amp;quot; — plays at random intervals for entertainment. Behind the scenes, the pipeline sends a structured prompt to the text model asking it to return a JSON object with the book&amp;#039;s synopsis, page count, publication date, author name (invented if not supplied), and an author biography. The prompt instructs the model to write the synopsis in Markdown with bold and italic formatting, to keep it under two paragraphs of 120 words each, and to anchor it with concrete names, places, and objects rather than vague abstractions.&lt;br /&gt;
&lt;br /&gt;
=== Author reuse and continuity ===&lt;br /&gt;
&lt;br /&gt;
Before generating a new author, the pipeline queries the database for existing authors whose names are at least 80% similar (by sequence-matching across four name-format permutations). If a match is found — which happens for roughly a quarter of all generated books — the pipeline reuses the existing author record. It supplies the model with a context dossier listing the author&amp;#039;s existing books, genres, and publication dates, and instructs it to revise the biography so that it naturally covers the author&amp;#039;s expanded body of work. A hard constraint requires that any new publication date differ from the author&amp;#039;s existing dates by at least six months. Biographies are post-processed by a helper that detects and corrects any instance of the &amp;quot;Lastname, Firstname&amp;quot; storage format that may have leaked into the prose.&lt;br /&gt;
&lt;br /&gt;
=== Synopses and formatting ===&lt;br /&gt;
&lt;br /&gt;
Author biographies are written in Markdown: the author&amp;#039;s name is &amp;#039;&amp;#039;&amp;#039;bold&amp;#039;&amp;#039;&amp;#039; on first mention and for notable awards, book titles are &amp;#039;&amp;#039;italicised&amp;#039;&amp;#039;, and paragraphs are separated by blank lines. Synopses follow similar conventions. All Markdown content is rendered through a Jinja filter backed by the Python &amp;lt;code&amp;gt;markdown&amp;lt;/code&amp;gt; library (with extensions for line breaks, fenced code, and tables). On grid pages where biographies appear as truncated previews, the Markdown is first rendered to HTML, then stripped of tags, so that formatting tokens do not leak into the plaintext snippet.&lt;br /&gt;
&lt;br /&gt;
=== Review generation and rating profiles ===&lt;br /&gt;
&lt;br /&gt;
Reviews are generated in a separate API call. Before prompting, the pipeline constructs a deterministic &amp;#039;&amp;#039;rating profile&amp;#039;&amp;#039; by seeding a random number generator with the SHA-256 hash of the book&amp;#039;s title, synopsis, and publication date. This seed selects one of four rating clusters — low (18% probability, mean 2.2–3.0), mid (34%, mean 3.2–3.9), high (26%, mean 4.0–4.6), or polarised (22%, mean 3.0–3.8 with high spread) — and derives a target mean, standard deviation, skew direction, and a rant count (zero to two extended reviews of either one–two or five stars).&lt;br /&gt;
&lt;br /&gt;
The model is asked to produce four to eight reviews, each assigned a distinct &amp;#039;&amp;#039;stylistic palette&amp;#039;&amp;#039; from a rotating set of eight: crisp capsule (one or two punchy sentences), craft critique (prose, structure, pacing), character study (interiority, dialogue, motives), worldbuilding lens (setting, atmosphere, rules), theme tracer (motifs and subtext), comparative take (comparisons to two non-celebrity authors), sceptic&amp;#039;s ledger (bullet-point pros and cons), and librarian angle (audience notes). Each review also rotates through a primary focus — characters, plot, prose, world, themes, or audience — so that no two reviews in a set read the same way. Review dates must fall between the book&amp;#039;s publication date and the present day. The prompt bans stock phrases (&amp;quot;page-turner,&amp;quot; &amp;quot;unputdownable&amp;quot;) and requires diverse, plausible reviewer names.&lt;br /&gt;
&lt;br /&gt;
After creation, the pipeline calculates the book&amp;#039;s average rating and review count directly from the database and stores them on the book record.&lt;br /&gt;
&lt;br /&gt;
=== Cover generation ===&lt;br /&gt;
&lt;br /&gt;
Cover images are generated via OpenAI&amp;#039;s &amp;lt;code&amp;gt;gpt-image-1&amp;lt;/code&amp;gt; model at 1024 × 1536 pixels (2:3 portrait aspect). The prompt is tailored to the book&amp;#039;s genre through a weighted style-selection system: cookbooks receive an eightfold bias toward photographic covers; graphic novels toward vector/graphic styles; science fiction, fantasy, and horror toward illustration; biography and history toward photography; and romance toward an even mix. The prompt specifies the exact title and author name as cover typography and bans watermarks. Generated images are decoded from base64, converted to optimised progressive JPEG via Pillow (quality 92), and saved as &amp;lt;code&amp;gt;book_{id}.jpg&amp;lt;/code&amp;gt;. The pipeline allows two attempts; if both fail, the entire book-generation transaction is rolled back so that no coverless book can enter the database.&lt;br /&gt;
&lt;br /&gt;
=== Author-photo generation ===&lt;br /&gt;
&lt;br /&gt;
After a successful cover, the pipeline checks whether the author already has a portrait. If not, it sends a prompt to &amp;lt;code&amp;gt;gpt-image-1&amp;lt;/code&amp;gt; for a photorealistic head-and-shoulders portrait at 1024 × 1024 pixels. The result is centre-cropped to a 512 × 512 square JPEG and saved as &amp;lt;code&amp;gt;author_{id}.jpg&amp;lt;/code&amp;gt;. Unlike cover generation, photo generation is non-blocking: failures are logged but do not abort the job. In the interface, authors without photos receive a CSS-generated avatar showing their initials.&lt;br /&gt;
&lt;br /&gt;
=== Error handling and transaction safety ===&lt;br /&gt;
&lt;br /&gt;
If any exception occurs during book creation, review insertion, or image generation, the pipeline issues an immediate &amp;lt;code&amp;gt;db.session.rollback()&amp;lt;/code&amp;gt; to discard all uncommitted data. The &amp;#039;&amp;#039;GenerationJob&amp;#039;&amp;#039; and &amp;#039;&amp;#039;GenerationLog&amp;#039;&amp;#039; records — committed in an earlier transaction — survive, so the failure is auditable. The job is marked as failed with a descriptive error message. In templates, all cover &amp;lt;code&amp;gt;&amp;amp;lt;img&amp;amp;gt;&amp;lt;/code&amp;gt; tags include an &amp;lt;code&amp;gt;onerror&amp;lt;/code&amp;gt; handler that replaces a broken image with a CSS gradient placeholder bearing the book&amp;#039;s title, as a defence against stale database references to missing files.&lt;br /&gt;
&lt;br /&gt;
== Daily batch generation ==&lt;br /&gt;
&lt;br /&gt;
A standalone script, &amp;lt;code&amp;gt;fast_generator.py&amp;lt;/code&amp;gt;, generates twelve book seeds (title, author, genre) in a single GPT-5 API call lasting roughly twelve seconds. The batch pipeline incorporates several layers of anti-repetition machinery.&lt;br /&gt;
&lt;br /&gt;
=== Goodreads title inspiration ===&lt;br /&gt;
&lt;br /&gt;
Before prompting, the script scrapes two to three randomly chosen genre pages on [[Goodreads]] (from a pool of eight: historical fiction, history, fantasy, science fiction, romance, cookbooks, general fiction, and horror). It extracts book titles from &amp;lt;code&amp;gt;div.bookBox img[alt]&amp;lt;/code&amp;gt; elements, cleans them of series numbers, author suffixes, and emoji, and presents a sample as &amp;quot;inspiration titles — for tone and flavour only, not to be reproduced.&amp;quot; If Goodreads is unreachable, the script falls back to a curated JSON file of titles harvested from the [[Internet Archive]] and [[Open Library]].&lt;br /&gt;
&lt;br /&gt;
=== Anti-sameness system ===&lt;br /&gt;
&lt;br /&gt;
To prevent the model from gravitating toward a narrow band of title shapes, the script enforces structural diversity through five mechanisms:&lt;br /&gt;
&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Title-class rotation.&amp;#039;&amp;#039;&amp;#039; Ten structural classes — concrete object, place or institution, named person, event or incident, documentary phrase, odd juxtaposition, fragment or question, subtitle-led, idiomatic phrase, and one-word punch — are allocated evenly across the batch so that each slot has a designated shape.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Avoidance guidance.&amp;#039;&amp;#039;&amp;#039; The script analyses a rolling memory of up to 2,000 previously generated titles (persisted in a JSON file) plus all titles already in the database. It identifies the twenty most overused content words, the most common title frames (e.g., &amp;quot;The Last ___,&amp;quot; &amp;quot;Beyond the ___&amp;quot;), the most frequent opening words, and the most frequent closing nouns, and encodes these as explicit &amp;quot;do not use&amp;quot; directives in the prompt.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Batch validator.&amp;#039;&amp;#039;&amp;#039; After generation, each title in the batch is scored for repetitiveness: collisions on first word, end noun, or structural frame within the batch incur penalties of 0.5–1.5 points; matches against historical overuse patterns add further penalties; and two-word titles composed entirely of generic mood words score a 2.0 penalty. Titles exceeding a cumulative penalty of 3.0 are rejected and regenerated.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Title memory.&amp;#039;&amp;#039;&amp;#039; Accepted titles are appended to the rolling memory file (capped at 2,000 entries, oldest dropped first) and checked against the database to prevent exact duplicates.&lt;br /&gt;
# &amp;#039;&amp;#039;&amp;#039;Database deduplication.&amp;#039;&amp;#039;&amp;#039; Each final title is compared case-insensitively to every existing book in the database; duplicates are silently skipped.&lt;br /&gt;
&lt;br /&gt;
The output is written to &amp;lt;code&amp;gt;daily_books.json&amp;lt;/code&amp;gt;, a flat array of title/author/genre objects with generation timestamps.&lt;br /&gt;
&lt;br /&gt;
=== Hourly processing ===&lt;br /&gt;
&lt;br /&gt;
A companion script, &amp;lt;code&amp;gt;simple_hourly_processor.py&amp;lt;/code&amp;gt;, runs once per hour via cron. It selects one book from the daily JSON file — indexed by a hash of the current date and hour — and triggers its full generation (metadata, reviews, cover, author photo) by calling the application&amp;#039;s &amp;lt;code&amp;gt;/cron/hourly_book_full&amp;lt;/code&amp;gt; endpoint. If all twelve daily seeds have already been processed, the hour is skipped. In production, a typical crontab runs the batch generator daily at midnight and the hourly processor every other hour, yielding up to twelve new books per day.&lt;br /&gt;
&lt;br /&gt;
== Cron security ==&lt;br /&gt;
&lt;br /&gt;
Cron endpoints are protected by a localhost check: the request&amp;#039;s client IP must be &amp;lt;code&amp;gt;127.0.0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;::1&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;localhost&amp;lt;/code&amp;gt;. For deployments where the cron job calls the public URL rather than the loopback address, two overrides are available: an &amp;lt;code&amp;gt;X-API-Key&amp;lt;/code&amp;gt; header (or &amp;lt;code&amp;gt;api_key&amp;lt;/code&amp;gt; query parameter) checked against an environment variable, or a global &amp;lt;code&amp;gt;DISABLE_CRON_LOCALHOST_CHECK&amp;lt;/code&amp;gt; flag.&lt;br /&gt;
&lt;br /&gt;
== Public interface ==&lt;br /&gt;
&lt;br /&gt;
=== Home page ===&lt;br /&gt;
&lt;br /&gt;
The home page displays a hero section with the title &amp;quot;Possible Books,&amp;quot; a rotating tagline (changed every thirty minutes, seeded by the current UTC half-hour), and a generation form with an autocomplete datalist of existing authors. Below the form, a grid of the twelve most recent books (with cover images or gradient fallbacks) appears alongside the twenty-five most recent reviews, sorted by review date.&lt;br /&gt;
&lt;br /&gt;
=== Book pages ===&lt;br /&gt;
&lt;br /&gt;
Each book page shows the cover image (or fallback), title, author byline linked to the author&amp;#039;s page, genre tag, page count, publication date, and average star rating. The synopsis and author biography are rendered from Markdown. Below, reviews are listed with reviewer name, star display (filled and hollow star characters), review date, and Markdown-formatted review text. Administrators see additional controls: edit book, regenerate cover, delete book, regenerate reviews, and recalculate rating.&lt;br /&gt;
&lt;br /&gt;
=== Author pages ===&lt;br /&gt;
&lt;br /&gt;
The author page displays the portrait (or a CSS initials placeholder), the full Markdown biography, and a grid of the author&amp;#039;s books. The authors index lists all authors sorted alphabetically by surname, with a plaintext preview of each biography (Markdown rendered, tags stripped, truncated to 100 characters).&lt;br /&gt;
&lt;br /&gt;
=== Archive ===&lt;br /&gt;
&lt;br /&gt;
The archive page presents a paginated, filterable grid of all books. A sidebar offers filters by publication-year range (dynamically bucketed), minimum average rating (one to five stars), and genre (checkboxes, grouped by popularity). Filters are applied via form auto-submission on change; an explicit &amp;quot;clear filters&amp;quot; link resets the view.&lt;br /&gt;
&lt;br /&gt;
=== Genres ===&lt;br /&gt;
&lt;br /&gt;
The genres page lists all active genres alphabetically with book counts.&lt;br /&gt;
&lt;br /&gt;
=== Theme ===&lt;br /&gt;
&lt;br /&gt;
The site defaults to a dark colour scheme (&amp;lt;code&amp;gt;#111315&amp;lt;/code&amp;gt; background, &amp;lt;code&amp;gt;#e6e6e6&amp;lt;/code&amp;gt; text) and supports a light mode toggled via a moon-icon button in the masthead, persisted in &amp;lt;code&amp;gt;localStorage&amp;lt;/code&amp;gt;. The toggle respects the operating system&amp;#039;s &amp;lt;code&amp;gt;prefers-color-scheme&amp;lt;/code&amp;gt; setting as a default. Typography uses Roboto Serif for display headings and Roboto for body text and UI elements.&lt;br /&gt;
&lt;br /&gt;
== Progressive web application ==&lt;br /&gt;
&lt;br /&gt;
The site ships a Web App Manifest declaring standalone display mode, dark background and theme colours, and maskable icons at 192 and 512 pixels. A service worker registers at the root scope but operates in passthrough mode — all fetch events are returned unmodified — to satisfy PWA installability requirements without risking stale cached pages in a database-backed application that publishes new content hourly.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Procedural generation]]&lt;br /&gt;
* [[Flask (web framework)]]&lt;br /&gt;
* [[OpenAI]]&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
{{reflist}}&lt;/div&gt;</summary>
		<author><name>Mvuijlst</name></author>
	</entry>
</feed>