tinytag
tinytag copied to clipboard
Support BytesIo objects for tagging
It would be awesome to support BytesIO or any other IO types with TinyTag. This way it can be used in some scenarios where files are packaged and do not have a normal path.
Have you tried using the parser classes directly? If you know what kind of file type you're handling, you should be able to use:
from tinytag import MP3
mp3_info = MP3(my_bytes_io)
mp3_info.load(tags=True, duration=True)
I agree that this is pretty ugly API wise, so we can discuss how we could make this more accessible, but I think this should work.
I got this to work by writing my BytesIO object into a temporary file and then reading that file into TinyTag. The temporary file automatically gets deleted as soon as the with statement is finished.
Assuming data contains a BytesIO object:
import tempfile
with tempfile.NamedTemporaryFile() as tmp:
tmp.write(data.getvalue())
tmp.seek(0)
tag = TinyTag.get(tmp.name)
Here is a full example showing how it works in practice:
Full example
import tempfile
import requests
import io
from tinytag import TinyTag
def download_file(url: str) -> io.BytesIO:
"""
Downloads the file from the given URL and returns it as a BytesIO object
Args:
url: The url to run from
Returns:
An object containing the file
"""
try:
response = requests.get(url, stream=True)
response.raise_for_status()
except requests.exceptions.RequestException as e:
raise Exception(f"Could not run file from {url}: {e}")
file = io.BytesIO()
for chunk in response.iter_content(chunk_size=8192):
file.write(chunk)
file.seek(0)
return file
def read_bytes_io_as_tiny_tag(data: io.BytesIO) -> TinyTag:
"""
Reads the given BytesIO object as a file and returns it
Args:
data: The BytesIO object to read
Returns:
The file
"""
with tempfile.NamedTemporaryFile() as tmp:
tmp.write(data.getvalue())
tmp.seek(0)
tag = TinyTag.get(tmp.name)
return tag
def run():
url = "https://example.com/file.mp3"
data = download_file(url)
tag = read_bytes_io_as_tiny_tag(data=data)
print('This track is by %s.' % tag.artist)
print('It is %f seconds long.' % tag.duration)
Tested on Windows, the code above won't run if you don't set delete = False in tempfile.NamedTemporaryFile()
I don't know why but this should work ` import tempfile
with tempfile.NamedTemporaryFile(delete = False, suffix = ".mp3") as tmp:
tmp.write(data.getvalue())
tmp.seek(0)
tag = TinyTag.get(tmp.name)
` Do you have any solution for this? Deleting temp file manually seems not a good idea :(
Can you check if this branch works well for your needs? https://github.com/mathiascode/tinytag/tree/bytesio
You can provide a file-like object like this:
tag = TinyTag.get(file_obj=file_obj)