文章發佈流程

我把文章發佈到網站的流程其實還挺複雜的,在這裏説明一下。

我用 hugo(網站鏈接) 這個網站生成器建立網站,只要把文字和圖片擺放成 hugo 指定的結構,就可以把它們轉換成網站的代碼,然後把整份代碼上傳到網上去。

hugo 用 content 文件夾存放全部文章內容。可以設置文章分區,每個文章分區都是個文件夾。文章本身也是個文件夾,同一篇文章的多個語言版本都會放在裏面。

  • blog
    • content
      • gallery
      • posts
        • cardinal-ordinal
          • index.en.md
          • index.zh.md

但我不會直接在這文件夾裏增加文章,而是先在 Joplin 寫好,再用代碼把文章自動複製到適合的位置。

Joplin 的筆記本結構和上面一樣。每篇文章都是一個筆記本,放在分區筆記本裏,文章筆記本裏放不同語言的文章。

  • posts
    • cardinal-ordinal
      • 量數和序數
      • Cardinals and ordinals

為了區分沒寫完的文章和要發佈的文章,我會用 Joplin 的標籤功能,在準備好發佈的文章上加一個語言標籤。

然後我會用 python 導出文章到 content 文件夾,再用 shell script 生成網站代碼然後上傳。

以上就是大致的流程,接下來要説一些代碼了。到底要怎麼把文章按照需要的結構,從 Joplin 導出到 content 文件夾裏面呢?joppy (github) 把 Joplin 的 API 用 Python 包裝了一遍,這樣我就能用 python 存取 Joplin 的筆記了。

除了要上傳的文章以外,我的 Joplin 裏面還放了幾百篇不相關的筆記,如果要逐篇筆記檢查它屬於哪本筆記本的話會很耗時間。我只想檢查需要的筆記本,但是這個 API ,或者説 Joplin 存放文件的結構,有個麻煩的地方。所有的筆記本雖然表面上有層級關係,實際上是平級的。我不能像一般的樹狀結構一樣從最頂層往下搜尋筆記本,而是只能從底層,也就是所有筆記本開始往上篩選,才能知道這個筆記本的上一級到底是哪一個筆記本。舉個例子,我要查看 cardinal-ordinal 筆記本才能知道它的上一級是 posts 筆記本,而不能直接查看 posts 筆記本裏面有什麼筆記本。這種結構好像是叫作 Parent list,每個端點會指向它的 Parent。

這樣就知道怎樣篩選筆記本了,剩下的就只是麻煩的搬運工作,代碼如下:

import os
from joppy.client_api import ClientApi as Api

# 調用 joppy 的 API
api = Api(token="YOUR_API_TOKEN")

def output_posts_in_certain_notebook_id(all_notebooks, notebook_id):
    category = api.get_notebook(notebook_id).title
    for notebook_data in all_notebooks:
    
        # 只有當筆記本(文章)放在需要的筆記本(分區)裏面才繼續
        if notebook_data.parent_id != None and notebook_data.parent_id == notebook_id:
        
            # 獲取筆記本裏面的所有筆記(不同的語言版本)
            post_list = api.get_notes(notebook_data.id, fields='created_time,updated_time,body,order,title,id')
            
            folder_name = notebook_data.title
            print(notebook_data.title)
            zh_posts = []
            en_posts = []
            tok_posts = []
            for post_language_version in post_list.items:
                title = post_language_version.title
                
                '''
                如果文章沒有標籤,就跳過
                '''
                tag_list = get_tag_list(post_language_version.id)
                if not tag_list: 
                    continue
                    
                # 把文章按語言版本分類
                if 'zh' in tag_list: 
                    language_code = 'zh'
                    zh_posts.append(post_language_version)
                elif 'en' in tag_list:
                    language_code = 'en'
                    en_posts.append(post_language_version)
                elif 'tok' in tag_list:
                    language_code = 'tok'
                    tok_posts.append(post_language_version)
                else:
                    continue
            combine_and_output_posts_in_same_language(category, folder_name, 'zh', zh_posts)
            combine_and_output_posts_in_same_language(category, folder_name, 'en', en_posts)
            combine_and_output_posts_in_same_language(category, folder_name, 'tok', tok_posts)

