about summary refs log tree commit diff stats
path: root/sfeed_html.awk
diff options
context:
space:
mode:
Diffstat (limited to 'sfeed_html.awk')
-rwxr-xr-xsfeed_html.awk341
1 files changed, 341 insertions, 0 deletions
diff --git a/sfeed_html.awk b/sfeed_html.awk new file mode 100755 index 0000000..8477677 --- /dev/null +++ b/sfeed_html.awk
@@ -0,0 +1,341 @@
1#!/bin/awk -f
2# Convert sfeed(1) formatted files into an HTML webpage.
3# Usage: sfeed_html.awk -- FILES...
4
5## Set up variables
6BEGIN {
7 # Sidebar
8 if (! ASIDE) {
9 ASIDE = envor("SFEED_HTML_ASIDE", "/tmp/sfeed-aside.html")
10 }
11 print > ASIDE # clear out ASIDE
12 in_aside = 0 # Don't show the feed in the sidebar
13 # Formatting
14 if (! DATEFMT) {
15 DATEFMT = envor("SFEED_HTML_DATEFMT", "<span class=date>%F</span> <span class=time>%RZ</span>")
16 }
17 if (! TITLE) {
18 TITLE = envor("SFEED_HTML_TITLE", "Planet ACDW")
19 }
20 if (! SEPARATOR) {
21 SEPARATOR = envor("SFEED_HTML_SEPARATOR", "//")
22 }
23 if (! ALT_LINK_STAMP) {
24 ALT_LINK_STAMP = envor("SFEED_ALT_LINK_STAMP", "&")
25 }
26 if (! ENCLOSURE_STAMP) {
27 ENCLOSURE_STAMP = envor("SFEED_ENCLOSURE_STAMP", "@")
28 }
29 if (! SILO_STAMP) {
30 SILO_STAMP = envor("SFEED_SILO_STAMP", "%")
31 }
32 # Limiting posts...
33 ## by time
34 if (! NOW) {
35 datecmd = "date +%s"
36 datecmd | getline NOW
37 close(datecmd)
38 }
39 if (! FRESHDAYS) {
40 FRESHDAYS = envor("SFEED_FRESHDAYS", 1.5)
41 }
42 if (! STALEDAYS) {
43 STALEDAYS = envor("SFEED_STALEDAYS", 4)
44 }
45 fresh_secs = FRESHDAYS * 24 * 60 * 60
46 stale_secs = STALEDAYS * 24 * 60 * 60
47 fresh_age = (NOW - fresh_secs)
48 stale_age = (NOW - stale_secs)
49 ## by number
50 if (! LIMIT) {
51 # If LIMIT == -1, ignore time limit as well.
52 LIMIT = envor("SFEED_LIMIT", 20)
53 }
54 # Alternate URLs for siloed content
55 if (! YOUTUBE_ALT_URL) {
56 YOUTUBE_ALT_URL = envor("SFEED_YOUTUBE_ALT_URL", "https://piped.kavin.rocks")
57 }
58 if (! TWITTER_ALT_URL) {
59 TWITTER_ALT_URL = envor("SFEED_TWITTER_ALT_URL", "https://nitter.net")
60 }
61 if (! REDDIT_ALT_URL) {
62 REDDIT_ALT_URL = envor("SFEED_REDDIT_ALT_URL", "https://libreddit.spike.codes")
63 }
64 # Awk and convenience constants
65 FS = "\t"
66 STDERR = "/dev/stderr"
67}
68
69## Print HTML header
70BEGIN {
71 fortunecmd = "fortune"
72 fortunecmd | getline LOGO_TITLE
73 close(fortunecmd)
74 sub(/"/, "", LOGO_TITLE)
75 datecmd = "date -u +'" DATEFMT "'"
76 datecmd | getline UPDATE_TIME
77 close(datecmd)
78 print "<!DOCTYPE html>"
79 print "<html>"
80 # <head>
81 print "<head>"
82 print "<meta charset=\"utf-8\">"
83 print "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"
84 print "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">"
85 print "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
86 print "<title>" TITLE "</title>"
87 print "<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">"
88 print "<link rel=\"shortcut icon\" type=\"image/png\" href=\"mars-eyes.png\">"
89 print "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"rss (full)\" href=\"feeds.xml\">"
90 print "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"rss (short)\" href=\"feeds-short.xml\">"
91 print "<link rel=\"alternate\" type=\"application/xml\" title=\"opml\" href=\"feeds.opml\">"
92 print "<link rel=\"alternate\" type=\"text/plain\" title=\"twtxt\" href=\"feeds.txt\">"
93 print "</head>"
94 print "<body>"
95 # <header>
96 print "<header id=\"page-header\">"
97 print "<h1>"
98 printf "<a id=\"logo\" href=\"index.html\"><img src=\"mars-eyes.png\""
99 printf " title=\"%s\" ", LOGO_TITLE
100 printf "width=\"40\" height=\"39\""
101 print "alt=\"mars, but with eyes\" /></a>"
102 print TITLE "</h1>"
103 print "<p id=\"last-updated\">last updated at <time>" UPDATE_TIME "</time></p>"
104 print "</header>"
105 # <nav>
106 print "<nav id=\"page-nav\">"
107 print "<a href=\"#\" id=\"stalebutton\">hide stale</a>", SEPARATOR
108 print "<a href=\"feeds-short.xml\">rss</a>", SEPARATOR
109 print "<a href=\"feeds.html\">all</a>", "(<a href=\"feeds.xml\">rss</a>)", SEPARATOR
110 print "<a href=\"feeds.txt\">twtxt</a>", SEPARATOR
111 print "<a href=\"feeds.opml\">opml</a>"
112 print "</nav>"
113 # first part of <main>
114 print "<main>"
115 print "<section id=\"list\">"
116}
117
118FNR == 1 {
119 if (file_count) {
120 # End the previous file before beginning the current one
121 end_file()
122 }
123 # Filename
124 name = FILENAME
125 sub(/.*\//, "", name)
126 safe_name = name
127 sub(/\//, "_", safe_name)
128 dir = FILENAME
129 sub(/[^\/]*$/, "", dir)
130 if (name ~ /\[yt\]$/) {
131 sub(/ \[yt\]$/, "", name)
132 yt = " class=\"yt\""
133 } else {
134 yt = ""
135 }
136 URLS = envor("SFEED_DATA", ENVIRON["HOME"] "/.sfeed") "/urls/" safe_name
137 # State variables
138 stamp = "."
139 buf = ""
140 ni = 0
141 stale_count = 0
142 fresh_count = 0
143 item_count = 0
144 file_count++
145 siloed = 0
146}
147
148{
149 # Skip if we have too many items
150 if ((LIMIT > 0) && (++ni > LIMIT)) {
151 next
152 }
153 # Collect fields
154 timestamp = $1
155 title = $2
156 silo = $3
157 link = silo_link(silo)
158 content = unescape($4)
159 content_type = $5
160 id = $6
161 author = $7
162 enclosure = $8
163 category = $9
164 # Skip if the item is too old
165 if ((LIMIT >= 0) && (timestamp < stale_age)) {
166 next
167 }
168 # Otherwise, we're showing it
169 stale_count++
170 # Is this item fresh?
171 if (timestamp >= fresh_age) {
172 is_fresh = 1
173 stamp = "!"
174 fresh_count++
175 } else {
176 is_fresh = 0
177 }
178 # Print!
179 bufprint("<tr class=\"entry " (is_fresh ? "fresh" : "stale") "\">")
180 # Timestamp
181 datecmd = "date -d \"@" timestamp "\" +'" DATEFMT "'"
182 if (timestamp) {
183 datecmd | getline timestamp
184 close(datecmd)
185 }
186 bufprint("<td class=\"entry-timestamp\"><time>" timestamp "</time></td>")
187 # Extra links
188 bufprint("<td class=\"entry-extra\">")
189 if (siloed) {
190 # "siloed" links like youtube, facebook, etc. --- I convert them
191 # to more privacy-friendly links, but sometimes those don't
192 # work. TODO: also try to circumvent paywalls.
193 stamp = SILO_STAMP
194 print_link(silo, "Silo: " silo, stamp)
195 }
196 if (enclosure) {
197 # enclosures --- podcast files, etc.
198 stamp = ENCLOSURE_STAMP
199 print_link(enclosure, "Enclosure: " enclosure, stamp)
200 }
201 if ((link != id) && (id != enclosure) && (id ~ /^https?:/)) {
202 # alternate links (comments, etc.)
203 stamp = ALT_LINK_STAMP
204 print_link(id, "alternate link", stamp)
205 }
206 bufprint("</td>")
207 # Title
208 bufprint("<td class=\"entry-title" silo_class(silo) "\">")
209 print_link(link, "", title)
210 bufprint("</td>")
211 # End row
212 bufprint("</tr>")
213 item_count++
214}
215
216END {
217 # End the last file and the #list section
218 end_file()
219 print "</section>"
220 # Sidebar
221 print "<aside><ul id=\"feedlist\">"
222 system("cat " ASIDE)
223 close(ASIDE)
224 print "</ul></aside>"
225 # End of <main>
226 print "</main>"
227 # footer
228 printf "<footer>"
229 print "Generated by <a href=\"https://codemadness.org/sfeed-simple-feed-parser.html\">sfeed</a>"
230 print "using <a href=\"https://git.acdw.net/sfeed/\">this configuration</a>."
231 print "<a href=\"mailto:planet@me.acdw.net\">suggest a feed!</a></footer>"
232 # end of HTML
233 print "<script src=\"script.js\"> </script>"
234 print "</body>"
235 print "</html>"
236 printf(SEPARATOR) > STDERR
237}
238
239
240function bufprint(text, sep)
241{
242 buf = buf text (sep ? sep : ((sep == 0) ? "" : "\n"))
243}
244
245function end_file()
246{
247 if (! item_count) {
248 return 1
249 }
250 # Header
251 printf "<section id=\"%s\" class=\"%s\">\n", name, (fresh_count ? "fresh" : "stale")
252 printf "<header><h2%s><a class=\"anchor\" href=\"#%s\">#</a> %s</h2>\n", yt, name, name
253 # Feed links
254 printf "<nav class=\"flinks\">"
255 feed_url = ""
256 site_url = ""
257 getline feed_url < URLS
258 getline site_url < URLS
259 if (site_url) {
260 printf "<a class=\"site-url\" href=\"%s\">%s</a>\n", site_url, (yt ? "chan" : "site")
261 }
262 if (feed_url) {
263 printf "<a class=\"feed-url\" href=\"%s\">feed</a>\n", feed_url
264 }
265 # Top link
266 printf "<a class=\"top\" href=\"#\">top</a>"
267 printf "</nav>"
268 printf "</header>\n"
269 # Feed entries
270 printf "<table class=\"entries\">\n"
271 printf "%s", buf
272 printf "</table>\n</section>\n"
273 # Sidebar
274 if (stale_count) {
275 printf("<li class=\"%s\">", (fresh_count ? "fresh" : "stale")) >> ASIDE
276 printf("<a href=\"#%s\"%s>%s</a></li>\n", name, (yt ? " class=\"yt\"" : ""), name) >> ASIDE
277 }
278 # Log
279 printf("%s", stamp) > STDERR
280}
281
282function envor(var, def)
283{
284 return (ENVIRON[var] ? ENVIRON[var] : def)
285}
286
287function html_escape(t)
288{
289 gsub(/</, "\\&lt;", t)
290 gsub(/>/, "\\&gt;", t)
291 gsub(/&/, "\\&amp;", t)
292 return t
293}
294
295function print_link(href, title, text)
296{
297 bufprint("<a href=\"" href "\" title=\"" title "\" target=\"_blank\">" text "</a>", 0)
298}
299
300function silo_class(link)
301{
302 if (link ~ /youtube\.com/) {
303 return " youtube"
304 }
305 if (link ~ /facebook\.com/) {
306 return " facebook"
307 }
308 if (link ~ /twitter\.com/) {
309 return " twitter"
310 }
311 if (link ~ /reddit\.com/) {
312 return " reddit"
313 }
314 return ""
315}
316
317function silo_link(link)
318{
319 ret = link
320 http = "https?://[^\\.]*\\.?"
321 siloed = 1
322 if (ret ~ (http "youtube\\.com")) {
323 sub(http "youtube\\.com", YOUTUBE_ALT_URL, ret)
324 } else if (ret ~ (http "reddit\\.com")) {
325 sub(http "reddit\\.com", REDDIT_ALT_URL, ret)
326 } else if (ret ~ (http "twitter\\.com")) {
327 sub(http "twitter\\.com", TWITTER_ALT_URL, ret)
328 } else {
329 siloed = 0
330 }
331 return ret
332}
333
334function unescape(t)
335{
336 t = html_escape(t)
337 gsub(/\\\t/, "\t", t)
338 gsub(/\\\n/, "\n", t)
339 gsub(/\\\\/, "\\", t)
340 return t
341}