1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

シャニマスのガシャ・イベントロゴを収集したい!

Last updated at Posted at 2022-03-21

⚠️ : 加筆修正した記事を Zenn に投稿しています。

まえがき

アイドルマスターシャイニーカラーズ は バンダイナムコエンターテインメントが運営する、「THE IDOLM@STER シリーズ」のブラウザゲームです。

このシャニマス、とにかく「イラストが凄く綺麗」なのですが、ロゴも最高に綺麗 なんですよね...。

そんなロゴたちを集めていつでも見られるようにしたい!

方法

ガシャ・イベント共に、開催前にツイッターに以下のような動画が投稿される場合が多いので、今回はこの動画を保存して、ロゴ部分のフレームを画像としてエクスポートしようと思います。

環境

  • macOS Monterey 12.3 (Intel)
  • zsh
  • Python / Deno の環境がある
  • Twitter の開発者アカウントがある

手順

1. ツイートを集める

Twitter の Recent search API では 直近7日間のツイートしか検索できないので、Python製スクレイピングツールの Twint を利用しました。

pip3 install twint

Basic-usage を参考にしつつ検索していきます。

# 「復刻を除くシナリオイベントかつ、動画があるツイート」を検索してJSONで出力
twint -u imassc_official -s "シナリオイベント -復刻" --videos -o sc_event.json --json --limit 1000

これを実行すると検索結果を含んだ JSON ファイルが出力されます。

が、なぜか配列になっていないので手直しする必要があります。

私の場合は VSCode 上で、行頭・行末に角括弧を追加、正規表現検索で }\n$},\n に置換して対応しました。

sc_event.json
[
  {"id": 1505063643624665093, "conversation_id": "1505063643624665093", "created_at": "2022-03-19 15:08:30 JST", "date": "2022-03-19", "time": "15:08:30", "timezone": "+0900", "user_id": 958615648799662080, "username": "imassc_official", "name": "アイドルマスター シャイニーカラーズ公式", "place": "", "tweet": "「期間限定 聞こえていますか? 冬優子・真乃スタンプガシャPlus」のアップデート情報を動画でご紹介いたしますね~  #シャニマス #idolmaster  https://t.co/VvQuag1edY", "language": "ja", "mentions": [], "urls": [], "photos": [], "replies_count": 0, "retweets_count": 2280, "likes_count": 3776, "hashtags": ["シャニマス", "idolmaster"], "cashtags": [], "link": "https://twitter.com/imassc_official/status/1505063643624665093", "retweet": false, "quote_url": "https://twitter.com/imassc_official/status/1505063015292366848", "video": 1, "thumbnail": "https://pbs.twimg.com/ext_tw_video_thumb/1505063202748780546/pu/img/Z7_2g_k9kkNiMvKj.jpg", "near": "", "geo": "", "source": "", "user_rt_id": "", "user_rt": "", "retweet_id": "", "reply_to": [], "retweet_date": "", "translate": "", "trans_src": "", "trans_dest": ""},
  {"id": 1501801978669993986, "conversation_id": "1501801978669993986", "created_at": "2022-03-10 15:07:48 JST", "date": "2022-03-10", "time": "15:07:48", "timezone": "+0900", "user_id": 958615648799662080, "username": "imassc_official", "name": "アイドルマスター シャイニーカラーズ公式", "place": "", "tweet": "「SHEER 円香・愛依スタンプガシャ」のアップデート情報を動画でご紹介いたしますね~  #シャニマス #idolmaster  https://t.co/jS913EfKD2", "language": "ja", "mentions": [], "urls": [], "photos": [], "replies_count": 1, "retweets_count": 1951, "likes_count": 3322, "hashtags": ["シャニマス", "idolmaster"], "cashtags": [], "link": "https://twitter.com/imassc_official/status/1501801978669993986", "retweet": false, "quote_url": "https://twitter.com/imassc_official/status/1501801021454102530", "video": 1, "thumbnail": "https://pbs.twimg.com/ext_tw_video_thumb/1501801566273437698/pu/img/DoqJ66rj1-kgJLyA.jpg", "near": "", "geo": "", "source": "", "user_rt_id": "", "user_rt": "", "retweet_id": "", "reply_to": [], "retweet_date": "", "translate": "", "trans_src": "", "trans_dest": ""}
  // ...
]

2. 動画を保存する

先ほどの JSON ファイルからツイートの ID を読み込み、Twitter API の statuses/lookup を叩いて動画のURLを取得。URLにアクセスして動画を保存していきます。

今回使用したコードは以下のリポジトリに置いています。

Deno、便利 🦕

これで out/sc_event 以下に動画が保存されます。

deno run -A main.js sc_event.json --video

引っかかったとこ: Twitter API から動画の URL を取得する

現在提供されている Twitter API v2 では 動画のURLが取得できない ので、従来の Standard v1.1 を利用しました。

また、tweet_mode=extended を付加しないと extended_entities がレスポンスに含まれないのでお忘れなく...。

twitter.js
/**
 * 動画のURLを取得
 * @param {string[]} ids ツイートIDの配列
 * @returns 動画URLの配列
 */
export async function fetchVideoUrl(ids) {
  const limit = 100;

  /** @type {string[]} */
  let results = [];

  for (let i = 0; i < Math.ceil(ids.length % limit); i += limit) {
    // NOTE: API v2では動画のURLが取得できないのでv1.1のエンドポイントを使用
    const endpointUrl = new URL(
      "https://api.twitter.com/1.1/statuses/lookup.json"
    );

    endpointUrl.searchParams.append("tweet_mode", "extended");
    endpointUrl.searchParams.append("id", ids.slice(i, i + limit).join(","));

    const res = await fetch(endpointUrl.toString(), {
      headers: {
        Authorization: `Bearer ${Deno.env.get("BEARER_TOKEN")}`,
      },
    });

    /** @type {TweetResponse[]} */
    const json = await res.json();

    const urls = json.map((e) => {
      // bitrateがないものを除外
      const variants = e.extended_entities.media[0].video_info.variants.filter(
        (e) => typeof e.bitrate !== "undefined"
      );

      // ビットレートで降順ソート
      variants.sort((a, b) => b.bitrate - a.bitrate);

      return variants[0].url;
    });

    results = results.concat(urls);
  }

  return results;
}

3. 動画からロゴ部分のフレーム画像を抜き出す

ffmpeg を利用します。

ガシャの動画の場合

動画の冒頭1秒から2秒間程度ロゴが表示されるので、そこを画像としてエクスポートします。

参考: 期間限定 聞こえていますか? 冬優子・真乃スタンプガシャPlus

for file in *.mp4; do
  ffmpeg -ss 1 -i "$file" -t 2 -r 1 "${file%.*}_%d.png"
done

シナリオイベントの場合

「Star n dew by me」のように最初にロゴが表示されるパターンと、「天塵」のように最後にロゴが表示されるパターンがあります。

この2つに対応するため、以下のようにしました。

for file in *.mp4; do
  # 冒頭2秒からの2秒間を切り出す
  ffmpeg -ss 2 -i "$file" -t 2 -r 1 "${file%.*}_0_%d.png"
  # 末尾4秒前からの2秒間を切り出す
  ffmpeg -sseof -4 -i "$file" -t 2 -r 1 "${file%.*}_1_%d.png"
done

4. 選別する

以上までの工程を踏むと png 画像が何枚か出力されるので、あとは人力で選別していきます...。

あとがき

image.png

ガシャについては動画があるもの・ないものがあるらしく(?)全ては収集できませんでしたが、シナリオイベントについてはほとんど網羅できました!

image.png

ここまでやってから、最近実装された「Pデスク」でシナリオイベントのロゴが見れることに気づいたのは内緒です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?