Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No io.BytesIO support in mutagen.File function #572

Open
helltraitor opened this issue Jul 18, 2022 · 3 comments
Open

No io.BytesIO support in mutagen.File function #572

helltraitor opened this issue Jul 18, 2022 · 3 comments

Comments

@helltraitor
Copy link

I need to open the file from BytesIO but mutagen doesn't allow me to use it.

is_fileobj: Broken - doesn't checks anything for real (almost any types is not str or bytes). You can use runtime_checkable protocols for example (or abstract classes from standart library)

mutagen/mutagen/_util.py

Lines 62 to 69 in 30f373f

def is_fileobj(fileobj):
"""Returns:
bool: if an argument passed ot mutagen should be treated as a
file object
"""
return not (isinstance(fileobj, (str, bytes)) or
hasattr(fileobj, "__fspath__"))

_openfile: Useless check - FileThing is just a container but isinstance check deny to use user containers variants. Consider to add special class or make FileThing public (PyCharm warning: Access to a protected member _util of a module) (for now you can just check attributes of filething by hasattr as you do in other places)

mutagen/mutagen/_util.py

Lines 219 to 223 in 30f373f

# to allow stacked context managers, just pass the result through
if isinstance(filething, FileThing):
filename = filething.filename
fileobj = filething.fileobj
filething = None

You know, that's very funny when open is not supported BytesIO but save supports.
For now temporary solution is to use ThingFile from non-public module _util.py

If you now the correct way to use BytesIO in mutagen, let me now.
P.S. Mutagen code is awful, have a nice day :D

@lazka
Copy link
Member

lazka commented Jul 19, 2022

import io
import urllib.request
import mutagen

r = urllib.request.urlopen("https://opus-codec.org/static/examples/ehren-paper_lights-96.opus")
f = io.BytesIO(r.read())
print(mutagen.File(f))

P.S. Mutagen code is awful, have a nice day :D

yeah, this was all added years later while trying to not break existing users, so lots of hacks and magic in that area...

@helltraitor
Copy link
Author

Mutagen waits that music will have ID3 tag, but it may not (most common situation for me is mp3/m4a music file without tag header - I create it with my code).
https://github.com/quodlibet/mutagen/blob/master/mutagen/mp3/__init__.py#L458-L461

But here we have only pure BytesIO (with music content) so Kinds have 0 score.
Debugger: results = [(0, 'MP3'), (0, 'TrueAudio'), (0, 'OggTheora'), (0, 'OggSpeex'), (0, 'OggVorbis'), (0, 'OggFLAC'), (0, 'FLAC'), (0, 'AIFF'), (0, 'APEv2File'), (0, 'MP4'), (False, 'ID3FileType'), (0, 'WavPack'), (0, 'Musepack'), (0, 'MonkeysAudio'), (0, 'OptimFROG'), (0, 'ASF'), (0, 'OggOpus'), (0, 'AAC'), (0, 'AC3'), (False, 'SMF'), (0, 'TAK'), (0, 'DSF'), (0, 'DSDIFF'), (0, 'WAVE')]
https://github.com/quodlibet/mutagen/blob/master/mutagen/_file.py#L289-L290

Funny story that I have codec from service (but I cannot be sure that I will cover any cases), so I want to use this codec within BytesIO.

class Descriptor:
    ...
    @contextlib.asynccontextmanager
    async def to_track(self) -> AsyncIterator[mutagen.FileType]:
        async with aiofiles.open(self.__filepath, "br") as file:
            buffer = io.BytesIO(await file.read())
            logging.debug("BUFFER IS READY")

        # if (track := mutagen.File(FileThing(buffer, self.__filepath.name, self.__filepath.name))) is None:
        if (track := mutagen.File(buffer)) is None:
            logging.debug("MUTAGEN ERROR")
            raise RuntimeError
        ...

This is an example from my application (for now I totally rewrite it and public ready pieces in my GitHub). Commented line is solution.
If you have the solution for BytesIO + codec - let me know. Because I don't want to use music formats directly

@lazka
Copy link
Member

lazka commented Jul 24, 2022

I see, thanks. So we need some way to pass a filename as a hint for the type selection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants