Travis Shirk avatar Travis Shirk committed f56378e Merge

Merge

Comments (0)

Files changed (18)

 src/eyed3/info.py
 tags
 allmusic-regtest
+src/test/data
+2012-10-23  Travis Shirk  <travis@pobox.com>
+
+	* src/eyed3/main.py:
+	textwrapp --plugins output
+	[5be0db9dab8d] [tip] <stable>
+
+	* MANIFEST.in:
+	Fixed for deleted/moved files
+	[37278ee7692f] <stable>
+
+	* pavement.py:
+	Version bump
+	[df809ec16a43]
+
+	* docs/_static/.keepme, docs/api.rst, docs/compliance.rst,
+	docs/eyed3.core.rst, docs/eyed3.rst, docs/install.rst,
+	docs/license.rst, docs/plugins/mp3_plugin.rst:
+	merge
+	[ff8093ebe790]
+
+2012-10-21  Travis Shirk  <travis@pobox.com>
+
+	* docs/.static/.keepme, docs/.templates/.keepme, version:
+	merge
+	[dda949875e1b]
+
+2012-10-19  Travis Shirk  <travis@pobox.com>
+
+	* etc/config.ini:
+	Merge
+	[021084b35fea]
+
+2012-10-23  Travis Shirk  <travis@pobox.com>
+
+	* src/eyed3/id3/frames.py, src/eyed3/id3/tag.py,
+	src/eyed3/plugins/classic.py, src/eyed3/utils/__init__.py,
+	src/test/id3/test_tag.py:
+	Support for APIC frames that contain links to the image rather than
+	data. Removed -F since args may be being parsed before it is, thus
+	useless (Use \ to escape : in CLI arg values), etc.
+	[f8096a8d2e24] <stable>
+
+2012-10-22  Travis Shirk  <travis@pobox.com>
+
+	* src/eyed3/id3/tag.py, src/test/id3/test_tag.py,
+	src/test/test_main.py, src/test/test_plugins.py:
+	XDOR <--> TDRC, and fixes.
+	[5215b1cb32e5] <stable>
+
+	* docs/api/plugins.rst, docs/cli.rst, docs/plugins/mp3_plugin.rst,
+	etc/eyeD3rc, src/eyed3/main.py, src/eyed3/plugins/examples.py:
+	--plugins now sees user added, removed mp3 and echo plugin, etc.
+	[1672b4d062d5] <stable>
+
+	* .hgignore, README.rst, docs/Makefile, docs/_static/.keepme,
+	docs/api.rst, docs/api/eyed3.rst, docs/api/id3.rst,
+	docs/api/mp3.rst, docs/api/plugins.rst, docs/api/utils.rst,
+	docs/changelog.rst, docs/cli.rst, docs/compliance.rst, docs/conf.py,
+	docs/eyed3.core.rst, docs/eyed3.rst, docs/index.rst,
+	docs/install.rst, docs/installation.rst, docs/license.rst,
+	fabfile.py, pavement.py:
+	docs cleanup and progress.
+	[46ef92825790] <stable>
+
+2012-10-21  Travis Shirk  <travis@pobox.com>
+
+	* README.rst:
+	Don't use sphinx directive in the README
+	[3ac7d6c500e3] <stable>
+
+	* README.rst:
+	Updated
+	[b8a5946e875c] <stable>
+
+2012-10-19  Travis Shirk  <travis@pobox.com>
+
+	* docs/_static/rtd.css, docs/conf.py:
+	Nicer theme
+	[2567e9d838f3] <stable>
+
+	* .hgignore, README.rst, bin/cli_examples.sh, fabfile.py, pavement.py:
+	deploy, pavement, etc.
+	[8e43e3c0e55e] <stable>
+
+	* pavement.py:
+	process
+	[8048fd2675f1] <stable>
+
+	* version:
+	removed
+	[49a279be4552] <stable>
+
+	* docs/.static/.keepme, docs/.templates/.keepme, docs/_static/.keepme,
+	docs/_templates/.keepme, docs/changelog.rst, docs/conf.py,
+	pavement.py:
+	No more version file, docs stuff
+	[16a4844c94f2] <stable>
+
+	* src/eyed3/id3/headers.py, src/eyed3/id3/tag.py:
+	doc strings, exception consistency
+	[1aba2e98a7c1] <stable>
+
+	* pavement.py, src/eyed3/id3/frames.py, src/eyed3/mp3/__init__.py:
+	fixesme
+	[334d88302bd4] <stable>
+
+	* etc/config.ini, etc/eyeD3rc:
+	Rename
+	[0cd7d9ea1f72] <stable>
+
+	* etc/config.ini, src/eyed3/info.py.in, src/eyed3/main.py,
+	src/eyed3/plugins/__init__.py:
+	Hacking on user config, etc.
+	[35a72b69f6d6] <stable>
+
+2012-10-18  Travis Shirk  <travis@pobox.com>
+
+	* src/eyed3/id3/tag.py, src/eyed3/plugins/classic.py:
+	Better support for --unique-file-id
+	[54ab6b262595] <stable>
+
+	* src/eyed3/plugins/classic.py:
+	Added --backup
+	[5da2be60ff7f] <stable>
+
+	* src/eyed3/id3/tag.py:
+	Bug fix for when no conversion of non standard frames is actually
+	necessary.
+	[f46d69da4fb7] <stable>
+
+	* src/eyed3/id3/tag.py:
+	FIXME
+	[856147aff31d] <stable>
+
+	* src/eyed3/id3/frames.py, src/eyed3/id3/tag.py,
+	src/test/id3/test_tag.py:
+	Conversion of sort order frames (XSO* in 2.3 to TSO* in 2.4)
+	[987f2b94bea5] <stable>
+
 2012-10-17  Travis Shirk  <travis@pobox.com>
 
