メインコンテンツまでスキップ

· 約3分
wen

0. 背景&需求

我的博客是使用 Cloudflare Pages 部署的,

但是 Cloudflare Pages 并没有提供部署状态通知的功能,所以我想到了使用 Github Actions 来实现这个功能。

1. 前提

  • 已经在 Github 上创建了你的网站的仓库
  • 已经在 Cloudflare Pages 上设置好了你的 Github 仓库
    • 比如我是设置成了 main 分支更新时自动部署

2. 方法

3. 实现

arddluma/cloudflare-pages-slack-notification 这个 Github Action 已经实现这个了我们所要的功能。

我们只需要准备好这个 Github Action 中使用的变量。

- name: Await CF Pages and send Slack notification
uses: arddluma/cloudflare-pages-slack-notification@v4
with:
# Cloudflare API token
apiToken: ${{ secrets.CF_API_TOKEN }}
# CloudFlare account ID
accountId: ${{ secrets.CF_ACC_ID }}
# CloudFlare Pages project name
project: ${{ secrets.CF_PAGES_PROJECT }}
# Create Slack Incoming webhook and add as variable https://hooks.slack.com/...
slackWebHook: ${{ secrets.SLACK_WEBHOOK }}
# Add this if you want to wait for a deployment triggered by a specfied commit
commitHash: ${{ steps.push-changes.outputs.commit-hash }}

3.1 获得变量

1️⃣ 2️⃣ 3️⃣ 是 Github Actions 通过 Cloudflare Pages 的 API 来获取部署状态所需要的参数。

4️⃣ 是 Github Actions 发送消息给 Slack 所需要的 webhook URL。

3.2 设置变量

在你的网站的 Github 仓库中,点击 Settings -> Secrets and variables -> New repository secret,然后设置上面的变量。

img

4. Github Actions 代码

