Skip to content

Commit

Permalink
Merge pull request #35 from nv95/feature/ogg
Browse files Browse the repository at this point in the history
Add Ogg/Vorbis support
  • Loading branch information
lachhebo authored Apr 23, 2022
2 parents de802f1 + 08d7e3d commit 47dc46a
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 2 deletions.
6 changes: 5 additions & 1 deletion src/audio_getter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from os import path

from .audio_mp3_file_handler import Mp3FileHandler
from .audio_ogg_file_handler import OggFileHandler
from .dir_manager import DIR_MANAGER
from .extension_manager import get_file_extension

Expand All @@ -12,7 +13,10 @@ def get_file_manager(filename):
output : an Handler or None
"""

if get_file_extension(filename) == "mp3":
ext = get_file_extension(filename)
if ext == "mp3":
return Mp3FileHandler(path.join(DIR_MANAGER.directory, filename))
elif ext == "ogg":
return OggFileHandler(path.join(DIR_MANAGER.directory, filename))
else:
return None
136 changes: 136 additions & 0 deletions src/audio_ogg_file_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import base64
import mutagen.flac
from typing import Dict
from mutagen.flac import Picture
from mutagen.id3 import ID3, TIT2, APIC, TALB, TPE1 # noqa:F401
from mutagen.id3 import TCON, TRCK, TDRC, USLT # noqa:F401
from mutagen.oggvorbis import OggVorbis, OggVCommentDict

from .audio_extension_handler import AudioExtensionHandler
from .tools import file_size_to_string
from .tools import music_length_to_string

TAG_PARAMS = {
"title": "TITLE",
"cover": "METADATA_BLOCK_PICTURE",
"album": "ALBUM",
"artist": "ARTIST",
"genre": "GENRE",
"track": "TRACKNUMBER",
"year": "DATE",
}


class OggFileHandler(AudioExtensionHandler):

@staticmethod
def get_extension():
return ".ogg"

def __init__(self, path_file):
"""
We initialise the path of the file and the tagging tool we use
"""
self.path_file = path_file
self.audio = OggVorbis(path_file)
self.id3 = self.audio.tags

if self.id3 is None:
self.audio.add_tags()
self.id3 = self.audio.tags

def get_one_tag(self, id3_name_tag: str, data_type: str):
"""
A function to return the first tag of an id3 label
"""
if self.id3 is None:
return ""

if self.id3 is OggVCommentDict:
return self.id3.get(id3_name_tag)

tag_needed = self.id3.get(id3_name_tag, "")

if len(tag_needed) == 0:
return ""

if data_type == "text":
return tag_needed[0]
elif data_type == "data":
try:
return base64.b64decode(tag_needed[0])
except (TypeError, ValueError):
return ""
else:
return ""

def get_tag_research(self):
return [
self.get_one_tag(TAG_PARAMS["title"], "text"),
self.get_one_tag(TAG_PARAMS["artist"], "text"),
self.get_one_tag(TAG_PARAMS["album"], "text"),
]

def get_tag(self, tag_key):
"""
We handle tag using a switch, it is working well because it
is basically the structure.
"""

if tag_key == "cover":
cover_bytes = self.get_one_tag("METADATA_BLOCK_PICTURE", "data")
if cover_bytes == "":
return ""
try:
picture = Picture(cover_bytes)
return picture.data
except mutagen.flac.error:
return ""
elif tag_key == "size":
return file_size_to_string(self.path_file)
elif tag_key == "length":
return music_length_to_string(self.audio.info.length)
else:
return self.get_one_tag(TAG_PARAMS[tag_key], "text")

def get_tags(self) -> Dict:
tags = [
"title",
"album",
"artist",
"genre",
"cover",
"year",
"track",
"length",
"size",
]
result = {}
for tag in tags:
result[tag] = self.get_tag(tag)
return result

def check_tag_existence(self, key):
return len(self.id3.get(TAG_PARAMS[key])) > 0

def set_tag(self, tag_key, tag_value):

if tag_key != "cover":
self.id3[TAG_PARAMS[tag_key]] = [tag_value]
return 1

if tag_value == "":
return 0

if isinstance(tag_value, str):
tag_value = open(tag_value, "rb").read()
picture = Picture()
picture.data = tag_value
binary_data = picture.write()
self.id3[TAG_PARAMS[tag_key]] = [base64.b64encode(binary_data).decode("ascii")]

def save_modifications(self):
"""
Save definitively the modification we have made.
"""
self.audio.save()
2 changes: 1 addition & 1 deletion src/constant.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
HANDLED_EXTENSIONS = ["mp3"]
HANDLED_EXTENSIONS = ["mp3", "ogg"]
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ gabtag_sources = [
'audio_extension_handler.py',
'audio_getter.py',
'audio_mp3_file_handler.py',
'audio_ogg_file_handler.py',

'crawler_data.py',
'crawler_directory.py',
Expand Down
76 changes: 76 additions & 0 deletions tests/test_audio_ogg_file_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from unittest.mock import patch, Mock, call

from src.audio_ogg_file_handler import OggFileHandler

TESTED_MODULE = "src.audio_ogg_file_handler"


def test_get_extension__return_ogg():
# given

# when
result = OggFileHandler.get_extension()

# then
assert result == ".ogg"


@patch(f"{TESTED_MODULE}.OGG")
def test_get_one_tag__return_empty_string_if_no_id3(m_ogg):
# given
oggfilehandler = OggFileHandler("fake_path.ogg")
oggfilehandler.id3 = Mock()
oggfilehandler.id3.get.return_value = []

# when
result = oggfilehandler.get_one_tag("title", "text")

# then
assert result == ""


@patch(f"{TESTED_MODULE}.MP3")
def test_get_one_tag__return_tag_if_id3_with_text(m_ogg):
# given
oggfilehandler = OggFileHandler("fake_path.ogg")
oggfilehandler.id3 = Mock()
fake_tag = Mock()
fake_tag.text = ["title"]
oggfilehandler.id3.get.return_value = [fake_tag]

# when
result = oggfilehandler.get_one_tag("title", "text")

# then
assert result == "title"


@patch(f"{TESTED_MODULE}.MP3")
def test_get_one_tag__return_tag_if_id3_with_data(m_ogg):
# given
oggfilehandler = OggFileHandler("fake_path.ogg")
oggfilehandler.id3 = Mock()
fake_tag = Mock()
fake_tag.data = ["data"]
oggfilehandler.id3.get.return_value = [fake_tag]

# when
result = oggfilehandler.get_one_tag("title", "data")

# then
assert result == ["data"]


@patch(f"{TESTED_MODULE}.MP3")
@patch(f"{TESTED_MODULE}.OgFileHandler.get_one_tag")
def test_get_tag_research__return_title_artist_and_album(m_get, m_ogg):
# given
oggfilehandler = OggFileHandler("fake_path.ogg")
oggfilehandler.id3 = Mock()
calls = [call("TITLE", "text"), call("ARTIST", "text"), call("ALBUM", "text")]

# when
oggfilehandler.get_tag_research()

# then
m_get.assert_has_calls(calls=calls, any_order=True)

0 comments on commit 47dc46a

Please sign in to comment.