2025年10月16日木曜日

ラブブを定価で買う方法

ラブブを定価で買う方法

ちゃんと正規のお店に行って買うのが一番です
一応買えたのでメモ
買い方が複雑すぎる、、

結論

  • LivePocket で抽選を受けて当選したら現地に行って購入する

お店に入るには抽選を受ける必要がある

  • ここで定期的に抽選しているのでまずはこれに応募し当選する
  • 1人1店舗しか応募できない
  • 平日の方が当選しやすいとかあるのだろうか
  • 普通にお店にいってもラブブのぬいぐるみのやつは買えない?
  • 確証はないが抽選のない通常営業日に朝から並べば買えたりするのかも (確証はない

そして抽選に当選しても買える保証がない

  • 自分はマカロンとエネジーというシリーズがほしかったが売り切れだった (しかも眼の前で
  • 購入制限はあるが売り切れることがある
  • ブラインドボックスなるガチャ箱が人気らしくそれが1人複数個買えるらしいので自分の番になる前に売り切れる可能性がある
  • 単品物(写真でいうところの紫のやつ)も人気のやつがあるらしいがこっちは比較的購入しやすそう

抽選の倍率は

  • 不明
  • とにかく抽選し続けるしかない
  • 店舗によっても変わりそう
  • 1ヶ月に1回程度なのでそもそも倍率は高くなりそう

購入制限について

  • ぬいぐるみ系は基本制限あり
  • ブラインドボックスは2種類までで個数は1ボックスに入ってる数まで
    • 例えばマカロンとエネジーでマカロン2個とエネジー3個とか
    • マカロンもエナジーもボックスは6個入っているので6個まで買えるということ
  • 単品系はそれぞれ1個まで買える
  • 事前に現在の売り切れ情報を教えてくれる
  • が自分はそうだったが入店後にエナジーが売り切れたのでこの段階で商品の在庫があるかどうか保証するものではない
  • また以下の商品が購入制限のある商品だったがこれらは店内になくレジで直接伝えて購入するのでこれらだけがほしい場合は手ぶらでレジに直行でOK
  • ただこれも説明があると思うが1人1会計のみなので店内に陳列してある商品もほしい場合はそれらを手に取ってからレジに並ばなければならない

当選番号もかなり重要

  • 自分は240番だったがその番号だと人気シリーズ(というかほしかったマカロンとエナジー)は売り切れ状態だった
  • 前にも書いたが購入制限はあるものの前の方々が上限いっぱいまで書い続ければそれだけ売り切れの可能性が増えるので若い番号のほうがいい
  • 100番内ならほぼほしいやつが買えるかもしれない
  • 当日並んだときもところどころ空き番号があったので常連は当選しても番号次第ではお目当てのものは買えないとわかっているので来ないのかもしれない

当日購入までの流れ

  • 当選したチケットの確認
  • 身分証明書を持参する
  • 当選番号で入れる時間が決まるのでその時間の確認
  • 時間の15分前に整列が始まるのでそれくらいに現地に到着すること
  • 整列
  • チケットのQRコードと身分証明書の確認
  • 購入制限の説明と現在の在庫状況の説明
  • 時間まで整列しながら待機
  • 入店
  • レジ直行
  • ほしいやつ伝えて購入
  • そく退店

という感じです
お店によりますが待機列が外の場合があるので冬は防寒対策も必須です

お店に行くとオリジナルの紙袋(40円)も購入できるのでファンには嬉しいのかも

ネットで買えるのか

  • PopMart の公式オンラインストアはある
  • がラブブ系はほぼ在庫切れ
  • 商品が入荷したら通知が来るようにできるが通知がきてすぐに行ってもほぼ売り切れ
  • 予約注文が可能になることがあるのでそのときに予約注文して届くのを待つのはあり
  • ただ箱1個とかはほぼなくボックス売りの予約注文がほぼ
  • 合計10,000万円以下の送料は660円
  • 箱1個だけの購入とかはほぼ不可能に近い印象

転売は未だに高い

  • 各種フリマサイトやショッピングサイト系を見ると未だに高い印象
  • 一時期よりかはだいぶ安くなったイメージもある?
    • エナジー定価 2,250円
    • 転売現状 5,000 - 6,000円
    • 転売少し前 10,000円

最後に

人気があるコンテンツなので仕方ないですが面倒だったので忘れないようにメモしました
入店の抽選方式は転売対策とは言えないかもですがそれでも長蛇の列を作らない、入店時のパニックなどを回避する手段としては有効かなと思いました

マカロンとエナジーは買えませんでしたがそれでも子どもたちがよろこんでくれたのでよかったです

昔のハイパーヨーヨーとかたまごっちみたいな一時的に爆発的に人気になるコンテンツは今も昔も変わらずあるんだろうなと感じた、、

2025年10月9日木曜日

Sinatra4.2.0にアップデートしたらホームページが動かなくなったので対応した

Sinatra4.2.0にアップデートしたらホームページが動かなくなったので対応した

私のホームページはSinatraでできています

背景

Sinatra が 4.2.0 になり PATH_INFO の扱いが変わりました https://github.com/sinatra/sinatra/compare/v4.1.1...f53a68482f3d5a1a868320f5da3ca7e14bfd8fdf

それに伴いルーティングの定義を仕方も変わったのでそれに対応しました

結論

ちゃんと get '/' do ... end を使いましょう

仕組み

自分のホームページは各ページをクラスとして定義しています
例えば /about を管理するページは以下のようにクラス化されています

# frozen_string_literal: true

require './libs/ogp'
require './controllers/base_page'

# 自己紹介ページのルーティングを定義するクラス
class AboutPage < BasePage
  def initialize
    @path = '/' # config.ru 側でパスを指定するのでここはすべて / になる
    @real_path = '/about' # sitemap 生成用のパス
    @tpl_name = :about
    @title = 'About @kakakikikeke'
    @description = '興味のある分野、職歴、資格について紹介しています'
    @image = OGP::DEFAULT_IMAGE
    @sitemap = { priority: 0.8 }
    super(@real_path, @sitemap)
    routing
  end

  def routing
    AboutPage.get @path do
      @ogp = OGP.new(@title, @description, @path, @image)
      erb @tpl_name
    end
  end
end

でこのクラスを config.ru で Rack::URLMap を使って読み込み

# frozen_string_literal: true

require './libs/routing'

run Rack::URLMap.new(Routing.definition)

展開しています

# frozen_string_literal: true

Dir[File.expand_path('../controllers', __dir__) << '/*.rb'].sort.each do |file|
  require file
end

# ルーティングを定義するモジュール、ちゃんとメニューの順番と同じになるようにrouting.jsonを定義すること
# また動的に生成するページは各Pageクラス内で定義すること
module Routing
  def self.definition
    JSON.parse(File.read('./routing.json'))['routing'].map do |info|
      [info['path'], Module.const_get(info['class']).new]
    end.to_h
  end
end

今回の現象

上記のように展開すると本来は 200 が返るのですがなぜか 404 になるようになってしまいました

対応策

で結果的に対応策は以下のようになりました
各ページで定義しているルーティング情報をメソッド内で呼び出すのではなくちゃんとクラス配下に定義してあげるようにしています

# frozen_string_literal: true

require './libs/ogp'
require './controllers/base_page'

# 自己紹介ページのルーティングを定義するクラス
class AboutPage < BasePage
  PATH = '' # config.ru 側でパスを指定するのでここはすべて空になる

  def initialize
    @real_path = '/about' # sitemap 生成用のパス
    @tpl_name = :about
    @title = 'About @kakakikikeke'
    @description = '興味のある分野、職歴、資格について紹介しています'
    @image = OGP::DEFAULT_IMAGE
    @sitemap = { priority: 0.8 }
    super(@real_path, @sitemap)
  end

  AboutPage.get PATH do
    @ogp = OGP.new(@title, @description, PATH, @image)
    erb @tpl_name
  end
end
  • @path を PATH として定数にする
  • かつ PATH は必ず空にする
  • routing メソッドを削除し get を直接クラス配下に定義する

これで一応動作するようになりました

おまけ: Rack::Lint::LintError: uppercase character in header name: Content-Security-Policy (Rack::Lint::LintError)

CSP ヘッダなどをミドルウェアで定義する場合は大文字を使ってはいけないようです

# frozen_string_literal: true

require './libs/routing'

# 静的ファイルに対するヘッダーを設定するミドルウェア
class StaticHeadersMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)

    path = env['PATH_INFO']
    if path =~ %r{^/(robots\.txt|favicon\.ico|img/|js/|css/|ipa/)}
      headers['content-security-policy'] ||= "default-src 'self';"
      headers['cross-origin-embedder-policy'] ||= 'require-corp'
      headers['cross-origin-opener-policy'] ||= 'same-origin'
      headers['cross-origin-resource-policy'] ||= 'same-origin'
      headers['permissions-policy'] = 'camera=(), microphone=(), geolocation=(), fullscreen=(self)'
    end

    [status, headers, body]
  end
end

use StaticHeadersMiddleware

run Rack::URLMap.new(Routing.definition)

最後に

メソッド内で get などを定義すると実際にアクセスされた際にルーティングの定義が見つからずエラーになるようです

2025年10月1日水曜日

ボットは絵も動画もブログも作成できるようになった

ボットは絵も動画もブログも作成できるようになった

人生とはボットを育てることなのかもしれない

これまで

https://blog.kakakikikeke.com/2025/04/kakakikikeke-die-twice.html

自分のツイートを学習させて自分っぽいツイートができるようになりました

アカウント

https://x.com/kakakikikekebot

現在のボットにできること

2025/09/26 現在ボットは成長し以下ができるようになりました

  • ツイートをする
  • ブログを書く
  • 絵を書く
  • ショート動画を作る

以下それぞれの仕組みを詳細に説明します

共通点

  • 動作マシンはローカルの M2 Pro mac mini
  • バックエンドでは「CPU50%のマイニング」「Ollamaサーバの起動」「SD-webui」が稼働している
  • 生成時は MPS にすべて対応しているものを採用
  • Python で実装
  • 生成時間は動画以外数分で完了 (絵、文章系)

ツイートをする

Twitter API + Ollama API (gemma3)

  • 自分のツイートを500件準備、そこから20件をプロンプトのコンテキストとして与える
  • ツイートを生成してもらうようなプロンプトを入力
  • レスポンスをパース、サニタイズなど調整してからツイート
  • ハッシュタグやリンクなどは含めないようにしている
  • プロンプトは以下
def build_prompt(past_tweets, sample_size=20):
    examples = "\n".join(random.sample(past_tweets, sample_size))
    return f"""以下は過去のツイートです:

----ここから過去のツイート----
{examples}
----ここまで過去のツイート----

これらの文体や話題を参考にして、あなたらしい新しいツイートを1つ生成してください。
日本語でかつ文章の意味がわかるような自然なツイートにしてください。
ツイートに「https://」から始まるようなURLは絶対に含めないでください。
ツイートの長さは50文字以内にしてください。
ツイートを作成した理由などは説明しないください、ツイートのみ生成してください。
"""

ブログを書く

Twitter API + Blogger API + Ollama (gemma3)

  • テーマを与えてそのテーマに基づいたブログ記事を書いてもらう
  • 出力は必ずHTMLになるようにプロンプトで調整、デフォルトがMarkdownっぽいので可能な限りMarkdownにならないようにする
  • テーマはランダムで100件保持、100件分のテーマを消費すると停止する
  • テーマはChatGPTに生成してもらっている
  • プロンプトは以下
def build_prompt_for_blog_post(theme):
    return f"""以下はブログ記事のテーマです。

----ここからテーマ----
{theme}
----ここまでテーマ----

このテーマを参考にして、あなたらしい新しいブログ記事を1つ生成してください。
ブログ記事内では Markdown 記法は使えません、Markdown 記法は自動で HTML に変換されません。絶対に Markdown 記法を使わないでください。
ブログ記事は Markdown 記法を使わずに生成してください。
ブログ記事内では HTML タグが使えます。
コードなどシンタックスハイライトが必要な場合は HTML タグを使ってください。
ブログ記事に画像は含めないでください。
ブログ記事内に StyleSheet や CSS の記述を含めることは可能ですがブログ本体の StyleSheet や CSS に影響を与えないようにしてください。
ブログ記事を作成した理由などは説明しないください、ブログ記事の内容のみ生成してください。
"""

絵を書く

Twitter API + Stable Diffusion WebUI API (model はランダム)

  • SD-Web UI を API モードで起動してそれをコールする
  • SD-Web UI の命令はそのまま JSON のペイロードとして API に送信できるので楽
  • 生成された画像のサイズは896x1152
  • モデルは固定ではなく10個ほど保持しておりそれを順番に使用していくように Python 側で制御
  • プロンプトもランダムになるようにしておりベース、性別、向き、服装、背景など要素ごとに毎回ランダムで生成するようにしている
def generate_prompt():
    # プロンプトの定義
    base = "masterpiece, best quality, ultra-detailed, 8k, cinematic lighting"
    gender = "".join(
        ("1 beautiful japanese young women, fair skin, light makeup, realistic face")
    )
    viewpoint = get_random_viewpoint()
    posing = get_random_posing()
    hair_cut = get_random_hair_cut()
    hair_color = get_random_hair_color()
    hair = f"{hair_color} {hair_cut}"
    face = get_random_face()
    background = get_random_backend()
    tops = get_random_tops()
    bottoms = get_random_bottoms()
    clothes = f"{tops}, {bottoms}"
    return "".join(
        f"{base}, {gender}, {viewpoint}, {posing}, {hair}, {face}, {background}, {clothes}",
    )

ショート動画を作る

Twitter API + Stable Diffusion WebUI API + LTX-Video (txv-2b-0.9.8-distilled)

  • text-to-video ではなく image-to-video の手法
  • 先に SD-WebUI を使って画像を作成してもらう
  • その画像を使って LTX-Video で動画にする
  • 256x384の4秒動画を作成 (num_frames=121、frame_rate=30)
  • 4秒以上の動画を生成する場合は192x256にすれば生成できる
  • 本当は896x1152で生成したいがリソースが足りない
  • 動画の生成はほぼマシンリソースをすべて使うので生成前にマイニングおよび ollama の runner を停止している
  • 各種プロセスを停止しないとメモリが枯渇してほぼ100%クラッシュする
  • 1動画生成するのに20-60分ほどかかる
  • 生成時間にムラがありおそらく入力している画像の影響だと思われるが安定しない
  • LTX-Video をサブプロセスとしてコールしているのでその作りが微妙
def convert_image_to_video(
    img_filename: str, width: int, height: int, prompt: str = ""
) -> Optional[str]:
    # 環境変数からパスを取得、設定がなければデフォルト値を使用
    work_dir = os.getenv("LTX_WORK_DIR", "/Users/kakakikikeke/Documents/work/LTX-Video")
    img_dir = os.getenv("LTX_IMG_DIR", "/Users/kakakikikeke/data/repo/videobot")

    # 仮想環境の Python を直接指定
    venv_python = os.path.join(work_dir, "env", "bin", "python")

    # 実行コマンド
    command = f"""
    cd {work_dir} && \
    PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 \
    {venv_python} inference.py \
    --prompt "{prompt}" \
    --conditioning_media_paths {img_dir}/{img_filename} \
    --conditioning_start_frames 0 \
    --height {height} \
    --width {width} \
    --pipeline_config configs/ltxv-2b-0.9.8-distilled.yaml
    """

今後追加したい機能

  • 音楽の作成
    • AudioGen
    • RVC
  • 長めの動画
    • 今のマシンだと画像サイズを落としてフレーム数を上げるしかない
    • もっと軽量なモデルを探す
    • 本当はWan2.2やVeo3レベルの動画を生成したい
  • 俳句
    • gemma3 でプロントプト換えるだけ
  • 音楽+動画
    • 上記で生成した音楽と動画を何かしらで組み合わせる
    • 最終形はこれなのかもしれない
  • 昔の写真や自分で書いた絵を動画にしたい
    • 過去の復元
    • 手書きの絵 -> アニメ化 -> 動画化
  • NanoBanana 連携
    • 有料だとかなり強力
    • というかお金を使う前提ならローカルで全部動かさないで Google AI Studo API や Colab でガンガン動かして高品質、高画質、長尺コンテンツを作りまくれる
    • ただマネタイズ考えないと大変なことなりそう
    • 動画にするだけならVeo3でもいける

課題

  • Twitter API Free プランの上限
    • 500ツイート/1日にほぼ当たっている
    • 有料プランにすれば解決するがどうするか
    • コンテキスト(メディア系、テキスト系)にアカウントを分割するか、管理は面倒になる
  • Ollama で動作させるモデル
    • gemma3 はだいぶ軽量なので動作する
    • gpt-oss のような大規模LLMは動作しない (動作はするがスワップだらけでかなり遅くなる)
    • openhermes など試したがハッシュタグや無駄に絵文字を使うのでやめたりした
    • いいモデルを探すのが難しい
  • プロンプトだけで制御するのには限界がある
    • 含めないでと命令しても含めてくる
    • モデルに合ったプロンプトを調整しなければいけないのが辛いのでなんとかしたい (プロンプト最適化など)
  • プロンプトのパターン追加
    • テーマなど手動の部分があるので自動化したい
    • 絵を描く際のプロンプトのランダム抽出の候補を追加する
    • そもそも完全ランダムにする方法を考える
  • 絶望的にマシンスペックが足りない
    • やはり AppleSilliocn だとかなり辛い
    • 基本は RTX などの CUDA が使えるグラボがあることが前提 (xformers動作など)
    • 動画生成は特に VRAM を消費するので VRAM モリモリのグラボがないと厳しい (Wan2.2など)
  • ディスクも足りない
    • ローカルにモデルを置きまくっているのでディスクが枯渇する

最後に

多少の知識はいるが生成系は本当に楽な時代になった