github/workflows/cloudflare-pages.yml
name: Detect Cloudflare pages deployment status and notify to Slack
on:
push:
branches:
- main
paths-ignore:
- .github/**
pull_request:
branches:
- main
types: [closed]
paths-ignore:
- .github/**
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Await CF Pages and send Slack notification
id: cf-pages
uses: arddluma/cloudflare-pages-slack-notification@v4
with:
# Clouodflare API token
apiToken: ${{ secrets.CF_API_TOKEN }}
# CloudFlare account ID
accountId: ${{ secrets.CF_ACC_ID }}
# CloudFlare Pages project name
project: ${{ secrets.CF_PAGES_PROJECT }}
# Create Slack Incoming webhook and add as variable https://hooks.slack.com/...
slackWebHook: ${{ secrets.SLACK_WEBHOOK }}
# Add this if you want to wait for a deployment triggered by a specfied commit
commitHash: ${{ steps.push-changes.outputs.commit-hash }}

效果

  • Github Action Build Progress: img

  • Slack notification: img

Reference

Setup Cloudflare Pages Slack notifications

· 約2分
wen

问题

Docuaurus 的文档(Docs)默认的 URL 路径名是 /docs,如:https://thewang.net/docs

如果你想把它改成 /api,如:https://thewang.net/api ,该怎么做呢?

解决方法

docusaurus.config.js 中,

  • 找到 docs 的配置项,添加 routeBasePath 属性,值为你想要的 URL 路径名,如:api
  • 找到 navbar 的配置项,
docusaurus.config.js
  presets: [
[
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
+ routeBasePath: 'api',
sidebarPath: require.resolve('./sidebars.js'),
...
docusaurus.config.js
     navbar: {
...
items: [
{ to: '/blog', label: 'Blog', position: 'left' },
// API docs
{
to: '/api',
type: 'docSidebar',
label: 'API',
...
},

补充

那那些已经公开的文档(Docs)的 URL 路径不能访问了該怎么办呢?

可以安装 docusaurus/plugin-client-redirects 插件,来实现重定向解决。

  npm install @docusaurus/[email protected] // 注意和你的 docusaurus 版本对应
docusaurus.config.js
...
+ plugins: [
+ [
+ '@docusaurus/plugin-client-redirects',
+ {
+ createRedirects(existingPath) {
+ if (existingPath.includes('/api')) {
+ // Redirect from /docs to /weekly
+ return [
+ existingPath.replace('/api', '/docs'),
+ ];
+ }
+ return undefined; // Return a falsy value: no redirect created
+ },
+ },
+ ]
+ ],

presets: [
...
PRODUCTION ONLY

This plugin is always inactive in development and only active in production because it works on the build output.

@docusaurus/plugin-client-redirects 只在生产环境下生效。

· 約2分
wen

背景

以前经常用 plantuml 来生成各种示意图。

自从 Github 支持了 mermaid 语法后,就慢慢的转向了 mermaid。

今天就来记录一下如何使用 mermaid 来绘制 Gitflow 的示意图。

ヒント

Mermaid 是一个基于 JavaScript 的图表绘制工具,

它使用简单的文本描述来定义图表,然后将文本转换为图表。

Mermaid 支持流程图、序列图、类图、状态图、Git 图、甘特图等多种图表。

官方还提供了一个在线编辑器

1. Gitflow 的 Mermaid 代码

gitGraph LR:
commit id: "1:init"
branch "hotfix/{ticket_number}" order: 1
branch "release/{x}" order: 2
branch develop order: 3
commit id: "2:init"
branch "feature/{ticket_number}" order: 4
checkout "feature/{ticket_number}"
commit id: "3:commit-x"
checkout develop
merge "feature/{ticket_number}" id: "4:merge-feat" type: HIGHLIGHT

# release
checkout "release/{x}"
merge develop id: "5:release-x"
checkout main
merge "release/{x}" id: "6:merge-release" tag: "v.1.0.0" type: HIGHLIGHT

# hotfix
checkout "hotfix/{ticket_number}"
commit id: "7:commit-hotfix"
checkout develop
merge "hotfix/{ticket_number}" id: "8:merge-hotfix" type: HIGHLIGHT

2. Mermaid 代码的渲染结果

似乎 本博客( Docusaurus )的渲染结果 和官方提供的在线编辑器 生成的结果(↓)稍微有点不一样。

不过,大家可以通过修改 Mermaid 的配置 调整显示效果。

img

· 約4分
wen

需求

  • Golang 中将结构体的数组保存到 CSV 文件中
  • CSV 文件的列顺序和结构体中的字段顺序不同
user.go
type User struct {
Name string
Age int
}

↓↓↓

AgeName
20"Musk"

调查

比较有人气的的 Golang CSV 库有:

但是两者都不支持指定结构体字段的顺序,而且也没有提供相应的 writer 接口,所以也无法自己覆盖接口实现。

本来想 fork 上面的其中一个库改造下,但搜索发现了 shigetaichi/xsv 这个库,在 gocsv 的代码的基础上实现了指定结构体字段顺序的功能。

字段的顺序可以通过 xsvSortOrder 属性指定一个数组,数组中的值是结构体中相应字段的索引(starting from 0)。

比如, 如果想要将结构体按照 Age, Name 的顺序输出,可以这样指定:

type User struct {
Name string `csv:"name"`
Age int `csv:"age"`
}

xsvWrite.SortOrder = []int{1, 0}

但是个人觉得字段顺序的指定方式不太友好。 既然已经有了 csv tag,为什么不直接使用 csv tag 来指定字段的顺序呢?

比如:

type User struct {
Name string `csv:"name,order:1"`
Age int `csv:"age,order:0"`
}

我的方案

代码

代码
package main

import (
"fmt"
"log"
"os"
"reflect"
"strconv"
"strings"

"github.com/shigetaichi/xsv"
)

type User struct {
Name string `csv:"name, order:1"`
Age int `csv:"age, order:0"`
}

func main() {
users := []*User{
{Name: "Alice", Age: 20},
{Name: "Bob", Age: 30},
}

// Create a csv file to write
file, err := os.OpenFile("users.csv", os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
panic(err)
}
defer file.Close()

xsvWrite := xsv.NewXsvWrite[*User]()
// Get the order of the fields in the struct and set it to the writer
orders := getOrderOfFields(reflect.TypeOf(User{}))
xsvWrite.SortOrder = orders

// Write the users to the csv file
err = xsvWrite.SetFileWriter(file).Write(users)
if err != nil {
log.Println(err)
return
}
}

// Get the order of the fields in specified struct
func getOrderOfFields(structType reflect.Type) []int {
// Create a slice of int with the same length as the number of fields in the struct
var res []int
// Iterate through the struct fields
for i := 0; i < structType.NumField(); i++ {
// Get the field
field := structType.Field(i)

// Get the "order" tag value
order := getTagValue(field, "csv", "order")
if order >= 0 {
res = append(res, order)
}
}

return res
}

// getTagValue は指定されたフィールドの指定されたタグの値を取得する
func getTagValue(field reflect.StructField, tag string, tagField string) int {
tagValue, _ := field.Tag.Lookup(tag)
// Split the tag string by ","
tagParts := strings.Split(tagValue, ",")

// Iterate through the tag parts to find the "order" value
res := -1
for _, part := range tagParts {
part = strings.TrimSpace(part)
// Check if the part starts with "order:"
prefix := fmt.Sprintf("%s:", tagField)
if strings.HasPrefix(part, prefix) {
// Extract the numeric value after "order:"
valueStr := strings.TrimPrefix(part, prefix)
value, err := strconv.Atoi(valueStr)
if err == nil {
res = value
}
break
}
}

return res
}

保存的 CSV 文件

agename
20Alice
30Bob

· 約4分
wen

背景

在使用 Golang 的时候,经常会遇到需要将结构体转换为 JSON 的情况,

但是在转换的时候,JSON 的字段顺序并不是我们想要的,这时候就需要我们自己来指定 JSON 的字段顺序。

解决方案

一个比较简单的方案是利用结构体的 tag 来指定 JSON 的字段顺序,

然后在转换的时候,将结构体的字段按照 tag 中的顺序进行排序。

type User struct {
Name string `json:"name,order:2"`
Age int `json:"age,order:1"`
}

代码实现

package main

import (
"encoding/json"
"fmt"
"reflect"
"sort"
"strconv"
"strings"

//orderedmap "github.com/wk8/go-ordered-map/v2"
"github.com/iancoleman/orderedmap"
)

type User struct {
Name string `json:"name,order:3"`
Age int `json:"age,order:2"`
Score int `json:"score,order:1"`
}

type Address struct {
City string `json:"city,order:10"`
Street string `json:"street,order:9"`
ZipCode string `json:"zip_code,order:8"`
}

func main() {
user := User{
Name: "Wen",
Age: 30,
Score: 100,
}
address := Address{
City: "Hangzhou",
Street: "XiHuDaDao",
ZipCode: "10001",
}

// User構造体を指定した順序でJSONに変換
userJSON, err := MarshalJSONWithOrder(user)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("User JSON:", string(userJSON))

// Address構造体を指定した順序でJSONに変換
addressJSON, err := MarshalJSONWithOrder(address)
if err != nil {
fmt.Println("Error:", err)
return
}

fmt.Println("Address JSON:", string(addressJSON))
}

// MarshalJSONWithOrder は構造体を指定した順序でJSONに変換する
// 構造体での指定方法: `json:"{struct field name},order:{integer}"`
// 指定例: `json:"name,order:10"`
func MarshalJSONWithOrder(obj interface{}) ([]byte, error) {
val := reflect.ValueOf(obj)
typ := reflect.TypeOf(obj)

// ソート用のスライス
var fields []fieldWithOrder

// フィールドの数だけループ
for i := 0; i < val.NumField(); i++ {
fieldName := typ.Field(i).Name

order := getTagValue(typ.Field(i), "json", "order")
fields = append(fields, fieldWithOrder{
Name: fieldName,
Order: order,
})
}

// フィールドのソート
sort.Slice(fields, func(i, j int) bool {
return fields[i].Order < fields[j].Order
})

// ソート後の順序に従ってJSONを生成
//result := orderedmap.New[string, any]()
result := orderedmap.New()
for _, f := range fields {
//result[f.Name] = val.FieldByName(f.Name).Interface()
result.Set(f.Name, val.FieldByName(f.Name).Interface())
}

// マーシャリング
return json.Marshal(result)
}

// getTagValue は指定されたフィールドの指定されたタグの値を取得する
func getTagValue(field reflect.StructField, tag string, tagField string) int {
tagValue, _ := field.Tag.Lookup(tag)

// Split the tag string by ","
tagParts := strings.Split(tagValue, ",")

// Iterate through the tag parts to find the "order" value
res := -1
for _, part := range tagParts {
// Check if the part starts with "order:"
prefix := fmt.Sprintf("%s:", tagField)
if strings.HasPrefix(part, prefix) {
// Extract the numeric value after "order:"
orderStr := strings.TrimPrefix(part, prefix)
order, err := strconv.Atoi(orderStr)
if err == nil {
res = order
}
}
}

return res
}

// fieldWithOrder はソート用の構造体
type fieldWithOrder struct {
Name string
Order int
}

NOTE

  • Ordered Map 的使用

    Golang 中的 map 是无序的,如果需要有序的 map, 可以使用 wk8/go-ordered-map 或者 iancoleman/orderedmap。 由于后者的性能似乎比较好,代码中采用的是后者。

  • ChatGPT 的使用

    代码大部分是用 ChatGPT 生成的,但是生成的代码中有几个问题,例如上面的 Ordered Map,我尝试让 ChatGPT 修改了几次,都没有成功。 最后还是自己手动修改了代码。

    感觉 ChatGPT 对于代码的细节部分的理解还有待改善,而且有时候还胡说八道(现阶段生成式 AI 的通病)。

· 約5分
wen

背景

Docusaurus 默认没有集成评论系统。

在之前的博客中用过 Giscus ,

它是一个基于 GitHub Discussions 的评论系统,可以很方便地集成到 GitHub Pages、VuePress、Docusaurus 等静态网站生成器中。

这次打算把它添加到 Docusaurus。

ヒント

如果不想依存 Github 的服务,可以开卡 Waline 评论系统,可以添加自己的后端服务。

准备工作

ヒント

可以点击流程图中带下划线的 node 跳转到相关页面

步骤

  • 流程图

1. 配置 Giscus

Giscus 的网站上配置你的评论系统,并复制你的配置代码(高亮部分会在步骤 3 中用到)。

giscus config
<script src="https://giscus.app/client.js"
data-repo="your_org_name/your-repo"
data-repo-id="your-repo-id"
data-category="Announcements"
data-category-id="your-category-id"
... // other options
async>
</script>

2. 创建评论组件

2.1. 安装依赖

npm install @giscus/react mitt

# or
yarn add @giscus/react mitt

2.2. 创建组件

点击查看代码
src/components/GiscusComments/index.tsx
import React from "react";
import { useThemeConfig, useColorMode } from "@docusaurus/theme-common";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import { ThemeConfig } from "@docusaurus/preset-classic";
import BrowserOnly from "@docusaurus/BrowserOnly";
import Giscus, { GiscusProps } from "@giscus/react";

interface CustomThemeConfig extends ThemeConfig {
giscus: GiscusProps & { darkTheme: string };
}

const defaultConfig: Partial<GiscusProps> & { darkTheme: string } = {
id: "comments",
mapping: "title",
reactionsEnabled: "1",
emitMetadata: "0",
inputPosition: "top",
lang: "zh-CN",
theme: "light",
darkTheme: "dark",
};

export default function Comment(): JSX.Element {
const themeConfig = useThemeConfig() as CustomThemeConfig;
const { i18n } = useDocusaurusContext();

// merge default config
const giscus = { ...defaultConfig, ...themeConfig.giscus };

if (!giscus.repo || !giscus.repoId || !giscus.categoryId) {
throw new Error(
"You must provide `repo`, `repoId`, and `categoryId` to `themeConfig.giscus`."
);
}

giscus.theme =
useColorMode().colorMode === "dark" ? giscus.darkTheme : giscus.theme;
giscus.lang = i18n.currentLocale;

return (
<BrowserOnly fallback={<div>Loading Comments...</div>}>
{() => <Giscus {...giscus} />}
</BrowserOnly>
);
}

用 Docusaurus 的 swizzle 命令对博客页面对应组件进行修改(添加前面创建的评论组件)。

npm run swizzle @docusaurus/theme-classic BlogPostPage -- --eject --typescript
# or
yarn run swizzle @docusaurus/theme-classic BlogPostPage -- --eject --typescript

以上命令会在 src/theme/BlogPostPage/index.tsx 中生成一些文件,我们只需要修改 index.tsx 文件即可。

以下高亮部分为修改代码:

点击查看代码
src/theme/BlogPostPage/index.tsx
import React, {type ReactNode} from 'react';
import clsx from 'clsx';
import {HtmlClassNameProvider, ThemeClassNames} from '@docusaurus/theme-common';
import {BlogPostProvider, useBlogPost} from '@docusaurus/theme-common/internal';
import BlogLayout from '@theme/BlogLayout';
import BlogPostItem from '@theme/BlogPostItem';
import BlogPostPaginator from '@theme/BlogPostPaginator';
import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata';
import TOC from '@theme/TOC';
import type {Props} from '@theme/BlogPostPage';
import Unlisted from '@theme/Unlisted';
import type {BlogSidebar} from '@docusaurus/plugin-content-blog';
import Comment from '../../components/GiscusComments';

function BlogPostPageContent({
sidebar,
children,
}: {
sidebar: BlogSidebar;
children: ReactNode;
}): JSX.Element {
const {metadata, toc} = useBlogPost();
const {nextItem, prevItem, frontMatter, unlisted} = metadata;
const {
hide_table_of_contents: hideTableOfContents,
toc_min_heading_level: tocMinHeadingLevel,
toc_max_heading_level: tocMaxHeadingLevel,
hide_comment: hideComment,
} = frontMatter;
return (
<BlogLayout
sidebar={sidebar}
toc={
!hideTableOfContents && toc.length > 0 ? (
<TOC
toc={toc}
minHeadingLevel={tocMinHeadingLevel}
maxHeadingLevel={tocMaxHeadingLevel}
/>
) : undefined
}>
{unlisted && <Unlisted />}
<BlogPostItem>{children}</BlogPostItem>

{(nextItem || prevItem) && (
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
)}
{!hideComment && <Comment />}
</BlogLayout>
);
}

...

2.3. 配置 Docusaurus

docusaurus.config.js 中添加配置项:

docusaurus.config.js
module.exports = {
...
themeConfig: {
...
// giscus options
giscus: {
repo: 'your_org/your-blog', // edit this
repoId: 'your_repo_id', // edit this
category: 'Announcements',
categoryId: 'your_category_id', // edit this
}
...
},
...
}

3. 重启 Docusaurus

npm start
# or
yarn start

参考

Docusaurus 添加评论功能

评论服务

· 約3分
wen

背景

最近参与的一个项目,需要经常地进入 AWS ECS 的 Fargate 服务中的容器中。

使用 awscli 的 ECS Exec 可以非常方便地连接到运行中的容器。

img

其大概的原理如下图所示:

ECS Exec 把 SSM Agent 绑定到容器,从而可以通过 Systems Manager (SSM) 的会话管理器(Session Manager)访问容器。

img

所以执行该命令,除了需要安装 AWS CLI 之外,还需要安装 session-manager-plugin

对于团队里的一部分成员来说,安装这些工具还是比较麻烦的,所以我写了一个脚本,可以一键连接到 ECS 的容器中。

脚本

connect-to-aws-ecs.sh
#!/bin/bash -eu

# check if aws-cli is installed
if ! type aws >/dev/null 2>&1; then
echo "aws-cli is not installed"
echo "installing aws-cli...."
brew install awscli
echo "aws-cli is installed successfully. please set your credentials by 'aws configure' command"
fi

# check if session-manager-plugin is installed
if ! type session-manager-plugin >/dev/null 2>&1; then
echo "session-manager-plugin is not installed"
echo "installing session-manager-plugin...."
# Download and unzip the Session Manager plugin installer
mkdir -p $HOME/session-manager-plugin/bin
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac/sessionmanager-bundle.zip" -o "$HOME/session-manager-plugin/bin/sessionmanager-bundle.zip"
unzip -d $HOME/session-manager-plugin/bin $HOME/session-manager-plugin/bin/sessionmanager-bundle.zip
sudo $HOME/session-manager-plugin/bin/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin

echo "session-manager-plugin is installed successfully."
fi

# set your container name and cluster name
container_name="your-container-name"
cluster_name="your-ecs-cluster-name"

# 执行脚本时,使用 `--profile` 指定的 AWS 配置文件
aws_profile="default"
while [ "$#" -gt 0 ]; do
case "$1" in
--profile=*)
aws_profile="${1#*=}"
;;
*)
# exit when unknown option is specified
echo "未知のオプション: $1"
exit 1
;;
esac
shift
done
echo "aws_profile: $aws_profile"

# fetch task arn
task_arn=$(aws ecs list-tasks \
--profile "$aws_profile" \
--cluster $cluster_name \
--desired-status RUNNING \
--query 'taskArns[0]' \
--output text)

# spit task id from task arn
task_id=$(echo "$task_arn" | awk -F/ '{print $NF}')
echo "task_id: $task_id"

# enter container with session manager
aws ecs execute-command \
--profile "$aws_profile" \
--cluster $cluster_name \
--task "$task_id" \
--container $container_name \
--command "/bin/sh" \
--interactive

使用方法

  1. 将上述脚本保存为 connect-to-aws-ecs.sh 文件
  2. 执行 chmod +x connect-to-aws-ecs.sh 赋予脚本执行权限
  3. 修改脚本中的 container_namecluster_name 为你自己的值
  4. 执行 ./connect-to-aws-ecs.sh 即可连接到 ECS 的容器中

Reference

使用 ECS Exec 访问 Fargate 上的容器

Amazon ECS Exec を使ってみる

· 約1分
wen

背景

当你的博客文章越来越多时,你可能会发现你的博客需要一个 tags 菜单,以便用户可以快速找到他们感兴趣的文章。

本文将介绍如何为 Docusaurus 博客添加 tags 菜单。

img

添加 tags 菜单

只需要在 docusaurus.config.js 配置文件中,添加如下配置即可:

docusaurus.config.js
      navbar: {
title: 'thewang',
logo: {
alt: 'thewang logo',
src: 'img/logo.png',
},
items: [
{ to: '/blog', label: 'Blog', position: 'left' },
// language dropdown menu
{
type: 'localeDropdown',
position: 'right',
},
...
// tags menu
{
to: '/blog/tags',
label: 'Tags',
position: 'left',
},

· 約4分
wen

Background

在 civitai 上看到 DivineEleganceMix 模型 生成的图片,感觉很美,

就想尝试用 Stable Diffusion WebUI 重现一下。(为了方便描述,以下用 WebUI 代替 Stable Diffusion WebUI )

img

过程

1. 获取 prompt 和参数

Prompts 等参数可以在 这个页面 点击 Copy Generation data 复制得到。

(masterpiece, best quality:1.2), (face focus:1.4), back focus, from behind, blonde hair, long hair, high ponytail, long ponytail, red eyes, long eyelashes, thick eyelashes, looking at viewer, red dress, backless dress, gold trim dress, puffy sleeves, juliet sleeves, long sleeves, red sleeves, (black background:1.2), light particles, blurry, bloom, shiny hair, <lora:Add Detail:0.5>
Negative prompt: (EasyNegative:0.8), (worst quality, low quality:1.2),
(from below:1.2), (ass:1.2), collar, tight, skin tight, impossible clothes, bangs, off shoulder, frills, white sleeves, red background, yellow sleeves,
Steps: 30, VAE: kl-f8-anime2.vae.pt, Size: 512x768, Seed: 3842746991, Model: DivineEleganceMix8, "Module: canny, Sampler: Euler a, VAE hash: df3c506e51, CFG scale: 8, Clip skip: 2, Model hash: 4dbe20030f, ADetailer model: face_yolov8n.pt, ADetailer steps: 30, ADetailer sampler: Euler a, ADetailer version: 23.10.1, Denoising strength: 0.3, ADetailer CFG scale: 8.0, ADetailer CLIP skip: 2, ADetailer mask blur: 16, ADetailer confidence: 0.3, ADetailer dilate/erode: 32, ADetailer inpaint width: 768, ADetailer inpaint height: 768, ADetailer inpaint padding: 32, ADetailer mask merge/invert: Merge, ADetailer denoising strength: 0.3, ADetailer use separate steps: True, ADetailer inpaint only masked: True, ADetailer use separate sampler: True, ADetailer use separate CFG scale: True, ADetailer use separate CLIP skip: True, ADetailer use inpaint width/height: True

2. 安装插件

Generation data 中提到了 VAE: kl-f8-anime2.vae.ptAdetailer 插件,需要先安装。

2.1 安装 VAE:kl-f8-anime2.vae.pt

在 HuggingFace 上找到了 kl-f8-anime2.vae.pt , 下载到本地放到 stable-diffusion-webui/models/VAE/ 里面。

stable-diffusion-webui 默认没有启用 VAE,需要手动启用。

如下图所示,在 stable-diffusion-webui 的 Settings 页面,点击 1️⃣ → 2️⃣ , 添加 sd_vae 后,

点击 Apply settingsReload UI

img

如下图所示,刷新界面后就出现了 1️⃣ SD VAE 选项, 选择我们之前下载的 kl-f8-anime2.ckpt

img

2.2 安装 Adetailer

到 stable-diffusion-webui 的 Extensions 页面,

按照 [Adetailer](https://github.com/Bing-su/adetailer) 的说明安装。

3. 导入参数

如下图所示,在 WebUItxt2img 页面,点击 1️⃣ 的箭头按钮,粘贴 prompt。

然后点击 2️⃣ 的箭头按钮,在弹出窗口中点击 3️⃣ 按钮粘贴参数。

img img

有些参数好像不能直接粘贴,需要手动修改一下。

比如 Height, ADetailerEnable 选项等。

4. 生成图片

还不错,不过和模型作者的图片好像还是有差距,可能有些设置没改好,有时间再看看。 img

  • 修改了下眼睛部分的 prompt,效果好了点。 img

Appendix

  • ADetailer 的作用是什么?

    • ADetailer 是一个插件,对模型生成的图片中的手部(比如 6 手指问题)、脸部的变形等问题进行处理,使得生成的图片更加真实。
  • 为什么要用 VAE?

Reference

WebUI の拡張機能で 手軽に CivitAI のモデルを利用する

· 約3分
wen

Background

之前试了 Diffusers 等软件,生成的图片都不太理想(甚至难看 😸 ),

有些甚至不能选择模型,UI 也不太好用。

所以这次试试 Stable Diffusion WebUI,据说他的 UI 很好用。

我的环境

我的环境是 MacBook Pro M2 Pro。

$ system_profiler SPHardwareDataType

Hardware Overview:
Model Name: MacBook Pro
Chip: Apple M2 Pro
Total Number of Cores: 12 (8 performance and 4 efficiency)
Memory: 16 GB

安装方法&运行

安装方法很简单,具体看Github 的 readme

brew install cmake protobuf rust [email protected] git wget # install dependencies
git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui # clone the repo
cd stable-diffusion-webui # enter the directory
./weiui.sh # start the app

脚本运行后,会自动打开一个网页。

如果没有自动打开,可以手动打开 http://127.0.0.1:7860/ , 就可以看到界面了。

使用方法

下载模型

ヒント

默认提供一个模型 Stable Diffusion v1-5-pruned-emaonly.safetensors,效果惨不忍睹。

建议直接从 Hugging Face 或者 Civitai 下载。

不相信我提示的可以点击这里看看😄
  • 例 1: Prompts: a little boy playing with a puppy img

  • 例 2: Prompts: 1girl,face,white background img

  • 例 3: Prompts: 1girl,face,white background

  • Negative Prompts: (worst quality:2),(low quality:2),(normal quality:2),lowres,watermark img

我从 Civitai 下载了一个比较有人气的模型 majicMIX realistic, 再用之前的 prompt 试了试,效果比默认的模型好太多了 😄。

生成结果

  • Prompts: 1girl,face,white background img

  • Prompts: 1girl,face,white background

    Negative Prompts: (worst quality:2),(low quality:2),(normal quality:2),lowres,watermark img

ヒント

Negative Prompts : 用来指定不想要的图片特征,比如 lowres 代表不想要低分辨率的图片。

Reference

Stable Diffusion モデルの探し方

Stable Diffusion WebUI のおすすめモデル 10 選をご紹介!導入方法も解説