+	* version:
+	Merged stable
+	[7f1db4ea1938]
+
+	* src/eyed3/utils/cli.py:
+	ANSI goodness
+	[dffb203bbfbe] <stable>
+
+	* version:
+	Bumped
+	[99beb1a0d1c1]
+
+	* ChangeLog, pavement.py:
+	tests as part of test_dist again.
+	[38db2c364645] <stable>
+
 	* pavement.py:
 	Added --coverage as a test option, not on by default now.
-	[5c80cfe0d01b] [tip] <stable>
+	[5c80cfe0d01b] <stable>
 
 	* version:
 	bumped
-include version
 include setup.py
 include pavement.py
 include paver-minilib.zip
 include ChangeLog
 include COPYING
 include AUTHORS
-include etc/config.ini
 graft src/eyed3
 include bin/eyeD3
+include etc/eyeD3rc

bin/cli_examples.sh

 eyeD3 --artist="Token Entry" --title="Entities" example.id3 -Q
 # [[[endsection]]]
 
-# [[[section ALB_YR_SET]]]
-eyeD3 -A "Jaybird" -Y 1987 example.id3 -Q
-eyeD3 -G "Hardcore" example.id3 -Q
+# [[[section ALB_YR_G_SET]]]
+eyeD3 -A "Jaybird" -Y 1987 -G "Hardcore" example.id3 -Q
 eyeD3 example.id3
 # [[[endsection]]]
 
-
-# [[[section CLEAR_SET]]]
-eyeD3 --genre="" example.id3
+# [[[section NONSTD_GENRE_SET]]]
+eyeD3 --genre="New York City Hardcore" example.id3 -Q
+eyeD3 example.id3
 # [[[endsection]]]
 
-# [[[section ALL]]]
+# [[[section CONVERT1]]]
+# Convert the current v2.4 frame to v2.3
+eyeD3 --to-v2.3 example.id3 -Q
+# Convert back
+eyeD3 --to-v2.4 example.id3 -Q
+# Convert to v1, this will lose all the more advanced data members ID3 v2 offers
+eyeD3 --to-v1.1 example.id3 -Q
+# [[[endsection]]]
+
+# [[[section DISPLAY_V1]]]
+eyeD3 -1 example.id3
+# [[[endsection]]]
+
+# [[[section SET_WITH_VERSIONS]]]
 # Set an artist value in the ID3 v1 tag
 eyeD3 -1 example.id3 -a id3v1
 # The file now has a v1 and v2 tag, change the v2 artist
 eyeD3 -2 example.id3 -a id3v2
-
 # Take all the values from v2.4 tag (the default) and set them in the v1 tag.
 eyeD3 -2 --to-v1.1 example.id3
 # Take all the values from v1 tag and convert to ID3 v2.3
 eyeD3 -1 --to-v2.3 example.id3
+# [[[endsection]]]
 
-# Remove all the tags
+# [[[section IMG_URL]]]
+eyeD3 --add-image http\\://example.com/cover.jpg:FRONT_COVER example.id3
+# [[[endsection]]]
+
+
+# [[[section REMOVE_ALL_TAGS]]]
 eyeD3 --remove-all example.id3
 # [[[endsection]]]

docs/installation.rst

 
 Dependencies
 ============
-eyeD3 |version| has been tested with Python 2.7. Currently it is the only supported version of
-Python. Support for older versions has been ruled out since 2.7 version provides the best migration
-path to supporting Python3.
+eyeD3 |version| has been tested with Python 2.7. Currently it is the only
+supported version of Python. Support for older versions has been ruled out
+since 2.7 version provides the best migration path to supporting Python3.
 
-The primary interface for building and installing is `Setuptools`_. For example,
-``python setup.py install``. This is an illusion though, ``setuptools`` is *NOT* required because
-`Paver`_ is used instead and it provides the common ``setup.py`` interface that everyone knows
-and loves. In addition, ``Paver`` itself is not required for building/installing, only when
+The primary interface for building and installing is `Setuptools`_. For
+example, ``python setup.py install``. This is an illusion though,
+``setuptools`` is *NOT* required because `Paver`_ is used instead and it
+provides the common ``setup.py`` interface that everyone knows and loves. In
+addition, ``Paver`` itself is not required for building/installing, only when
 doing development on eyeD3 itself.
 
-eyeD3 has NO hard dependencies other thant Python itself but it may take advantage of other
-packages if they are available.
+eyeD3 has NO hard dependencies other than Python itself but it may take
+advantage of other packages if they are available.
 
 .. _setuptools: http://pypi.python.org/pypi/setuptools
 .. _Paver: http://paver.github.com/paver/
 Optional Dependencies
 ---------------------
 
-* `python-magic`_: If this package is installed (or the older ``magic.py`` that often ships with
-  libmagic) it will be used to do better file mime-type detection.
+* `python-magic`_: If this package is installed (or the older ``magic.py`` that
+  often ships with libmagic) it will be used to do better file mime-type
+  detection.
 
 .. _python-magic: https://github.com/ahupp/python-magic
 
 Download Source Archive
 =======================
 
-Source packages are available from the `release archive`_ in tgz and zip formats.
-After unarchiving the distribution file you can install in the common manner:
+Source packages are available from the `release archive`_ in tgz and zip
+formats.  After unarchiving the distribution file you can install in the common
+manner:
 
 .. code-block:: sh
 
 
 The eyeD3 project is managed with `Mercurial
 <http://mercurial.selenic.com/wiki/>`_. To follow eyeD3's development via
-Mercurual instead of downloading official releases, you have the following
+Mercurial instead of downloading official releases, you have the following
 options from the `eyeD3 BitBucket page`_.
 
 * Clone the repository using ``hg`` and the clone URL provided.
-* Make your own fork of the eyeD3 repository by logging into BitBucket and clicking the ``Fork``
-  button on the `eyeD3 BitBucket page`_.
+* Make your own fork of the eyeD3 repository by logging into BitBucket and
+  clicking the ``Fork`` button on the `eyeD3 BitBucket page`_.
 
 To clone the repository to your computer, for instance:
 
 .. note::
   When submitting patches please base them on the 'stable' branch.
 
-It is recommended that you work on eyeD3 within a virtual Python environment since it allows you
-to install the required tools without root access and without clobbering your system installation
-of Python. The top-level directory makes this very easy if you have `virtualenvwrapper`_ installed.
+It is recommended that you work on eyeD3 within a virtual Python environment
+since it allows you to install the required tools without root access and
+without clobbering your system installation of Python. The top-level directory
+makes this very easy if you have `virtualenvwrapper`_ installed.
 
 .. code-block:: sh
 
     $ workon eyeD3
     $ paver test
 
-In the above command a virtual enviroment called `eyeD3` was created and all of the necessary
-developer tools were installed. We then "switch" to this new environment with ``workon`` and
-run the eyeD3 unit tests using ``paver``. The last call to `Paver`_ will run from the virtual
-enviroment, as will the ``Nose`` library that the unit tests require.
+In the above command a virtual enviroment called `eyeD3` was created and all of
+the necessary developer tools were installed. We then "switch" to this new
+environment with ``workon`` and run the eyeD3 unit tests using ``paver``. The
+last call to `Paver`_ will run from the virtual enviroment, as will the
+``Nose`` library that the unit tests require.
 
 .. note::
-  The ``mkenv.bash`` script requires `virtualenvwrapper`_. It provides a nice interface around
-  ``virtualenv`` including the easy switching of environments via the ``workon`` command. If you
-  do not wish to install the wrapper you can use ``virtualenv`` directly but may wish to consult
-  the script for the required steps.
+  The ``mkenv.bash`` script requires `virtualenvwrapper`_. It provides a nice
+  interface around ``virtualenv`` including the easy switching of environments
+  via the ``workon`` command. If you do not wish to install the wrapper you can
+  use ``virtualenv`` directly but may wish to consult the script for the
+  required steps.
 
 .. _eyeD3 BitBucket page: https://bitbucket.org/nicfit/eyed3
 .. _virtualenvwrapper: http://www.doughellmann.com/projects/virtualenvwrapper

docs/plugins/classic_plugin.rst

 Examples
 --------
 eyeD3 can do more than edit exiting tags, it can also create new tags from
-nothing. For these examples we'll use a dummy file.
+nothing. For these examples we'll make a dummy file to work with.
 
 .. {{{cog cli_example("bin/cli_examples.sh", "SETUP", lang="bash") }}}
 .. {{{end}}}
 .. {{{cog cli_example("bin/cli_examples.sh", "ART_TIT_SET", lang="bash") }}}
 .. {{{end}}}
 
