システム構成
処理を担う3コンポーネント(プロセス)と、共有するデータストアで構成されます。画像処理のような重い処理は専用のコンポーネントへ隔離し、スケールできるように配慮されています。
| 構成要素 | 役割 |
|---|---|
| web (Node.js) | フロントエンドです。レンダリングと軽量なAPIを公開します。 |
| worker-front (Node.js) | ジョブ管理です。Web・Bot・メールの各経路からのリクエストを管理し、画像処理ノードへの指示、およびそのためのAPIを公開します。また、一部の定期ジョブのスケジューラーも兼ねています。 |
| worker (Node.js) | 画像処理です。外部には公開されていません。 |
| PostgreSQL | 投稿・ユーザー・実績などのデータを保存します。 |
| Object Storage | 生成した画像やサムネイルを保存します。 |
画像処理
元画像とパラメーターを受け取ると、即座に画像処理を行い、出力します。処理は、ベータテスト期間中のA/Bテストを通じて、品質を確保しつつより早い処理を実現するパイプライン処理を最適化しています。
向きの補正・デコード
スマートフォンなどでは、縦向きで撮影した場合でも、画像自体は横向きに保存されることが多いです。EXIF の Orientation タグを参照したうえで、向きを補正します。
リサイズ
長辺が 2048px を超える入力画像は、あらかじめアスペクト比を保ったまま長辺 2048px へ縮小してから後続の処理に回します。
文字合成
テキストオーバーレイを Canvas 描画し、画像に合成します。パフォーマンスを維持するため、合成画像は一度JPEG(quality 90)で出力します。
最終出力
投稿先に応じた形式・サイズ上限で書き出します。この出力時の再エンコードで、元画像の EXIF メタデータは全て取り除かれます。
| 投稿先 | 形式 | サイズ上限 | EXIF |
|---|---|---|---|
| Mastodon | AVIF | 16MB | 削除 |
| Misskey | AVIF | 250MB | 削除 |
AVIF出力では、 quality 80, effort 2 で書き出し、サイズを確認します。万が一出力がサイズ上限を超えた場合、quality を段階的に下げて再エンコードし、上限以下に収まった時点の出力を採用します。
派生画像
通常、OGP用には本来の生成画像を指定することも可能です。しかし、XはAVIFに対応していないため、他のレガシーなフォーマットへの変換が必要です。そこで、SHAMEZO内ではWebP変換を行わず、管理者が開発・運用している別インフラ(自前のMisskeyメディアプロキシ)でオンデマンド変換を行い、OGP用のサムネイルを作成しています。このサムネイルはストレージには格納されず、主にCDNのキャッシュに頼っています。
タイムアウト
不明なエラーを発生させないため、Tier ごとにカスケードタイムアウトを設定しています。
| Tier | 制限時間 |
|---|---|
| 画像処理本体 | 18秒 |
| API | 22秒 |
| ブラウザ(Web投稿の場合) | 25秒 |
アップロード処理は全体のタイムアウト値に含まれません。アップロードのタイムアウト値はなく、回線が遅い場合でも原則として永遠に待ち続けます。ただし、アップロードが20秒以上まったく進まなくなった場合のみ、中断されます。
入力制限
画像
合成テキスト
代替テキスト(ALT)
Misskeyへ投稿する場合、代替テキストの上限は 512 文字に自動で切り詰められます。ただし、その場合でも SHAMEZO 上には最大 1,500 文字が保存されます。
テキスト合成
文字位置
文字サイズ
サイズ指定(小/中/大/特大)は、画像に対する相対的なサイズです。画像の短辺に14文字が納まる文字サイズを基準(中)とし、指定した倍率で描画します。
| 設定 | 基準に対する倍率 | 短辺に納まる文字数 |
|---|---|---|
| 小 | 0.75倍 | 約18文字 |
| 中 | 1.0倍 | 約14文字 |
| 大 | 1.4倍 | 約10文字 |
| 特大 | 2.35倍 | 約6文字 |
文字サイズには下限14px・上限500pxのハードリミットがあり、上記よりも優先されます。
文字幅
等幅フォントの場合でも、ASCII文字列 U+0020–U+007E と半角カナ U+FF61–U+FF9F は半分の幅になるよう調整します。また、後述する Emoji は別処理となります。
縦書き
縁取りとカラー
視認性を高めるため、すべての文字に縁取りを追加して描画します。薄い色(白・緑・黄・桃・橙)には黒い縁取り、濃い色(赤・青・茶)には白い縁取りが付きます。縁取りは文字サイズのおよそ0.1倍(ただし最小は2px)となります。
| カラー | 文字色 | 縁取り色 |
|---|---|---|
| 白 | #FFFFFF | #000000 |
| 赤 | #FF0000 | #FFFFFF |
| 青 | #0000FF | #FFFFFF |
| 緑 | #00FF00 | #000000 |
| 黄 | #FFFF00 | #000000 |
| 茶 | #8B4513 | #FFFFFF |
| 桃 | #FFC0CB | #000000 |
| 橙 | #FFA500 | #000000 |
Emoji
ユーザーのフォント設定によらず、絵文字は常に Noto Emoji を用いて描画します。このフォントは本来プロポーショナルフォントのため、等幅フォントのふい字・ラノベPOPとともに使う場合は、ラインがずれないよう、 Noto Emoji 側の表示位置を調整しています。
EXIFに関する追加処理
位置情報・撮影日時・カメラ機種などが含まれるEXIF(Exchangeable image file format)情報は、個人情報が含まれる可能性があるため、常に削除してから投稿・アップロードします。 また、受け取った画像のEXIFは通常解析しません。
しかし、ユーザーが明示的に希望した場合に限って、EXIFを解析し、希望した範囲の情報を保存・投稿することもできます。 この場合でも、撮影日時・撮影方向・GPS座標は保存しません。
サーバーに保存される内容
| 投稿形式 | カメラ機種 | カメラ撮影情報 | 位置情報 | EXIF全体 |
|---|---|---|---|---|
| Web | 希望時のみ | 希望時のみ | 希望時のみ | × |
| Bot | × | × | × | × |
| メール | 希望時のみ | 希望時のみ | 希望時のみ | × |
解析方法
位置情報の変換
ユーザーが希望した場合のみ、国土地理院の逆ジオコーディング API に GPS 座標を送信し、市区町村コードを取得します。 この場合でも、保存されるのは都道府県または市町村までの情報で、GPS 座標は詳細なデバッグ用のログを含め、一切記録されません。
ブラウザの制限についての補足
スマートフォンのプライバシー保護機能により、Web投稿の場合、位置情報のみが自動的に除去されることがあります。
なお、Web投稿では、写真に位置情報が含まれない場合、過去に一度投稿したことのある都道府県または市町村に限って、手動で設定するオプションがあります。
レート制限
画像生成を短時間に連続して行った場合や、投稿数が普段より急増した場合など、いくつかのレート制限を設けています。詳細はセキュリティ上の理由から非開示とします。
Webブラウザ以外の投稿方法
Bot投稿(メンション投稿)
Bot宛にメンション付きで画像とコメントを送信して投稿します。BotはWebSocketでメンションを監視しており、ほぼ即座に処理が開始されます。定期的なポーリングで取りこぼしを補完します。処理が正常に完了した場合、元投稿は削除される仕組みとなっています(設定で変更可能)。
使い方・稼働状況をみるメール投稿
メニューで確認できる専用のメールアドレスに画像を添付して送信して投稿します。メールは Cloudflare Email Routing により Cloudflare Workers で処理され、ほぼ即座に処理が開始されます。セキュリティ対策(リスト型攻撃対策・MTAのバウンス防止)のため、投稿の成功・失敗によらず、サービス側からは一切のメール返信を行いません。
使い方をみる機能: リアクション
公開・未収載の投稿には好きな絵文字でリアクションできます。Fediverse上(Mastodonサーバー・Misskeyサーバー)でのお気に入り・リアクションとSHAMEZOのリアクションは自動で同期され、合算して表示されます。
機能: サーバーで開く
Fediverseでは、同じ投稿でもサーバーごとに別々のローカルID(Mastodonのステータス ID/Misskeyのノート ID)が割り当てられます。そのため、投稿者のサーバー上のURLをそのまま開いても、閲覧者は自分のアカウントで返信・ブースト(リノート)・お気に入りといった操作ができません。「サーバーで開く」は、投稿のActivityPub URIを閲覧者自身のサーバーで解決し、閲覧者のアカウントで操作できる状態にして開く機能です。
閲覧者が自分のFediverseサーバーのWebにログインしており、かつ元投稿が実際にFediverseへ投稿されている場合にのみ利用できます。
ローカルID解決方法
機能: 皆勤賞
皆勤賞は、SHAMEZOにおける最も栄誉のある実績です。
機能: カレンダー
ユーザーページの「カレンダー」タブでは、その月の投稿を日ごとのサムネイルで一覧表示します。 編集モードでは、各日に表示するサムネイルを変更できるほか、皆勤賞の穴埋めに使う投稿も選び直せます (サムネイルを変更後、その画像が削除された場合は、自動で再選出されます)。
トークンに必要な権限
ログイン時、連携する Fediverse サーバーに対し、以下の権限スコープを要求します。投稿・画像アップロード・リアクションなど、必要な最小限の範囲に限っています。
readアカウント情報の読み取り
ログインのほか、ユーザー名・アイコンの取得、重複投稿有無確認に必要。
write:statusesポストの投稿・削除
ポストの投稿や削除に必要。
write:media画像のアップロード
画像投稿に必要。
write:favouritesお気に入りの追加・削除
Web上でのお気に入り操作に必要。
行いません
権限を要求せず、技術的にできません
取得したアクセストークンは暗号化して厳重に保管します。
取得した個人情報はプライバシーポリシーに則り適切に取り扱います。