Skip to content

Instantly share code, notes, and snippets.

@chvck
Created June 26, 2018 12:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chvck/e6b4bac164f96f5a0af4409f52758e88 to your computer and use it in GitHub Desktop.
Save chvck/e6b4bac164f96f5a0af4409f52758e88 to your computer and use it in GitHub Desktop.
Migration from pelican to Ghost
package main
import (
"bufio"
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)
const (
layout = "2006-01-02 15:04"
ext = ".md"
)
type post struct {
ID uint32 `json:"id"`
Title string `json:"title"`
Slug string `json:"slug"`
Markdown string `json:"markdown"`
Status string `json:"status"`
FeatureImage string `json:"image"`
Visibility string `json:"visibility"`
AuthorID uint32 `json:"author_id"`
CreatedAt int64 `json:"created_at"`
CreatedBy uint32 `json:"created_by"`
UpdatedAt int64 `json:"updated_at"`
UpdatedBy uint32 `json:"updated_by"`
PublishedAt int64 `json:"published_at"`
PublishedBy uint32 `json:"published_by"`
}
type tag struct {
ID uint32 `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
}
type postTag struct {
TagID uint32 `json:"tag_id"`
PostID uint32 `json:"post_id"`
}
type meta struct {
Exported int64 `json:"exported_on"`
Version string `json:"version"`
}
type data struct {
Posts []post `json:"posts"`
Tags []tag `json:"tags"`
PostsTags []postTag `json:"posts_tags"`
}
type metaAndData struct {
Meta meta `json:"meta"`
Data data `json:"data"`
}
func main() {
srcPath := os.Args[1]
destFile := os.Args[2]
srcStat, err := os.Stat(srcPath)
if err != nil {
panic(err)
}
if !srcStat.IsDir() {
panic("source directory must be a directory")
}
posts := make([]post, 0)
tags := make(map[string]uint32)
postTags := make([]postTag, 0)
filepath.Walk(srcPath, func(path string, fInfo os.FileInfo, _ error) error {
if !fInfo.IsDir() {
if filepath.Ext(path) == ext {
p, err := postFromFile(path, tags, &postTags, uint32(len(posts)+1))
if err != nil {
panic(err)
}
posts = append(posts, *p)
}
}
return nil
})
arrTags := make([]tag, 0)
for t, i := range tags {
arrTags = append(arrTags, tag{
ID: i,
Name: t,
Slug: strings.ToLower(t),
})
}
jsonData := metaAndData{
Meta: meta{
Exported: time.Now().Unix() * 1000,
Version: "003",
},
Data: data{
Posts: posts,
Tags: arrTags,
PostsTags: postTags,
},
}
bytes, err := json.Marshal(jsonData)
if err != nil {
panic(err)
}
write, err := os.Create(destFile)
if err != nil {
panic(err)
}
_, err = write.Write(bytes)
if err != nil {
panic(err)
}
}
func postFromFile(path string, tags map[string]uint32, postTags *[]postTag, postID uint32) (*post, error) {
fOpen, err := os.Open(path)
if err != nil {
return nil, err
}
defer fOpen.Close()
scanner := bufio.NewScanner(fOpen)
p := post{
ID: postID,
CreatedBy: 1,
UpdatedBy: 1,
Status: "published",
AuthorID: 1,
PublishedBy: 1,
Visibility: "public",
}
inMeta := true
markdown := ""
re := regexp.MustCompile(`<(.|\n)*?>`)
for scanner.Scan() {
line := scanner.Text()
if inMeta && line == "" {
inMeta = false
continue
}
if inMeta {
processTags(line, tags, postTags, postID)
date := processField(line, "Date:")
if date != "" {
dateTime, err := time.Parse(layout, date)
if err != nil {
panic(err)
}
p.CreatedAt = dateTime.Unix() * 1000
p.UpdatedAt = dateTime.Unix() * 1000
p.PublishedAt = dateTime.Unix() * 1000
}
title := processField(line, "Title:")
if title != "" {
p.Title = title
}
slug := processField(line, "Slug:")
if slug != "" {
p.Slug = slug
}
thumbnail := processField(line, "Thumbnail:")
if thumbnail != "" {
thumbnail = strings.Replace(thumbnail, "/thumbnails/thumbnail_wide", "/content/images/thumbnails", -1)
p.FeatureImage = thumbnail
}
} else {
text := strings.Replace(line, "\u003cbr /\u003e", "\n", -1)
text = re.ReplaceAllString(text, "")
text = strings.Replace(text, "{filename}", "/content/", -1)
format := "%s %s"
if strings.HasPrefix(text, "--") {
format = "%s\n%s\n"
}
markdown = fmt.Sprintf(format, markdown, text)
}
}
p.Markdown = markdown
return &p, err
}
func processField(line string, field string) string {
str := ""
if strings.HasPrefix(line, field) {
str = strings.TrimLeft(line, field)
str = strings.Trim(str, " ")
}
return str
}
func processTags(line string, tags map[string]uint32, postTags *[]postTag, postID uint32) {
if strings.HasPrefix(line, "Tags:") {
tagsStr := strings.TrimLeft(line, "Tags:")
for _, tag := range strings.Split(tagsStr, ",") {
tag = strings.Trim(tag, " ")
tag = strings.Replace(tag, " ", "-", -1)
if _, ok := tags[tag]; !ok {
tags[tag] = uint32(len(tags) + 1)
}
*postTags = append(*postTags, postTag{
TagID: tags[tag],
PostID: postID,
})
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment