X Media Assist for Safari

X (Twitter) の動画・GIFアニメを保存するmacOS用Safari拡張機能

View the Project on GitHub jemielttf/X-MediaAssist-safari

X Media Assist for Safari

Xに投稿された動画はMP4、GIFアニメはgifskiでGIFへ変換してMacのダウンロードフォルダへ保存するSafari拡張です。GIFはXが配信するMP4から再生成するもので、アップロード時の原本GIFを復元するものではありません。

保存できるもの

非公開・削除済み・閲覧制限のある投稿、Syndicationで公開されないメディア、ライブ配信/HLS、引用先の自動ダウンロード、静止画は対象外です。引用動画は引用元の投稿を開いてください。MP4の画質選択・保存先変更・履歴・再開機能も今回の対象外です。

起動

macOS 14以降のApple Silicon搭載Mac(arm64)のみ対応しています。Intel搭載Mac(x86_64)には対応していません。生成したXcodeプロジェクトはXcode 27形式です。今回の開発・動作確認環境はmacOS 27 / Xcode 27 / Safari 27です。

Rust 1.98.1(rustup)が必要です。Xcodeのビルドフェーズとスクリプトの両方で、同じ固定版gifskiの静的ライブラリを生成します。初回はCargo依存のダウンロードが必要です。FFmpegや外部Gifski.appは不要です。

署名のTeam IDはローカル設定で管理します。初回はリポジトリのルートで次を実行し、作成した Signing.local.xcconfig の YOUR_TEAM_ID を自分のTeam IDへ置き換えてください。

cp "X Media Assist/Config/Signing.local.xcconfig.example" "X Media Assist/Config/Signing.local.xcconfig"

共有の Signing.xcconfig がこのファイルを任意読み込みし、本体・ExtensionのDebug/Releaseへ同じTeam IDを適用します。Signing.local.xcconfig はGit管理対象外です。ファイルがない場合はTeam IDが空になるため、署名付きビルドにはローカル設定またはビルド時の DEVELOPMENT_TEAM 指定が必要です。Team IDの変更はこのファイルで行ってください。XcodeのSigning & CapabilitiesでTeamを選び直すと、project.pbxproj に値が書き戻されます。

バージョン番号(MARKETING_VERSION)とbuild番号(CURRENT_PROJECT_VERSION)は共有の Signing.xcconfig で管理し、本体・ExtensionのDebug/Releaseへ共通で適用します。Signing.local.xcconfig にはTeam IDだけを記述してください。バージョン番号を変更するときは、Extension/manifest.json の version も同じ値へ更新します。

gifskiは固定コミットを参照するGit submoduleです。新規取得時は git clone --recurse-submodules https://github.com/jemielttf/X-MediaAssist-safari.git を使用するか、既存のcloneで以下の初期化コマンドを実行してください。

git submodule update --init --recursive
rustup toolchain install 1.98.1 --profile minimal
# Apple Silicon向けのターゲットを用意
rustup target add --toolchain 1.98.1 aarch64-apple-darwin
./script/build_and_run.sh

CodexのRunボタンも同じコマンドを実行します。アプリは build/DerivedData/Build/Products/Debug/X Media Assist.app に生成されます。

  1. 上記のローカル設定でTeam IDを指定し、そのTeamの開発用証明書をXcodeで利用できる状態にします。本体とExtensionの両ターゲットは同じTeamと自動署名設定を使用し、スクリプトもアドホック署名への上書きは行いません。本体のBundle Identifierは com.jemielttf.XMediaAssist、Extensionは com.jemielttf.XMediaAssist.Extension です。
  2. 案内アプリのボタンからSafariの拡張機能設定を開き、X Media Assist を有効にします。見つからない場合はアプリを起動し直し、Safariの設定を開き直してください。
  3. 使用するSafariプロファイルで有効にし、Xと cdn.syndication.twimg.com へのアクセスを許可してください。
  4. 投稿を開き直し、投稿内の「動画を保存」、またはツールバーの拡張ボタンから保存します。
  5. 完了表示までタブ・ポップアップを開いておいてください。途中で閉じた場合や通信が切れた場合は、再試行前にダウンロードフォルダを確認してください。

初期MVPの検証ではアドホック署名とSafariの「未署名の機能拡張を許可」を使用しましたが、現在のスクリプトはXcodeの開発用署名設定に従います。配布用の署名・公証・App Store対応は含めていません。

Bundle Identifierを変更した後は、Safariを通常終了して起動し直し、Xのページも再読み込みしてください。Safariが旧Identifierのネイティブ接続先を保持していると、拡張が有効でも保存できないことがあります。保存開始前に接続を確認し、15秒以内に応答しない場合は再起動を案内します。