-Many options have a shorter name that can be used to save typing. Let's add
-the album name (``-A``) and the year (``-Y``) it was released.
+Most options have a shorter name that can be used to save typing. Let's add
+the album name (``-A``), the genre (``-G``), and the year (``-Y``) the
+record was released.
 
-.. {{{cog cli_example("bin/cli_examples.sh", "ALB_YR_SET", lang="bash") }}}
+.. {{{cog cli_example("bin/cli_examples.sh", "ALB_YR_G_SET", lang="bash") }}}
 .. {{{end}}}
 
-.. {{{cog cli_example("bin/cli_examples.sh", "CLEAR_SET", lang="bash") }}}
+Notice how the genre displayed as "Hardcore (id 129)" in the above tag listing.
+This happens because the genre is a recognized value as defined by the ID3 v1
+standard. eyeD3 used to be very strict about genres, but no longer. You can
+store any value you'd like.
+
+TODO: reference genres plubin
+
+.. {{{cog cli_example("bin/cli_examples.sh", "NONSTD_GENRE_SET", lang="bash") }}}
 .. {{{end}}}
 
-.. {{{cog cli_example("bin/cli_examples.sh", "ALL", lang="bash") }}}
+By default writes ID3 v2.4 tags. This is the latest standard and supports
+UTF-8 which is a very nice thing. Some players are not caught up with the
+latest standards (iTunes, pfft) so it may be necessary to convert amongst the
+various versions. In some cases this can be a lossy operation if a certain
+data field is not supported, but eyeD3 does its best to convert when the
+data whenever possible.
+
+.. {{{cog cli_example("bin/cli_examples.sh", "CONVERT1", lang="bash") }}}
 .. {{{end}}}
+
+The last conversion above converted to v1.1, or so the output says. The 
+final listing shows that the tag is version 2.4. This is because tags can
+contain both versions at once and eyeD3 will always show/load v2 tags first.
+To select the version 1 tag use the ``-1`` option. Also note how the
+the non-standard genre was lost by the conversion, thankfully it is still
+in the v2 tag.
+
+.. {{{cog cli_example("bin/cli_examples.sh", "DISPLAY_V1", lang="bash") }}}
+.. {{{end}}}
+
+The ``-1`` and ``-2`` options also determine which tag will be edited, or even
+which tag will be converted when one of the conversion options is passed.
+
+.. {{{cog cli_example("bin/cli_examples.sh", "SET_WITH_VERSIONS", lang="bash") }}}
+.. {{{end}}}
+
+At this point the tag is all messed up with by these experiments, you can always
+remove the tags to start again.
+
+.. {{{cog cli_example("bin/cli_examples.sh", "REMOVE_ALL_TAGS", lang="bash") }}}
+.. {{{end}}}
+
+Complex Options
+---------------
+
+Some of the command line options contain multiple pieces of information in
+a single value. Take for example the ``--add-image`` option::
+  
+  --add-image IMG_PATH:TYPE[:DESCRIPTION]
+
+This option has 3 pieced of information where one (DESCRIPTION) is optional
+(denoted by the square brackets). Each invidual value is seprated by a ':' like
+so:
+
+.. code-block:: bash
+  
+  $ eyeD3 --add-image cover.png:FRONT_COVER
+
+This will load the image data from ``cover.png`` and store it in the tag with
+the type value for FRONT_COVER images. The list of valid image types are
+listed in the ``--help`` usage information which also states that the IMG_PATH
+value may be a URL so that the image data does not have to be stored in the
+the tag itself. Let's try that now.
+
+.. code-block:: bash
+  $ eyeD3 --add-image http://example.com/cover.jpg:FRONT_COVER
+  eyeD3: error: argument --add-image: invalid ImageArg value: 'http://example.com/cover.jpg:FRONT_COVER'
+
+The problem is the ':' character in the the URL, it confuses the format description of the option value. To solve this escape all delimeter characters in 
+option values with '\'. 
+
+.. {{{cog cli_example("bin/cli_examples.sh", "IMG_URL", lang="bash") }}}
+.. {{{end}}}
+
     put("./dist/%s.md5" % os.path.splitext(SRC_DIST_TGZ)[0], RELEASE_D)
 
 def deploy_docs():
-    raise NotImplementedError("FIXME: disabled until I know I won't blow away the 0.6 files")
     put("./dist/%s" % DOC_DIST, "~")
-    run("tar xzf eyeD3_docs-0.7.0-rc1.tgz -C ./www/eyeD3 --strip-components=1")
+    run("tar xzf %s -C ./www/eyeD3 --strip-components=1" % DOC_DIST)
 
 def deploy():
     deploy_sdist()
                 if not cmd.startswith('#'):
                     cmd_line = "$ %s\n" % cmd
                 else:
