🔖

画像生成AIのプロンプトをColabから自動でNotionに保存してみた話

2022/12/02に公開

こんにちは。Kaibaです。最近は画像生成AIで性癖探求の旅に出ています。
怠惰でAutomatic1111に手を出せてないんですが絶対使った方が良い絵を出せるんだろうなぁとTwitterを眺めています。

なんの記事?

プロンプト(呪文)の研究時にColabの出力からNotionに一々画像とプロンプトをコピペするの怠いなと思って自動化してみました。もはやコピペすら面倒くさがる怠惰の極み。

以下のような知見を求める人には役に立つと思います。

  • Colab(or Python)からNotionのAPIを叩きたい人
  • NotionのAPIがImageBlockに直接画像をアップロードさせてくれなくて苦しんでいる人

環境

言わずと知れたGoogle Colabratoryです。あるいはJupyterNotebook??
ColabのPythonのバージョンは3.7なのだとか。(記憶している限りではそうだったはず……)

やりたいこと

  1. Colabで画像生成を行い画像と一緒にボタンを出力
  2. 出力欄に出てきた画像が良さげだったら出力欄のボタンをポチ-
  3. Notionのデータベースに画像とprompt,seed諸々が保存される

実装

セル毎に分けて書いていきます。取り敢えず動けばいいや精神で書いたコードなので最適ではないです。適宜変更してください。

また、Notion公式APIでは画像のアップロードがサポートされていないため、非公式のAPIであるnotion-pyのフォークであるneneka/notion-pyを使わせて頂いています。
詳しくはこちら

セットアップ

notion-pyのフォーク版をインストール

!pip install git+https://github.com/neneka/notion-py.git

API呼び出しのためのトークンを設定
トークンの取得方法やデータベースのID取得方法などはこちらで説明してくださってます。
v2トークンの取得方法はこちらとかで説明してくださってます。

NOTION_TOKEN="ここにNotionのトークン"
DB_ID="ここにページを自動で追加したいデータベースのID"
NOTION_V2TOKEN="ここにNotionのV2トークン"

初期化

from notion.client import NotionClient
client = NotionClient(token_v2=NOTION_V2TOKEN)

画像をポストする関数を宣言。分けているのには特に意味はないです。
また、Too Many Requestで弾かれないようにsleepをはさんでいます。

from notion.block import ImageBlock,CodeBlock,TextBlock
from google.colab import output
import os
import time

def post_image(page_url,image_binary,img_path,title,desc):
	# 今回はコレクションの列に対するアップロードなのでコレクションになっているページを想定
  page = client.get_block(page_url)
  time.sleep(1)
  page.title=title

  time.sleep(1)
  # Blockとして追加する
  image = page.children.add_new(ImageBlock)
  time.sleep(1)
  image.upload_file_bin(image_binary, os.path.basename(img_path), "image/png")

  ppts=desc.split('\n')
  
  time.sleep(1)
  page.children.add_new(TextBlock,title="prompt")
  time.sleep(1)
  page.children.add_new(CodeBlock,title=ppts[0])
  time.sleep(1)
  page.children.add_new(TextBlock,title="negative prompt")
  time.sleep(1)
  page.children.add_new(CodeBlock,title=ppts[1])
  time.sleep(1)
  page.children.add_new(CodeBlock,title=ppts[2])


def upload_image(page_url,img_path,title,description):
  post_image(page_url=page_url,image_binary=open(img_path,"rb").read(),img_path=img_path,title=title,desc=description)

ページのタグを設定するAPI。タグを日本語で渡さないといけないのが罠。
今回はリテラルでそのままタグを記入していますが、必要に応じて変えるといいと思います。

def set_tag(page_id):
  url = 'https://api.notion.com/v1/pages/'+page_id

  headers = {
      "Accept": "application/json",
      "Notion-Version": "2022-06-28",
      "Content-Type": "application/json",
      "Authorization": "Bearer " + NOTION_TOKEN
  }

  payload = {
      "properties": {
      
          "タグ": {
            "multi_select": [
              {
                "name": "Auto-Generated"
              },
              {
                "name": "diffusers"
              },
              {
                  "name":"EularDiscrete"
              }
            ]
          }
      }
  }


  response = requests.patch(url, json=payload, headers=headers)

  result_dict = response.json()
  print(result_dict)
  print("setting tags complete.")

ページ追加のAPIを呼び出す関数の宣言
PropertiesにTagsを入れてたらAPI仕様変わってたらしく怒られました。


import requests
import json
import datetime

def create_page(img_path,title,desc):

  url = 'https://api.notion.com/v1/pages'

  headers = {
      "Accept": "application/json",
      "Notion-Version": "2022-06-28",
      "Content-Type": "application/json",
      "Authorization": "Bearer " + NOTION_TOKEN
  }

  payload = {
      "parent": {
          "database_id": DB_ID
      },
      "properties": {
      },
      "children":[
      ],
  }


  response = requests.post(url, json=payload, headers=headers)

  result_dict = response.json()
  print("page created. ID :"+result_dict['id'])
  print("url : "+result_dict['url'])

  time.sleep(1)
  set_tag(result_dict['id'])
  time.sleep(1)
  upload_image(result_dict['url'],img_path,title,desc)

ここまでくればあとは関数を呼び出すだけです。ボタンのコールバック関数にページ作成の関数を登録。
ボタンについては前に記事を書きました。良ければどうぞ。

from IPython.display import display
from ipywidgets import Button

button = Button(description="Notionデータベースに登録", layout=Layout(width='400px', height='50px'))
button.on_click(lambda _: create_page("/content/登録したい画像.png","ページのタイトル","prompt \n negative prompt \n seed"))
display(button)

このセルを実行して出力欄に出てくるボタンを押すと自動でデータベースにページが作られます。


うーむ。いい感じのタイトル付けられないだろうか……

解説のようなもの

  1. ipywidgetのButtonをクリック
  2. create_page関数でNotionの公式APIを叩いてページを作成
  3. レスポンスから作成したページのIDをset_tag関数に渡してタグを設定
  4. ページIDと画像のパス、プロンプト情報を渡してupload_image関数で画像データを読み込み、post_image関数へ
  5. post_image関数でnotion-pyを使って画像のアップロードとプロンプトのコードブロックへの記入を行う

問題点

  • Colabでインスタンスを繋いでいる間しか送信が出来ない。
  • Too Many Request回避の為に時間が掛かる。
    ほんとはColabでJavaScriptを使ってブラウザからAPI呼び出ししたかったんですが、CORSでダメでした。それにしてもWebは慣れないですね……

Discussion