./script/build_and_run.sh --build-only  # ビルドのみ
./script/build_and_run.sh --verify      # ビルド・起動・プロセス確認
./script/build_and_run.sh --logs        # 起動してネイティブログを表示
./script/test.sh                        # Node + Swiftの隔離テスト

メニューバーと起動設定

起動中はメニューバーにX Media Assistのアイコンが表示されます。ウインドウを閉じても常駐し、アイコンのメニューから再表示・終了できます。

ログイン時起動を使う場合は、アプリを継続利用する場所(例:/Applications)に置いてから有効にしてください。

構成

場所 責務
Extension/content.js 投稿の検出、保存ボタン、結果表示
Extension/core.js URL検証、Syndication解析、MP4選択、保存の順序制御
Extension/background.js メッセージ送信元確認、同時実行制御、nativeMessaging
Extension/popup.* URL指定の保存UI
AppSupport/AppPreferences.swift Dock表示の設定保存、ログイン項目の登録・解除
Native/GIFConverter.swift AVFoundationでフレーム抽出、libgifski C APIでGIF生成
Native/MediaSave.swift 形式の分岐、GIF失敗時のMP4保持
Native/MediaDownload.swift URLSessionでストリーム保存、応答検証、上書きしない公開処理
X Media Assist/ macOS案内アプリ、Safari Web ExtensionのXcodeプロジェクト
Tests/ JavaScript/Swiftの回帰テスト、ブラウザ用UI fixture

共通CSSの正本は Docs/layout/style.css です。画面のレイアウトは Tests/localization-fixture.html で実際のHTMLとCSSを使って確認します。Extension/popup.css とアプリの Resources/Style.css はこのCSSへの相対シンボリックリンクです。管理画面の Resources/X-Media-Assist.svg も X-Media-Assist.icon/Assets/X-Media-Assist.svg への相対リンクです。リンク先はリポジトリ内で完結し、Xcodeの Copy Bundle Resources がリンクを解決して通常ファイルとしてバンドルします。CSSはリンク先の正本を編集してください。Gitのチェックアウト時はシンボリックリンクを保持してください。

SafariのnativeMessagingは同梱されたApp Extensionが受信し、そのプロセスがダウンロード・変換・保存を行います。GIF基本設定のみ、両ターゲットの App Group $(DEVELOPMENT_TEAM).com.jemielttf.XMediaAssist.shared の UserDefaults に共有します。別の常駐ヘルパーやデータベースは導入していません。案内アプリ自体にダウンロード処理を中継する構成ではありません。

多言語対応

保存境界と制約

GIF変換

ライセンスとソース配布

Copyright 2026 X Media Assist contributors. プロジェクト全体の配布ライセンスは AGPL-3.0-or-later です。本文は LICENSE を参照してください。第三者コードはそれぞれの著作権表示・ライセンスを維持し、Licenses/THIRD_PARTY_NOTICES.txt 、Rust標準ライブラリの表示 と Vendor/README.md に記録しています。案内アプリの「ライセンス・著作権表示」からもライセンス本文を開けます。

配布はビルド済みアプリを対象とし、自分でビルドする場合はこのリポジトリをcloneしてください。配布ページには、アプリをビルドしたコミットのリリースタグと、そのタグのソース・ビルド手順へのリンクを記載します。

リポジトリ: https://github.com/jemielttf/X-MediaAssist-safari

配布版と同じソースからビルドする場合

git clone --recurse-submodules https://github.com/jemielttf/X-MediaAssist-safari.git
cd X-MediaAssist-safari
git switch --detach <配布ページに記載されたリリースタグ>
git submodule update --init --recursive

その後、上記「起動」の手順に従ってRustツールチェーンとXcodeを用意し、Signing.local.xcconfig に自分のTeam IDを設定してビルドします。配布者の秘密鍵や証明書はリポジトリに含めません。

親リポジトリのタグがgifskiのコミットを固定し、gifski内の Cargo.lock がRust依存を固定します。ビルドには cargo build --locked を使用し、Rust自体は rust-toolchain.toml で固定します。初回は依存ソースを取得するネットワーク接続が必要です。GitHubが自動生成する「Source code」ZIP/tar.gzにはsubmoduleの実ソースが含まれないため、上記のclone手順を使用してください。

専用のソースアーカイブ作成は通常の配布手順に含めません。ライブラリ更新時は python3 script/generate_notices.py で著作権表示を再生成します。配布用署名・公証は別工程です。

技術資料