-                    cmd_line = cmd
+                    cmd_line = cmd + '\n'
 
-                cmd_line = '\n' + (' ' * 2) + cmd_line
+                cmd_line = (' ' * 2) + cmd_line
                 self.cog.gen.out(cmd_line)
                 output = sh(cmd, capture=True)
                 if output:
                     self.cog.gen.out("\n")
                 for ol in output.splitlines(True):
                     self.cog.gen.out(' ' * 2 + ol)
+                if output:
+                    self.cog.gen.out("\n")
 
 @task
 def cog(options):

src/eyed3/binfuncs.py

-################################################################################
-#  Copyright (C) 2001  Ryan Finne <ryan@finnie.org>
-#  Copyright (C) 2002-2011  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, write to the Free Software
-#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
-################################################################################
-
-
-##
-# Accepts a string of bytes (chars) and returns an array of bits
-# representing the bytes in big endian byte order.
-#
-# \param bytes String of characters to convert.
-# \param sz An optional max size for each byte (default 8 bits/byte) which can
-#           be used to mask out higher bits.
-def bytes2bin(bytes, sz=8):
-    if sz < 1 or sz > 8:
-       raise ValueError("Invalid sz value: %d" % sz)
-
-    '''
-    # I was willing to bet this implementation was gonna be faster, tis not
-    retval = []
-    for bite in bytes:
-        bits = [int(b) for b in bin(ord(bite))[2:].zfill(8)][-sz:]
-        assert(len(bits) == sz)
-        retval.extend(bits)
-    return retval
-    '''
-
-    retVal = []
-    for b in bytes:
-        bits = []
-        b = ord(b)
-        while b > 0:
-            bits.append(b & 1)
-            b >>= 1
-
-        if len(bits) < sz:
-            bits.extend([0] * (sz - len(bits)))
-        elif len(bits) > sz:
-            bits = bits[:sz]
-
-        # Big endian byte order.
-        bits.reverse()
-        retVal.extend(bits)
-
-    return retVal
-
-
-## Convert an array of bits (MSB first) into a string of characters.
-def bin2bytes(x):
-    bits = []
-    bits.extend(x)
-    bits.reverse()
-
-    i = 0
-    out = ''
-    multi = 1
-    ttl = 0
-    for b in bits:
-        i += 1
-        ttl += b * multi
-        multi *= 2
-        if i == 8:
-            i = 0
-            out += chr(ttl)
-            multi = 1
-            ttl = 0
-
-    if multi > 1:
-        out += chr(ttl)
-
-    out = list(out)
-    out.reverse()
-    out = ''.join(out)
-    return out
-
-
-## Convert and array of "bits" (MSB first) to it's decimal value.
-def bin2dec(x):
-    bits = []
-    bits.extend(x)
-    bits.reverse()  # MSB
-
-    multi = 1
-    value = 0
-    for b in bits:
-        value += b * multi
-        multi *= 2
-    return value
-
-
-def bytes2dec(bytes, sz=8):
-    return bin2dec(bytes2bin(bytes, sz))
-
-
-##
-# Convert a decimal value to an array of bits (MSB first), optionally
-# padding the overall size to p bits.
-def dec2bin(n, p=0):
-    assert(n >= 0)
-    retVal = []
-
-    while n > 0:
-       retVal.append(n & 1)
-       n >>= 1
-
-    if p > 0:
-        retVal.extend([0] * (p - len(retVal)))
-    retVal.reverse()
-    return retVal
-
-
-def dec2bytes(n, p=0):
-    return bin2bytes(dec2bin(n, p))
-
-
-##
-# Convert a list of bits (MSB first) to a synch safe list of bits
-# (section 6.2 of the ID3 2.4 spec.
-def bin2synchsafe(x):
-    n = bin2dec(x)
-    if len(x) > 32 or n > 268435456:   # 2^28
-        raise ValueError("Invalid value: %s" % str(x))
-    elif len(x) < 8:
-        return x
-
-    bites = ""
-    bites += chr((n >> 21) & 0x7f)
-    bites += chr((n >> 14) & 0x7f)
-    bites += chr((n >>  7) & 0x7f)
-    bites += chr((n >>  0) & 0x7f)
-    bits = bytes2bin(bites)
-    assert(len(bits) == 32)
-
-    return bits
-
-def bytes2str(bites):
-    s = bytes("")
-    for b in bites:
-        s += ("\\x%02x" % ord(b))
-    return s
-
-

src/eyed3/id3/frames.py

 
 from .. import core
 from ..utils import requireUnicode
-from ..binfuncs import (bytes2dec, dec2bytes, bin2bytes, dec2bin, bytes2bin,
-                        bin2dec)
+from ..utils.binfuncs import *
 from . import ID3_V2, ID3_V2_3, ID3_V2_4
 from . import (LATIN1_ENCODING, UTF_8_ENCODING, UTF_16BE_ENCODING,
                UTF_16_ENCODING, DEFAULT_LANG)

