Skip to content

Commit

Permalink
Usin filetype.py instead of python-magic
Browse files Browse the repository at this point in the history
  • Loading branch information
nicfit committed Dec 21, 2019
2 parents 4c45cc8 + dbda31d commit 2660219
Show file tree
Hide file tree
Showing 22 changed files with 341 additions and 183 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ pre-release: lint test changelog requirements
@git status -s -b

requirements:
nicfit requirements
tox -e requirements

changelog:
last=`git tag -l --sort=version:refname | grep '^v[0-9]' | tail -n1`;\
Expand Down
9 changes: 4 additions & 5 deletions eyed3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@
from .__about__ import __version__ as version

_DEFAULT_ENCODING = "latin1"
# The local encoding, used when parsing command line options, console output,
# etc. The default is always ``latin1`` if it cannot be determined, it is NOT
# the value shown.
LOCAL_ENCODING = locale.getpreferredencoding(do_setlocale=True)
"""The local encoding, used when parsing command line options, console output,
etc. The default is always ``latin1`` if it cannot be determined, it is NOT
the value shown."""
if not LOCAL_ENCODING or LOCAL_ENCODING == "ANSI_X3.4-1968": # pragma: no cover
LOCAL_ENCODING = _DEFAULT_ENCODING

# The local file system encoding, the default is ``latin1`` if it cannot be determined.
LOCAL_FS_ENCODING = sys.getfilesystemencoding()
"""The local file system encoding, the default is ``latin1`` if it cannot be
determined."""
if not LOCAL_FS_ENCODING: # pragma: no cover
LOCAL_FS_ENCODING = _DEFAULT_ENCODING

Expand Down
40 changes: 19 additions & 21 deletions eyed3/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@
import dataclasses

from . import LOCAL_FS_ENCODING
from .utils import guessMimetype
from .utils.log import getLogger
log = getLogger(__name__)

# Audio type selector for no audio.
AUDIO_NONE = 0
"""Audio type selector for no audio."""
# Audio type selector for MPEG (mp3) audio.
AUDIO_MP3 = 1
"""Audio type selector for mpeg (mp3) audio."""


AUDIO_TYPES = (AUDIO_NONE, AUDIO_MP3)
Expand All @@ -31,14 +30,14 @@

VARIOUS_ARTISTS = "Various Artists"

# A key that can be used in a TXXX frame to specify the type of collection
# (or album) a file belongs. See :class:`eyed3.core.ALBUM_TYPE_IDS`.
TXXX_ALBUM_TYPE = "eyeD3#album_type"
"""A key that can be used in a TXXX frame to specify the type of collection
(or album) a file belongs. See :class:`eyed3.core.ALBUM_TYPE_IDS`."""

# A key that can be used in a TXXX frame to specify the origin of an
# artist/band. i.e. where they are from.
# The format is: city<tab>state<tab>country
TXXX_ARTIST_ORIGIN = "eyeD3#artist_origin"
"""A key that can be used in a TXXX frame to specify the origin of an
artist/band. i.e. where they are from.
The format is: city<tab>state<tab>country"""


