A mirror of my website's source code.

By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 build.py

View raw Download
text/x-script.python • 11.86 kiB
Python script, Unicode text, UTF-8 text executable
        
            
1
# This code is licensed under the LGPL.
2
# The reason for such is that it allows for the use of it
3
# within proprietry applications.
4
#
5
# To view a copy of the LGPL, please refer to
6
# https://www.gnu.org/licenses/lgpl-3.0.en.html
7
from Renderers import RenderTemplate, RenderMarkdown
8
from sys import argv
9
from shutil import rmtree as DeleteDirectory
10
from os import mkdir as CreateDirectory, listdir as ListDirectory, unlink as DeleteFile
11
from os.path import isfile as IsFile, exists as PathExists
12
from distutils.dir_util import copy_tree as CopyDirectory
13
from datetime import datetime
14
from json import dump as DumpJSON
15
from yaml import safe_load as LoadYML
16
from re import sub as RegReplace
17
from typing import Literal
18
19
def GetCurTime():
20
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
21
22
START_TIME = GetCurTime()
23
24
DEPLOY_BUILD_DIR = {
25
"gh-pages-deploy": "build",
26
"gl-pages-deploy": "public",
27
"cb-pages": "build",
28
} # Separate because this site is built with an action that won't work if they aren't
29
LOCAL_BUILD_DIR = "build"
30
31
PAGES_DEPLOY = argv[1] if len(argv) > 1 else None
32
BUILD_DIRECTORY = DEPLOY_BUILD_DIR[PAGES_DEPLOY] if PAGES_DEPLOY is not None else LOCAL_BUILD_DIR
33
34
PAGES = {
35
"index.html": "index.html",
36
"blog-list.html": "blog/index.html",
37
"blog-feed.rss": "blog/feed.rss",
38
"blog-feed.atom": "blog/feed.atom",
39
"404.html": "404.html",
40
"404.html": "not_found.html",
41
}
42
43
DISALLOWED_SITEMAP = {
44
"404.html",
45
"blog-feed.rss",
46
"blog-feed.atom",
47
"blog-list.html",
48
"index.html",
49
"not_found.html",
50
}
51
52
REDIRECTS = {
53
"link-tree.html": "https://steve0greatness.nekoweb.org/list/link-tree.html", # Old location -> new location
54
"extras.html": "https://steve0greatness.nekoweb.org/extras",
55
"guestbook.html": "https://steve0greatness.atabook.org/",
56
}
57
58
SITEMAP_HREF = "https://steve0greatness.nekoweb.org/"
59
sitemap = [
60
SITEMAP_HREF + "blog/",
61
SITEMAP_HREF,
62
]
63
64
def EscapeHTMLForRSS(HTML: str) -> str:
65
values = {
66
"&": "&", # & is first for a reason, do not change it's location.
67
"<": "&lt;",
68
">": "&gt;",
69
"§": "&#xa7;",
70
}
71
RssHtml = HTML
72
for old, new in values.items():
73
RssHtml = RssHtml.replace(old, new)
74
return RssHtml
75
76
def WipeFinalDir():
77
if not PathExists(BUILD_DIRECTORY):
78
print("Directory didn't existing, creating it...")
79
CreateDirectory(BUILD_DIRECTORY)
80
return
81
print("Directory exists, wiping it...")
82
for item in ListDirectory(BUILD_DIRECTORY):
83
path = BUILD_DIRECTORY + "/" + item
84
if IsFile(path):
85
DeleteFile(path)
86
continue
87
DeleteDirectory(path)
88
89
def PostDateToDateObj(Date):
90
return datetime.strptime(Date, "%Y %b %d")
91
92
def PostSortHelper(Post):
93
return PostDateToDateObj(Post["date"])
94
95
def GetBlogList():
96
print("Grabbing post list")
97
PostSlugs: tuple[tuple[Literal["blog-posts", "drafts"], str], ...] = tuple( ("blog-posts", file) for file in ListDirectory("blog-posts") )
98
if not PAGES_DEPLOY and PathExists("drafts"):
99
PostSlugs = PostSlugs + tuple( ("drafts", file) for file in ListDirectory("drafts") )
100
Posts = []
101
for dir, slug in PostSlugs:
102
if not slug.endswith(".md"):
103
continue
104
print("Grabbing post list blog-posts/%s" % (slug))
105
with open(dir + "/" + slug, encoding="utf-8") as MDFile:
106
RawMD = MDFile.read()
107
PostHTML = RenderMarkdown(RawMD)
108
Item = PostHTML.metadata
109
Item["content"] = PostHTML
110
Item["raw-content"] = RawMD
111
Item["rss-content"] = EscapeHTMLForRSS(PostHTML)
112
Item["rss-post-time"] = PostDateToDateObj(Item["date"]).strftime("%a, %d %b %Y") + " 00:00:00 GMT"
113
Item["atom-post-time"] = PostDateToDateObj(Item["date"]).strftime("%Y-%m-%d") + "T00:00:00Z"
114
Item["opengraph-date"] = PostDateToDateObj(Item["date"]).strftime("%Y-%m-%d")
115
Item["opengraph-update"] = Item["opengraph-date"]
116
Item["atom-update-time"] = Item["atom-post-time"]
117
if "updated" in Item:
118
Item["atom-update-time"] = PostDateToDateObj(Item["updated"]).strftime("%Y-%m-%d") + "T00:00:00Z"
119
Item["opengraph-update"] = PostDateToDateObj(Item["updated"]).strftime("%Y-%m-%d")
120
Item["pathname"] = slug.replace(".md", ".html")
121
Item["plaintext"] = slug.replace(".md", ".txt")
122
Item["origin"] = slug
123
Item["is-draft"] = dir == "drafts"
124
Posts.append(Item)
125
PostsByDate = sorted(Posts, key=PostSortHelper, reverse=True)
126
return PostsByDate
127
128
PostList = []
129
130
131
def ListParseCategory(Obj, depth):
132
html = "<h%d id=\"%s\">%s</h%d>" % (2+depth, Obj["id"], Obj["title"], 2+depth)
133
if "paragraph" in Obj:
134
html += "<p>%s</p>" % Obj["paragraph"]
135
listType = "ul"
136
if "list-type" in Obj and Obj["list-type"] == "ordered":
137
listType = "ol"
138
html += "<%s>" % listType
139
for item in Obj["list"]:
140
html += "<li>" + LIST_PARSER_DICT[item["type"]](item, depth + 1) + "</li>"
141
html += "</%s>" % listType
142
return html
143
144
def ListParseLink(Obj, depth):
145
html = "<a href=\"%s\">" % Obj["href"]
146
text = Obj["text"]
147
if "text-type" in Obj and Obj["text-type"] == "text/markdown":
148
text = RenderMarkdown(text).replace("<p>", "").replace("</p>", "")
149
html += text + "</a>"
150
if "comment" in Obj:
151
html += "(%s)" % Obj["comment"]
152
return html
153
154
def ListParseText(Obj, depth):
155
text = Obj["text"]
156
# if "text-type" in Obj and Obj["text-type"] == "text/markdown":
157
# print(RenderMarkdown(text))
158
# text = RenderMarkdown(text) # this doesn't work???
159
if "comment" in Obj:
160
text += "(%s)" % Obj["comment"]
161
return text
162
163
LIST_PARSER_DICT = {
164
"category": ListParseCategory,
165
"link": ListParseLink,
166
"text": ListParseText,
167
}
168
169
def GetLists():
170
ListSlugs = ListDirectory("lists")
171
Lists = []
172
for slug in ListSlugs:
173
List = {
174
"title": "",
175
"content": "",
176
"filename": slug
177
}
178
with open("lists/" + slug, encoding="utf-8") as ListYML:
179
ListDict = LoadYML(ListYML.read())
180
List["title"] = ListDict["title"]
181
if "paragraph" in ListDict:
182
List["content"] += "<p>%s</p>" % ListDict["paragraph"]
183
List["content"] += "<ul>"
184
for item in ListDict["list"]:
185
List["content"] += "<li>" + LIST_PARSER_DICT[item["type"]](item, 0) + "</li>"
186
List["content"] += "</ul>"
187
Lists.append(List)
188
sitemap.append(SITEMAP_HREF + "list/" + slug)
189
sitemap.append(SITEMAP_HREF + "list/")
190
return Lists
191
192
def RenderPosts():
193
global PostList
194
for post in PostList:
195
Revised = post["updated"] if "updated" in post else False
196
RenderedHTML = RenderTemplate(
197
"blog-post.html",
198
Revised=Revised,
199
Title=post["title"],
200
PostDate=post["date"],
201
Content=post["content"],
202
PostPath=post["pathname"],
203
PlaintextPath=post["plaintext"],
204
IsDraft=post["is-draft"],
205
OpenGraphDate=post["opengraph-date"],
206
post=post,
207
CompileTime=GetCurTime(),
208
SiteCompileTime=START_TIME,
209
ViewName="blog-post.html"
210
)
211
print("Building blog-posts/%s to %s/blog/%s" % (post["origin"], BUILD_DIRECTORY, post["pathname"]))
212
with open(BUILD_DIRECTORY + "/blog/" + post["pathname"], "w", encoding="utf-8") as PostHTMLFile:
213
PostHTMLFile.write(RenderedHTML)
214
print("Copying blog-posts/%s to %s/blog/%s" % (post["origin"], BUILD_DIRECTORY, post["plaintext"]))
215
with open(BUILD_DIRECTORY + "/blog/" + post["plaintext"], "w", encoding="utf-8") as PostHTMLFile:
216
PostHTMLFile.write(post["raw-content"])
217
sitemap.append(SITEMAP_HREF + "blog/" + post["pathname"])
218
219
def RenderPage(PageInput: str, ContentDest: str, AllowSitemap: bool = True, **kwargs):
220
print("Building views/%s to %s/%s" % (PageInput, BUILD_DIRECTORY, ContentDest))
221
if AllowSitemap:
222
sitemap.append(SITEMAP_HREF + ContentDest)
223
with open(BUILD_DIRECTORY + "/" + ContentDest, "w", encoding="utf-8") as DestLocation:
224
DestLocation.write(RenderTemplate(PageInput, **kwargs))
225
226
def CreateJSONFeed():
227
global PostList
228
CreatedJSON = {
229
"version": "https://jsonfeed.org/version/1",
230
"title": "Steve0Greatness' Blog",
231
"home_page_url": "https://steve0greatness.nekoweb.org",
232
"feed_url": "https://steve0greatness.nekoweb.org/blog/feed.rss",
233
"language": "en-US",
234
"favicon": "https://steve0greatness.nekoweb.org/favicon.ico",
235
"description": "A blog by a human being.",
236
"authors": [
237
{
238
"name": "Steve0Greatness",
239
"url": "https://steve0greatness.nekoweb.org"
240
}
241
],
242
"items": []
243
}
244
for post in PostList:
245
CreatedJSON["items"].append({
246
"id": "https://steve0greatness.nekoweb.org/blog/" + post["pathname"],
247
"title": "JSON Feed version 1.1",
248
"icon": "https://steve0greatness.nekoweb.org/favicon.ico",
249
"content_html": post["content"],
250
"date_published": post["atom-post-time"],
251
"date_modified": post["atom-update-time"],
252
"url": "https://steve0greatness.nekoweb.org/blog/" + post["pathname"]
253
})
254
with open(BUILD_DIRECTORY + "/blog/feed.json", "w") as JSONFeedFile:
255
DumpJSON(CreatedJSON, JSONFeedFile)
256
257
def RenderLists():
258
Lists = GetLists()
259
CreateDirectory(BUILD_DIRECTORY + "/list/")
260
ListIndex = "<ul>"
261
for List in Lists:
262
FileLocation = "/list/" + List["filename"].replace(".yml", ".html")
263
Title = List["title"]
264
print("%s -> %s" % ("lists/" + List["filename"], BUILD_DIRECTORY + FileLocation))
265
with open(BUILD_DIRECTORY + FileLocation, "w") as file:
266
file.write(RenderTemplate(
267
"list.html",
268
Content=List["content"],
269
Title=Title,
270
Location=FileLocation,
271
CompileTime=GetCurTime(),
272
SiteCompileTime=START_TIME,
273
ViewName="blog-post.html"
274
))
275
ListIndex += "<li><a href=\"%s\">%s</a></li>" % (FileLocation, Title)
276
ListIndex += "</ul>"
277
print("Building list index")
278
with open(BUILD_DIRECTORY + "/list/index.html", "w") as file:
279
file.write(
280
RenderTemplate(
281
"list-index.html",
282
Content=ListIndex,
283
CompileTime="",
284
SiteCompileTime="",
285
ViewName="list-index.html"
286
)
287
)
288
289
def main():
290
global PostList
291
PostList = GetBlogList()
292
print("Wiping directory")
293
WipeFinalDir()
294
print("Creating blog holder")
295
CreateDirectory(BUILD_DIRECTORY + "/blog")
296
print("Rendering posts")
297
RenderPosts()
298
CreateJSONFeed()
299
print("Copying static directory")
300
CopyDirectory("static", BUILD_DIRECTORY)
301
print("Creating lists")
302
RenderLists()
303
304
print("Building pages")
305
for file, path in PAGES.items():
306
AllowSitemap = file not in DISALLOWED_SITEMAP
307
RenderPage(
308
file,
309
path,
310
AllowSitemap,
311
PostList=PostList,
312
CompileTime=GetCurTime(),
313
SiteCompileTime=START_TIME,
314
ViewName=file
315
)
316
317
print("Building redirects")
318
for OldLocation, NewLocation in REDIRECTS.items():
319
RenderPage("redirect.html", OldLocation, False, redirect=NewLocation, old=OldLocation)
320
321
with open(BUILD_DIRECTORY + "/sitemap.txt", "w") as SitemapFile:
322
SitemapFile.write("\n".join(sitemap))
323
324
if __name__ == "__main__":
325
main()
326
327