Skip to content

Commit

Permalink
Implement Popularimeter beetbox#23
Browse files Browse the repository at this point in the history
  • Loading branch information
Ingo Fischer committed Jan 6, 2020
1 parent f403f0e commit 5496c2b
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 55 deletions.
57 changes: 5 additions & 52 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,57 +1,10 @@
dist: trusty
language: python
sudo: false

env:
global:
# Undocumented feature of nose-show-skipped.
NOSE_SHOW_SKIPPED: 1
services:
- docker

matrix:
include:
- python: 2.7.13
env: {TOX_ENV: py27-cov, COVERAGE: 1}
- python: 2.7.13
env: {TOX_ENV: py27-test}
- python: 3.4
env: {TOX_ENV: py34-test}
- python: 3.5
env: {TOX_ENV: py35-test}
- python: 3.6
env: {TOX_ENV: py36-test}
- python: 3.7
env: {TOX_ENV: py37-test}
dist: xenial
- python: 3.8-dev
env: {TOX_ENV: py38-test}
dist: xenial
- python: pypy
env: {TOX_ENV: pypy-test}
- python: 2.7
env: {TOX_ENV: py27-flake8}
- python: 3.5
env: {TOX_ENV: py35-flake8}

# Non-Python dependencies.
addons:
apt:
packages:
- bash-completion

# To install dependencies, tell tox to do everything but actually running the
# test.
install:
- travis_retry pip install 'tox<=3.8.1'
- travis_retry tox -e $TOX_ENV --notest

script:
- tox -e $TOX_ENV

# Report coverage to codecov.io.
before_install:
- "[ ! -z $COVERAGE ] && travis_retry pip install codecov || true"
after_success:
- "[ ! -z $COVERAGE ] && codecov || true"
- make build

cache:
pip: true
script:
- make tox
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ RUN apt-get update \
&& apt-get clean

COPY requirements.txt .
COPY requirements-dev.txt .
RUN pip install -r requirements.txt
RUN pip install -r requirements-dev.txt

COPY . .
RUN pip install .
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,27 @@ tox:
test:
$(DOCKER_CMD) python -m unittest discover .

.PHONY: testpopm
testpopm:
$(DOCKER_CMD) python -m unittest test.test_mediafile.MP3Test

.PHONY: lint
lint:
$(DOCKER_CMD) flake8

.PHONY: ipython
ipython:
$(DOCKER_CMD) ipython

.PHONY: virtualenv
virtualenv:
virtualenv --python=/usr/bin/python3.7 venv
. ./venv/bin/activate && \
pip install -r requirements.txt && \
pip install -r requirements-dev.txt && \
pip install .


.PHONY: testpy
testpy:
$(DOCKER_CMD) python _test.py
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
MediaFile: read and write audio files' tags in Python
=====================================================

.. image:: https://travis-ci.org/ifischer/mediafile.svg?branch=master
:target: https://travis-ci.org/ifischer/mediafile
.. image:: https://travis-ci.com/ifischer/mediafile.svg?branch=master
:target: https://travis-ci.com/ifischer/mediafile

.. image:: http://img.shields.io/pypi/v/mediafile.svg
:target: https://pypi.python.org/pypi/mediafile
Expand Down
45 changes: 45 additions & 0 deletions mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1222,6 +1222,8 @@ def _none_value(self):
return False
elif self.out_type == six.text_type:
return u''
elif self.out_type == dict:
return {}


class ListMediaField(MediaField):
Expand Down Expand Up @@ -1414,6 +1416,34 @@ def __delete__(self, mediafile):
delattr(mediafile, 'images')


class PopmMediaField(MediaField):
""" Access Popularimeter as dictionary."""


class MP3PopmStorageStyle(StorageStyle):
formats = ['MP3']

def get(self, mutagen_file):
"""Returns POPM dictionary: {EMAIL: {'rating': RATING, 'count': COUNT}}
"""
return {
popm.email: {'rating': popm.rating, 'count': popm.count}
for popm in mutagen_file.tags.getall('POPM')
}

def set(self, mutagen_file, value):
"""Set POPM. 'count' will be set to 0 by default."""
popm_list = [
mutagen.id3.POPM(
email=email,
rating=value[email]['rating'],
count=value[email].get('count', 0)
)
for email in value.keys()
]
mutagen_file.tags.setall('POPM', popm_list)


class QNumberField(MediaField):
"""Access integer-represented Q number fields.
Expand Down Expand Up @@ -1849,6 +1879,10 @@ def update(self, dict):
StorageStyle('MUSICBRAINZ_ALBUMCOMMENT'),
ASFStorageStyle('MusicBrainz/Album Comment'),
)
popm = PopmMediaField(
MP3PopmStorageStyle(key='POPM', as_type=list),
out_type=dict
)

# Release date.
date = DateField(
Expand Down Expand Up @@ -2090,6 +2124,17 @@ def update(self, dict):
ASFStorageStyle('INITIALKEY'),
)

# @property
# def popm(self):
# # return {}
# return self._popm
#
# @popm.setter
# def popm(self, value):
# self._popm = value
# # from ipdb import set_trace; set_trace()
# # pass

@property
def length(self):
"""The duration of the audio in seconds (a float)."""
Expand Down
3 changes: 3 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hunter==3.0.5
ipdb==0.12.3
ipython==7.10.1
5 changes: 5 additions & 0 deletions snippets.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
popm_tags = mediafile.mgfile.tags.getall('POPM')
return {
tag.email: Popularimeter(email=tag.email, rating=tag.rating, count=tag.count)
for tag in popm_tags
}
Binary file added test/rsrc/popm.mp3
Binary file not shown.
33 changes: 32 additions & 1 deletion test/test_mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,37 @@ def test_unknown_apic_type(self):
mediafile = self._mediafile_fixture('image_unknown_type')
self.assertEqual(mediafile.images[0].type, ImageType.other)

def test_write_single_popm(self):
mediafile = self._mediafile_fixture('empty')
test_email = '[email protected]'
mediafile.popm = {
test_email: {'rating': 1, 'count': 1}
}
mediafile.save()
self.assertEqual(mediafile.popm, {
test_email: {'rating': 1, 'count': 1}
})

def test_write_popm_without_count(self):
mediafile = self._mediafile_fixture('empty')
test_email = '[email protected]'
mediafile.popm = {test_email: {'rating': 1}}
mediafile.save()
self.assertEqual(mediafile.popm, {
test_email: {'rating': 1, 'count': 0}
})

def test_write_multiple_popm(self):
mediafile = self._mediafile_fixture('full')
mediafile.popm = {'[email protected]': {'rating': 1, 'count': 1},
'[email protected]': {'rating': 2, 'count': 2}}
mediafile.save()

self.assertEqual(mediafile.popm, {
'[email protected]': {'rating': 1, 'count': 1},
'[email protected]': {'rating': 2, 'count': 2},
})


class MP4Test(ReadWriteTestBase, PartialTestMixin,
ImageStructureTestMixin, unittest.TestCase):
Expand Down Expand Up @@ -975,7 +1006,7 @@ def test_properties_from_readable_fields(self):

def test_known_fields(self):
fields = list(ReadWriteTestBase.tag_fields)
fields.extend(('encoder', 'images', 'genres', 'albumtype'))
fields.extend(('encoder', 'images', 'genres', 'albumtype', 'popm'))
assertCountEqual(self, MediaFile.fields(), fields)

def test_fields_in_readable_fields(self):
Expand Down

0 comments on commit 5496c2b

Please sign in to comment.