src/eyed3/id3/headers.py

 #
 ################################################################################
 import math, binascii
-from ..binfuncs import bytes2bin, bin2dec, bin2bytes, bin2synchsafe, dec2bin
+from ..utils.binfuncs import *
 from .. import core
 
 from . import ID3_DEFAULT_VERSION, isValidVersion, normalizeVersion

src/eyed3/main.py

 import sys, exceptions, os.path
 import ConfigParser
 import traceback, pdb
+import textwrap
 import eyed3, eyed3.utils, eyed3.utils.cli, eyed3.plugins, eyed3.info
 
 
     for plugin in set(all_plugins.values()):
         plugin_names.append(plugin.NAMES[0])
 
+    print("Type 'eyeD3 --plugin=<name> --help' for more help")
+    print("")
+
     plugin_names.sort()
     for name in plugin_names:
         plugin = all_plugins[name]
         alt_names = plugin.NAMES[1:]
         alt_names = " (%s)" % ", ".join(alt_names) if alt_names else ""
 
-        print("- %s%s:\n%s\n" % (eyed3.utils.cli.boldText(name),
-                                 alt_names, plugin.SUMMARY))
+        print("- %s%s:" % (eyed3.utils.cli.boldText(name), alt_names))
+        for l in textwrap.wrap(plugin.SUMMARY):
+            print(l)
+        print("")
 
 
 def _loadConfig(config_file=None):

src/eyed3/mp3/headers.py

 
 from . import Mp3Exception
 
-from ..binfuncs import bytes2bin, bytes2dec, bin2dec
+from ..utils.binfuncs import bytes2bin, bytes2dec, bin2dec
 from .. import core
 
 import logging

src/eyed3/plugins/classic.py

                 raise ValueError("Unable to read file")
             return (unicode(data), desc, lang)
         def PlayCountArg(pc):
+            if not pc:
+                raise ValueError("value required")
             increment = False
             if pc[0] == '+':
                 pc = int(pc[1:])
                           dest="remove_image", default=[],
                           metavar="DESCRIPTION",
                           help=ARGS_HELP["--remove-image"])
+        gid3.add_argument("--remove-all-images", action="store_true",
+                          dest="remove_all_images",
+                          help=ARGS_HELP["--remove-all-images"])
         gid3.add_argument("--write-images", dest="write_images_dir",
                           metavar="DIR", type=DirArg,
                           help=ARGS_HELP["--write-images"])
