見出し画像

定期開催DJイベのあらゆる作業を自動化しまくった

DJイベントの準備って、大変

皆さん、DJイベントは主催してますか?僕は沢山してます。

ライブの打ち上げDJなど単発のものを含めると今年は8件、内100人以上の集客をした大きなイベントが2件ありました。自分が主催とデザイナーを兼務しているものも多く、告知やイベントの直前は毎回ヒィコラ言っています。

そんなこんなで常に2〜3くらい未来の主催イベを抱えて生きているのですが、この度新規イベを立ち上げました。なんで?

アイマスの新曲がかかりまくる平日夜DJイベント「New Beat M@STER」を立ち上げました。
次回以降の開催日は未公開ですが、「定期イベ」です。
共同主催の2人も含め全員色々掛け持ちしているので、楽できるところは楽したい…!と頭を悩ませていたところ、思いつきました

これ自動化できんじゃね?

DJイベントのために管理画面を作った

画像
DJ管理
画像
フライヤー生成
画像
TwiPla HTML生成

自動化しました。

最近はAI Agentが発達したおかげでこういう管理画面的なものを作るのはかなり楽になりましたね。
本業や別の作業をしつつ片手間で進め、2週間くらいで一通り動作するところまで作れた気がします。

この管理画面でできることは現状以下の4つです。

  • フライヤーの生成

  • DJ紹介用のネームカードの生成

  • タイムテーブルの生成

  • TwiPlaの文面(HTML形式)の生成

皆さんTwiPlaってHTMLを直接いじって更新できるの知ってました?
技術チョットワカル人はあれコピーしてきてローカルでAIに編集させたほうが早いケースも結構あると思います。
画像のアップロードとかめちゃくちゃめんどくさいから…

生成にあたって手動でやるべきことは

  • 開催番号を入力

  • TwiPlaを立ててURLを入力(QRコードの生成に必要)

  • DJ情報をCSV形式でアップロード(Google Formと紐づけたスプレッドシートをそのままアップするだけ)

  • 出演順を入力

以上です。あら楽ちん。

かなり時間の節約になりますし、心理的な負担も相当減るんじゃないでしょうか。
CSVをマスタデータとするので、誤字のリスクなんかもなくなるわけです。(フォーム入力した本人が打ち間違えてない限り)

なお初回であるvol.0はこのシステムの微調整にめちゃくちゃ時間を取られたので絶対手作業のほうが早かったです。
ただITエンジニアは将来楽するために今苦労する生き物。
早くこの借金をペイできることに期待しています。

他にももっと自動化したくなっちゃった

Q. なぜ自動化するのか
A. そこに面倒なルーティンがあったから

DJイベント主催の皆さん、セトリ共有表や広報素材の管理ってどうしてますか?
セトリ共有表使ってないし素材も直接DMでやりとりしてるよって方もいると思いますが、何かしらでGoogle Driveのお世話になってる方が多いんじゃないかなと思います。

Google Driveってフォルダを丸ごとコピーすることができず、地味に不便なんですよね。
前のイベントから1つ1つコピーしたりと手運用をしていると、一部書き換え忘れがあったり、なんか毎回フォルダの構成がバラバラだったり…
これも面倒なので自動化したいなぁと思いました。できれば僕以外の非エンジニアメンバーも使える形で。

ということでGoogle App Script(Googleの色んなサービスをプログラム経由で操作できるやつ)で何ができるか調べていたところ、こんなものを発見しました。

画像
イベント作成用のカスタムメニュー

スプレッドシートにカスタムメニューを追加する機能があるんですね。GASちょこちょこ使ってるけど初めて知った。
スプレッドシートに次のイベントの情報を入れ、このボタンを押すと

画像
イベント作成モーダル

こんな感じのモーダルが表示されます。作成を押すと、テンプレート用のフォルダにあるファイルの名前や内容を書き換えてコピーしてくれます。

画像
テンプレートフォルダの例

◯月◯日に開催するNew Beat M@STER vol.▢のプロフ収集フォームです。
△月△日までにご記入お願いします。
みたいな内容を全部書き換えてくれるわけです。

自動化、楽できるだけじゃなくてミスが減るんですよね。これが嬉しい。

おわりに

ここまで自動化してるDJイベントは中々ないんじゃないでしょうか。
ルーティンが自動化できると、浮いた時間と気力で新しいことができます。
「今回はどういうイベントにしようかな」と考える余力が生まれます。
運営一同、楽できるところは楽しながら、イベントの内容を良くするために頑張っていきたいと思っています。

最後にもう1回宣伝させて!!!!!!

アイマス新曲が沢山かかるDJイベント New Beat M@STER vol.0 は12/9(火) 19:00より渋谷R-Lounge 7Fにて開催!
2,000円(1Drink付)とお安めですが、なんと23歳未満の方なら更に500円オフになります!

平日なので適度にゆるいイベントになるはずです。
新曲の話はもちろん、直近のライブや供給の話も大歓迎!
今回紹介した管理画面の詳細が気になる奇特な方は僕に聞きに来てください。
(いないとは思うけど)ウチのイベントでも真似したいとかあったら相談くらいは乗れます。

それでは12/9(火)、渋谷R-Loungeでお会いしましょう!みりおっつ〜

おまけ: 技術解説

