|
|
@ -1,3 +1,7 @@
|
|
|
1
|
// Command blog renders a YAML file to a little HTML blog.
|
|
|
2
|
//
|
|
|
3
|
// Feel free to adapt it and use it in your setup, if you want your own
|
|
|
4
|
// personal digital writing implement.
|
|
1
|
5
|
package main
|
|
2
|
6
|
|
|
3
|
7
|
import (
|
|
|
@ -22,8 +26,12 @@ import (
|
|
22
|
26
|
"gopkg.in/yaml.v2"
|
|
23
|
27
|
)
|
|
24
|
28
|
|
|
|
29
|
// Post is a single post, title, tags and all.
|
|
|
30
|
//
|
|
|
31
|
// It contains all information to get rendered by something, as determined by
|
|
|
32
|
// the `Type`.
|
|
25
|
33
|
type Post struct {
|
|
26
|
|
Id string `yaml:"id"`
|
|
|
34
|
ID string `yaml:"id"`
|
|
27
|
35
|
Title string `yaml:"title"`
|
|
28
|
36
|
URL string `yaml:"url"`
|
|
29
|
37
|
Content string `yaml:"content"`
|
|
|
@ -32,11 +40,13 @@ type Post struct {
|
|
32
|
40
|
Type string `yaml:"type"`
|
|
33
|
41
|
}
|
|
34
|
42
|
|
|
|
43
|
// Options are options that can be specified in the YAML file itself or on the
|
|
|
44
|
// commandline, to control how the output is done.
|
|
35
|
45
|
type Options struct {
|
|
36
|
46
|
WriteBack bool `yaml:"write_back"`
|
|
37
|
|
HashIds bool `yaml:"hash_ids"`
|
|
|
47
|
HashIDs bool `yaml:"hash_ids"`
|
|
38
|
48
|
Reverse bool `yaml:"reverse"`
|
|
39
|
|
Css string `yaml:"css"`
|
|
|
49
|
CSS string `yaml:"css"`
|
|
40
|
50
|
NoDefaultStyle bool `yaml:"no_default_style"`
|
|
41
|
51
|
Title string `yaml:"title"`
|
|
42
|
52
|
After string `yaml:"after"`
|
|
|
@ -142,9 +152,9 @@ article.does-not-match {
|
|
142
|
152
|
|
|
143
|
153
|
func init() {
|
|
144
|
154
|
flag.BoolVar(&flags.WriteBack, "write-back", false, "Rewrite the YAML file with the generated ids")
|
|
145
|
|
flag.BoolVar(&flags.HashIds, "hash-ids", false, "Use hash-based post ids")
|
|
|
155
|
flag.BoolVar(&flags.HashIDs, "hash-ids", false, "Use hash-based post ids")
|
|
146
|
156
|
flag.BoolVar(&flags.Reverse, "reverse", false, "Reverse the order of the articles in the file")
|
|
147
|
|
flag.StringVar(&flags.Css, "css", "", "Use custom `css` styles")
|
|
|
157
|
flag.StringVar(&flags.CSS, "css", "", "Use custom `css` styles")
|
|
148
|
158
|
flag.BoolVar(&flags.NoDefaultStyle, "no-default-style", false, "Don't use the default styles")
|
|
149
|
159
|
flag.BoolVar(&flags.PrintDefaultStyle, "print-default-style", false, "Print the default styles")
|
|
150
|
160
|
flag.StringVar(&flags.Title, "title", "A blog", "Custom `title` to use")
|
|
|
@ -210,11 +220,11 @@ func main() {
|
|
210
|
220
|
case "write-back":
|
|
211
|
221
|
flags.WriteBack = opts.WriteBack
|
|
212
|
222
|
case "hash-ids":
|
|
213
|
|
flags.HashIds = opts.HashIds
|
|
|
223
|
flags.HashIDs = opts.HashIDs
|
|
214
|
224
|
case "reverse":
|
|
215
|
225
|
flags.Reverse = opts.Reverse
|
|
216
|
226
|
case "css":
|
|
217
|
|
flags.Css = opts.Css
|
|
|
227
|
flags.CSS = opts.CSS
|
|
218
|
228
|
case "no-default-style":
|
|
219
|
229
|
flags.NoDefaultStyle = opts.NoDefaultStyle
|
|
220
|
230
|
case "title":
|
|
|
@ -261,7 +271,7 @@ func main() {
|
|
261
|
271
|
</head>
|
|
262
|
272
|
|
|
263
|
273
|
<body>
|
|
264
|
|
`, template.HTMLEscapeString(flags.Title), defaultStyle, flags.Css)
|
|
|
274
|
`, template.HTMLEscapeString(flags.Title), defaultStyle, flags.CSS)
|
|
265
|
275
|
|
|
266
|
276
|
if flags.Reverse {
|
|
267
|
277
|
l := len(posts)
|
|
|
@ -274,8 +284,8 @@ func main() {
|
|
274
|
284
|
|
|
275
|
285
|
tags := map[string]bool{}
|
|
276
|
286
|
for i, post := range posts {
|
|
277
|
|
if post.Id == "" {
|
|
278
|
|
posts[i].Id = generateId(post)
|
|
|
287
|
if post.ID == "" {
|
|
|
288
|
posts[i].ID = generateID(post)
|
|
279
|
289
|
post = posts[i]
|
|
280
|
290
|
}
|
|
281
|
291
|
|
|
|
@ -310,9 +320,9 @@ func main() {
|
|
310
|
320
|
case (u.Host == "youtube.com" || u.Host == "www.youtube.com") && u.Query().Get("v") != "":
|
|
311
|
321
|
provider = "youtube"
|
|
312
|
322
|
post.URL = fmt.Sprintf("https://www.youtube.com/embed/%s", u.Query().Get("v"))
|
|
313
|
|
case u.Host == "vimeo.com" && getVimeoId(u.Path) != "":
|
|
|
323
|
case u.Host == "vimeo.com" && getVimeoID(u.Path) != "":
|
|
314
|
324
|
provider = "vimeo"
|
|
315
|
|
post.URL = fmt.Sprintf("https://player.vimeo.com/video/%s", getVimeoId(u.Path))
|
|
|
325
|
post.URL = fmt.Sprintf("https://player.vimeo.com/video/%s", getVimeoID(u.Path))
|
|
316
|
326
|
default:
|
|
317
|
327
|
exit(fmt.Errorf("unsupported video url '%s'", post.URL))
|
|
318
|
328
|
}
|
|
|
@ -331,7 +341,7 @@ func main() {
|
|
331
|
341
|
}
|
|
332
|
342
|
|
|
333
|
343
|
sortedTags := []string{}
|
|
334
|
|
for tag, _ := range tags {
|
|
|
344
|
for tag := range tags {
|
|
335
|
345
|
sortedTags = append(sortedTags, tag)
|
|
336
|
346
|
}
|
|
337
|
347
|
sort.Strings(sortedTags)
|
|
|
@ -516,7 +526,7 @@ var baseTmpl = template.Must(template.New("base").
|
|
516
|
526
|
<header>
|
|
517
|
527
|
{{- if .Title }}
|
|
518
|
528
|
<h1>{{ .Title }}</h1>{{ end }}
|
|
519
|
|
<a class="permalink" href="#{{ .Id }}">∞</a>
|
|
|
529
|
<a class="permalink" href="#{{ .ID }}">∞</a>
|
|
520
|
530
|
{{- if .Date }}
|
|
521
|
531
|
<time>{{ .Date }}</time>{{ end }}
|
|
522
|
532
|
</header>
|
|
|
@ -533,10 +543,10 @@ var baseTmpl = template.Must(template.New("base").
|
|
533
|
543
|
|
|
534
|
544
|
var shellTmpl = template.Must(baseTmpl.New("shell").
|
|
535
|
545
|
Funcs(funcs).Parse(`
|
|
536
|
|
<article id="{{ .Id }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
|
546
|
<article id="{{ .ID }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
537
|
547
|
<header>
|
|
538
|
548
|
<h1><code class="language-shell">{{ .Title }}</code></h1>
|
|
539
|
|
<a class="permalink" href="#{{ .Id }}">∞</a>
|
|
|
549
|
<a class="permalink" href="#{{ .ID }}">∞</a>
|
|
540
|
550
|
{{- if .Date }}
|
|
541
|
551
|
<time>{{ .Date }}</time>{{ end }}
|
|
542
|
552
|
</header>
|
|
|
@ -553,10 +563,10 @@ var shellTmpl = template.Must(baseTmpl.New("shell").
|
|
553
|
563
|
|
|
554
|
564
|
var linkTmpl = template.Must(baseTmpl.New("link").
|
|
555
|
565
|
Funcs(funcs).Parse(`
|
|
556
|
|
<article id="{{ .Id }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
|
566
|
<article id="{{ .ID }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
557
|
567
|
<header>
|
|
558
|
568
|
<h1><a href="{{ .URL }}">{{ .Title }}</a></h1>
|
|
559
|
|
<a class="permalink" href="#{{ .Id }}">∞</a>
|
|
|
569
|
<a class="permalink" href="#{{ .ID }}">∞</a>
|
|
560
|
570
|
{{- if .Date }}
|
|
561
|
571
|
<time>{{ .Date }}</time>{{ end }}
|
|
562
|
572
|
</header>
|
|
|
@ -573,7 +583,7 @@ var linkTmpl = template.Must(baseTmpl.New("link").
|
|
573
|
583
|
|
|
574
|
584
|
var imageTmpl = template.Must(baseTmpl.New("image").
|
|
575
|
585
|
Funcs(funcs).Parse(`
|
|
576
|
|
<article id="{{ .Id }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
|
586
|
<article id="{{ .ID }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
577
|
587
|
{{- template "title" . }}
|
|
578
|
588
|
<img src="{{ safe_url .URL }}" />
|
|
579
|
589
|
{{- if .Content }}
|
|
|
@ -589,7 +599,7 @@ var imageTmpl = template.Must(baseTmpl.New("image").
|
|
589
|
599
|
|
|
590
|
600
|
var songTmpl = template.Must(baseTmpl.New("song").
|
|
591
|
601
|
Funcs(funcs).Parse(`
|
|
592
|
|
<article id="{{ .Id }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
|
602
|
<article id="{{ .ID }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
593
|
603
|
{{- template "title" . }}
|
|
594
|
604
|
<audio src="{{ safe_url .URL }}" controls>
|
|
595
|
605
|
Your browser can't play {{ .URL }}.
|
|
|
@ -607,7 +617,7 @@ var songTmpl = template.Must(baseTmpl.New("song").
|
|
607
|
617
|
|
|
608
|
618
|
var textTmpl = template.Must(baseTmpl.New("text").
|
|
609
|
619
|
Funcs(funcs).Parse(`
|
|
610
|
|
<article id="{{ .Id }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
|
620
|
<article id="{{ .ID }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
611
|
621
|
{{- template "title" . }}
|
|
612
|
622
|
{{- if .Content }}
|
|
613
|
623
|
|
|
|
@ -621,7 +631,7 @@ var textTmpl = template.Must(baseTmpl.New("text").
|
|
621
|
631
|
`))
|
|
622
|
632
|
|
|
623
|
633
|
var videoTmpl = template.Must(baseTmpl.New("video").Parse(`
|
|
624
|
|
<article id="{{ .Id }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
|
634
|
<article id="{{ .ID }}" class="{{ .Type }}" {{- if .Tags }} data-tags="{{ json .Tags }}"{{ end }}>
|
|
625
|
635
|
{{- template "title" . }}
|
|
626
|
636
|
{{ if (eq .Provider "youtube" "vimeo") -}}
|
|
627
|
637
|
<iframe width="560" height="315" src="{{ safe_url .URL }}" frameborder="0" allowfullscreen loading="lazy"></iframe>
|
|
|
@ -651,14 +661,14 @@ func exit(err error) {
|
|
651
|
661
|
os.Exit(1)
|
|
652
|
662
|
}
|
|
653
|
663
|
|
|
654
|
|
func generateId(p Post) string {
|
|
655
|
|
if flags.HashIds {
|
|
656
|
|
return hashId(p)
|
|
|
664
|
func generateID(p Post) string {
|
|
|
665
|
if flags.HashIDs {
|
|
|
666
|
return hashID(p)
|
|
657
|
667
|
}
|
|
658
|
|
return slugId(p)
|
|
|
668
|
return slugID(p)
|
|
659
|
669
|
}
|
|
660
|
670
|
|
|
661
|
|
func hashId(p Post) string {
|
|
|
671
|
func hashID(p Post) string {
|
|
662
|
672
|
h := md5.New()
|
|
663
|
673
|
io.WriteString(h, p.Title)
|
|
664
|
674
|
io.WriteString(h, p.Content)
|
|
|
@ -666,7 +676,7 @@ func hashId(p Post) string {
|
|
666
|
676
|
return hex.EncodeToString(h.Sum(nil))
|
|
667
|
677
|
}
|
|
668
|
678
|
|
|
669
|
|
func randomId() string {
|
|
|
679
|
func randomID() string {
|
|
670
|
680
|
buf := make([]byte, 16)
|
|
671
|
681
|
_, err := rand.Read(buf)
|
|
672
|
682
|
if err != nil {
|
|
|
@ -678,11 +688,11 @@ func randomId() string {
|
|
678
|
688
|
|
|
679
|
689
|
var usedSlugs = map[string]int{}
|
|
680
|
690
|
|
|
681
|
|
func slugId(p Post) string {
|
|
|
691
|
func slugID(p Post) string {
|
|
682
|
692
|
slug := toSlug(p.Title)
|
|
683
|
693
|
n, ok := usedSlugs[slug]
|
|
684
|
694
|
if ok {
|
|
685
|
|
n += 1
|
|
|
695
|
n++
|
|
686
|
696
|
} else {
|
|
687
|
697
|
n = 1
|
|
688
|
698
|
}
|
|
|
@ -715,7 +725,7 @@ func toSlug(s string) string {
|
|
715
|
725
|
return strings.Trim(s, "-")
|
|
716
|
726
|
}
|
|
717
|
727
|
|
|
718
|
|
func getVimeoId(p string) string {
|
|
|
728
|
func getVimeoID(p string) string {
|
|
719
|
729
|
i := strings.LastIndex(p, "/")
|
|
720
|
730
|
if i == -1 || len(p) == i+1 {
|
|
721
|
731
|
return ""
|