Skip to content

Deleted Metadata Preservation

log5r edited this page Oct 7, 2026 · 2 revisions

削除した歌詞と文字項目の保持

Issue #36 では、歌詞の削除状態が再起動で失われ、ファイルに残った別のタグから削除済みの値が復元される問題を扱った。同じ P2 の性能改善より、保存した編集結果が変わる問題を優先した。

保存先と優先順位

歌詞の編集済み状態と、文字項目の編集済み状態を MediaItem の Bool 値として保存する。歌詞はアプリ内保存と埋め込み保存のどちらでも編集済みとして扱い、空値は lyricsRaw == nil と編集済み状態の組み合わせで保持する。歌詞の自動読み込みは未編集の曲に限定し、非同期の結果を適用するときにも編集済み状態を確認する。

文字項目のライターは編集可能な文字項目を一式置換する。そのため、文字項目を保存した後は、編集用ドラフトと一括編集でライブラリに保存した一式を優先する。空値も埋め込み値で補完しない。曲名・アーティスト・アルバムについては、表示用の補完値とは別の editedTitle、editedArtist、editedAlbum に編集値を保存する。空文字と、文字列としての Unknown Artist・Unknown Album を区別して保持する。歌詞だけ、または画像だけの編集では文字項目を更新せず、文字項目の編集済み状態も設定しない。情報編集では読み込み時のドラフトと全ての文字項目を比較し、一括編集では選択された項目に文字項目が含まれるかを確認する。拡張音声形式からドラフトを作る場合も同じ優先順位を適用する。Music の照合は取り込み時に限定する既存の仕様を維持する。

既存ストアでは二つの状態を false として移行する。旧バージョンでの意図的な削除と、初めから値がなかった状態は保存データから区別できない。今回の修正前に削除した項目について、削除の意図をさかのぼって復元することはできない。

ファイル内の復元元の除去

ID3 の歌詞変更・削除では USLT/ULT と SYLT/SLT をまとめて除去し、必要な場合だけ新しい非同期歌詞を保存する。歌詞を変更しない文字項目編集では同期歌詞を保持する。フレーム名と本文の構成は ID3v2.2 の仕様 と ID3v2.4 の仕様 で確認した。

MP4 の文字項目変更・削除では sonm、soar、soal、soaa、soco も除去する。これらは読み取り側で表示値の代替として使用するため、残すと空値が再び埋まる。歌詞だけ、または画像だけを編集するときはソート用タグを保持する。タグの対応はアプリの MP4 リーダーと FFmpeg の MOV リーダー で確認した。

再現と検証

環境は macOS 27.0.1、Xcode 27.1 Beta(27A9269)、Swift 6.4。作業のベースは e7660a7。

条件 確認方法と結果
アプリ内の歌詞削除後に再起動 ディスク上の SwiftData ストアとサービスを再作成。修正前は削除した歌詞が復元された。修正後は空値を維持し、置換した歌詞も維持した
歌詞の埋め込み保存後に再起動 同じストア再作成テストで、削除・置換とファイルの再読み込みを確認
同期歌詞と非同期歌詞の併存 ID3v2.2、2.3、2.4 のフィクスチャで削除・置換を実行。修正前は同期歌詞が残存。修正後は両方を除去し、未知フレームと音声のバイト列を保持
MP4 ソート用タグからの復元 五つのソート用タグを持つフィクスチャで文字項目を削除・置換。修正前は削除値が復元され、置換時も古いソート値が残存。修正後はソート用タグを除去し、未知項目と音声を保持
関係のない編集 ID3 の文字項目編集で同期歌詞を保持。MP4 の歌詞編集でソート用タグを保持
古い埋め込み値の再読み込み 再生可能な MP3、M4A、WAV、FLAC に保存した後、古いタグを再配置。新しい ModelContext とサービスのドラフト、および一括編集でも保存済みの空値を優先
遅い歌詞読み込み 読み込みを保留し、歌詞を空にして埋め込みメタデータを保存した後に古い結果を返す。古い歌詞は適用されない
未編集曲と旧ストア 既存の自動補完・キャンセル・削除中の読み込みテストを維持。旧スキーマからの移行でメタデータ、プレイリスト、アートワークと二つの初期値を確認
ID3v1 と v2 のコメント併存 再生可能な MP3 に両方のタグを配置してコメントを削除。v1 の末尾タグは保持され、編集画面の値は空のまま。今回の AVFoundation では v1 のコメントへのフォールバック自体は観測されなかった

修正前の六つの回帰テストで 16 件の期待値失敗を確認した。修正後はメタデータ・歌詞・キャンセル関連の 82 テストが通過し、追加した再読み込みと遅い完了のケースを含む最終の三つのスイート(18 テスト)も通過した。最後に歌詞の埋め込み保存後のストア再作成を同じ回帰スイートで確認した。

macOS のテストビルドと iOS Simulator 向けアプリビルドが通過した。iPhone/iPad 実機、iOS 上での単体テスト、画面操作のテストは未実施。UI の変更はない。通常の SwiftLint は通過し、キャッシュを使わない比較でも修正前後とも既存警告 43 件、エラー 0 件だった。新しいファイル・ルールの警告は増えておらず、ベースラインは変更していない。--strict は既存警告によって失敗する。

MP3 フィクスチャは 440 Hz、44.1 kHz、0.1 秒の合成音を FFmpeg の libmp3lame で 64 kbps に符号化し、ID3 タグを付けずに生成した。FFmpeg はテスト用ファイルの生成だけに使い、アプリやテスト実行時の依存には追加していない。

表示用の補完値と画像だけの保存に関する追加検証

PR #45 の二つの指摘は回帰テストで再現できた。最初の実装は表示用の値から編集用ドラフトを再構築していたため、空にした曲名をファイル名に戻し、後の保存でファイル名を埋め込んでいた。文字列として保存した Unknown Artist と Unknown Album も未設定値として扱っていた。修正前には、MP3/M4A のディスクストア再作成後の読み込みと単独・一括の再保存、および画像だけの一括保存で計 31 件の期待値失敗を確認した。

追加修正では上記三項目の編集値を表示値と分けて永続化し、画像だけの保存では文字項目の変更フラグを設定しないようにした。変換書き出しでもアーティスト・アルバムの編集値を読み、コピーに引き継ぐ。コピー用の値は書き出し開始時のスナップショットに保持する。

最終の macOS 検証は 13 スイート、84 テストが通過した。空の曲名と Unknown Artist・Unknown Album の保存、ストア再作成、再保存、変換コピーのタグとモデルの一致、情報編集と一括編集それぞれの画像だけの保存、画像と文字項目の同時保存を確認した。全ての編集可能な文字項目について情報編集の変更判定も確認した。画像だけの保存では五つの MP4 ソート用タグとライブラリの文字項目を維持し、文字項目も変更する場合はソート用タグを除去する。旧ストア移行では追加した三つの保存先が nil となることを確認した。

追加修正でも通常の lint は既存警告 43 件、エラー 0 件で通過し、ベースラインは変更していない。iPhone/iPad 向けの iOS Simulator アプリビルドも通過した。iOS 上での単体テストと実機確認は未実施。保存の変更判定は画面から呼ぶ共通関数を用いたサービス統合テストで確認し、画像選択を含む UI テストは未実施。

Clone this wiki locally