def combine_and_output_posts_in_same_language(category, folder_name, language_code, posts):
    if posts:
    
        '''
        這是個把拆成幾段的筆記合併到一起的功能。我是想着用來把一段段的小説合併起來,不過還沒有什麼實際作用
        '''
        posts.sort(key=lambda x: x.order, reverse=True)
        title = posts[0].title
        tag_list = get_tag_list(posts[0].id)
        tag_list.remove(language_code)
        body = ''
        created_time = None
        updated_time = None
        for post in posts:
            if (created_time is None) or (post.created_time < created_time):
                created_time = post.created_time
            if (updated_time is None) or (post.updated_time > updated_time):
                updated_time = post.updated_time
            body += post.body + '\n\n'
        
        '''
        提取筆記的資料,然後扔到 generate_front_matter() 裏面,自動生成 hugo 需要的
        markdown front matter,也就是文章的屬性資料。
        '''
        
        date = created_time.isoformat()[:19] + '+08:00'
        lastmod = updated_time.isoformat()[:19] + '+08:00'
        home_dir = os.environ['HOME'] 
        
        '''
        如果文件夾裏放了圖片,自動在 front matter 裏加入資料,作為文章的標題圖片。
        '''
        feature_png_directory = home_dir + '/Blog/blog/evthronblog/content/' + category + '/' + folder_name + '/' + folder_name + '.' + 'png'
        feature_jpg_directory = home_dir + '/Blog/blog/evthronblog/content/' + category + '/' + folder_name + '/' + folder_name + '.' + 'jpg'
        if os.path.exists(feature_png_directory):
            image = folder_name + '.png'
            front_matter = generate_front_matter(title, date, lastmod=lastmod, tags=repr(tag_list), categories=category, image=image)
        elif os.path.exists(feature_jpg_directory):
            image = folder_name + '.jpg'
            front_matter = generate_front_matter(title, date, lastmod=lastmod, tags=repr(tag_list), categories=category, image=image)
        else:
            front_matter = generate_front_matter(title, date, lastmod=lastmod, tags=repr(tag_list), categories=category)
            
        '''
        把生成的 Markdown 文章複製到指定的文件夾裏面
        '''
        folder_directory = home_dir + '/Blog/blog/evthronblog/content/' + category + '/' + folder_name
        if not os.path.exists(folder_directory):
            os.makedirs(folder_directory)
            
        path = folder_directory + '/index.' + language_code + '.md'
        with open(path, 'w', encoding='utf-8') as fout:
            fout.write(front_matter + '\n' + body)
            print("done in", language_code)
    else:
        print("no post in", language_code)

def generate_front_matter(title, date, description = '', lastmod = '', image = '', categories = '', tags = '', slug = '', layout = '') -> str:
    if lastmod == '':
        lastmod = strftime("%Y-%m-%dT%H:%M:%S+08:00", localtime())
    front_matter = '---\n'
    front_matter += 'title: "' + title + '"\n'
    front_matter += 'description: ' + description + '\n'
    front_matter += 'date: ' + date + '\n'
    front_matter += 'lastmod: ' + lastmod + '\n'
    front_matter += 'image: ' + image + '\n'
    front_matter += 'categories: ' + categories + '\n'
    front_matter += 'tags: ' + tags + '\n'
    front_matter += 'math: \nlicense: \nhidden: false\ncomments: true\n'
    if slug:
        front_matter += 'slug: "' + slug + '"\n'
    if layout:
        front_matter += 'layout: "' + layout + '"\n'
    front_matter += '---\n'
    return front_matter


def get_tag_list(note_id):
    info = api.get_tags(note_id).items
    tag_list = []
    if info:
        for tagdata in info:
            tag = tagdata.title
            tag_list.append(tag)
    return tag_list
               

def main():
    all_notebooks = api.get_all_notebooks()
    # 為需要的筆記本生成檔案
    post_notebook_id = 'PUT_NOTEBOOK_ID_HERE'
    output_posts_in_certain_notebook_id(all_notebooks, post_notebook_id)
    gallery_notebook_id = 'PUT_NOTEBOOK_ID_HERE'
    output_posts_in_certain_notebook_id(all_notebooks, gallery_notebook_id)

if __name__ == '__main__':
    main()

如果我完全不用 Joplin 的話,流程應該可以變得更簡單一些,但我做得這麼辛苦,不是很想改。

最後更新 2024-12-27 08:41 +08:00
🌐Check out other language versions!
網站使用 Hugo 製作
模板 StackJimmy 設計