さて、ここからは同業の皆様お待ちかねの技術解説のコーナーです。
興味ねーよって人はここまででブラウザバックしちゃってください。
ただここまで読んでくれた貴方なら、プログラミングに詳しくなくても専門用語だけ飛ばし読みしてもらえれば面白い…ような気がします。
なんならここからが本番説もあります。

フロントエンド

  • React+React Router v7

  • Tailwind CSS v4

長らくNext.jsを使っていましたが、どんどん独自機能モリモリになっていくので「もっと薄くていいな…」と思い、公私ともに1年ほど前にRemixに乗り換えました。エンジニアって経験積むと結局シンプルで標準に近いものに帰ってきますよね。
最近React Router v7に統合されたのでそのままReact Routerを使っています。
CSSライブラリはReact RouterにデフォルトでくっついてくるTailwind CSSを採用。
どちらも普段から業務で使っているため、今回新たに勉強するようなことは特になかったです。
あと今回は「分かりやすくて使えればなんでもいい」案件ではあったので、特に厳密なデザイン設計などもせずAIに適当に生成させました。
AIってすぐグラデーション使うよな。

バックエンド・インフラ

  • サーバー処理: React Router v7

  • データベース、画像ストレージ: Supabase

  • 画像生成部: Puppeteer Core + Chromium

  • TwiPla HTMLテンプレート生成: Handlebars

  • デプロイ先: Vercel

自分の中で枯れた技術多めですが、ちょっと新しいことしたかったのでずっと気になりつつ触れてなかったSupabaseを導入してみました。
これめっっっちゃ楽。
無料版は複数環境を持てない&しばらく触らないとプロジェクトが停止しますが、今の用途なら問題なさげ。ローカル検証時はDockerでSupabase local立ち上げられるし。

画像はHTMLでレンダリングしています。今回は固定サイズのピクセルパーフェクトな画像を生成するだけなので環境差とかも考える必要なし。

画像
フライヤーベース画像→レンダリング後

こんな感じで毎回変わらない要素を埋め込んだベース画像を用意し、変動部であるDJ名・vol番号・TwiPla QRコードを重ねてレンダリングしています。
タイムテーブルやネームカードも同様。
ただネームカードだけは人によって適切なフォントサイズや改行すべき位置が変わるのでちょっと苦心しました。

画像
人によってバラつきのあるネームカード

下の人担当アイドル多すぎるだろ。反省してほしい。
こういうパターンを9割方網羅できるような分岐コードを書いています。

export function splitIdolsIntoLines(idols: string[]): string[][] {
  if (idols.length === 0) return [];

  // 3人以下: 1行にまとめる
  if (idols.length <= 3) {
    return [idols];
  }

  // 4人以上: 均等配分
  // 例: 4人→2-2, 5人→3-2, 6人→3-3, 8人→3-3-2, 11人→4-4-3
  const totalIdols = idols.length;

  // 最適な行数を計算(1行あたり2〜4人を目安)
  let numLines: number;
  if (totalIdols <= 6) {
    numLines = 2; // 4-6人 → 2行
  } else if (totalIdols <= 9) {
    numLines = 3; // 7-9人 → 3行
  } else if (totalIdols <= 12) {
    numLines = 3; // 10-12人 → 3行
  } else {
    numLines = 4; // 13人以上 → 4行
  }

  // 各行の人数を計算(できるだけ均等に)
  const basePerLine = Math.floor(totalIdols / numLines);
  const remainder = totalIdols % numLines;

  const lines: string[][] = [];
  let currentIndex = 0;

  for (let i = 0; i < numLines; i++) {
    // 余りがある場合は前の行から詰める
    const lineSize = i < remainder ? basePerLine + 1 : basePerLine;
    lines.push(idols.slice(currentIndex, currentIndex + lineSize));
    currentIndex += lineSize;
  }

  return lines;
}

動けばよかろうでAIに書かせたのでまじまじと読まれると恥ずかしいのですが、担当アイドルの人数によって何行で表示させるかを分岐するコードです。

コメントのフォントサイズだけは「どうしてもあと2pxだけ落としたい…」みたいなことが発生しがちだったので、プロフ情報CSVにcommentFontAdjustという列を追加し、±の数値を入れることで微調整できるようにしました。
こういう現実的な割り切りも大事。

開発環境・品質管理

  • Claude Code

  • Vitest

  • ESLint + Prettier

最近は公私ともにほとんどClaude Codeでコーディングしています。
多い時だとターミナル同時に5つくらい開いてる。

ここ半年くらいかなり力を入れてAgentic codingに取り組んできて分かったんですが、AIに書かせるからこそ設計や静的解析はキッチリやるべきですね。
自分用の管理画面としては過剰じゃない?ってくらい結構しっかり責務分離したコードになっているんですが、おかげでテスタブルになり品質も安定しています。なんかテストケース620個ある。
今回の開発で得た知見を本業に活かしたりもできたので、かなり実のある開発でした。

というわけで今度こそおしまい!
聞きたいことあったらいつでも連絡ください。
でもニュビマスに来て直接聞いてもらえるのが一番嬉しいです。

いいなと思ったら応援しよう!

ピックアップされています

てっく

  • 9本

コメント

コメントするには、 ログイン または 会員登録 をお願いします。
買うたび 抽選 ※条件・上限あり \note クリエイター感謝祭ポイントバックキャンペーン/最大全額もどってくる! 12.1 月〜1.14 水 まで
定期開催DJイベのあらゆる作業を自動化しまくった|コン
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1