Project Plateau
Project Plateau: four glass plates, one living valley, and the road back to camp.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗公開された Google スプレッドシートを BMS 難易度表として配信します。 データは保存しません。 リクエストのたびにスプレッドシートから取得して変換するだけです。
https://bmstable.vercel.app/t/<スプレッドシートID>/<表の名前>/
この URL(表のページ)を beatoraja の難易度表に登録します。ID を差し替えれば誰の表でも読めます。事前登録は不要です。 1 つのスプレッドシートに複数の難易度表(データシート)を置けます。
| ルート | 内容 |
|---|---|
/t/<ID>/ | スプレッドシート内のシート一覧 |
/t/<ID>/<表の名前>/ | その難易度表のページ。beatoraja に登録する URL |
/t/<ID>/<表の名前>/header.json | ページの <meta name="bmstable"> が指すヘッダ |
/t/<ID>/<表の名前>/data.json | 譜面データ(header.json から相対参照) |
/t/<ID>/<段位シートの名前>/ | その段位認定のページ(課題曲を曲名で並べる) |
/ | スプレッドシートの URL から一覧ページへ飛ぶページ |
data.json は level_order の順 → 曲名順に並べ替えて返します。
スプレッドシートの行順には依存しないので、シートを並べ替えたり行を追記しても順序は安定します。
曲名の比較は Intl.Collator("ja", { numeric: true }) です
(漢字は ICU 日本語照合の順。読み順にするにはシートに読みの列が必要です)。
<表の名前> はデータシートのタブ名です(URL エンコードされます)。
タブ名に / が含まれて指定できない場合は、代わりに gid を使えます。
段位認定も出せます。 シートを 1 枚足してヘッダシートの course 列で名指しすると、
header.json の course に入って beatoraja の選曲画面に段位コースが並びます
(書式は段位認定(course))。
| status | 意味 |
|---|---|
| 200 | 成功 |
| 400 | ID の形式が不正 |
| 404 | ルートが存在しない / そのシートが無い・難易度表でない(本文にシート一覧を出します)/ 段位シートの header.json を叩いた(本文で表の URL を案内します) |
| 502 | スプレッドシートが非公開 / 存在しない / 構成が条件を満たさない / Google がレート制限中 |
結果は 600 秒キャッシュします。
シート 1 枚 = CSV 1 本なので、シートの枚数がそのまま Google へのリクエスト数になります。 40 枚を超えるスプレッドシートを一度に全部取ると Google が 429 を返します (Vercel の出口 IP は共有なので、手元の端末から叩くより早く制限に当たります)。 そのため次のようにしています。
header.json・data.json は 3 リクエスト(段位認定を付けた表は 4)。
読むのは対象のデータシートとヘッダシートの 2 枚だけです。シート一覧(/htmlview)は
CSV を読まないので、枚数に関係なく 1 本で済みます。ヘッダシートは先頭のタブ 1 枚しか
見ないので、シートが何枚あっても本数は変わりません(詳細は下の「ヘッダシートの位置」)。
段位認定シートはヘッダ行の course 列が名指ししたときだけ読むので、
増えるのは段位を持つ表だけ 1 本です(探索はしません)。
段位認定のページは 3 本(曲名が書かれていない課題曲があるときだけ 4 本)です。/t/<ID>/ の一覧ページだけですが、
そこも Promise.all で一斉に投げず、4 本ずつに絞ります。Retry-After があればそれに従います。
再試行の待ち時間はリクエスト全体で 15 秒の予算を共有します
(Edge Middleware の実行時間上限は 25 秒)。ヘッダシートの CSV が読めなかった場合も、表そのものは返します
(name はタブ名、symbol は空文字になり、警告が出ます)。
ヘッダシートは先頭のタブにある場合だけ読みます。 どれがヘッダシートかは列名を
見るまで分からないので、対象のデータシートを除いた先頭の 1 枚だけを読んで判定し、
外れたら探しません。これで構成に関係なく必ず 3 リクエスト
(/htmlview + 対象のデータシート + 1 枚)で終わります。
探し続けないのは、リクエスト数がスプレッドシートの構成次第で膨らむからです。
以前は見つかるまで 1 枚 → 4 枚 → 4 枚…と読んでいたので、ヘッダシートが後ろにあるほど
増え(CSV 1 → 5 → 9 → …)、ヘッダシートを置いていない表では毎回全シートを
読んでいました(シート 40 枚で 41 リクエスト。打ち切る条件が無いため)。
beatoraja は難易度表を読み込むたびに表ページ・header.json・data.json を叩くので、
ここが上限を持たないのは具合が悪いです。
| 構成 | 以前 | 現在 |
|---|---|---|
| ヘッダが先頭 | 3 リクエスト | 3 リクエスト |
| ヘッダが 3 枚目 | 7 リクエスト | 3 リクエスト(ヘッダは読まない) |
| ヘッダ無し・データ 40 枚 | 41 リクエスト | 3 リクエスト |
ヘッダが先頭・course 列あり | — | 4 リクエスト |
段位認定シートを探さないのも同じ理由です。どのタブが段位かは列名を見るまで
分からないので、ヘッダ行の course 列に名指しさせています。course 列が空欄なら
1 本も増えず、名指ししたタブが読めなくても表自体は返します(警告を出します)。
先頭に無い場合、その表は name がタブ名、symbol が空文字になり、description は
消え、level_order はシートに出てきた level を並べたものになります。黙って変わると
気付けないので、表ページと一覧ページに警告を出します。一覧ページは全シートを読むので
ヘッダシートが何枚目にあるかを知っており、そこだけが「ヘッダシートはあるが先頭にない」と
断定できる場所です。
ヘッダシートを置いていない表(データシートだけで成立する構成)でも表ページの警告は 出ます。先頭 1 枚を見ただけでは「無い」のか「後ろにある」のか区別できないためで、 文言は「読んでいません」にしてあります。データシートが 1 枚だけなら読む先が無いので 警告は出ません(2 リクエスト)。
なお一覧ページ(/t/<ID>/)はヘッダシートの位置と無関係です。譜面数を出すために
どうせ全データシートを読むので、ヘッダがどこにあってもシートの枚数 + 1 本になります
(シート 47 枚のスプレッドシートで 48 本・約 9〜12 秒を実測)。一覧を速くするには
譜面数の列を捨てる必要があり、それは別の判断です。
一覧(/htmlview)を省いてシート名で直接 CSV を取れれば更に 1 本減りますが、
両方の手段が黙って壊れるので gid 経由にしています。どちらも実測で確認しました。
/export?format=csv&sheet=<名前> は sheet= を無視して常に先頭シートを返す。
sheet=ほげぇ でもヘッダシートの中身が 200 で返ってきます。エラーにならないので
気付けません。/gviz/tq?tqx=out:csv&sheet=<名前> は名前が効きますが、gviz が列の型を推論して
合わない値を空にします。 ほげぇ の level 列は数値と判定され、交互 が "" に
なりました。level が空の行は読み飛ばすので、譜面が黙って消えます。シート名も gid も決め打ちしていません。列名だけを見て役割を判定します。
| 役割 | 目印の列 |
|---|---|
| データシート | md5 または sha256 |
| ヘッダシート | name / symbol / level_order / description のいずれか |
| 段位認定シート | name と md5 / sha256 の両方 |
段位認定シートはデータシートと同じ列を持てるので、name 列があるかどうかで見分けます
(譜面名はデータシートでは title、段位シートの name は段位名)。ヘッダシートも name を
持ちますが md5 を持たないので、name と md5 の両方があれば段位認定シートです。
そのためデータシートに name 列を作らないでください(段位シートとして扱われます)。
md5 または sha256 の列を持つシート。1 行目が列名、2 行目以降が 1 行 1 譜面。
| level | title | md5 | artist | comment |
|---|---|---|---|---|
| 18 | -+ -EX- | bca300b4… |
level と title、そして md5 / sha256 のどちらかが必須。欠けた行は読み飛ばすartist comment など他の列はそのまま data.json に通るname 列は作らないこと。 name と md5 の両方があるシートは
段位認定シートとして扱われる<表の名前> になる。 対応するヘッダ行が無ければ表の名前にもなる表ページでは次の列がリンクになります。値が空の行は素のテキストで表示します。
| 列 | リンク先 | 由来 |
|---|---|---|
| Level | url_diff | 差分の配布元 |
| Title | Stellaverse IR の譜面ページ | md5 から生成 |
| Artist | url | 本体の配布元 |
url / url_diff はスプレッドシート由来の任意の文字列なので、http:// と https:// で
始まるものだけをリンクにします(javascript: などを踏ませないため)。
この 2 列は beatoraja 側でも使われます。url が BMSTableElement.getURL()、
url_diff が DifficultyTableElement.getAppendURL() として読まれ、
SongData の url / appendurl に入ります。表ページのリンク先を
url = 本体、url_diff = 差分としているのはこれに合わせたものです。
name / symbol / level_order / description のいずれかの列を持つシート。1 行目が列名、2 行目以降が 1 行 1 表。
| name | description | symbol | level_order |
|---|---|---|---|
| さつき難易度表 | ふつうの表 | ☆ | [1,2,3] |
| おにレベル表 | きつい表 | ◆ | [A,B] |
This is the beginning of the README. Read the rest at the original. Read the full README ↗