-        gid3.add_argument("--remove-all-images", action="store_true",
-                          dest="remove_all_images",
-                          help=ARGS_HELP["--remove-all-images"])
 
         gid3.add_argument("--add-object", action="append", type=ObjectArg,
                           dest="objects", default=[],
             increment, pc = playcount_arg
             if increment:
                 printWarning("Incrementing play count by %d" % pc)
-                if tag.play_count is None:
-                    tag.play_count = 0
                 tag.play_count += pc
             else:
                 printWarning("Setting play count to %d" % pc)

src/eyed3/utils/binfuncs.py

+################################################################################
+#  Copyright (C) 2001  Ryan Finne <ryan@finnie.org>
+#  Copyright (C) 2002-2011  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, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+################################################################################
+
+
+def bytes2bin(bytes, sz=8):
+    '''Accepts a string of ``bytes`` (chars) and returns an array of bits
+    representing the bytes in big endian byte order. An optional max ``sz`` for
+    each byte (default 8 bits/byte) which can  be used to mask out higher
+    bits.'''
+    if sz < 1 or sz > 8:
+        raise ValueError("Invalid sz value: %d" % sz)
+
+    '''
+    # I was willing to bet this implementation was gonna be faster, tis not
+    retval = []
+    for bite in bytes:
+        bits = [int(b) for b in bin(ord(bite))[2:].zfill(8)][-sz:]
+        assert(len(bits) == sz)
+        retval.extend(bits)
+    return retval
+    '''
+
+    retVal = []
+    for b in bytes:
+        bits = []
+        b = ord(b)
+        while b > 0:
+            bits.append(b & 1)
+            b >>= 1
+
+        if len(bits) < sz:
+            bits.extend([0] * (sz - len(bits)))
+        elif len(bits) > sz:
+            bits = bits[:sz]
+
+        # Big endian byte order.
+        bits.reverse()
+        retVal.extend(bits)
+
+    return retVal
+
+
+## Convert an array of bits (MSB first) into a string of characters.
+def bin2bytes(x):
+    bits = []
+    bits.extend(x)
+    bits.reverse()
+
+    i = 0
+    out = ''
+    multi = 1
+    ttl = 0
+    for b in bits:
+        i += 1
+        ttl += b * multi
+        multi *= 2
+        if i == 8:
+            i = 0
+            out += chr(ttl)
+            multi = 1
+            ttl = 0
+
+    if multi > 1:
+        out += chr(ttl)
+
+    out = list(out)
+    out.reverse()
+    out = ''.join(out)
+    return out
+
+
+def bin2dec(x):
+    '''Convert ``x``, an array of "bits" (MSB first), to it's decimal value.'''
+    bits = []
+    bits.extend(x)
+    bits.reverse()  # MSB
+
+    multi = 1
+    value = 0
+    for b in bits:
+        value += b * multi
+        multi *= 2
+    return value
+
+
+def bytes2dec(bytes, sz=8):
+    return bin2dec(bytes2bin(bytes, sz))
+
+
+def dec2bin(n, p=0):
+    '''Convert a decimal value ``n`` to an array of bits (MSB first).
+    Optionally, pad the overall size to ``p`` bits.'''
+    assert(n >= 0)
+    retVal = []
+
+    while n > 0:
+        retVal.append(n & 1)
+        n >>= 1
+
+    if p > 0:
+        retVal.extend([0] * (p - len(retVal)))
+    retVal.reverse()
+    return retVal
+
+
+def dec2bytes(n, p=0):
+    return bin2bytes(dec2bin(n, p))
+
+
+def bin2synchsafe(x):
+    '''Convert ``x``, a list of bits (MSB first), to a synch safe list of bits.
+    (section 6.2 of the ID3 2.4 spec).'''
+    n = bin2dec(x)
+    if len(x) > 32 or n > 268435456:   # 2^28
+        raise ValueError("Invalid value: %s" % str(x))
+    elif len(x) < 8:
+        return x
+
+    bites = ""
+    bites += chr((n >> 21) & 0x7f)
+    bites += chr((n >> 14) & 0x7f)
+    bites += chr((n >>  7) & 0x7f)
+    bites += chr((n >>  0) & 0x7f)
+    bits = bytes2bin(bites)
+    assert(len(bits) == 32)
+
+    return bits
+
+def bytes2str(bites):
+    s = bytes("")
+    for b in bites:
+        s += ("\\x%02x" % ord(b))
+    return s
+
+

src/test/id3/test_headers.py

 import unittest
 from cStringIO import StringIO
 from nose.tools import *
-from eyed3.binfuncs import dec2bin, bin2bytes, bin2synchsafe
+from eyed3.utils.binfuncs import dec2bin, bin2bytes, bin2synchsafe
 from eyed3.id3.headers import *
 from eyed3.id3 import ID3_DEFAULT_VERSION, TagException
 

src/test/test_binfuncs.py

 ################################################################################
 from nose.tools import *
 
-from eyed3.binfuncs import *
+from eyed3.utils.binfuncs import *
 
 def test_bytes2bin():
     # test ones and zeros, sz==8

src/test/test_classic_plugin.py

             assert_is_not_none(af.tag)
             assert_equal(af.tag.original_release_date.year, 1981)
 
-    # TODO: --orig-release-date, --recording-date, --encoding-date,
-    #       --tagging-date, -p, --play-count, --bpm, --unique-file-id, etc..
-    #       --rename, --force-update, -F, -1, -2, etc...
+    def testNewTagRecordingDate(self, version=id3.ID3_DEFAULT_VERSION):
+        for opts in [ ["--recording-date=1993-10-30", self.test_file] ]:
+            self._addVersionOpt(version, opts)
+
+            with RedirectStdStreams() as out:
+                args, _, config = main.parseCommandLine(opts)
+                retval = main.main(args, config)
+                assert_equal(retval, 0)
+
+            af = eyed3.load(self.test_file)
+            assert_is_not_none(af)
+            assert_is_not_none(af.tag)
+            assert_equal(af.tag.recording_date.year, 1993)
+            assert_equal(af.tag.recording_date.month, 10)
+            assert_equal(af.tag.recording_date.day, 30)
+
+    def testNewTagEncodingDate(self, version=id3.ID3_DEFAULT_VERSION):
+        for opts in [ ["--encoding-date=2012-10-23T20:22", self.test_file] ]:
+            self._addVersionOpt(version, opts)
+
+            with RedirectStdStreams() as out:
+                args, _, config = main.parseCommandLine(opts)
+                retval = main.main(args, config)
+                assert_equal(retval, 0)
+
+            af = eyed3.load(self.test_file)
+            assert_is_not_none(af)
+            assert_is_not_none(af.tag)
+            assert_equal(af.tag.encoding_date.year, 2012)
+            assert_equal(af.tag.encoding_date.month, 10)
+            assert_equal(af.tag.encoding_date.day, 23)
+            assert_equal(af.tag.encoding_date.hour, 20)
+            assert_equal(af.tag.encoding_date.minute, 22)
+
+    def testNewTagTaggingDate(self, version=id3.ID3_DEFAULT_VERSION):
+        for opts in [ ["--tagging-date=2012-10-23T20:22", self.test_file] ]:
+            self._addVersionOpt(version, opts)
+
+            with RedirectStdStreams() as out:
+                args, _, config = main.parseCommandLine(opts)
+                retval = main.main(args, config)
+                assert_equal(retval, 0)
+
+            af = eyed3.load(self.test_file)
+            assert_is_not_none(af)
+            assert_is_not_none(af.tag)
+            assert_equal(af.tag.tagging_date.year, 2012)
+            assert_equal(af.tag.tagging_date.month, 10)
+            assert_equal(af.tag.tagging_date.day, 23)
+            assert_equal(af.tag.tagging_date.hour, 20)
+            assert_equal(af.tag.tagging_date.minute, 22)
+
+    def testNewTagPlayCount(self):
+        for expected, opts in [ (0, ["--play-count=0", self.test_file]),
+                                (1, ["--play-count=+1", self.test_file]),
+                                (6, ["--play-count=+5", self.test_file]),
+                                (7, ["--play-count=7", self.test_file]),
+                                (10000, ["--play-count=10000", self.test_file]),
+                              ]:
+
+            with RedirectStdStreams() as out:
+                args, _, config = main.parseCommandLine(opts)
+                retval = main.main(args, config)
+                assert_equal(retval, 0)
+
+            af = eyed3.load(self.test_file)
+            assert_is_not_none(af)
+            assert_is_not_none(af.tag)
+            assert_equal(af.tag.play_count, expected)
+
+    def testNewTagPlayCountInvalid(self):
+        for expected, opts in [ (0, ["--play-count=", self.test_file]),
+                                (0, ["--play-count=-24", self.test_file]),
+                                (0, ["--play-count=+", self.test_file]),
+                                (0, ["--play-count=abc", self.test_file]),
+                                (0, ["--play-count=False", self.test_file]),
+                              ]:
+
+            with RedirectStdStreams() as out:
+                try:
+                    args, _, config = main.parseCommandLine(opts)
+                except SystemExit as ex:
+                    assert_not_equal(ex.code, 0)
+                else:
+                    assert_false("Should not have gotten here")
+
+    def testNewTagBpm(self):
+        for expected, opts in [ (1, ["--bpm=1", self.test_file]),
+                                (180, ["--bpm=180", self.test_file]),
+                                (117, ["--bpm", "116.7", self.test_file]),
+                                (116, ["--bpm", "116.4", self.test_file]),
+                              ]:
+
+            with RedirectStdStreams() as out:
+                args, _, config = main.parseCommandLine(opts)
+                retval = main.main(args, config)
+                assert_equal(retval, 0)
+
+            af = eyed3.load(self.test_file)
+            assert_is_not_none(af)
+            assert_is_not_none(af.tag)
+            assert_equal(af.tag.bpm, expected)
+
+    def testNewTagBpmInvalid(self):
+        for expected, opts in [ (0, ["--bpm=", self.test_file]),
+                                (0, ["--bpm=-24", self.test_file]),
+                                (0, ["--bpm=+", self.test_file]),
+                                (0, ["--bpm=abc", self.test_file]),
+                                (0, ["--bpm", "=180", self.test_file]),
+                              ]:
+
+            with RedirectStdStreams() as out:
+                try:
+                    args, _, config = main.parseCommandLine(opts)
+                except SystemExit as ex:
+                    assert_not_equal(ex.code, 0)
+                else:
+                    assert_false("Should not have gotten here")
+
+    def testNewTagPublisher(self):
+        for expected, opts in [
+                ("Drag City", ["-p", "Drag City", self.test_file]),
+                ("SST", ["--publisher", "SST", self.test_file]),
+                ("Dischord", ["--publisher=Dischord", self.test_file]),
+               ]:
+
+            with RedirectStdStreams() as out:
+                args, _, config = main.parseCommandLine(opts)
+                retval = main.main(args, config)
+                assert_equal(retval, 0)
+
+            af = eyed3.load(self.test_file)
+            assert_is_not_none(af)
+            assert_is_not_none(af.tag)
+            assert_equal(af.tag.publisher, expected)
+
+    # TODO:
+    #       --unique-file-id
+    #       --add-comment, --remove-comment, --remove-all-comments
+    #       --add-lyrics, --remove-lyrics, --remove-all-lyrics
+    #       --text-frame, --user-text-frame
+    #       --url-frame, --user-user-frame
+    #       --add-image, --remove-image, --remove-all-images, --write-images
+    #       etc.
+    #       --rename, --force-update, -1, -2, --exclude
 
     def testNewTagSimpleComment(self, version=id3.ID3_DEFAULT_VERSION):
         if version[0] == 1:
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.