internals¶
Programming conventions, internal working mechanisms and metadata behaviour.
Conventions¶
- Program internal, all tags and assertion values are saved lowercase (casefold), except ID3 tags which are uppercase.
Variable Naming¶
| code_long | code_short | description |
|---|---|---|
| tag | t | single tag key-value-pair e.g. ‘artist’: Elvis |
| tag_key | key, k | single key of tag e.g. ‘artist’ |
| tag_val | val, v | single value of tag e.g. ‘Elvis’ |
| frame_id | single tag id3-key e.g. ‘TPE1’ | |
| tags | any arbitrary list or dictionary of tag | |
| audio | a | AudioFile containing the metadata of a file as dict |
| datab | d | Database |
| meta | m | Metadata containing audio and datab |
Behaviour Metadata¶
- multivalues are internally saved as list
- multiple values in one string, which are divided by a char, will be extracted to multivalues and saved as in 1)
- comment is used as default and not description
- album artwork is/are a dict item as any other tags on metadata level. Differentiation between Picture Types and how to represent them in the allocation map might needs to be implemented. A possible implementation would be to use the id3 picture types eg.:
xiph¶
- multivalues of a key are writen as multiple tags
mp3¶
- no format checking of values (e.g. TimeStampTextFrame)
- all values are written as utf8, encoding options have to be defined
module formats¶
effect of options in file_save¶
In dict meta are to kinds of “empty”:
- None : tag does not exist (and was not in source file metadata)
- Empty: tag does exist with an empty value (and was saved in source file with empty value). Values are handled as if they had a value.
Save behaviour:
- None is skipped, existing data on file is unchanged
- Empty write_empty==False: if tag exists in target it is deleted
- Empty write_empty==True : tag with value Empty is saved in file (and created)
- Value is always writen to file (and created)
The equivalent of write_empty (also remove empty) behaviour differs between programs:
- write_empty==False: audacious, EasyTAG, Kid3, VLC Media Player, Clementine
- write_empty==True : Ex Falso (QuodLibet), MusicBrainz Picard, Puddletag
To clear values not in the metadata list (=unprocessed tag) or to clear empty values without loading the dict with those, use remove_existing.
Testing (pytest)¶
Test is not optimized for speed. Each functions that writes to files, copies all or some data from the 3 source folders A_flac, B_mp3, C_mp3 to its onw test (pytest.fixture).
In test_formats.py was tested that all supported formats are loaded correctly into the AudioFile dict. It is therefore enough to test metadata only with one format which will be flac. Mmusicc test are using mp3 as target since it is a very common format.
All Tests use the default association map delivered with the program. Only test_formats uses a special mapping, which tries to test every possible tag, especially for ID3.
General¶
- database.db is the exported file of
test_export_db_tag[AlbumMetadata]with the title column intagsandpickle_tagsset to NULL (e.g. sqlitebrowser)
music_lib¶
- A_flac and B__flac have identical structure and metadata
- B_flac media stream is created with ffmpeg and without any arguments and custom metadata
- C_flac uses files from B_flac with additional changes in metadata, artist_puddletag is missing
- Test file metadata is manipulated with kid3 for the default files and ex falso for files with empty tags
A_flac¶
- Folder with a dot in its name (risk of confusion with file if path does not (yet) exist) and an audio file full of characters that need to be masked in Bash and are most likely to cause problems.
- Playlist file that has an “audio/” MIME-Type and should be ignored.
- This is also the only testfile with an album cover and a tag overhead (copy of formats_xiph.flac).
C_ogg metadata changes¶
- missing date
- wrong album title (“wrong metadata, also date is missing”)
- composer tag (“should not be here”)
- license tag (“cc”)
- CD_01 got the default format
- CD_02 has the same tag keys that CD_01 that plus tag keys with empty values
test data structure¶
data/music_lib
├── A_flac
│ ├── artist_puddletag
│ │ ├── album_good_(2018)
│ │ │ ├── 01_track1.flac
│ │ │ └── 02_track2.flac
│ │ └── audio_at_artist_level.flac
│ ├── artist_quodlibet
│ │ ├── album_bar_-_single_(2020)
│ │ │ └── 01_track1.flac
│ │ └── album_fuu_(2019)
│ │ ├── 01_track1.flac
│ │ └── 02_track2.flac
│ └── various_artists
│ ├── album_best_hits_compilation_(2010)
│ │ ├── CD_01
│ │ │ ├── 01_track1.flac
│ │ │ └── 02_track2.flac
│ │ └── CD_02
│ │ ├── 01_track1.flac
│ │ └── 02_track2.flac
│ └── Escape_Character_No._1_(2012) --> A)
│ ├── A dot. Don't_Stop! & Who put the bomb out?.flac --> C)
│ └── playlist.m3u --> B)
├── B_ogg
│ ├── artist_puddletag
│ │ ├── album_good_(2018)
│ │ │ ├── 01_track1.ogg
│ │ │ └── 02_track2.ogg
│ │ └── audio_at_artist_level.ogg
│ ├── artist_quodlibet
│ │ ├── album_bar_-_single_(2020)
│ │ │ └── 01_track1.ogg
│ │ └── album_fuu_(2019)
│ │ ├── 01_track1.ogg
│ │ └── 02_track2.ogg
│ └── various_artists
│ ├── album_best_hits_compilation_(2010)
│ │ ├── CD_01
│ │ │ ├── 01_track1.ogg
│ │ │ └── 02_track2.ogg
│ │ └── CD_02
│ │ ├── 01_track1.ogg
│ │ └── 02_track2.ogg
│ └── Escape_Character_No._1_(2012)
│ └── A dot. Don't_Stop! & Who put the bomb out?.ogg
├── C_ogg
│ ├── artist_quodlibet
│ │ ├── album_bar_-_single_(2020)
│ │ │ └── 01_track1.ogg --> 1)
│ │ └── album_fuu_(2019)
│ │ ├── 01_track1.ogg
│ │ └── 02_track2.ogg
│ └── various_artists
│ └── album_best_hits_compilation_(2010)
│ ├── CD_01 --> 2)
│ │ ├── 01_track1.ogg
│ │ └── 02_track2.ogg
│ └── CD_02 --> 2)
│ ├── 01_track1.ogg
│ └── 02_track2.ogg
└── README.rst