@dataclasses.dataclass
Expand Down Expand Up @@ -69,37 +68,36 @@ def load(path, tag_version=None):
eventual format of the metadata.
"""
from . import mp3, id3
from .mimetype import guessMimetype

if not isinstance(path, pathlib.Path):
path = pathlib.Path(path)
log.debug("Loading file: %s" % path)
log.debug(f"Loading file: {path}")

if path.exists():
if not path.is_file():
raise IOError("not a file: %s" % path)
raise IOError(f"not a file: {path}")
else:
raise IOError("file not found: %s" % path)
raise IOError(f"file not found: {path}")

mtypes = guessMimetype(path, all_types=True)
log.debug(f"File mime-type: {mtypes}")
mtype = guessMimetype(path)
log.debug(f"File mime-type: {mtype}")

if set(mtypes).intersection(set(mp3.MIME_TYPES)):
return mp3.Mp3AudioFile(path, tag_version)
elif (set(mtypes).intersection(set(mp3.OTHER_MIME_TYPES)) and
path.suffix.lower() in mp3.EXTENSIONS):
# Same as above, but mp3 was not typed detected; making this odd/special
if mtype in mp3.MIME_TYPES:
return mp3.Mp3AudioFile(path, tag_version)
elif "application/x-id3" in mtypes:
elif mtype == id3.ID3_MIME_TYPE:
return id3.TagFile(path, tag_version)
else:
return None


class AudioInfo(object):
"""A base container for common audio details."""

# The number of seconds of audio data (i.e., the playtime)
time_secs = 0.0
"""The number of seconds of audio data (i.e., the playtime)"""
# The number of bytes of audio data.
size_bytes = 0
"""The number of bytes of audio data."""


class Tag(object):
Expand Down
54 changes: 19 additions & 35 deletions eyed3/id3/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
################################################################################
# Copyright (C) 2002-2014 Travis Shirk <travis@pobox.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
################################################################################
import re

from .. import core
Expand All @@ -24,37 +7,39 @@

log = getLogger(__name__)

# Version constants and helpers
# Version 1, 1.0 or 1.1
ID3_V1 = (1, None, None)
"""Version 1, 1.0 or 1.1"""
# Version 1.0, specifically
ID3_V1_0 = (1, 0, 0)
"""Version 1.0, specifically"""
# Version 1.1, specifically
ID3_V1_1 = (1, 1, 0)
"""Version 1.1, specifically"""
# Version 2, 2.2, 2.3 or 2.4
ID3_V2 = (2, None, None)
"""Version 2, 2.2, 2.3 or 2.4"""
# Version 2.2, specifically
ID3_V2_2 = (2, 2, 0)
"""Version 2.2, specifically"""
# Version 2.3, specifically
ID3_V2_3 = (2, 3, 0)
"""Version 2.3, specifically"""
# Version 2.4, specifically
ID3_V2_4 = (2, 4, 0)
"""Version 2.4, specifically"""
# The default version for eyeD3 tags and save operations.
ID3_DEFAULT_VERSION = ID3_V2_4
"""The default version for eyeD3 tags and save operations."""
# Useful for operations where any version will suffice.
ID3_ANY_VERSION = (ID3_V1[0] | ID3_V2[0], None, None)
"""Useful for operations where any version will suffice."""

# Byte code for latin1
LATIN1_ENCODING = b"\x00"
"""Byte code for latin1"""
# Byte code for UTF-16
UTF_16_ENCODING = b"\x01"
"""Byte code for UTF-16"""
# Byte code for UTF-16 (big endian)
UTF_16BE_ENCODING = b"\x02"
"""Byte code for UTF-16 (big endian)"""
# Byte code for UTF-8 (Not supported in ID3 versions < 2.4)
UTF_8_ENCODING = b"\x03"
"""Byte code for UTF-8 (Not supported in ID3 versions < 2.4)"""

# Default language code for frames that contain a language portion.
DEFAULT_LANG = b"eng"
"""Default language code for frames that contain a language portion."""

ID3_MIME_TYPE = "application/x-id3"
ID3_MIME_TYPE_EXTENSIONS = (".id3", ".tag")


def isValidVersion(v, fully_qualified=False):
Expand Down Expand Up @@ -335,6 +320,7 @@ def initTag(self, version=ID3_DEFAULT_VERSION):
self.tag.file_info = FileInfo(self.path)


# ID3 genres, as defined in ID3 v1. The position in the list is the genre's numeric byte value.
ID3_GENRES = [
u'Blues',
u'Classic Rock',
Expand Down Expand Up @@ -530,11 +516,9 @@ def initTag(self, version=ID3_DEFAULT_VERSION):
u'Garage Rock',
u'Psybient',
]
"""ID3 genres, as defined in ID3 v1. The position in the list is the genre's
numeric byte value."""

# A map of standard genre names and IDs per the ID3 v1 genre definition.
genres = GenreMap()
"""A map of standard genre names and IDs per the ID3 v1 genre definition."""

from . import frames # noqa
from .tag import Tag, TagException, TagTemplate, FileInfo # noqa
29 changes: 14 additions & 15 deletions eyed3/id3/frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def encrypt(data):

@requireBytes(1)
def _disassembleFrame(self, data):
assert(self.header)
assert self.header
header = self.header
# Format flags in the frame header may add extra data to the
# beginning of this data.
Expand Down Expand Up @@ -176,7 +176,7 @@ def _disassembleFrame(self, data):

@requireBytes(1)
def _assembleFrame(self, data):
assert(self.header)
assert self.header
header = self.header

# eyeD3 never writes unsync'd frames
Expand Down Expand Up @@ -248,7 +248,8 @@ def encoding(self, enc):
if not isinstance(enc, bytes):
raise TypeError("encoding argument must be a byte string.")
elif not (LATIN1_ENCODING <= enc <= UTF_8_ENCODING):
raise ValueError("Unknown encoding value {}".format(enc))
log.warning("Unknown encoding value {}".format(enc))
enc = LATIN1_ENCODING
self._encoding = enc


Expand Down Expand Up @@ -287,9 +288,8 @@ def parse(self, data, frame_header):
try:
self.text = decodeUnicode(text_data, self.encoding)
except UnicodeDecodeError as err:
log.warning("Error decoding text frame {fid}: {err}"
.format(fid=self.id, err=err))
self.test = ""
log.warning(f"Error decoding text frame {self.id}: {err}")
self.text = ""
log.debug("TextFrame text: %s" % self.text)

def render(self):
Expand Down Expand Up @@ -745,9 +745,9 @@ def makeFileName(self, name=None):

class ObjectFrame(Frame):
@requireUnicode("description", "filename")
def __init__(self, id=OBJECT_FID, description="", filename="",
def __init__(self, fid=OBJECT_FID, description="", filename="",
object_data=None, mime_type=None):
super(ObjectFrame, self).__init__(OBJECT_FID)
super().__init__(fid)
self.description = description
self.filename = filename
self.mime_type = mime_type
Expand Down Expand Up @@ -1111,15 +1111,15 @@ def text(self, text):
def parse(self, data, frame_header):
super(DescriptionLangTextFrame, self).parse(data, frame_header)

self.encoding = encoding = self.data[0:1]
self.encoding = self.data[0:1]
self.lang = self.data[1:4]
log.debug("%s lang: %s" % (self.id, self.lang))

try:
(d, t) = splitUnicode(self.data[4:], encoding)
self.description = decodeUnicode(d, encoding)
(d, t) = splitUnicode(self.data[4:], self.encoding)
self.description = decodeUnicode(d, self.encoding)
log.debug("%s description: %s" % (self.id, self.description))
self.text = decodeUnicode(t, encoding)
self.text = decodeUnicode(t, self.encoding)
log.debug("%s text: %s" % (self.id, self.text))
except ValueError:
log.warning("Invalid %s frame; no description/text" % self.id)
Expand Down Expand Up @@ -1534,8 +1534,6 @@ def deunsyncData(data):
# Create and return the appropriate frame.
def createFrame(tag_header, frame_header, data):
fid = frame_header.id
FrameClass = None

if fid in ID3_FRAMES:
(desc, ver, FrameClass) = ID3_FRAMES[fid]
elif fid in NONSTANDARD_ID3_FRAMES:
Expand Down Expand Up @@ -1591,7 +1589,8 @@ def splitUnicode(data, encoding):
except ValueError as ex:
log.warning("Invalid 2-tuple ID3 frame data: %s", ex)
d, t = data, b""
return (d, t)

return d, t


def id3EncodingToString(encoding):
Expand Down
6 changes: 5 additions & 1 deletion eyed3/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@
from eyed3.utils.log import initLogging

DEFAULT_PLUGIN = "classic"
# FIXME: deprecated, use ~/.config/eyeD3/ as new default
DEFAULT_CONFIG = os.path.expandvars("${HOME}/.eyeD3/config.ini")
USER_PLUGINS_DIR = os.path.expandvars("${HOME}/.eyeD3/plugins")
# FIXME: new
DEFAULT_CONFIG_XXX = os.path.expandvars("${HOME}/.config/eyeD3/config.ini")
USER_PLUGINS_DIR_XXX = os.path.expandvars("${HOME}/.config/eyeD3/plugins")


def main(args, config):
Expand Down Expand Up @@ -145,7 +149,7 @@ def makeCmdLineParser(subparser=None):
from eyed3.utils import ArgumentParser

p = (ArgumentParser(prog=eyed3.__about__.__project_name__, add_help=True)
if not subparser else subparser)
if not subparser else subparser)

setFileScannerOpts(p)

Expand Down
Loading

0 comments on commit 2660219

Please sign in to comment.