Skip to content

Commit

Permalink
fix: Fixed MP3 header search to not false match on BOMs
Browse files Browse the repository at this point in the history
  • Loading branch information
nicfit committed Mar 13, 2019
1 parent db5b5db commit 5bf413f
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 43 deletions.
19 changes: 1 addition & 18 deletions src/eyed3/mp3/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,3 @@
################################################################################
# Copyright (C) 2002-2007 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 os
import re

Expand Down Expand Up @@ -71,6 +54,7 @@ def __init__(self, file_obj, start_offset, tag):
self.bit_rate = (None, None)
'''2-tuple, (vrb?:boolean, bitrate:int)'''

header_pos = 0
while self.mp3_header is None:
# Find first mp3 header
(header_pos,
Expand Down Expand Up @@ -116,7 +100,6 @@ def __init__(self, file_obj, start_offset, tag):
self.size_bytes = os.stat(file_obj.name)[stat.ST_SIZE]

# Compute track play time.
tpf = None
if self.xing_header and self.xing_header.vbr:
tpf = timePerFrame(self.mp3_header, True)
self.time_secs = tpf * self.xing_header.numFrames
Expand Down
27 changes: 21 additions & 6 deletions src/eyed3/mp3/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,19 +73,35 @@ def findHeader(fp, start_pos=0):
bytes. If no header is found header_int will equal 0.
"""

def isBOM(buffer, pos):
try:
return 254 in (buffer[pos + 1], buffer[pos - 1])
except IndexError:
return False

def find_sync(fp, start_pos=0):
CHUNK_SIZE = 8192 # Measured as optimal

fp.seek(start_pos)
data = fp.read(CHUNK_SIZE)

while data:
sync_pos = data.find(b'\xff', 0)
if sync_pos >= 0:
header = data[sync_pos:sync_pos + 4]
if len(header) == 4:
return (start_pos + sync_pos, header)
pos = 0
while pos >= 0 and pos < CHUNK_SIZE:
pos = data.find(b"\xff", pos)
if pos == -1:
break

if not isBOM(data, pos):
header = data[pos:pos + 4]
if len(header) == 4:
return (start_pos + pos, header)

pos += 1

start_pos += len(data)
data = fp.read(CHUNK_SIZE)

return (None, None)

sync_pos, header_bytes = find_sync(fp, start_pos)
Expand All @@ -103,7 +119,6 @@ def timePerFrame(mp3_header, vbr):
rate from ``mp3_header`` are used to compute the number of seconds
(fractional float point value) per mp3 frame. Be sure to set ``vbr`` True
when dealing with VBR, otherwise playtimes may be incorrect."""

# https://bitbucket.org/nicfit/eyed3/issue/32/mp3audioinfotime_secs-incorrect-for-mpeg2
if mp3_header.version >= 2.0 and vbr:
row = _mp3VersionKey(mp3_header.version)
Expand Down
21 changes: 2 additions & 19 deletions src/test/mp3/test_mp3.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
# -*- coding: utf-8 -*-
################################################################################
# Copyright (C) 2009 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 unittest
import os
from io import BytesIO
from .. import DATA_D

import eyed3


def testvalidHeader():
from eyed3.mp3.headers import isValidHeader

Expand Down Expand Up @@ -59,6 +42,7 @@ def testvalidHeader():
assert isValidHeader(0xfffb7050)
assert isValidHeader(0xfffb32c0)


def testFindHeader():
from eyed3.mp3.headers import findHeader

Expand Down Expand Up @@ -86,7 +70,6 @@ def testFindHeader():
assert header_int == 0xfffb9064



@unittest.skipIf(not os.path.exists(DATA_D), "test requires data files")
def testBasicVbrMp3():
audio_file = eyed3.load(os.path.join(DATA_D, "notag-vbr.mp3"))
Expand Down

0 comments on commit 5bf413f

Please sign in to comment.