beetbox / mediafile Goto Github PK
View Code? Open in Web Editor NEWelegant audio file tagging
Home Page: http://mediafile.readthedocs.io/
License: MIT License
elegant audio file tagging
Home Page: http://mediafile.readthedocs.io/
License: MIT License
I use mediafile to update my music as an alternative Python-based ampache scanner, and it works wonderfully; however the one field that ampache needs is the barcode field, and I have to make an expensive call back to MusicBrainz to fetch it. Can someone please add the barcode field to the list of fields that it can retrieve?
Building python-mediafile
from the AUR I'm getting the following error. https://aur.archlinux.org/pkgbase/python-mediafile/#comment-758314
~/python-mediafile/src/python3 ~/python-mediafile/src ~/python-mediafile/src
..............................................................................................................................................................................................................................................................................................................................................................................F.....................................................................
======================================================================
FAIL: test_read_audio_properties (test.test_mediafile.WavpackTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/build/python-mediafile/src/python3/test/test_mediafile.py", line 442, in test_read_audio_properties
self.assertEqual(getattr(mediafile, key), value)
AssertionError: 16 != 0
----------------------------------------------------------------------
Ran 436 tests in 0.983s
I tried naively changing the value to 16 on line 910 of the test but the test then fails for python2. I'm not sure what the right change is. https://github.com/beetbox/mediafile/blob/master/test/test_mediafile.py#L910
An outgrowth of #1043, courtesy of @kiefermat.
Apparently, some software uses COMM
frames with funky desc
fields to store comments. (Most software uses an empty desc
field.) We should probably read these various frames and interpret them as comments.
The question gets a little trickier when writing. I lean toward automatically "standardizing" these fields when MediaFile needs to update the tag—i.e., we should drop any frames with custom desc
fields and just preserve the desc
less frame. That would require a bit more machinery than we currently have in MP3DescStorageStyle
.
I'm getting a test failure in the 0.10.0 release (Arch Linux, python 3.10.8), is that an oversight?
..............................................................................................................................................................................................................................................................................................................................................F.....................................................................................................................................
======================================================================
FAIL: test_read_audio_properties (test.test_mediafile.WAVETest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/build/python-mediafile/src/mediafile/test/test_mediafile.py", line 445, in test_read_audio_properties
self.assertEqual(getattr(mediafile, key), value)
AssertionError: 705600 != 88200
----------------------------------------------------------------------
Ran 468 tests in 0.563s
FAILED (failures=1)
Hi!
I'm trying to use this library with remote files defined as file-likes in python.
mutagen
supports file-like interface, why tho mediafile doesn't?
Docs say it needs local file path as first argument.
Working on a personal project and noticed that the PyPi release hasn't been updated to 0.4.0 yet. Thought I would bring it to your attention since it seems likely that it was an oversight.
The test suite is currently failing across both Python2 and Python3, on multiple Linux distributions:
.........F........................................................................................................................................................................................................................................................................................................................................................................................................................................
======================================================================
FAIL: test_read_audio_properties (test.test_mediafile.AIFFTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/apetresc/src/arch/python-mediafile/src/python3/test/test_mediafile.py", line 441, in test_read_audio_properties
self.assertEqual(getattr(mediafile, key), value)
AssertionError: 16 != 0
----------------------------------------------------------------------
Ran 434 tests in 0.663s
FAILED (failures=1)
This is blocking the Arch Linux AUR package (among, probably, many others) for everyone.
I think we should have some tests for this as It's supported but not checked if works.
I'm pretty sure big part of multi-valued tags would fail and this should be only expected failure.
Apparently, some iTunes version around 12.5 changed the "grouping" field to map to the ID3 tag GRP1
, while it was previously TIT1
. It now uses TIT1
for the work title. Picard apparently has a special config option just to deal with this mapping. There's more info in a post on the mp3tag forum.
We do this for beets, but should we do it for mediafile?
Hey!
When trying to fix my PR (#42) I found error I can't fix myself.
mb_artistid
and mb_albumartistid
should be lists!
mediafile
's version:
>>> mf = MediaFile('/home/juniorjpdj/temp/Szpadyzor crew.flac')
>>> mf.mb_artistid
'164ed128-525d-4798-883f-be13502de86b'
>>> mf.mb_albumartistid
'164ed128-525d-4798-883f-be13502de86b'
Picard is tagging it like that (mutagen dump):
FLAC: 'musicbrainz_artistid': ['164ed128-525d-4798-883f-be13502de86b', 'ded6b91b-26d8-4bc0-8aa9-fab9b54cd157'],
MP3 (ID3v2.3): 'TXXX:MusicBrainz Artist Id': TXXX(encoding=<Encoding.UTF16: 1>, desc='MusicBrainz Artist Id', text=['ca780457-5bf6-4a7d-95fa-0d0d4920f26f/258a67d1-172d-4d16-8d6c-09efde60896f/af262d86-542d-4864-8527-083065166d6c']),
Fixing FLAC parsing is easy I can't make it work in MP3 case.
Current code is returning only the first author in case of FLAC and '/'
separated string in case of MP3.
Helping fixing this will also help me solve some problems with #42 ;D
Going off the docs.
MediaFile attempts to always return a usable value (i.e., it never returns None or throws an exception when a tag is accessed). If a tag is not present, an empty and false value of the appropriate type – such as zero or the empty string – is returned.
>>> f = MediaFile('tests/resources/audio_files/empty.mp3')
>>> print(f.title)
None
Is this going against expected behavior or maybe the docs are out of date?
I started the import run for the first time, and it got about ten albums in, and this happened.
The import will not proceed past this problem.
beet crash.txt
.
Please let me know what other information I need to provide here to assist you.
Looks like imghdr is deprecated as of python 3.11 and slated for removal in 3.13.
https://peps.python.org/pep-0594/
Some alternatives suggested in the above pep:
https://pypi.org/project/filetype/
https://pypi.org/project/puremagic/
https://pypi.org/project/python-magic/
While testing beets with pytest
under windows python 3.12 the following warning appears:
C:\Users\arogl\AppData\Local\Programs\Python\Python312\Lib\site-packages\mediafile.py:52
C:\Users\arogl\AppData\Local\Programs\Python\Python312\Lib\site-packages\mediafile.py:52: DeprecationWarning: 'imghdr' is deprecated and slated for removal in Python 3.13
import imghdr
As discussed in beetbox/beets#3849 and beetbox/beets#4060, we would like most/all of beets file operations to be atomic. This requires support by mediafile, which in turn either requires that we shuffle around temporary files, or someone invests the time to implement this in mutagen directly, see quodlibet/mutagen#241. Ideally, on CoW file systems, this should use reflinks.
While packaging mediafile
0.5.0 for NixOS I encountered a test failure:
python2.7-mediafile> ======================================================================
python2.7-mediafile> FAIL: test_known_fields (test.test_mediafile.MediaFieldTest)
python2.7-mediafile> ----------------------------------------------------------------------
python2.7-mediafile> Traceback (most recent call last):
python2.7-mediafile> File "/build/mediafile-0.5.0/test/test_mediafile.py", line 979, in test_known_fields
python2.7-mediafile> assertCountEqual(self, MediaFile.fields(), fields)
python2.7-mediafile> File "/nix/store/k948hw0if2rsyicvxgqa07mpfjqsr0gv-python2.7-six-1.15.0/lib/python2.7/site-packages/six.py", line 679, in assertCountEqual
python2.7-mediafile> return getattr(self, _assertCountEqual)(*args, **kwargs)
python2.7-mediafile> AssertionError: Element counts were not equal:
python2.7-mediafile> First has 1, Second has 0: u'read_only_test'
python2.7-mediafile> ----------------------------------------------------------------------
python2.7-mediafile> Ran 436 tests in 1.072s
python2.7-mediafile> FAILED (failures=1)
python2.7-mediafile> Test failed: <unittest.runner.TextTestResult run=436 errors=0 failures=1>
python2.7-mediafile> error: Test failed: <unittest.runner.TextTestResult run=436 errors=0 failures=1>
builder for '/nix/store/av1l4k090h4sphyn8glj1nj3i1lcdh6a-python2.7-mediafile-0.5.0.drv' failed with exit code 1; last 10 log lines:
return getattr(self, _assertCountEqual)(*args, **kwargs)
AssertionError: Element counts were not equal:
First has 1, Second has 0: u'read_only_test'
----------------------------------------------------------------------
Ran 436 tests in 1.072s
FAILED (failures=1)
Test failed: <unittest.runner.TextTestResult run=436 errors=0 failures=1>
error: Test failed: <unittest.runner.TextTestResult run=436 errors=0 failures=1>
The tests seem to pass with Python 3.8, however
The other day I noticed that a lot of my files were missing label info in the beets database, despite them being present on the files. After a little looking, I realized that the beets tag definition was cased differently than how its listed on MusicBrainz tag mapping page.
The code in question:
https://github.com/beetbox/beets/blob/7b910c3fde50d502e63b890f1ffbaf3d59515c0e/beets/mediafile.py#L1736
On https://picard.musicbrainz.org/docs/mappings/ it is listed as:
----:com.apple.iTunes:LABEL
I ended up just replacing it on my machine for the quick fix(which worked, but probably not ideal for the official fix).
Could you please eventually consider to drop dependency on six as long as you declare only python 3 support?
I'm trying to make my files as small as possible and I'd like to control padding - that's just my use case and probably there will be much more examples of why should it be passable.
Beets seems to write replaygain tags in upper-case regardless of changelog entry 1.3.2 (https://beets.readthedocs.io/en/stable/changelog.html#december-22-2013)
There are music player in the wild which only understand lower-case ReplayGain tags (Poweramp, KODI),
Using an actual version of a of Beets I can only find upper-case ReplayGain tags in my tagged files with a hex editor.
How I reproduced it:
First of all I download a clean WAV file from bandcamp.com for example. I convert the WAV file to a mp3 via Foobar using the Lame mp3 encoder. After that this mp3 goes into Picard. This goes through processing by Beets. For this I use a Docker Beets version (https://hub.docker.com/r/linuxserver/beets). After that I get a mp3 file including replay gain tags: 03 - New Light.mp3
Using a hex editor I only find upper-case ReplayGain tags in the header of the file.
I tried beet write
(files have not changed) and beet write -f
(files have changed) but the mp3 files still only have upper-case ReplayGain tags.
I also tried: I have completely deleted the ReplayGain tags from a series of files with zero
and checked that they are really gone in the library (info
) and the mp3 files. After that I re-tagged them with replaygain (replaygain
). After that I found the ReplayGain tags again, only in upper-case.
I changed the lines of code in mediafile.py (
Lines 2202 to 2209 in edc6414
scrub
and relaygain
again, I suddenly found two tags in my file, although with different names.It seems that Plex does not properly detect the com.apple.iTunes:ORIGINAL YEAR'
freeform tag (presented as ORIGINAL YEAR
by ffmpeg).
After searching around the web for a little, it seems that Plex relies on ffmpeg output to determine tags of certain files. I've confirmed with a small script (https://gist.github.com/chrisbenincasa/3b518514fd2b0d3cc494dce82b84b3a7) that setting ----:com.apple.iTunes:ORIGINALDATE
on m4a files (mimicking behavior of Vorbis) allows Plex to recognize the tag.
It doesn't seem as if Picard defines a mapping for this field (https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html) but the popular Typescript music-metadata library has mappings for to ORIGINALYEAR
and ORIGINALDATE
: https://github.com/Borewit/music-metadata/blob/master/doc/common_metadata.md
One way to fix this would be to append a second tag for m4a called ORIGINALDATE
. I'm not sure what the implications of changing the existing tag would be... It would probably also be beneficial to set ORIGINALYEAR
in a separate tag.
It would be cool to have support for this. It enables karaoke parties to be based on mediafile
tagged files ;D
I'm doing some research how this should be handled in files.
The only thing I found atm is ID3v2.4 SYLT
frame and that Tidal for example sends to client LRC formatted data through API.
If a list tag does not exist, it returns an empty list rather than None
, but if a non-list tag does not exist, None
is returned.
I believe non-existent list tags should also return None
so that this behavior is consistent.
The Python standard library will be removing imghdr
in Python 3.13. Per PEP-594 suggested replacements are filetype, puremagic, and python-magic.
Running this command in verbose (-vv
) mode:
$ beet -vv import .
Led to this problem:
/home/home/src/beets/beets/mediafile.py:21: UserWarning: beets.mediafile is deprecated; use mediafile instead
warnings.warn("beets.mediafile is deprecated; use mediafile instead")
....
....
....
Success. Distance: 0.49
Requesting MusicBrainz release 1e4666e0-bfd7-4e00-88b6-101d18fe024a
Sending event: mb_track_extract
Sending event: mb_track_extract
Sending event: mb_track_extract
primary MB release type: ep
Sending event: mb_album_extract
Sending event: albuminfo_received
Candidate: Nick Curly - Sun City EP (1e4666e0-bfd7-4e00-88b6-101d18fe024a)
Computing track assignment...
...done.
Success. Distance: 0.52
Evaluating 4 candidates.
Traceback (most recent call last):
File "/home/home/miniconda3/bin/beet", line 11, in <module>
load_entry_point('beets', 'console_scripts', 'beet')()
File "/home/home/src/beets/beets/ui/__init__.py", line 1291, in main
_raw_main(args)
File "/home/home/src/beets/beets/ui/__init__.py", line 1278, in _raw_main
subcommand.func(lib, suboptions, subargs)
File "/home/home/src/beets/beets/ui/commands.py", line 973, in import_func
import_files(lib, paths, query)
File "/home/home/src/beets/beets/ui/commands.py", line 943, in import_files
session.run()
File "/home/home/src/beets/beets/importer.py", line 341, in run
pl.run_parallel(QUEUE_SIZE)
File "/home/home/src/beets/beets/util/pipeline.py", line 442, in run_parallel
six.reraise(exc_info[0], exc_info[1], exc_info[2])
File "/home/home/miniconda3/lib/python3.7/site-packages/six.py", line 693, in reraise
raise value
File "/home/home/src/beets/beets/util/pipeline.py", line 259, in run
msg = next(self.coro)
File "/home/home/src/beets/beets/importer.py", line 1320, in read_tasks
for t in task_factory.tasks():
File "/home/home/src/beets/beets/importer.py", line 1144, in tasks
tasks = self._create(self.album(paths, dirs))
File "/home/home/src/beets/beets/importer.py", line 1224, in album
items = [item for item in items if item]
File "/home/home/src/beets/beets/importer.py", line 1224, in <listcomp>
items = [item for item in items if item]
File "/home/home/src/beets/beets/importer.py", line 1273, in read_item
return library.Item.from_path(path)
File "/home/home/src/beets/beets/library.py", line 603, in from_path
i.read(path)
File "/home/home/src/beets/beets/library.py", line 710, in read
value = getattr(mediafile, key)
File "/home/home/miniconda3/lib/python3.7/site-packages/mediafile.py", line 1276, in __get__
out = style.get(mediafile.mgfile)
File "/home/home/miniconda3/lib/python3.7/site-packages/mediafile.py", line 612, in get
return self.get_list(mutagen_file)[0]
File "/home/home/miniconda3/lib/python3.7/site-packages/mediafile.py", line 619, in get_list
return [self.deserialize(item) for item in self.fetch(mutagen_file)]
File "/home/home/miniconda3/lib/python3.7/site-packages/mediafile.py", line 951, in fetch
return sum([el.split('/') for el in frame.text], start=[])
TypeError: sum() takes no keyword arguments
This is only happening with some folders, but I can't pin-point to what exactly. I have had a few similar issues since the new release, and wondering if it's not related to the mediafile warning above
My configuration (output of beet config
) is:
acousticbrainz:
auto: yes
badfiles:
commands: {}
# commands:
# ogg: myoggchecker --opt1 --opt2
# flac: flac --test --warnings-as-errors --silent
chroma:
auto: yes
convert:
auto: no
never_convert_lossy_files: yes
quiet: false
dest: /tmp/convert
formats:
alac:
command: ffmpeg -i $source -y -vn -aq 2 $dest
extension: flac
directory: /export/music/beet_library/
duplicates:
album: yes
path: yes
fetchart:
auto: yes
maxwidth: 1000
cover_names: cover Cover front Front art album portada folder Folder
google-key:
sources:
- filesystem
- coverart: release
- itunes
- covertart: releasegroup
- amazon
- albumart
- google
- '*'
ftintitle:
auto: yes
import:
copy: no
move: yes
write: yes
languages: en
resume: yes
autotag: yes
item_fields:
multidisc: 1 if disctotal > 1 else 0
genre_fix: genre.title()
genre_no_space: genre.lower().replace(' ','_')
lastgenre:
auto: yes
fallback: 'unknown'
source: artist
canonical: ~/.config/beets/genres-tree.yaml
whitelist: ~/.config/beets/beets_genre
#prefer_specific: true
wlg:
auto: no
force: no
count: 1
separator: ', '
whitelist: ~/.config/beets/beets_genre
library: /export/music/musiclibrary2.blb
replaygain:
backend: ffmpeg
sort_item: albumartist+ album+ disc+ track+
spotify:
mode: open
region_filter: US
show_failures: on
tiebreak: popularity
regex: [
{
field: "albumartist", # Field in the item object to regex.
search: "Something", # String to look for.
replace: "Replaced" # Replacement value.
},
{
field: "title",
search: "Something Else",
replace: "AlsoReplaced"
}
]
#the:
#the: yes
#a: yes
#format: '{0}, {1}'
#strip: no
# patterns: []
threaded: yes
paths:
default: '$genre_fix/%title{$albumartist}/$original_year - $album/%if{$multidisc,Disc $disc/}$track $title'
albumtype:soundtrack: Soundtracks/$albumartist - $original_year - $album/%if{$multidisc,Disc $disc/}$track $title
singleton: '$albumartist/$year - $album/$track $title'
mis:1: '$albumartist/$year - $album/$track $title'
per_disc_numbering: true
plugins:
- acousticbrainz
- artistcountry
- badfiles
- cmus
- convert
- discogs
- duplicates
- edit
- fetchartist
- fetchart
- fromfilename
- ftintitle
- fuzzy
- hook
- importadded
- info
- inline
- lastgenre
- mbsync
- play
- random
- replaygain
- rewrite
- scrub
- smartplaylist
- web
- wlg
pluginpath:
- ~/.config/beets/plugins/
- ~/.config/beets/plugins/bandcamp/beetsplug
- ~/.config/beets/plugins/airsonic/
- ~/.config/beets/plugins/fetchartist/beetsplug/
- ~/.config/beets/whatlastgenre/plugin/beets/beetsplug
# recreated at each run monthly import playlists
smartplaylist:
relative_to: /export/music/beet_library# as setup on docker container
playlist_dir: /export/music/playlists
playlists:
- name: 'New_%time{$added,%Y-%m}_$genre_no_space.m3u'
query: 'added:2018-10..' # first month of using beet. monthly playlists will be created afterwards
hook:
hooks:
# renaming the "relative_to" part of the playlist created in smartplaylist so this can be used in the airsonic docker container automatically
- event: cli_exit
command: "sh -c 'bash /home/lbesnard/bin/beet_playlist_management.sh'"
- event: import
command: "sh -c 'bash /home/lbesnard/bin/beet_playlist_management.sh'"
- event: import
command: "sh -c 'sh /home/lbesnard/bin/airsonic_update_lib.sh'"
edit:
itemfields: track title artist album albumartist genre year
albumfields: album albumartist genre year
importadded:
preserve_mtimes: yes
preserve_write_mtimes: yes
play:
command: mp3blaster --list
replace:
'[\\/]': _
'^\.':
'[\x00-\x1f]': _
'[<>:"\?\*\|]': _
'\.$': _
'\s+$': ''
'^\s+': ''
'[\xe2\x80\x90]': '-'
'[\u2010]': '-'
'‐': '-'
'’': "'"
'’': "'"
rewrite:
artist The Jimi Hendrix Experience: Jimi Hendrix
albumartist The Jimi Hendrix Experience: Jimi Hendrix
artist .*Grateful Dead.*: Grateful Dead
albumartist .*Grateful Dead.*: Grateful Dead
artist .*Bill Monroe.*: Bill Monroe
albumartist .*Bill Monroe.*: Bill Monroe
#albumartist Alt-j: "alt-J"
#albumartist Alt-j: "alt-J"
path_sep_replace: _
scrub:
auto: yes
ui:
color: yes
text_success: green
text_warning: yellow
text_error: red
text_highlight: red
text_highlight_minor: lightgray
action_default: turquoise
action: blue
# http://akeil.net/listings/beets.config.yaml.html
# Auto Tagger ----------------------------------------------------------------
match:
strong_rec_thresh: 0.1 # match 90% or better for auto import
medium_rec_thresh: 0.25
rec_gap_thresh: 0.25
max_rec:
missing_tracks: medium
unmatched_tracks: medium
distance_weights:
source: 2.0
artist: 3.0
album: 3.0
media: 1.0
mediums: 1.0
year: 1.0
country: 0.5
label: 0.5
catalognum: 0.5
albumdisambig: 0.5
album_id: 5.0
tracks: 2.0
missing_tracks: 0.9
unmatched_tracks: 0.6
track_title: 3.0
track_artist: 2.0
track_index: 1.0
track_length: 2.0
track_id: 5.0
preferred:
countries: []
media: []
original_year: no
ignored: []
track_length_grace: 10
track_length_max: 30
fetchartist:
cover_name: "folder"
Hi!
I'm currently working on packaging this library in Debian, and it would be great if new tags could be signed with an OpenPGP key :)
It's not much more work and it provides a very clear trust chain.
https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work
Thanks in advance,
I need to read/write the ID3 Popularimeter tag within my app, specifically in the way that Native Instruments Traktor writes them.
This is a sample file containing the tag as written by Traktor:
https://github.com/ifischer/django-tracks-api/raw/master/tracks_api/tests/fixtures/popm.mp3
This is how mutagen-inspect shows it on command line:
$ mutagen-inspect tracks_api/tests/fixtures/popm.mp3
-- tracks_api/tests/fixtures/popm.mp3
- MPEG 1 layer 3, 32678 bps (VBR), 44100 Hz, 1 chn, 5.04 seconds (audio/mp3)
[email protected]=0 102/255
TSSE=Lavf57.83.100
So far I'm using pure mutagen in my app to do it:
from mutagen.id3 import ID3
from mutagen.id3 import POPM
ID3_POPM = 'POPM'
class SimpleID3(ID3):
def __init__(self, filename):
super().__init__(filename)
@property
def rating(self):
rating = self.getall(ID3_POPM)
if rating:
rating = rating[0].rating
return int(rating / 51)
return None
@rating.setter
def rating(self, rating: int):
popm = POPM(email='[email protected]', rating=rating * 51, count=0)
self[ID3_POPM] = popm
How can I achieve something similar with mediafile, adding the popularimeter tag as a custom field?
Get empty file:
$ ffmpeg -f lavfi -i 'anullsrc=channel_layout=5.1:sample_rate=48000' -t 1 file.flac
Write track field
>>> from mediafile import MediaFile
>>> f = MediaFile('file.flac')
>>> f.track
>>> f.track = 1
>>> f.save()
>>> f.track
1
View result
$ metaflac --list "file.flac"
METADATA block #0
type: 0 (STREAMINFO)
is last: false
length: 34
minimum blocksize: 4608 samples
maximum blocksize: 4608 samples
minimum framesize: 26 bytes
maximum framesize: 28 bytes
sample_rate: 48000 Hz
channels: 6
bits-per-sample: 16
total samples: 48000
MD5 signature: 0dd0e989e9556334ac746c39d72d6aea
METADATA block #1
type: 4 (VORBIS_COMMENT)
is last: false
length: 116
vendor string: Lavf58.76.100
comments: 4
comment[0]: encoder=Lavf58.76.100
comment[1]: WAVEFORMATEXTENSIBLE_CHANNEL_MASK=0x3f
comment[2]: TRACK=1
comment[3]: TRACKNUMBER=1
METADATA block #2
type: 1 (PADDING)
is last: true
length: 8164
Specifically the TRACK
and TRACKNUMBER
fields. This is the case in all the files in my collection that are managed by beets. I haven't noticed this before, but I recently switched to ncmpc (for mpd), and noticed that the %track% format specifier outputs 1, 1. I saw this issue (MusicPlayerDaemon/ncmpc#71) and asked about it in the #mpd irc channel, where they said the problem was with my files having multiple track tags. I don't know how standardized the tag keys are, or where this bug belongs, but I'm filing this issue if it should be fixed here.
Side note: I also noticed this happens for a couple other fields like disc number as well
Line 1611 in 8846cdf
should read: ... "already exists on MediaFile" not MediaField
.
For some libraries, users might decide to store ID3v2.3 tags for greater compatibility with older software (as opposed to the ID3v2.4 default).
Now ID3v2.4 allows storing multiple tags of the same name (for multi-value entries like artists, genres and so on) and ID3v2.3 traditionally used a slash "/" character (which brought us the "AC/DC problem").
People (and tagging software like MusicBrainz Picard) have started to support other separators to store multiple values on one ID3v2.3 field. Typical separators used today are the semicolon ;
, the vertical bar |
, a comma ,
the NULL character \0
and even character sequences like semicolon blank ;
for better readability. The underlying Mutagen supports all of these by specifying v23_sep
.
This is a missing feature in MediaFile (and beets) and I’d like to add it to the MediaFile class so it could also be supported via a beets config entry:
class MediaFile(object):
"""Represents a multimedia file on disk and provides access to its
metadata.
"""
def __init__(self, path, id3v23=False, id3v23_sep='/'):
"""Constructs a new `MediaFile` reflecting the file at path. May
throw `UnreadableFileError`.
By default, MP3 files are saved with ID3v2.4 tags. You can use
the older ID3v2.3 standard by specifying the `id3v23` option
and (optionally) the desired separator `id3v23_sep` for
multi-value fields (defaults to a slash character).
To use a NULL character as terminator, pass `id3v23_sep=None`.
"""
Objections?
Would it need a u'/'
instead?
N.B.: Making this a core config entry in beets should also help some plugins who currently specify their own separators.
Follow up to beetbox/beets#2005 (comment)
@sampsyo said:
The thing to do, I suppose, would be to add a lossy field to MediaFile that more accurately detects lossiness per-format.
rtng is the rating field. it's 1 if the track is explicit and 0 if it's clean.
See this beetbox/beets#2088 and potentially other changes to mediafile https://github.com/beetbox/beets/commits/master/beets/mediafile.py
Hello, I'm just wondering what the release process is for this repo. A few months ago I authored 93c6c3b which unfortunately still hasn't been tied to an official release of this library, from what I can tell. Im hoping that we could get a release here so that I could potentially bump the version in beets and take advantage of the commit. Thanks!
Whenever I try to load a WAV file I get this exception:
>>> import mediafile
>>> mediafile.MediaFile("file_example_WAV_1MG.wav")Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/postlund/pyatv_dev/pyatv/lib/python3.8/site-packages/mediafile.py", line 163, in wrapper return mutagen_call('loadfile', '', f, *args, **kwargs)
File "/home/postlund/pyatv_dev/pyatv/lib/python3.8/site-packages/mediafile.py", line 135, in mutagen_call
return func(*args, **kwargs)
File "/home/postlund/pyatv_dev/pyatv/lib/python3.8/site-packages/mutagen/_util.py", line 156, in wrapper
return func(self, h, *args, **kwargs)
File "/home/postlund/pyatv_dev/pyatv/lib/python3.8/site-packages/mediafile.py", line 1595, in __init__
raise FileTypeError(self.filename, type(self.mgfile).__name__)
mediafile.FileTypeError: 'file_example_WAV_1MG.wav': of mutagen type WAVE
Loading it in mutagen directly seems to work fine though:
>>> import mutagen
>>> mutagen.File("file_example_WAV_1MG.wav")
{'TCON': TCON(encoding=<Encoding.LATIN1: 0>, text=['Cinematic']), 'TALB': TALB(encoding=<Encoding.LATIN1: 0>, text=['YouTube Audio Library']), 'TIT2': TIT2(encoding=<Encoding.LATIN1: 0>, text=['Impact Moderato']), 'TPE1': TPE1(encoding=<Encoding.LATIN1: 0>, text=['Kevin MacLeod'])}
I'm using this file as an example, but the same thing happens with other WAV files too.
Under Python 3 (python3 -m nose
):
EE
======================================================================
ERROR: Failure: ImportError (cannot import name '_common' from 'test' (/usr/lib/python3.7/test/__init__.py))
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.7/site-packages/nose/failure.py", line 39, in runTest
raise self.exc_val.with_traceback(self.tb)
File "/usr/lib/python3.7/site-packages/nose/loader.py", line 417, in loadTestsFromName
addr.filename, addr.module)
File "/usr/lib/python3.7/site-packages/nose/importer.py", line 47, in importFromPath
return self.importFromDir(dir_path, fqname)
File "/usr/lib/python3.7/site-packages/nose/importer.py", line 94, in importFromDir
mod = load_module(part_fqname, fh, filename, desc)
File "/usr/lib/python3.7/imp.py", line 234, in load_module
return load_source(name, filename, file)
File "/usr/lib/python3.7/imp.py", line 171, in load_source
module = _load(spec)
File "<frozen importlib._bootstrap>", line 696, in _load
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/tmp/makepkg/python-mediafile/src/python3/test/test_mediafile.py", line 28, in <module>
from test import _common
ImportError: cannot import name '_common' from 'test' (/usr/lib/python3.7/test/__init__.py)
======================================================================
ERROR: Failure: ImportError (cannot import name '_common' from 'test' (/usr/lib/python3.7/test/__init__.py))
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python3.7/site-packages/nose/failure.py", line 39, in runTest
raise self.exc_val.with_traceback(self.tb)
File "/usr/lib/python3.7/site-packages/nose/loader.py", line 417, in loadTestsFromName
addr.filename, addr.module)
File "/usr/lib/python3.7/site-packages/nose/importer.py", line 47, in importFromPath
return self.importFromDir(dir_path, fqname)
File "/usr/lib/python3.7/site-packages/nose/importer.py", line 94, in importFromDir
mod = load_module(part_fqname, fh, filename, desc)
File "/usr/lib/python3.7/imp.py", line 234, in load_module
return load_source(name, filename, file)
File "/usr/lib/python3.7/imp.py", line 171, in load_source
module = _load(spec)
File "<frozen importlib._bootstrap>", line 696, in _load
File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 728, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/tmp/makepkg/python-mediafile/src/python3/test/test_mediafile_edge.py", line 25, in <module>
from test import _common
ImportError: cannot import name '_common' from 'test' (/usr/lib/python3.7/test/__init__.py)
----------------------------------------------------------------------
Ran 2 tests in 0.025s
FAILED (errors=2)
Under Python 2 (python2 -m nose
):
EE
======================================================================
ERROR: Failure: ImportError (cannot import name _common)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/nose/loader.py", line 418, in loadTestsFromName
addr.filename, addr.module)
File "/usr/lib/python2.7/site-packages/nose/importer.py", line 47, in importFromPath
return self.importFromDir(dir_path, fqname)
File "/usr/lib/python2.7/site-packages/nose/importer.py", line 94, in importFromDir
mod = load_module(part_fqname, fh, filename, desc)
File "/tmp/makepkg/python-mediafile/src/python2/test/test_mediafile.py", line 28, in <module>
from test import _common
ImportError: cannot import name _common
======================================================================
ERROR: Failure: ImportError (cannot import name _common)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/nose/loader.py", line 418, in loadTestsFromName
addr.filename, addr.module)
File "/usr/lib/python2.7/site-packages/nose/importer.py", line 47, in importFromPath
return self.importFromDir(dir_path, fqname)
File "/usr/lib/python2.7/site-packages/nose/importer.py", line 94, in importFromDir
mod = load_module(part_fqname, fh, filename, desc)
File "/tmp/makepkg/python-mediafile/src/python2/test/test_mediafile_edge.py", line 25, in <module>
from test import _common
ImportError: cannot import name _common
----------------------------------------------------------------------
Ran 2 tests in 0.140s
FAILED (errors=2)
Same as with beetbox/confuse#54, this is needed to run tests during packaging at a stage after building but before packaging/installing.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.