tagpy-2013.1/0000755000175000017500000000000012067051706014004 5ustar andreasandreas00000000000000tagpy-2013.1/distribute_setup.py0000644000175000017500000003661612017513621017762 0ustar andreasandreas00000000000000#!python """Bootstrap distribute installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from distribute_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import os import sys import time import fnmatch import tempfile import tarfile from distutils import log try: from site import USER_SITE except ImportError: USER_SITE = None try: import subprocess def _python_cmd(*args): args = (sys.executable,) + args return subprocess.call(args) == 0 except ImportError: # will be used for python 2.3 def _python_cmd(*args): args = (sys.executable,) + args # quoting arguments if windows if sys.platform == 'win32': def quote(arg): if ' ' in arg: return '"%s"' % arg return arg args = [quote(arg) for arg in args] return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 DEFAULT_VERSION = "0.6.19" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" SETUPTOOLS_PKG_INFO = """\ Metadata-Version: 1.0 Name: setuptools Version: %s Summary: xxxx Home-page: xxx Author: xxx Author-email: xxx License: xxx Description: xxx """ % SETUPTOOLS_FAKED_VERSION def _install(tarball): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # installing log.warn('Installing Distribute') if not _python_cmd('setup.py', 'install'): log.warn('Something went wrong during the installation.') log.warn('See the error message above.') finally: os.chdir(old_wd) def _build_egg(egg, tarball, to_dir): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # building an egg log.warn('Building a Distribute egg in %s', to_dir) _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) finally: os.chdir(old_wd) # returning the result log.warn(egg) if not os.path.exists(egg): raise IOError('Could not build the egg.') def _do_download(version, download_base, to_dir, download_delay): egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' % (version, sys.version_info[0], sys.version_info[1])) if not os.path.exists(egg): tarball = download_setuptools(version, download_base, to_dir, download_delay) _build_egg(egg, tarball, to_dir) sys.path.insert(0, egg) import setuptools setuptools.bootstrap_install_from = egg def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15, no_fake=True): # making sure we use the absolute path to_dir = os.path.abspath(to_dir) was_imported = 'pkg_resources' in sys.modules or \ 'setuptools' in sys.modules try: try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): if not no_fake: _fake_setuptools() raise ImportError except ImportError: return _do_download(version, download_base, to_dir, download_delay) try: pkg_resources.require("distribute>="+version) return except pkg_resources.VersionConflict: e = sys.exc_info()[1] if was_imported: sys.stderr.write( "The required version of distribute (>=%s) is not available,\n" "and can't be installed while this script is running. Please\n" "install a more recent version first, using\n" "'easy_install -U distribute'." "\n\n(Currently using %r)\n" % (version, e.args[0])) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok return _do_download(version, download_base, to_dir, download_delay) except pkg_resources.DistributionNotFound: return _do_download(version, download_base, to_dir, download_delay) finally: if not no_fake: _create_fake_setuptools_pkg_info(to_dir) def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay=15): """Download distribute from a specified location and return its filename `version` should be a valid distribute version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ # making sure we use the absolute path to_dir = os.path.abspath(to_dir) try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen tgz_name = "distribute-%s.tar.gz" % version url = download_base + tgz_name saveto = os.path.join(to_dir, tgz_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: log.warn("Downloading %s", url) src = urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = src.read() dst = open(saveto, "wb") dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def _no_sandbox(function): def __no_sandbox(*args, **kw): try: from setuptools.sandbox import DirectorySandbox if not hasattr(DirectorySandbox, '_old'): def violation(*args): pass DirectorySandbox._old = DirectorySandbox._violation DirectorySandbox._violation = violation patched = True else: patched = False except ImportError: patched = False try: return function(*args, **kw) finally: if patched: DirectorySandbox._violation = DirectorySandbox._old del DirectorySandbox._old return __no_sandbox def _patch_file(path, content): """Will backup the file then patch it""" existing_content = open(path).read() if existing_content == content: # already patched log.warn('Already patched.') return False log.warn('Patching...') _rename_path(path) f = open(path, 'w') try: f.write(content) finally: f.close() return True _patch_file = _no_sandbox(_patch_file) def _same_content(path, content): return open(path).read() == content def _rename_path(path): new_name = path + '.OLD.%s' % time.time() log.warn('Renaming %s into %s', path, new_name) os.rename(path, new_name) return new_name def _remove_flat_installation(placeholder): if not os.path.isdir(placeholder): log.warn('Unknown installation at %s', placeholder) return False found = False for file in os.listdir(placeholder): if fnmatch.fnmatch(file, 'setuptools*.egg-info'): found = True break if not found: log.warn('Could not locate setuptools*.egg-info') return log.warn('Removing elements out of the way...') pkg_info = os.path.join(placeholder, file) if os.path.isdir(pkg_info): patched = _patch_egg_dir(pkg_info) else: patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) if not patched: log.warn('%s already patched.', pkg_info) return False # now let's move the files out of the way for element in ('setuptools', 'pkg_resources.py', 'site.py'): element = os.path.join(placeholder, element) if os.path.exists(element): _rename_path(element) else: log.warn('Could not find the %s element of the ' 'Setuptools distribution', element) return True _remove_flat_installation = _no_sandbox(_remove_flat_installation) def _after_install(dist): log.warn('After install bootstrap.') placeholder = dist.get_command_obj('install').install_purelib _create_fake_setuptools_pkg_info(placeholder) def _create_fake_setuptools_pkg_info(placeholder): if not placeholder or not os.path.exists(placeholder): log.warn('Could not find the install location') return pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) setuptools_file = 'setuptools-%s-py%s.egg-info' % \ (SETUPTOOLS_FAKED_VERSION, pyver) pkg_info = os.path.join(placeholder, setuptools_file) if os.path.exists(pkg_info): log.warn('%s already exists', pkg_info) return log.warn('Creating %s', pkg_info) f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() pth_file = os.path.join(placeholder, 'setuptools.pth') log.warn('Creating %s', pth_file) f = open(pth_file, 'w') try: f.write(os.path.join(os.curdir, setuptools_file)) finally: f.close() _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) def _patch_egg_dir(path): # let's check if it's already patched pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') if os.path.exists(pkg_info): if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): log.warn('%s already patched.', pkg_info) return False _rename_path(path) os.mkdir(path) os.mkdir(os.path.join(path, 'EGG-INFO')) pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() return True _patch_egg_dir = _no_sandbox(_patch_egg_dir) def _before_install(): log.warn('Before install bootstrap.') _fake_setuptools() def _under_prefix(location): if 'install' not in sys.argv: return True args = sys.argv[sys.argv.index('install')+1:] for index, arg in enumerate(args): for option in ('--root', '--prefix'): if arg.startswith('%s=' % option): top_dir = arg.split('root=')[-1] return location.startswith(top_dir) elif arg == option: if len(args) > index: top_dir = args[index+1] return location.startswith(top_dir) if arg == '--user' and USER_SITE is not None: return location.startswith(USER_SITE) return True def _fake_setuptools(): log.warn('Scanning installed packages') try: import pkg_resources except ImportError: # we're cool log.warn('Setuptools or Distribute does not seem to be installed.') return ws = pkg_resources.working_set try: setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', replacement=False)) except TypeError: # old distribute API setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) if setuptools_dist is None: log.warn('No setuptools distribution found') return # detecting if it was already faked setuptools_location = setuptools_dist.location log.warn('Setuptools installation detected at %s', setuptools_location) # if --root or --preix was provided, and if # setuptools is not located in them, we don't patch it if not _under_prefix(setuptools_location): log.warn('Not patching, --root or --prefix is installing Distribute' ' in another location') return # let's see if its an egg if not setuptools_location.endswith('.egg'): log.warn('Non-egg installation') res = _remove_flat_installation(setuptools_location) if not res: return else: log.warn('Egg installation') pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') if (os.path.exists(pkg_info) and _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): log.warn('Already patched.') return log.warn('Patching...') # let's create a fake egg replacing setuptools one res = _patch_egg_dir(setuptools_location) if not res: return log.warn('Patched done.') _relaunch() def _relaunch(): log.warn('Relaunching...') # we have to relaunch the process # pip marker to avoid a relaunch bug if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: sys.argv[0] = 'setup.py' args = [sys.executable] + sys.argv sys.exit(subprocess.call(args)) def _extractall(self, path=".", members=None): """Extract all members from the archive to the current working directory and set owner, modification time and permissions on directories afterwards. `path' specifies a different directory to extract to. `members' is optional and must be a subset of the list returned by getmembers(). """ import copy import operator from tarfile import ExtractError directories = [] if members is None: members = self for tarinfo in members: if tarinfo.isdir(): # Extract directories with a safe mode. directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 448 # decimal for oct 0700 self.extract(tarinfo, path) # Reverse sort directories. if sys.version_info < (2, 4): def sorter(dir1, dir2): return cmp(dir1.name, dir2.name) directories.sort(sorter) directories.reverse() else: directories.sort(key=operator.attrgetter('name'), reverse=True) # Set correct owner, mtime and filemode on directories. for tarinfo in directories: dirpath = os.path.join(path, tarinfo.name) try: self.chown(tarinfo, dirpath) self.utime(tarinfo, dirpath) self.chmod(tarinfo, dirpath) except ExtractError: e = sys.exc_info()[1] if self.errorlevel > 1: raise else: self._dbg(1, "tarfile: %s" % e) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" tarball = download_setuptools() _install(tarball) if __name__ == '__main__': main(sys.argv[1:]) tagpy-2013.1/PKG-INFO0000644000175000017500000000361012067051706015101 0ustar andreasandreas00000000000000Metadata-Version: 1.1 Name: tagpy Version: 2013.1 Summary: Python Bindings for TagLib Home-page: http://mathema.tician.de/software/tagpy Author: Andreas Kloeckner Author-email: inform@tiker.net License: MIT Description: TagPy is a set of Python bindings for Scott Wheeler's `TagLib `_. It builds upon `Boost.Python `_, a wrapper generation library which is part of the renowned Boost set of C++ libraries. Just like TagLib, TagPy can: * read and write ID3 tags of version 1 and 2, with many supported frame types for version 2 (in MPEG Layer 2 and MPEG Layer 3, FLAC and MPC), * access Xiph Comments in Ogg Vorbis Files and Ogg Flac Files, * access APE tags in Musepack and MP3 files. All these features have their own specific interfaces, but TagLib's generic tag reading and writing mechanism is also supported. It comes with a bunch of examples. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Multimedia :: Sound/Audio Classifier: Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping Classifier: Topic :: Multimedia :: Sound/Audio :: Editors Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Utilities tagpy-2013.1/configure.py0000755000175000017500000000013312017513621016331 0ustar andreasandreas00000000000000#! /usr/bin/env python from aksetup_helper import configure_frontend configure_frontend() tagpy-2013.1/Makefile.in0000644000175000017500000000034110700452175016044 0ustar andreasandreas00000000000000.PHONY : all install clean tags dist all: tags ${PYTHON_EXE} setup.py build dist: ${PYTHON_EXE} setup.py sdist install: tags ${PYTHON_EXE} setup.py install clean: rm -Rf build rm -f tags tags: ctags -R src || true tagpy-2013.1/src/0000755000175000017500000000000012067051706014573 5ustar andreasandreas00000000000000tagpy-2013.1/src/wrapper/0000755000175000017500000000000012067051706016253 5ustar andreasandreas00000000000000tagpy-2013.1/src/wrapper/common.hpp0000644000175000017500000001425111004161462020246 0ustar andreasandreas00000000000000// Copyright (c) 2006-2008 Andreas Kloeckner // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #include #include #include using namespace boost::python; using namespace TagLib; using namespace std; #define MF_OL(MF, MIN, MAX) \ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(MF##_overloads, MF, MIN, MAX); #define DEF_SIMPLE_METHOD(NAME) \ def(#NAME, &cl::NAME) #define DEF_VIRTUAL_METHOD(NAME) \ def(#NAME, pure_virtual(&cl::NAME)) #define DEF_OVERLOADED_METHOD(NAME, CAST) \ def(#NAME, (CAST) &cl::NAME, NAME##_overloads()) #define ENUM_VALUE(NAME) \ value(#NAME, scope::NAME) #define ADD_RO_PROPERTY(NAME) \ add_property(#NAME, &cl::NAME) #define TAGPY_TAGLIB_HEX_VERSION \ (TAGLIB_MAJOR_VERSION << 16) + \ (TAGLIB_MINOR_VERSION << 8) + \ (TAGLIB_PATCH_VERSION << 0) #if (TAGPY_TAGLIB_HEX_VERSION < 0x10400) #warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #warning TagPy is meant to wrap TagLib 1.4 and above. #warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #endif namespace { template inline object make_list(Iterator first, Iterator last) { boost::python::list result; while (first != last) result.append(*first++); return result; } // ------------------------------------------------------------- // Map // ------------------------------------------------------------- template Value &Map_getitem(Map &m, const Key &k) { if (!m.contains(k)) { PyErr_SetString( PyExc_KeyError, "key not in map"); throw error_already_set(); } return m[k]; } template void Map_setitem(Map &m, const Key &k, const Value &v) { m[k] = v; } template object Map_keys(Map &m) { boost::python::list keys; typedef Map map; typename map::Iterator first = m.begin(), last = m.end(); while (first != last) keys.append((first++)->first); return keys; } template void exposeMap(const char *name) { typedef Map map; class_(name) .def("__len__", &map::size) .def("size", &map::size) .def("clear", &map::clear, return_self<>()) .def("isEmpty", &map::isEmpty) .def("__getitem__", Map_getitem, return_internal_reference<>()) .def("__setitem__", Map_setitem) .def("__contains__", &map::contains) .def("keys", Map_keys) ; } // ------------------------------------------------------------- // List // ------------------------------------------------------------- template Value &List_getitem(List &l, TagLib::uint i) { if (i >= l.size()) { PyErr_SetString( PyExc_IndexError, "index out of bounds"); throw error_already_set(); } return l[i]; } template void List_setitem(List &l, TagLib::uint i, Value v) { if (i >= l.size()) { PyErr_SetString( PyExc_IndexError, "index out of bounds"); throw error_already_set(); } l[i] = v; } template void List_append(List &l, Value v) { l.append(v); } template void exposeList(const char *name) { typedef List list; class_(name) .def("__len__", &list::size) .def("size", &list::size) .def("clear", &list::clear, return_self<>()) .def("isEmpty", &list::isEmpty) .def("__getitem__", List_getitem, return_value_policy()) .def("__setitem__", List_setitem) .def("append", List_append) // MISSING: iterators, insert, find, contains, erase, // assignment, comparison ; } // ------------------------------------------------------------- // PointerList // ------------------------------------------------------------- template Value *&PointerList_getitem(List &l, TagLib::uint i) { if (i >= l.size()) { PyErr_SetString( PyExc_IndexError, "index out of bounds"); throw error_already_set(); } return l[i]; } template void PointerList_setitem(List &l, TagLib::uint i, auto_ptr v) { if (i >= l.size()) { PyErr_SetString( PyExc_IndexError, "index out of bounds"); throw error_already_set(); } l[i] = v.release(); } template void PointerList_append(List &l, auto_ptr v) { l.append(v.release()); } template void exposePointerList(const char *name) { typedef List list; class_(name) .def("__len__", &list::size) .def("size", &list::size) .def("clear", &list::clear, return_self<>()) .def("isEmpty", &list::isEmpty) .def("__getitem__", PointerList_getitem, return_internal_reference<>()) .def("__setitem__", PointerList_setitem) .def("append", PointerList_append) // MISSING: iterators, insert, find, contains, erase, // assignment, comparison ; } } tagpy-2013.1/src/wrapper/id3.cpp0000644000175000017500000003510612066450141017436 0ustar andreasandreas00000000000000// Copyright (c) 2006-2008 Andreas Kloeckner // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common.hpp" namespace { // ------------------------------------------------------------- // ID3v2 // ------------------------------------------------------------- struct id3v2_FrameWrap : ID3v2::Frame, wrapper { String toString() const { return this->get_override("toString")(); } protected: // maintain constructability id3v2_FrameWrap(const ByteVector &data) : ID3v2::Frame(data) { } // In docs, but not in code: /* id3v2_FrameWrap(ID3v2::Header *h) : ID3v2::Frame(h) { } */ }; void id3v2_Tag_addFrame(ID3v2::Tag &t, ID3v2::Frame *f) { ID3v2::Frame *f_clone = ID3v2::FrameFactory::instance()->createFrame(f->render()); t.addFrame(f_clone); } object id3v2_rvf_channels(const ID3v2::RelativeVolumeFrame &rvf) { List l = rvf.channels(); return make_list(l.begin(), l.end()); } #define MF_OL(MF, MIN, MAX) \ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(MF##_overloads, MF, MIN, MAX); MF_OL(createFrame, 1, 2); MF_OL(volumeAdjustmentIndex, 0, 1); MF_OL(volumeAdjustment, 0, 1); MF_OL(peakVolume, 0, 1); MF_OL(setVolumeAdjustmentIndex, 1, 2); MF_OL(setVolumeAdjustment, 1, 2); MF_OL(setPeakVolume, 1, 2); #if (TAGPY_TAGLIB_HEX_VERSION >= 0x10800) MF_OL(render, 0, 1) #endif // ------------------------------------------------------------- // MPEG // ------------------------------------------------------------- MF_OL(save, 0, 2) MF_OL(ID3v1Tag, 0, 1) MF_OL(ID3v2Tag, 0, 1) MF_OL(APETag, 0, 1) MF_OL(strip, 0, 1) } void exposeID3() { // ------------------------------------------------------------- // ID3v1 // ------------------------------------------------------------- def("id3v1_genre", ID3v1::genre); class_ > ("id3v1_Tag") .def("render", &ID3v1::Tag::render) ; // ------------------------------------------------------------- // ID3v2 // ------------------------------------------------------------- exposeMap("id3v2_FrameListMap"); exposePointerList("id3v2_FrameList"); { typedef ID3v2::FrameFactory cl; ID3v2::Frame *(ID3v2::FrameFactory::*cf1)(const ByteVector &, bool) const = &cl::createFrame; ID3v2::Frame *(ID3v2::FrameFactory::*cf2)(const ByteVector &, TagLib::uint) const = &cl::createFrame; class_ ("id3v2_FrameFactory", no_init) .def("createFrame", cf1, return_value_policy()) .def("createFrame", cf2, createFrame_overloads()[return_value_policy()]) .def("instance", &cl::instance, return_value_policy()) .staticmethod("instance") .DEF_SIMPLE_METHOD(defaultTextEncoding) .DEF_SIMPLE_METHOD(setDefaultTextEncoding) ; } { typedef ID3v2::Frame cl; class_("id3v2_Frame", no_init) .DEF_SIMPLE_METHOD(frameID) .DEF_SIMPLE_METHOD(size) .DEF_SIMPLE_METHOD(setData) .DEF_SIMPLE_METHOD(setText) .def("toString", pure_virtual(&ID3v2::Frame::toString)) .DEF_SIMPLE_METHOD(render) .def("headerSize", (TagLib::uint (*)()) &ID3v2::Frame::headerSize) .def("headerSize", (TagLib::uint (*)(TagLib::uint)) &ID3v2::Frame::headerSize) // MISSING: textDelimiter ; } { typedef ID3v2::Header cl; class_ ("id3v2_Header") // MISSING: second constructor .DEF_SIMPLE_METHOD(majorVersion) .DEF_SIMPLE_METHOD(revisionNumber) .DEF_SIMPLE_METHOD(extendedHeader) .DEF_SIMPLE_METHOD(experimentalIndicator) .DEF_SIMPLE_METHOD(footerPresent) .DEF_SIMPLE_METHOD(tagSize) .DEF_SIMPLE_METHOD(completeTagSize) .DEF_SIMPLE_METHOD(setTagSize) .DEF_SIMPLE_METHOD(setData) .DEF_SIMPLE_METHOD(render) .DEF_SIMPLE_METHOD(size) .staticmethod("size") .DEF_SIMPLE_METHOD(fileIdentifier) .staticmethod("fileIdentifier") ; } { typedef ID3v2::ExtendedHeader cl; class_ ("id3v2_ExtendedHeader", no_init) .DEF_SIMPLE_METHOD(size) .DEF_SIMPLE_METHOD(setData) ; } { typedef ID3v2::Footer cl; class_ ("id3v2_Footer", no_init) .DEF_SIMPLE_METHOD(render) .DEF_SIMPLE_METHOD(size) .staticmethod("size") ; } { typedef ID3v2::Tag cl; const ID3v2::FrameList &(cl::*fl1)(const ByteVector &) const = &cl::frameList; const ID3v2::FrameList &(cl::*fl2)() const = &cl::frameList; class_ >("id3v2_Tag") .def("header", &ID3v2::Tag::header, return_internal_reference<>()) .def("extendedHeader", &ID3v2::Tag::extendedHeader, return_internal_reference<>()) .def("footer", &ID3v2::Tag::footer, return_internal_reference<>()) .def("frameListMap", &ID3v2::Tag::frameListMap, return_internal_reference<>()) .def("frameList", fl1, return_internal_reference<>()) .def("frameList", fl2, return_internal_reference<>()) .def("addFrame", id3v2_Tag_addFrame) .DEF_SIMPLE_METHOD(removeFrame) .DEF_SIMPLE_METHOD(removeFrames) #if (TAGPY_TAGLIB_HEX_VERSION >= 0x10800) .DEF_OVERLOADED_METHOD(render, ByteVector (cl::*)(int) const) #else .DEF_SIMPLE_METHOD(render) #endif ; } // ------------------------------------------------------------- // ID3v2 frame types // ------------------------------------------------------------- { typedef TagLib::ID3v2::AttachedPictureFrame scope; enum_("id3v2_AttachedPictureFrame_Type") .ENUM_VALUE(Other) .ENUM_VALUE(FileIcon) .ENUM_VALUE(OtherFileIcon) .ENUM_VALUE(FrontCover) .ENUM_VALUE(BackCover) .ENUM_VALUE(LeafletPage) .ENUM_VALUE(Media) .ENUM_VALUE(LeadArtist) .ENUM_VALUE(Artist) .ENUM_VALUE(Conductor) .ENUM_VALUE(Band) .ENUM_VALUE(Composer) .ENUM_VALUE(Lyricist) .ENUM_VALUE(RecordingLocation) .ENUM_VALUE(DuringRecording) .ENUM_VALUE(DuringPerformance) .ENUM_VALUE(MovieScreenCapture) .ENUM_VALUE(ColouredFish) .ENUM_VALUE(Illustration) .ENUM_VALUE(BandLogo) .ENUM_VALUE(PublisherLogo) ; } { typedef ID3v2::AttachedPictureFrame cl; class_, boost::noncopyable> ("id3v2_AttachedPictureFrame", init >()) .DEF_SIMPLE_METHOD(textEncoding) .DEF_SIMPLE_METHOD(setTextEncoding) .DEF_SIMPLE_METHOD(mimeType) .DEF_SIMPLE_METHOD(setMimeType) .DEF_SIMPLE_METHOD(type) .DEF_SIMPLE_METHOD(setType) .DEF_SIMPLE_METHOD(description) .DEF_SIMPLE_METHOD(setDescription) .DEF_SIMPLE_METHOD(picture) .DEF_SIMPLE_METHOD(setPicture) ; } { typedef ID3v2::CommentsFrame cl; class_, boost::noncopyable> ("id3v2_CommentsFrame", init >()) .def(init()) .DEF_SIMPLE_METHOD(language) .DEF_SIMPLE_METHOD(setLanguage) .DEF_SIMPLE_METHOD(description) .DEF_SIMPLE_METHOD(setDescription) .DEF_SIMPLE_METHOD(textEncoding) .DEF_SIMPLE_METHOD(setTextEncoding) ; } { typedef ID3v2::RelativeVolumeFrame::PeakVolume cl; class_ ("id3v2_PeakVolume") .def_readwrite("bitsRepresentingPeak", &cl::bitsRepresentingPeak) .def_readwrite("peakVolume", &cl::peakVolume) ; } { typedef TagLib::ID3v2::RelativeVolumeFrame scope; enum_("id3v2_RelativeVolumeFrame_ChannelType") .ENUM_VALUE(Other) .ENUM_VALUE(MasterVolume) .ENUM_VALUE(FrontRight) .ENUM_VALUE(FrontLeft) .ENUM_VALUE(BackRight) .ENUM_VALUE(BackLeft) .ENUM_VALUE(FrontCentre) .ENUM_VALUE(BackCentre) .ENUM_VALUE(Subwoofer) ; } { typedef ID3v2::RelativeVolumeFrame cl; class_, boost::noncopyable> ("id3v2_RelativeVolumeFrame", init()) // MISSING: Empty constructor, gives symbol errors .def("channels", id3v2_rvf_channels) .DEF_SIMPLE_METHOD(setChannelType) .DEF_OVERLOADED_METHOD(volumeAdjustmentIndex, short (cl::*)(cl::ChannelType) const) .DEF_OVERLOADED_METHOD(setVolumeAdjustmentIndex, void (cl::*)(short, cl::ChannelType)) .DEF_OVERLOADED_METHOD(volumeAdjustment, float (cl::*)(cl::ChannelType) const) .DEF_OVERLOADED_METHOD(setVolumeAdjustment, void (cl::*)(float, cl::ChannelType)) .DEF_OVERLOADED_METHOD(peakVolume, cl::PeakVolume (cl::*)(cl::ChannelType) const) .DEF_OVERLOADED_METHOD(setPeakVolume, void (cl::*)(const cl::PeakVolume &, cl::ChannelType)) ; } { typedef ID3v2::TextIdentificationFrame cl; class_, boost::noncopyable> ("id3v2_TextIdentificationFrame", init >()) .def("setText", (void (cl::*)(const String &)) &cl::setText) .def("setText", (void (cl::*)(const StringList &)) &cl::setText) .DEF_SIMPLE_METHOD(textEncoding) .DEF_SIMPLE_METHOD(setTextEncoding) .DEF_SIMPLE_METHOD(fieldList) ; } { typedef ID3v2::UnsynchronizedLyricsFrame cl; class_, boost::noncopyable> ("id3v2_UnsynchronizedLyricsFrame", init >()) .def(init()) .DEF_SIMPLE_METHOD(language) .DEF_SIMPLE_METHOD(setLanguage) .DEF_SIMPLE_METHOD(description) .DEF_SIMPLE_METHOD(setDescription) .DEF_SIMPLE_METHOD(textEncoding) .DEF_SIMPLE_METHOD(setTextEncoding) ; } { typedef ID3v2::UserTextIdentificationFrame cl; class_, boost::noncopyable> ("id3v2_UserTextIdentificationFrame", init()) .def(init >()) .DEF_SIMPLE_METHOD(description) .DEF_SIMPLE_METHOD(setDescription) .DEF_SIMPLE_METHOD(fieldList) ; } { typedef ID3v2::UniqueFileIdentifierFrame cl; class_, boost::noncopyable> ("id3v2_UniqueFileIdentifierFrame", init()) .def(init()) .DEF_SIMPLE_METHOD(owner) .DEF_SIMPLE_METHOD(setOwner) .DEF_SIMPLE_METHOD(identifier) .DEF_SIMPLE_METHOD(setIdentifier) ; } { typedef ID3v2::UnknownFrame cl; class_, boost::noncopyable> ("id3v2_UnknownFrame", init()) .DEF_SIMPLE_METHOD(data) ; } // ------------------------------------------------------------- // MPEG // ------------------------------------------------------------- enum_("mpeg_TagTypes") .value("NoTags", MPEG::File::NoTags) .value("ID3v1", MPEG::File::ID3v1) .value("ID3v2", MPEG::File::ID3v2) .value("APE", MPEG::File::APE) .value("AllTags", MPEG::File::AllTags) ; { typedef MPEG::Properties cl; class_, boost::noncopyable> ("mpeg_Properties", //init() no_init ) .ADD_RO_PROPERTY(layer) // .ADD_RO_PROPERTY(protectionEnabled) (not implemented in TagLib 1.4) // .ADD_RO_PROPERTY(channelMode) (depends on ChannelMode type) .ADD_RO_PROPERTY(isCopyrighted) .ADD_RO_PROPERTY(isOriginal) ; } { typedef MPEG::File cl; class_, boost::noncopyable> ("mpeg_File", init >()) .def(init >()) .def("save", (bool (MPEG::File::*)(int, bool)) &cl::save, save_overloads()) .def("ID3v1Tag", (ID3v1::Tag *(MPEG::File::*)(bool)) &cl::ID3v1Tag, ID3v1Tag_overloads()[return_internal_reference<>()]) .def("ID3v2Tag", (ID3v2::Tag *(MPEG::File::*)(bool)) &cl::ID3v2Tag, ID3v2Tag_overloads()[return_internal_reference<>()]) .def("APETag", (APE::Tag *(cl::*)(bool)) &cl::APETag, APETag_overloads()[return_internal_reference<>()]) .def("strip", (bool (cl::*)(int)) &cl::strip, strip_overloads()) .DEF_SIMPLE_METHOD(setID3v2FrameFactory) .DEF_SIMPLE_METHOD(firstFrameOffset) .DEF_SIMPLE_METHOD(nextFrameOffset) .DEF_SIMPLE_METHOD(previousFrameOffset) .DEF_SIMPLE_METHOD(lastFrameOffset) ; } // MISSING: Header, XingHeader } // EMACS-FORMAT-TAG // // Local Variables: // mode: C++ // eval: (c-set-style "stroustrup") // eval: (c-set-offset 'access-label -2) // eval: (c-set-offset 'inclass '++) // c-basic-offset: 2 // tab-width: 8 // End: tagpy-2013.1/src/wrapper/basics.cpp0000644000175000017500000001563712066401373020235 0ustar andreasandreas00000000000000// Copyright (c) 2006-2008 Andreas Kloeckner // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #include #include #include #include #include #include #include #include #include #include "common.hpp" namespace { struct tlstring_to_unicode { static PyObject *convert(String const& s) { const char *cstr = s.toCString(true); return PyUnicode_DecodeUTF8(cstr, strlen(cstr), "ignore"); } }; struct tlbytevector_to_string { static PyObject *convert(ByteVector const& s) { return PyUnicode_FromStringAndSize(s.data(), s.size()); } }; struct ByteVectorIntermediate { string str; ByteVectorIntermediate(const string &_str) : str(_str) { } operator ByteVector() const { return ByteVector(str.data(), str.size()); } }; // ------------------------------------------------------------- // Basics // ------------------------------------------------------------- struct TagWrap : Tag, wrapper { String title() const { return this->get_override("title")(); } String artist() const { return this->get_override("artist")(); } String album() const { return this->get_override("album")(); } String comment() const { return this->get_override("comment")(); } String genre() const { return this->get_override("genre")(); } TagLib::uint year() const { return this->get_override("year")(); } TagLib::uint track() const { return this->get_override("track")(); } void setTitle(const String &v) const { this->get_override("setTitle")(v); } void setArtist(const String &v) const { this->get_override("setArtist")(v); } void setAlbum(const String &v) const { this->get_override("setAlbum")(v); } void setComment(const String &v) const { this->get_override("setComment")(v); } void setGenre(const String &v) const { this->get_override("setGenre")(v); } void setYear(TagLib::uint i) const { this->get_override("setYear")(i); } void setTrack(TagLib::uint i) const { this->get_override("setTrack")(i); } }; struct AudioPropertiesWrap : AudioProperties, wrapper { int length() const { return this->get_override("length")(); } int bitrate() const { return this->get_override("bitrate")(); } int sampleRate() const { return this->get_override("sampleRate")(); } int channels() const { return this->get_override("channels")(); } protected: AudioPropertiesWrap(ReadStyle style) : AudioProperties(style) { } }; struct FileWrap : File, wrapper { public: Tag *tag() const { return this->get_override("tag")(); } AudioProperties *audioProperties() const { return this->get_override("audioProperties")(); } bool save() { return this->get_override("save")(); } protected: FileWrap(const char *file) : File(file) { } }; } void exposeID3(); void exposeRest(); BOOST_PYTHON_MODULE(_tagpy) { // ------------------------------------------------------------- // Infrastructure // ------------------------------------------------------------- to_python_converter(); to_python_converter(); implicitly_convertible(); implicitly_convertible(); implicitly_convertible(); // ------------------------------------------------------------- // Basics // ------------------------------------------------------------- exposeList("StringListBase"); { typedef StringList cl; class_ > >("StringList") ; } { typedef Tag cl; class_("Tag", no_init) .add_property("title", &cl::title, &cl::setTitle) .add_property("artist", &cl::artist, &cl::setArtist) .add_property("album", &cl::album, &cl::setAlbum) .add_property("comment", &cl::comment, &cl::setComment) .add_property("genre", &cl::genre, &cl::setGenre) .add_property("year", &cl::year, &cl::setYear) .add_property("track", &cl::track, &cl::setTrack) .DEF_VIRTUAL_METHOD(isEmpty) .DEF_SIMPLE_METHOD(duplicate) .staticmethod("duplicate") ; } { typedef AudioProperties cl; class_("AudioProperties", no_init) .add_property("length", &cl::length) .add_property("bitrate", &cl::bitrate) .add_property("sampleRate", &cl::sampleRate) .add_property("channels", &cl::channels) ; } enum_("ReadStyle") .value("Fast", AudioProperties::Fast) .value("Average", AudioProperties::Average) .value("Accurate", AudioProperties::Accurate) ; { typedef File cl; class_("File", no_init) .def("name", &File::name) .def("audioProperties", pure_virtual(&File::audioProperties), return_internal_reference<>()) .def("tag", pure_virtual(&File::tag), return_internal_reference<>()) .DEF_VIRTUAL_METHOD(save) .DEF_SIMPLE_METHOD(readOnly) .DEF_SIMPLE_METHOD(isOpen) .DEF_SIMPLE_METHOD(isValid) .DEF_SIMPLE_METHOD(clear) .DEF_SIMPLE_METHOD(length) ; } enum_("StringType") .value("Latin1", String::Latin1) .value("UTF16", String::UTF16) .value("UTF16BE", String::UTF16BE) .value("UTF8", String::UTF8) .value("UTF16LE", String::UTF16LE) ; exposeID3(); exposeRest(); } // EMACS-FORMAT-TAG // // Local Variables: // mode: C++ // eval: (c-set-style "stroustrup") // eval: (c-set-offset 'access-label -2) // eval: (c-set-offset 'inclass '++) // c-basic-offset: 2 // tab-width: 8 // End: tagpy-2013.1/src/wrapper/rest.cpp0000644000175000017500000001767512066401370017747 0ustar andreasandreas00000000000000// Copyright (c) 2006-2008 Andreas Kloeckner // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #include #include #include #include #include #include #include #include #include #include #include "common.hpp" namespace { // ------------------------------------------------------------- // FLAC // ------------------------------------------------------------- MF_OL(ID3v1Tag, 0, 1); MF_OL(ID3v2Tag, 0, 1); MF_OL(xiphComment, 0, 1); // ------------------------------------------------------------- // Ogg // ------------------------------------------------------------- MF_OL(addField, 2, 3); MF_OL(removeField, 1, 2); MF_OL(render, 0, 1); // ------------------------------------------------------------- // APE // ------------------------------------------------------------- MF_OL(addValue, 2, 3); // ------------------------------------------------------------- // MPC // ------------------------------------------------------------- MF_OL(remove, 0, 1); //MF_OL(ID3v1Tag, 0, 1); MF_OL(APETag, 0, 1); } void exposeRest() { // ------------------------------------------------------------- // Ogg // ------------------------------------------------------------- exposeMap("ogg_FieldListMap"); { typedef Ogg::XiphComment cl; class_, boost::noncopyable> ("ogg_XiphComment", init >()) .DEF_SIMPLE_METHOD(fieldCount) .def("fieldListMap", &cl::fieldListMap, return_internal_reference<>()) .DEF_SIMPLE_METHOD(vendorID) .DEF_OVERLOADED_METHOD(addField, void (cl::*)(const String &, const String &, bool)) .DEF_OVERLOADED_METHOD(removeField, void (cl::*)(const String &, const String &)) .DEF_OVERLOADED_METHOD(removeField, void (cl::*)(const String &, const String &)) .DEF_OVERLOADED_METHOD(render, ByteVector (cl::*)(bool) const) ; } { typedef Ogg::File cl; class_, boost::noncopyable> ("ogg_File", no_init) .DEF_SIMPLE_METHOD(packet) .DEF_SIMPLE_METHOD(setPacket) // MISSING: page headers ; } { typedef Ogg::FLAC::File cl; class_, boost::noncopyable> ("ogg_flac_File", init >()) ; } { typedef Ogg::Vorbis::File cl; class_, boost::noncopyable> ("ogg_vorbis_File", init >()) ; } // ------------------------------------------------------------- // APE // ------------------------------------------------------------- { typedef APE::Footer cl; class_( "ape_Footer", init >()) .DEF_SIMPLE_METHOD(version) .DEF_SIMPLE_METHOD(headerPresent) .DEF_SIMPLE_METHOD(footerPresent) .DEF_SIMPLE_METHOD(isHeader) .DEF_SIMPLE_METHOD(setHeaderPresent) .DEF_SIMPLE_METHOD(itemCount) .DEF_SIMPLE_METHOD(setItemCount) .DEF_SIMPLE_METHOD(tagSize) .DEF_SIMPLE_METHOD(completeTagSize) .DEF_SIMPLE_METHOD(setTagSize) .DEF_SIMPLE_METHOD(setData) .DEF_SIMPLE_METHOD(renderFooter) .DEF_SIMPLE_METHOD(renderHeader) ; } { typedef APE::Item scope; enum_("ape_ItemTypes") .ENUM_VALUE(Text) .ENUM_VALUE(Binary) .ENUM_VALUE(Locator) ; } { typedef APE::Item cl; class_("ape_Item") .def(init()) .def(init()) .def(init()) .DEF_SIMPLE_METHOD(key) .DEF_SIMPLE_METHOD(value) .DEF_SIMPLE_METHOD(size) .DEF_SIMPLE_METHOD(toString) .DEF_SIMPLE_METHOD(toStringList) .DEF_SIMPLE_METHOD(render) .DEF_SIMPLE_METHOD(parse) .DEF_SIMPLE_METHOD(setReadOnly) .DEF_SIMPLE_METHOD(isReadOnly) .DEF_SIMPLE_METHOD(setType) .DEF_SIMPLE_METHOD(type) .DEF_SIMPLE_METHOD(isEmpty) ; } exposeMap("ape_ItemListMap"); { typedef APE::Tag cl; class_, boost::noncopyable>("ape_Tag") .def(init()) .def("footer", &cl::footer, return_internal_reference<>()) .def("itemListMap", &cl::itemListMap, return_internal_reference<>()) .DEF_SIMPLE_METHOD(removeItem) .DEF_OVERLOADED_METHOD(addValue, void (cl::*)(const String &, const String &,bool)) .DEF_SIMPLE_METHOD(setItem) ; } // ------------------------------------------------------------- // FLAC // ------------------------------------------------------------- { typedef FLAC::File cl; class_ >("flac_File", init >()) .def(init >()) .def("ID3v1Tag", (ID3v1::Tag *(FLAC::File::*)(bool)) &FLAC::File::ID3v1Tag, ID3v1Tag_overloads()[return_internal_reference<>()]) .def("ID3v2Tag", (ID3v2::Tag *(FLAC::File::*)(bool)) &FLAC::File::ID3v2Tag, ID3v2Tag_overloads()[return_internal_reference<>()]) .def("xiphComment", (Ogg::XiphComment *(FLAC::File::*)(bool)) &FLAC::File::xiphComment, xiphComment_overloads()[return_internal_reference<>()]) .DEF_SIMPLE_METHOD(setID3v2FrameFactory) .DEF_SIMPLE_METHOD(streamInfoData) .DEF_SIMPLE_METHOD(streamLength) ; } // ------------------------------------------------------------- // MPC // ------------------------------------------------------------- enum_("mpc_TagTypes") .value("NoTags", MPC::File::NoTags) .value("ID3v1", MPC::File::ID3v1) .value("ID3v2", MPC::File::ID3v2) .value("APE", MPC::File::APE) .value("AllTags", MPC::File::AllTags) ; { typedef MPC::File cl; class_, boost::noncopyable> ("mpc_File", init >()) .def("ID3v1Tag", (ID3v1::Tag *(cl::*)(bool)) &cl::ID3v1Tag, ID3v1Tag_overloads()[return_internal_reference<>()]) .def("APETag", (APE::Tag *(cl::*)(bool)) &cl::APETag, APETag_overloads()[return_internal_reference<>()]) .def("remove", (void (cl::*)(int)) &cl::remove, remove_overloads()) ; } } // EMACS-FORMAT-TAG // // Local Variables: // mode: C++ // eval: (c-set-style "stroustrup") // eval: (c-set-offset 'access-label -2) // eval: (c-set-offset 'inclass '++) // c-basic-offset: 2 // tab-width: 8 // End: tagpy-2013.1/README_SETUP.txt0000644000175000017500000000210212017513621016447 0ustar andreasandreas00000000000000Hi, welcome. This Python package uses aksetup for installation, which means that installation should be easy and quick. If you don't want to continue reading, just try the regular ./configure.py --help ./configure.py --some-options make sudo make install That should do the trick. (By the way: If a config option says "several ok", then you may specify several values, separated by commas.) aksetup also supports regular distutils installation, without using configure: python setup.py build sudo python setup.py install In this case, configuration is obtained from files in this order: /etc/aksetup-defaults.py $HOME/.aksetup-defaults.py $PACKAGEDIR/siteconf.py Once you've run configure, you can copy options from your siteconf.py file to one of these files, and you won't ever have to configure them again manually. In fact, you may pass the options "--update-user" and "--update-global" to configure, and it will automatically update these files for you. This is particularly handy if you want to perform an unattended or automatic installation via easy_install. tagpy-2013.1/aksetup_helper.py0000644000175000017500000006537112063225113017374 0ustar andreasandreas00000000000000# dealings with ez_setup ------------------------------------------------------ import distribute_setup distribute_setup.use_setuptools() import setuptools from setuptools import Extension def count_down_delay(delay): from time import sleep import sys while delay: sys.stdout.write("Continuing in %d seconds... \r" % delay) sys.stdout.flush() delay -= 1 sleep(1) print("") if not hasattr(setuptools, "_distribute"): print("-------------------------------------------------------------------------") print("Setuptools conflict detected.") print("-------------------------------------------------------------------------") print("When I imported setuptools, I did not get the distribute version of") print("setuptools, which is troubling--this package really wants to be used") print("with distribute rather than the old setuptools package. More than likely,") print("you have both distribute and setuptools installed, which is bad.") print("") print("See this page for more information:") print("http://wiki.tiker.net/DistributeVsSetuptools") print("-------------------------------------------------------------------------") print("I will continue after a short while, fingers crossed.") print("Hit Ctrl-C now if you'd like to think about the situation.") print("-------------------------------------------------------------------------") count_down_delay(delay=10) def setup(*args, **kwargs): from setuptools import setup import traceback try: setup(*args, **kwargs) except KeyboardInterrupt: raise except SystemExit: raise except: print ("----------------------------------------------------------------------------") print ("Sorry, your build failed. Try rerunning configure.py with different options.") print ("----------------------------------------------------------------------------") raise class NumpyExtension(Extension): # nicked from # http://mail.python.org/pipermail/distutils-sig/2007-September/008253.html # solution by Michael Hoffmann def __init__(self, *args, **kwargs): Extension.__init__(self, *args, **kwargs) self._include_dirs = self.include_dirs del self.include_dirs # restore overwritten property def get_numpy_incpath(self): from imp import find_module # avoid actually importing numpy, it screws up distutils file, pathname, descr = find_module("numpy") from os.path import join return join(pathname, "core", "include") def get_include_dirs(self): return self._include_dirs + [self.get_numpy_incpath()] def set_include_dirs(self, value): self._include_dirs = value def del_include_dirs(self): pass include_dirs = property(get_include_dirs, set_include_dirs, del_include_dirs) class PyUblasExtension(NumpyExtension): def get_module_include_path(self, name): from imp import find_module file, pathname, descr = find_module(name) from os.path import join, exists installed_path = join(pathname, "..", "include") development_path = join(pathname, "..", "src", "cpp") if exists(installed_path): return installed_path elif exists(development_path): return development_path else: raise RuntimeError("could not find C include path for module '%s'" % name) @property def include_dirs(self): return self._include_dirs + [ self.get_numpy_incpath(), self.get_module_include_path("pyublas"), ] class HedgeExtension(PyUblasExtension): @property def include_dirs(self): return self._include_dirs + [ self.get_numpy_incpath(), self.get_module_include_path("pyublas"), self.get_module_include_path("hedge"), ] # tools ----------------------------------------------------------------------- def flatten(list): """For an iterable of sub-iterables, generate each member of each sub-iterable in turn, i.e. a flattened version of that super-iterable. Example: Turn [[a,b,c],[d,e,f]] into [a,b,c,d,e,f]. """ for sublist in list: for j in sublist: yield j def humanize(sym_str): words = sym_str.lower().replace("_", " ").split(" ") return " ".join([word.capitalize() for word in words]) # siteconf handling ----------------------------------------------------------- def get_config(schema=None, warn_about_no_config=True): if schema is None: from setup import get_config_schema schema = get_config_schema() if (not schema.have_config() and not schema.have_global_config() and warn_about_no_config): print("*************************************************************") print("*** I have detected that you have not run configure.py.") print("*************************************************************") print("*** Additionally, no global config files were found.") print("*** I will go ahead with the default configuration.") print("*** In all likelihood, this will not work out.") print("*** ") print("*** See README_SETUP.txt for more information.") print("*** ") print("*** If the build does fail, just re-run configure.py with the") print("*** correct arguments, and then retry. Good luck!") print("*************************************************************") print("*** HIT Ctrl-C NOW IF THIS IS NOT WHAT YOU WANT") print("*************************************************************") count_down_delay(delay=10) return expand_options(schema.read_config()) def hack_distutils(debug=False, fast_link=True, what_opt=3): # hack distutils.sysconfig to eliminate debug flags # stolen from mpi4py def remove_prefixes(optlist, bad_prefixes): for bad_prefix in bad_prefixes: for i, flag in enumerate(optlist): if flag.startswith(bad_prefix): optlist.pop(i) break return optlist import sys if not sys.platform.lower().startswith("win"): from distutils import sysconfig cvars = sysconfig.get_config_vars() cflags = cvars.get('OPT') if cflags: cflags = remove_prefixes(cflags.split(), ['-g', '-O', '-Wstrict-prototypes', '-DNDEBUG']) if debug: cflags.append("-g") else: if what_opt is None: pass else: cflags.append("-O%s" % what_opt) cflags.append("-DNDEBUG") cvars['OPT'] = str.join(' ', cflags) cvars["CFLAGS"] = cvars["BASECFLAGS"] + " " + cvars["OPT"] if fast_link: for varname in ["LDSHARED", "BLDSHARED"]: ldsharedflags = cvars.get(varname) if ldsharedflags: ldsharedflags = remove_prefixes(ldsharedflags.split(), ['-Wl,-O']) cvars[varname] = str.join(' ', ldsharedflags) # configure guts -------------------------------------------------------------- def default_or(a, b): if a is None: return b else: return a def expand_str(s, options): import re def my_repl(match): sym = match.group(1) try: repl = options[sym] except KeyError: from os import environ repl = environ[sym] return expand_str(repl, options) return re.subn(r"\$\{([a-zA-Z0-9_]+)\}", my_repl, s)[0] def expand_value(v, options): if isinstance(v, str): return expand_str(v, options) elif isinstance(v, list): result = [] for i in v: try: exp_i = expand_value(i, options) except: pass else: result.append(exp_i) return result else: return v def expand_options(options): return dict( (k, expand_value(v, options)) for k, v in options.items()) class ConfigSchema: def __init__(self, options, conf_file="siteconf.py", conf_dir="."): self.optdict = dict((opt.name, opt) for opt in options) self.options = options self.conf_dir = conf_dir self.conf_file = conf_file from os.path import expanduser self.user_conf_file = expanduser("~/.aksetup-defaults.py") import sys if not sys.platform.lower().startswith("win"): self.global_conf_file = "/etc/aksetup-defaults.py" else: self.global_conf_file = None def get_conf_file(self): import os return os.path.join(self.conf_dir, self.conf_file) def set_conf_dir(self, conf_dir): self.conf_dir = conf_dir def get_default_config(self): return dict((opt.name, opt.default) for opt in self.options) def read_config_from_pyfile(self, filename): result = {} filevars = {} infile = open(filename, "r") try: contents = infile.read() finally: infile.close() exec(compile(contents, filename, "exec"), filevars) for key, value in filevars.items(): if key in self.optdict: result[key] = value return result def update_conf_file(self, filename, config): result = {} filevars = {} try: exec(compile(open(filename, "r").read(), filename, "exec"), filevars) except IOError: pass if "__builtins__" in filevars: del filevars["__builtins__"] for key, value in config.items(): if value is not None: filevars[key] = value keys = filevars.keys() keys.sort() outf = open(filename, "w") for key in keys: outf.write("%s = %s\n" % (key, repr(filevars[key]))) outf.close() return result def update_user_config(self, config): self.update_conf_file(self.user_conf_file, config) def update_global_config(self, config): if self.global_conf_file is not None: self.update_conf_file(self.global_conf_file, config) def get_default_config_with_files(self): result = self.get_default_config() import os confignames = [] if self.global_conf_file is not None: confignames.append(self.global_conf_file) confignames.append(self.user_conf_file) for fn in confignames: if os.access(fn, os.R_OK): result.update(self.read_config_from_pyfile(fn)) return result def have_global_config(self): import os result = os.access(self.user_conf_file, os.R_OK) if self.global_conf_file is not None: result = result or os.access(self.global_conf_file, os.R_OK) return result def have_config(self): import os return os.access(self.get_conf_file(), os.R_OK) def read_config(self, warn_if_none=True): import os cfile = self.get_conf_file() result = self.get_default_config_with_files() if os.access(cfile, os.R_OK): filevars = {} exec(compile(open(cfile, "r").read(), cfile, "exec"), filevars) for key, value in filevars.items(): if key in self.optdict: result[key] = value elif key == "__builtins__": pass else: raise KeyError("invalid config key in %s: %s" % ( cfile, key)) return result def add_to_configparser(self, parser, def_config=None): if def_config is None: def_config = self.get_default_config_with_files() for opt in self.options: default = default_or(def_config.get(opt.name), opt.default) opt.add_to_configparser(parser, default) def get_from_configparser(self, options): result = {} for opt in self.options: result[opt.name] = opt.take_from_configparser(options) return result def write_config(self, config): outf = open(self.get_conf_file(), "w") for opt in self.options: value = config[opt.name] if value is not None: outf.write("%s = %s\n" % (opt.name, repr(config[opt.name]))) outf.close() def make_substitutions(self, config): return dict((opt.name, opt.value_to_str(config[opt.name])) for opt in self.options) class Option(object): def __init__(self, name, default=None, help=None): self.name = name self.default = default self.help = help def as_option(self): return self.name.lower().replace("_", "-") def metavar(self): last_underscore = self.name.rfind("_") return self.name[last_underscore+1:] def get_help(self, default): result = self.help if self.default: result += " (default: %s)" % self.value_to_str( default_or(default, self.default)) return result def value_to_str(self, default): return default def add_to_configparser(self, parser, default=None): default = default_or(default, self.default) default_str = self.value_to_str(default) parser.add_option( "--" + self.as_option(), dest=self.name, default=default_str, metavar=self.metavar(), help=self.get_help(default)) def take_from_configparser(self, options): return getattr(options, self.name) class Switch(Option): def add_to_configparser(self, parser, default=None): option = self.as_option() if not isinstance(self.default, bool): raise ValueError("Switch options must have a default") if default is None: default = self.default option_name = self.as_option() if default: option_name = "no-" + option_name action = "store_false" else: action = "store_true" parser.add_option( "--" + option_name, dest=self.name, help=self.get_help(default), default=default, action=action) class StringListOption(Option): def value_to_str(self, default): if default is None: return None return ",".join([str(el).replace(",", r"\,") for el in default]) def get_help(self, default): return Option.get_help(self, default) + " (several ok)" def take_from_configparser(self, options): opt = getattr(options, self.name) if opt is None: return None else: if opt: import re sep = re.compile(r"(?`_. It builds upon `Boost.Python `_, a wrapper generation library which is part of the renowned Boost set of C++ libraries. Just like TagLib, TagPy can: * read and write ID3 tags of version 1 and 2, with many supported frame types for version 2 (in MPEG Layer 2 and MPEG Layer 3, FLAC and MPC), * access Xiph Comments in Ogg Vorbis Files and Ogg Flac Files, * access APE tags in Musepack and MP3 files. All these features have their own specific interfaces, but TagLib's generic tag reading and writing mechanism is also supported. It comes with a bunch of examples. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Operating System :: POSIX Classifier: Operating System :: Unix Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Multimedia :: Sound/Audio Classifier: Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping Classifier: Topic :: Multimedia :: Sound/Audio :: Editors Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Utilities tagpy-2013.1/tagpy.egg-info/dependency_links.txt0000644000175000017500000000000112067051706022670 0ustar andreasandreas00000000000000 tagpy-2013.1/tagpy.egg-info/SOURCES.txt0000644000175000017500000000111712067051706020506 0ustar andreasandreas00000000000000LICENSE MANIFEST.in Makefile.in README_SETUP.txt aksetup_helper.py configure.py distribute_setup.py setup.py src/wrapper/basics.cpp src/wrapper/common.hpp src/wrapper/id3.cpp src/wrapper/rest.cpp tagpy/__init__.py tagpy/ape.py tagpy/flac.py tagpy/id3v1.py tagpy/id3v2.py tagpy/mpc.py tagpy/mpeg.py tagpy.egg-info/PKG-INFO tagpy.egg-info/SOURCES.txt tagpy.egg-info/dependency_links.txt tagpy.egg-info/top_level.txt tagpy/ogg/__init__.py tagpy/ogg/flac.py tagpy/ogg/vorbis.py test/deb-bug-438556.py test/la.flac test/la.mp3 test/la.ogg test/mp3-framedumper.py test/tagprinter.py test/tagrenametagpy-2013.1/tagpy.egg-info/top_level.txt0000644000175000017500000000001512067051706021350 0ustar andreasandreas00000000000000tagpy _tagpy tagpy-2013.1/test/0000755000175000017500000000000012067051706014763 5ustar andreasandreas00000000000000tagpy-2013.1/test/la.flac0000644000175000017500000023474310564200273016216 0ustar andreasandreas00000000000000fLaC"  @ǞYٽ*t> H( reference libFLAC 1.1.2 20050205YnZEؑa tÒeh7οX|b=qssf^wk8΄)}dPtTRMx\V&Q$ (r,Hd!]\u>$C1zB+*vAeTN\g:~b1zTNQ=+^Zb{ 'r+n\a=RWd#(;˜w&8%"|&QSItF}fE#/iHSA;$c=ar[яx'eSw 罐n@!J vעm0:~o}m6vBW$dixi iR)0`@O]v*2e˖dlp. &^%Ep #=PqHgD*K@#&O83,g="oiEY؅m uD%0!8/(TNOr؎Nj Ws/&7QO\yy+{/e%iunbd2Fp_BCM/1vLt-ɨڔ qqKXKc~-*T~m Z DSS- 62'R YWFSU~*xߵH)nHTݛ'jrtF^8!m-ט*5'±d 7,eFИMd LAG kG|"3q3~ *\nXN ' ɉ,F2,t*)k\ig$lPtBaFBgAb2^R٩hƑgU?W=c BAyޣ6k2y)Dvҕ*ȷ/"fS[ڄؐ;\ȗ+3 ".jmHA3O|I BdQ-<`[Ȩ=v Qo.bN5=*g#}glG%gkJzK:<$2^ٜqW[edd%CbbE(c K#E"_S,oj  AJW4g  ń:i۰P|&0te9\<7)\.4f%G#7)I-)] gam)(Lv?+2]Ƞ\ 'ЫVcvwc m4CyD‹%KQ/elJlk>)׶Sbtd BԖ  ^7J Cv&5JJh3^bX f-/~uDW H߃b 22Np*tKJtD:y E6nPX`|LpUh$`6f/m8m圶E3"CrWV̸T`$*?īvxj$R)U%R 7Z,/P\oT{'D#&T_B&BXv]Yx{w*^;)ZMeҝ&[yύo҄z\\JYw-V#zZw*XSسj!m=7:;i0K> B+ߤ,r2д‰qv%O^(L*8RA ̓>N9foJwD7iaGeqm+FwE!r0΍f|hDW:L{[!IvG5){cx,l˾yL(ޚՔ2⴮fJb4D2Bolq|hlT$pS/+ḮjF H o &.KM&agm8j$(;KUGuҚayuGdz|D?&^|P>J9tsί~(48{?B`il+;@cgþ:J Ye YV5:r?J+j8c8~`[4y|l,mҴK߼kʋ^3]pQ[>z6NWVPSkjȝ>F^zg8^1E\Z$M啞/"`XJᮑS{WyFDͅY <bH)J^c4dj)vSDT ]<*W.]e(KKOid 1fHGmLPߍόefW9_ V~d/ߥ[gtIYA5r ;|﮽^&lٱ%CIvܴVn[H-[zD@VXօ|{Oɛ>p!CY؀o:RN Ba*214-ο3-岹"bĘѶfKs_ܭ:-L-+Y;NL'9:gep y(:*lIEo:e/MIJnvW,\i8Yf Aii>|ұgII9~EmNg> !nS?\Dt7'Oo7xf;7]> 6 DlcGM04nNFVKT^V(ƃߪmJ1Mu}uS3) κMX $. VifZ@9R%]:kއD[&Zk.vta}/ o1tpL~&yف6&9J?kme*5Z/ZVOCw!Uc4\cW\MA_8u"Lc 5˝6-zFdGеH%E+lkGktgbU& Dr\xY5mxؐ`TݴŭXJCH4L7vE!ۏ=|K+ 1_&I}y!_Oywr2L+8KP)?|}5R>OTVF켖?ewIf[]5b㋎y^ Rqz^>TTsAok$;G&e\.>r2Ugۣ>zћ4;,BG\;PLxVܕV'%ĈdgLƞKWJMONQю87p\TbNdܤ-U~4T@TVO4=3!Qϫ9"]oW4[y?=}ջ 3v|U\"pк,QXHi':c3)N$;gw%N^:$ *ΨsgvVv~IɎN{ ' +qf.O˞:stڵa'Z֊>iЛE5kTaa}~1ۦKdך5fkZF;* }]-^_^a+N(U]I0PpNH)2|xP@=zjVb&R^)7LCGWҝfdjb`D~PIF:I wE1IŸ;Kiq}N\^PlAn vȡ,}2ٿMޒjrF~]ftjej(TL9(vН<#eQa2]'oz[}g?6%[s5R<82s8oiQw9UsGv4Tكt5#\=ْ sK\h(|&"~ /9P>/O _EĬb!p{5WH~zΥR2*הؐ\Jkau"מfjbI-g%4浹`i@]᤬4Cc؃.ޘ^ֹt}cu)#ۢvԈrd`򎭝 +oFTvQN$wjzry|s36 "*CU6k=)0]Iw-)!bR*dRnR$nBZD"=9 gNp!аLTͅ]x0IJjd ĤF `mHf{wtT%4pyCE1WlLw>vGkާML]}SO*޸ΝJnR WI qc2eͫB:C&p;.ID=$ ϡ$dObt2CQƯ> JD}>Qu3:49s@fbj5!`u7&&V${t ^P?d^ҤLVV E,Bv$lq6fCEDv _$BrQ C骧$)hNTDZ۱oz)!ahu1[$ӟbNxUyTWDЎC[A$] ).VtVr-5[]64"%Hg,&wk*(%őzDLr'N'k"W7bT$R4钊>ӒS=(0dUcAqYf't?^:̭S|~)2fm31 ѐ(^%n`+#6 F(Ā% %dBb)'hu6;vfq$H#SO -A1GQeS 1}X2/tWbkCgwT'clW?imfr[c/'鱕XiSk]JJZhyp%YA|.]o"Jgu8'oq0\m[P"tЫ"()uʐv&*zHDLHu{r8R\hdjvt^ ^#CYb2/ F ڑɪXNxnEXUeEc57K/t>~@W!3 ̙26\BkbiHEqG!{aW/߉뾮,7YvO/UL{\=JʉiǶ&h x?[cކ{^v.W`ԈеDHN/OLF#Ĉ!4`V\kI F*p~*+82LbRhoݏ7xKU89%QgbhYm4TFꌃKԬh9[tF B% rb3ph-$f"veXimB{ݘzlָsu.OGr60A1ǗC5ʶsGZ]*U?'K٣_YYjvj+R$?StG0,@f HD MKBt㣢 X1e0@1nIUNsܳѿ7] ?_E&%2d(Qc)K9A%qz.ONT}eBE0>@aܩDφ=;:޷D>%I P>j',Q)]^ X2Pǃbשc6ۖbUfroCV4& /-eF2yIorы\DVa$|^K.6۫x)OaoI]t[R2g9$I Fk ?v$[ZkIGZ .@W@X6#(^R)&%!M!xeCbT(Usԙj^ӦQ}Kk]įZZ -pd-$XK΢E jmZ >2(\J|lY2 G:>>"kx(A>t\]R}-b|<5OHHj1n!:eҧxDe1$K4b=)6\[D Xb!H(\ZQY;gU)83a*4 @}j/m%6HJ?N-eԫHp@psa?8@?m˜x9m h-xZR$5Ą.N:n5Ů?>(KBwqUhBs4JTܧWq% XbaWC-WW-_ل0,@w I*I @ ο1: RBk:8_U,EgIGR_.(T 'C?V bX" @U +Bп~|uPQGH+B%-H'.Vps +UKlT2Q>5d\.5NblB]$^[`U 8'eh=)zu?cĚ:7U0ɽcmrc|yn. h BU;vp)I08 y)[Si&4N ?A5 RL&qEC1`ocqK?5M˛JFVC*/ATE TS"p Y%f(9d F+;y@g^IH*ѼqCW_I+M"~UV_+h${D湋)ƣjr *"" c zUa¢wpAM3mb{+/tU7©!B'*Sߢ!'Dq> bWZ!\B0Hނ)h1A3Va@I2".hCHK/KmY8sO,Кۻc'49eѴh-{S3c臍AG,=T뀟i':B^Ѹh~}A~+0 cBq. &?4l/~S9EX&M!RH'NQi (!(41T0D6SQBѴ=0,睱ţzsdt6]xKA thڍb1Ѵ=uʚ/F}Hg`Q䧺 .7;o BXP7N&rs*'XF:"0$~@9쒡YP Oz1.xQB4w[E!D+ш1}E"(A$G A[̀ _R6I!4ߪ"h]OI&j5Ah 5\RNnբ %!I+&̬!`SfVǖ =jk;Y 7O)7Ɍ*˩`jOȂR)%WS#qTȊ誘nC>m/ ,XkB)uk>IDQ0i M B tyt26Ɓݾ5ЬɞzĶb2ঝ2t8Ah!FQB,DKFY .tB@lag* .$;t2QvtrufMLEN8)X:"Q)OF0p4֨m)էȼ_:ܟ7I%DQ j( w#Kq[]bBj~i b,"Mm5~mW,Wʼnf_hS^{BPm)D2wOėȾr"/dNW"o浥h1rou o]L=f@H~/ݼȍ$#y4ߑۂWV\A~4")hgae\lUmz<[U04v6t{i& trc]vI_bc%:z8X6F-U%J!Rc{U],C"VrK; j>c*?9׍^눛iz ~Z"'&on?!vCSN5yҔAv*k:RfD2+C\ xjm\yxo>l%gj&TB6-Sn[y2yT8{z`|кT'_]uP;А>|X !)ՇftB*M+SJ0E.Aمd@OrC4 GA丢~(XqDv(c  $hdIsBpW\ 5f;XČOTv\3ei װAAHG&wKI(|^y=KyQ7tpk4,Z1!K_tQpq $ؖ␛TP_$H'w+o*yytNBvE7d< d?)(?N A.25*?Ekw"֥̉M.w5Mg2)jOY?-Dآ{Fycm{å![zLu5joeDe2^9k]sT*K-uWOi.t0p:R iﭗMܫ $);: G~ABV MV2}vG rPMUeۿQoAWS}RfN _f~ށR$8RaDO+%P[=kHl;XI*/(_)͢P'Ռ]Zj'b+ .VL}SY&Y!tJ?u}2tkdu!0,i9!?U 74@iIVujk@HH U=RX+fήJoo{v0]Y+Zi?HHm vގ+4־4*yiڝh0p)\$PuP6D0Sokhw&:db(XFhO9K/R\ V 7ZPrPWĉp# ,&:]ElmIHH'a 7P<ly0 8 a Q0(l'93M\ 棩B{Mf3%<£D# (%mu-z3)a,/͞LKSlC( eCa?7%)/kF5 iK$ gGz{Tl/eQI\Uq.fq5RhgWT Y ʑ/ :d6R g!m@@Y/PL8?I-u{US{I% aX4QJӴw# i>z:JaJ&me[%eu;lRHS9rK5dVN̉x~.-B+r#/{j3H!3UiӴPeu5>`xtT!8!U}?tUMe&7'V{Lޯ\T#v :=JgEiFJ%fE*WEԿKq]hkh?i\y0~Yd狩B,l^"B"V3wK/:+\b\Kh`8SIl͒d/ 3рr4h_ϔiiy]斲y5Y5^ו ;#u"wRO \cʶ"an23k> e} >|e2))tsUh=YEJo:KIS'q'z5\7{T7a~],#eô}.,Wz>{wb2=??gD+3膜AM?Dkp #jbć6!Sto~NґL3q~n? A,8[SO9,*i{Q;9peou3tL.C%C!#qu2f(Ϧ9IsY]ˢ)U/z)f{"+ɰX',c != 7۠ܕHq8(ێ-JdN  7Qj(Joa?[E^3hD.F)TBLr`OwKH9G5IE}bmXbF֞*`T=(n!W=c]MK _kDޘmZy4fۋd-H},LbĨj٫sD:DK9ԏX" BDsM & B* J4rԶd孳b3qfM+c⇙-8DS!,gLGŞn J5H[#Wx;,˕m1oEu NP6*EEƪ2HWևN_SUL HR12q `d,LF/ZT.4,z\&6 j VZyM6HMR*Ej{sdCNŠ :zzEzToSJ64毣ҕ4 JB`G$~Mm /E,.6L$mlHPq\7Չ>f ehhd|GЊL|Oypo-0kF,]faj_%"s%7"~7Wl ["sֹ\h59Ho 8'vMl)Z nCy&^zI:{i"5iFͿI%<9t3:J/[+SJ>|!mi7C0uL<"]ӧz͖r}(Mv~"/x&قb-$K[IQj@"&3.W(DH~{ ᦶ#M76>Iycy&C܅K9Z BXC# ))WZ%ZW)o֨Z%`+֔ɜIvLly Zl(:r bKo+&:O_kuK1p,HGZNWSK^BIMHQ?MT1KՃ'f#\ӈ6&^+vKP7S%89Jy!VίymCztIegsvUcO.<>>Lsf(#o"Q C +s1of^dKN2%"-|T=+& 'ݕ3م߾`͠pfd֥,/JQ`-=zT6 QjD'`;#9U:seqoُm˃Sfd|5)UErF% &c uXFb+]M+Tr׌z&%M.;NX_٬!0%bW;#hW腔#OzI )52F RoQ KI;'=]ሙ01u$>'T| [fs0H/nU('F\rl(?RVYM_9;;}vkvg-mX1j%^HX9y%YDl())5#m8! TG*Mx2_UDN>&29௧'D/[C!K6`)mID(@tm"2-[ҍ,wΡ(GgFwM9 2{E0vyQmHkDlS,Di&h)"8}8^-#<7U6e)d2+b/ZY_|]O&ͅo ÈܩZj-*JW;R6ypaB12{ {ڦ]A4 -KXdWY(Ԑ#ȟ__"[SѫTUy&92Ş>F: OLH; ?ֆK&ba─ &%JXWnnk5⮱SWҒ@r-2eDe6GNBOf0IBU"c'~ΒYEDk 1]4u!^LBrb*L:U4auv䶾_Ԯd$o#mᮈ S[8s̷G3`ȧFtLI)9mqKO;q/%i{ަ:tLV ޭsfi:%/ӥ:!q'&-pmkz6xO9Oބ%Qy2|p4g{?\x"f/S }=Y(K* "B ,\ '^kK<@;X>QC#R8IEyM0ϖ[!V\ ?k7 J!nivUA@Od{R4ڝhqAKtcڭE R37 qBA('a(k7$!͊(( `W3JD)kQc((µ9fELs|XO~D!}d*#|KR-Z( ݘ>fz[G9aБN8*䷤*.$3*iSoଇc(P^xN}N ؋^V>WM{iweQ1ȿ\gMXIro=m^If3%&˫ĐEשG)TZ"I'lNqb]DpS x?Ib dm4k"Ah鬓u ii6uRaGu8}n.EjhT<˔*{Φa)טۏil*gL'zcb o7Y|$#F.Hj7sp.[ h?e[pT4Q6nαRy%P򄼱*#04+“s " J8"*)v/C\y;Ƀ-DIrp\$# ]mx,[2):n p1NbK'#DAYjdSK])RUoD5Fo0_# I<$MZRThYBµ_dfzvTZ_2Q?89XSZd*yS˲ ㄋ糊A!N]Ų^z.//ASR';±+l5@JMl!rLm#1$!XNyZL)4Bpeҳш%;"J ff. H"n ~y͘NU9 $[6FfZH8V,uE:].ڱ,A)xGNg,R ZO!I i/iUkiirش{+8pg \$Z8RV⚎ D6m4"hO6] ]h>"ظ6f1|#~]YjgYYoDh(o1zOICN`pM=ʤ ~ey^nW9 ̝a_M:鈦ϱR{s]tk> {+nacA(XºsT'(av%P2F(EzRľgdJ3,~9:sIItܲbTyri܉9PUnW@|xc#ИF~JUqŠEmԨp_,sʛ{_oSW>MP UwWٱqگsdWs~=H[j)ɺa*ulji-r$`es*#_s[(y'7}=fQ;dc=&S9`wO$Dpdt1D#/*1kr,ke(SVP'̈ET=BxnK& Od`p'&ĤY,/ ޞ1{9LFjz5Όd}= zoǴnlg:X*4K #.WL "Vwe*HBO!nuζy՗;aE1>e\72dARa!I Br7sӽsMsTJʛE8&khj<*M#PDuOo(i*.ؔ5ZΒe5A D߀BBc\H +Ϛ!덄׊U鈺}4 Db԰ 7" 2" a3Dh[;t> \qɘ">kJO5-GDU~#'h>#'*Ŧ-ΑI.)v3 zj}Tl-gSRsNْ @&U2]5%'LMoZ"Qw)|X4 l9%5B:F阂UMmec_wjbX4#X^e)"̦`"q@w`iyOI3nH E? {a,T[\#9^\oFNq-tHz޶qu=[v'E4Ϟ;%1U:iVa˩TdYNH,t|\\ F,LbʚrUR}AhK~p?Tϔ+!lG$@3M!NHhaѴ` >ќ"t61UˋE9ҙ*t3FR'HRP_g= ӱfkP}9͖P&}j*1NX%&R{,|P<^4ц\vLRfrl]ϗ%^G[T*WE7e- z>IwFP8Xtr&^a {Ж̕@N+gcS\91ݓ[MIc6{sQQxhH%2Ȕ# ʲ' bA0I;X»`XCK:*$7 h_۱3MHM (ߧQ\a ,Tߒ&K^je.H:D]UZ}oI?V<ʞoE'PZM&Y-KJyJˮ(ii;eWVCȘ/77{ UYr4s\k2t}RO.śu*%_ĻȎ?8i]wGԎBZXJ-T?5~KMA±I(]Qs#D]vnsa%gLu')X\P#SXu LH);]n-#J\* I&gk4.BJd{SM)g, bh&s3 mNUwH1Dymc!xyjٴgc|iw(g4O BD1"qJr\ 85\Uo WVo)3NC[bFX΢${2]M )Z:Jv~2ϣ)tAh6$U bO.-8Lv%=64<2r(>qbv8|k ش0 #,K ehMbdD!tƩ{.ϫ~uL$:h=mUY*Wjԁ#N[o_{jYl9*$nUx rmɈWMѻꭒDžTd{S4ݾ1QhI+OMjO~w.Z =2L Q2F&i*LFTr"0 REw -WA7 }Úo!*br(7-6z;vjJ6s(6|#TWS-K.eR$v6>huۍmCiU[Bӡr)'{]Z {ڽ>km ɴ#m!]3/}RO'20{NR;0Q%%1Vse{[ۡPj"Zi. ~9c}R7#/U/ >Hk1>ji#F3%`e0zT,Ǽ!tq c;M;?C-؟j,lJEҫ"9pC} H"08CaĆSUB8T!2* ^"3 -*Y?*dn4d22#%j(˰sv27)N*hc_*6G ϻ'AzςrRHu!iWuU˶c+{QN[ԒLR h0[]|bNn}lqjI)'x$. k1t1H+ ahB `'єTP[ðaespA)Z ^1) -ӤXc,ES<*n;$X"&]MS!ħSbXQM#fi2*"@0#M̶uN M1%?]Ց(^7]V)6~y O>--6cKR"K27&bv{lf4s|>X^8 <G18VC*@.x (]#r3wY#5M0馎%PWҤ7I:V 縱3OQ q AWc4=u"ȇW&RM8kbR&wRB! 2 -MҔP:DtߵL4 'ibJ;S99f.K=H*\14CdSĠ@6#%֋'؎) ! Z c`DX2 [ MEGj6Hlv Q71&!>K $WRS̥i^wϱ~ؕ({8$BCn)]9udtnJ3ꔯhЌ"dx)+S>f( Ҭ0{6ZٯխCDg$e{̈aiYsƟO=杜߸L3,כdᴪ"3GSrymeK I>@{\s3o(4Ő)PC;H nSt@)q*S DhqylG)C'Xy•+Hl2|e&V!:Gjq(7,:۪봃C.Ӛv.|},Y'o pe.|u' HDbh 5YKvs QR]`dςL%& NmJH  ^& );>KpdNF2SR:/SPK%娬dOe嚉OW2) LAl&@Mf!rpFz b;B@XTD`ig4vcEܧiDB, ? ~uH`ZTfuljPP5I"wPβҳ^pF^Hə*{|w,( Hĝ}KR"0-;#=UsK&,s3UEng1ϻeaFWT'*5`INZ3JϞPvRekJB Z`QJ!OF*D L<+ L_ /x@[iᚢ9>ۍf _ndGD Q /1))x[aL%g7Ztw5NʅNDQaeC5bxp.$2\h5[,IKG1$&9/ۜ@j5後v)}R_zCAH/W^ZGM*.@{{<3QnЉP"!`O3ZGpMM"C4&d4M"H[}M|u#lra@mpW],P9('5~NZ8UA%ktN8 Byv#:Z;nO٧aY c'q3] Bf)cUmi[٪’PA.[T3#җi*loglVOOl(? (Ȗ`1 ])(+LMCa`!FOލ! Dr"ߜs龨c |#WtHNz a^lM5A4sdGL?//d3YJ[_  ۽EM#7 >2/mg;HW%m6ݹz U,mо2vH"Ez JE?Y N\bu~SIM ^#>VC$[kF"DÓi;;|S-&N: ~#0J][A"M8 =q*&`)xp+3mތ<"PDB 3uȓhacksFucƲŧ{3]FZ8o.W>|jX :m&o%{4m&jTL - D(jJ6T wOIm Q[tbsoŸ:g@VJo8>5:Hπ,_Dg%EDXlCpNI[`1Ղ%{_-Pk*sٸ%7'nD>|“`A' =7v:Fmi*(ǗFE{zW7RRhf =;hhPO0͈"{UǁI/\Y#(tW\t5fjmI^`u h}9(鎈KϢS'&j;<]Vg%e)[iNl703D$*2[ &A=J38I/ | wWYZfWXJyoz!Tʀ:2<,TO M(M(4UMO^7 (Ȱg ;aԨUbE3e,0)BAۺ١lrUQ>; tL#QM$0^n\{T¦;k5{WADxGݡѥshK1*3 tF8? B)26\woPJ^Cr";1 <6b*@<$ =T'W7"dv u۩6>[mrP)B%Κm5^WӇ'P| Ǣ=I)h}hvF BͿKK[2$*dxU}:s[4LAp׮8sVp~tQB*D%nY<3NmXPbX|]r+0EHYpGS$t(BcDJƒhp9b(N2k5ӖZ6fDŪ)`173tn5Gh4hzUn&JkGKHS)rȡY[!r{V^5gFoG6"[Ϻ)O#/'h͢%HNFӖ 3XE-'#0G0Tmv*L,( {!7*O$ j3hVDAu.BLHt$)dk7ݺ6Pkfk=bÌMHjə W+ďJi;Sq5f{Dq5i[ojnIO>'5ilTǫ@8՘kDP*DeD8D~T'Wչ{ 7yO5z=S?oKڦ&JgӭWRHNI#IR!pWu *tg zkG X~v?i T9Ll@!rhYR< tzfA*^ WJ+pd 1̀,w{)L!n7'.+̀>b)C# uw+)?:2鮬m8Zѐ$}IbdҏڍĝN7XӰܩ5d# E ӕlUZzZVdfZGad$qݎfbԼ'Caؐ*J&&#įש@vB GD[٭ay  }cb8w/^}䋋V/vWnP({v$# )Y]lG_ԺmX+?Y(} ט!)i_, I1Jb mZs=$$2y2!VX%s#Zʊ H}: 39 y+>wkVƞr"G_8 Hf juA_p$Hn,X߹'!]Xt~nJ&$Nq 5UcQc蛴@I&sN~WڱMO'\?bc[o,!Utkfy8*vxue7>!NU2Vn9YuU]%:J'_+1'ՖO*yvxPhN*fPe2ZLBEQRF5"|MtlBr,Q^;ɘvrB6Hɯԏέڢw aJծ.Ԍ-T(F"(YD;ij}4h-uQH 8-K M5Nb}ԋ+J~"(6f"GҎ;OL~_XP TbCڦ SBZoW#W#jK#>sh,Wr^VtrߞT"x(YoF"\#}q\88]rGߓH!0z3OߔCd&6[=#+uA+"+Sw2TY>; [d1~GE~cՍ2AyaT0v)md–>[EÄwr@.#ȶ3P©!KVgiWQ(хpm d%_ޞv ci<=++ О! $"6 u6 *ZFrxo DwM0Lo5.~gx]iTb< zX1g3/Oɭ=X2 gU0AOΛ?Lbx(ءH KJ#\^pX>6afSStogPQ`Sɠ؜Irm4&WS!LJc1 vQ: #!Pˣ%;o4p=Pkb$b1@U\Y`1@Ux-j0Oб>NǡJk69]*6d?{^yusllO/MFܩȵɍMrIz{VNxHmyx؝CeӨYC5qbVWm ad*'(^s@udžYwEY<1WV6B2}^;Ą‰j.XzX.P$(Ɋ/8*xwAA?֛=Nm{~:\XʰQʉ.CMs |Lqq1MQMLg#w>Ь,︺U}n+lrꢯ\U_}M~å@{]3gQ\kVO=QumTZ wvBI?HFM\=~4M,QNcA^EڡP1b i>? B1iJ曻YMلC6FI?$h*Qi}Jk3m,EoOi.$,&ùVD"\.}O#8y *CMt!7.PIpRb+dy7\2, IV9^㟞I Ne.ۑ uᓵkdur.bZ\>I# lZԞͺq$a]K_|DܦÝ@P2)+&HT&TYS h΢FEOfE9P(=jiRev5(֡yqUExbDmc&O|{$RΒ s?Hj,U!GH?~N,BT~r|2N P;G7ԆjQnF ')P_fE{)H܏^9Hw~ǿjoλZh'v5|E/!o뒤^mWM)5@չ![cS4e Wy ȘKa빖wb CtB YYTS;mRv?hb7TU fc@~R۸{<)8#u ?5Ev_z|ۈ[af+Sn w(ZzAR)  # Ygy٫IqGY9|޽ƂFn:zxCf*Wyyy9OO}7Yɏs]d/oܮnyl$Kgƽ aTK~RIݾT飅ٸC0}Wdqd؟}0HE;Jr*Sy,'ȇ5>QP󮤌$˗3aa9j-ˏ:H RC"CrE!ұqw Lkxje+KQ *_/H{ۓWY2kQ.]EӽVZ0J4;ٔ) rCL2SħF-aV2\T瞯A!".ydk5\Ft&^C%y5Guϝ̥҃E.K&3(C( A2?ҥd@7}.D&I VW(ZMfL9_E ^Q@d;n&IXNn|7smMl"H%RGxTrKgiP%2O$jP_3^iYZfOwl wBв_(ҨJ҂zbY]o-FJSًW ޮ\=[v]gы tUci=/o# &m$x&ɓxKw[I:w68~F [Z6 _nFJd:Uq@ )e6Ab\B>U1zh$R b`.QJ6&I峏z#&5Q6_##'ԋ6BNO2nBߜz'NLvml{'9,JVp`1ܳsj,ẚZs!UCu&Drnc$pbC]"j$ɒ\Rm5tzDڱS*C6*p+ٴ8Ypx>eD$Tug>"EMqOW0,x;yfYZګr Bm5cEK+ݍF\rAV_Qy'>o$X/zgY`RBĹ+v2i)@V+㬠M_ wV3]iT2* ٥"'eEuYml@ȔzjH|M uwJX tI)dWq9 |R4iϗGA¢1Gs/;\YbaLs2YyF+E^(["W2[fսHc' !wE$I;ȓBI)Tg*c7A(YQ4dN,zOƕ8iq j"S^@˥*G& {bQe(;ZcqPIBUvC2#CX%RـFȋ(Y}Bh,YCPrP8iQ& nm6 ϾsO\S^F1}+nCu Ӊ\mx1Q%)ZàGI,<&3'Pžߍ\ <; [m~DBb@k2cq) JO". (%=HI-xn]tI(8c#Q;"~x3goApK]\Ho E[ !_5 gaL\ [ѤbrЙ: 84G d`3{Ѥp{tO)U~|#H,zS$4n ԼFL,^'K. ynS-) R dEi>`E#x̟,@ 9e5]Ja]LU-J![H̥&8^$d/eFx!D&7A34{|RƓRTQB (o!>=UF )W:[b)s$U'ļfE/DAA>;/Vq1.;b̈Ox-Z#NFqiİSV:.JkB ,"0R{VȾVm΋ H#ɳ-(,RḡY) כ-h7F2m$J0 KBXM v׷MoOdU7 LKwLu$+h:B'OQ N9"%mı9E" &mAT|a#woR%νHv!Q3 4?^ꧢES\o]n yƞXiW.=E2]unΰGi%psnoAuB/AӛdJTi>RI](t; BFdeE>o|/HBj5&T'=n[,BJyC +UIb{fdշmXQ* LRwcԸ)3eEcPn|lP )v|N1L9JHvz_*fr {ˆN`24DQ% }oT#99]Q=1 C@/ 0>dos/*"F*E69M|aefw_Q >n%9D탻J|Wϝ;wF۩I%Sو򆲰[nv0\*ѸHa'ηhw`qh69t]G`XAp3KUa*z:i^JªE(+܋o_=1$A`1jp to߉?DYK<(;(7gNP/& yH @  oKֆvb2 c>(c<,RD'B:ŵIwЊ|w_[B;Ȗ KD$s5)2yˠ" @R1d.pLT)@S]h'0POw320Hylm3cH cԢQ._7ALZ /?6C!d)5'15Lm͗b)LH ,)~=:t.CɩI °@#z s$Y<yA8T* F戻E -nI}y[seIaw/{A$3%X72IWz b1< A~Ji.MD*pa943y R\"y]t MS s5A5]#ePZJ)D,8R4 ƒb$(|xNPRUmzZ)g F[J_H SJFB%y,օyR;uSN܇%ҴZE$R }} jIU-DR7D ,Z(h$aofi")anc%81!#}('7WZ:8U8,:"΍%HFChF4F> g$FtJȯM~S&p)SyqrR4vA䡹*Íet~[s'-_Xoا1LA@Fx.ugCȢ Yu5 /+0<`|`\^-!n#(6n;mD(:Ҥ;M#&q+{T };VTDֳrS XNG(r) u" 8IGAm99d7x\3jYK¸M>BM>V3%0qL( +Hp973 ZJ՗%lx֞~ky^Xv:QlL!kdjGp 5+ސ H7tP݈(@ * #$ D"FuqLk;Nn_๪Nͩ_p"p^|tp~::qj&9S_Oid[*$I- (opzbo.3:7䌋BA5_%غ ºdIRi0"M!HAb~ al`D! K!6ەMZ$pc8ޭMcMKHLj;vŤ.5|™PsR!㗶!B'I(弟 b CTUqYNm~0^7-WDC)ގP拧׀)g/ 32NF@ '.RnBXG2d!覠]N06CNchvx%{fc,OiR #2= \{LV[Hb Uă3) IpHo,0mNC)Yd=Dyn)4'U lܻσT[-zjBZVy;Éޤ_PT)ύ8( Cvx8*E6 a.Wa+,)R) pqq7&^܈V1ΫDQZ jC M+&Q+$a<AfZlq&5kF!"0p dh9O2ә8{&}.Fs1 Ă c_ FYRז4 F7'oћtpTW3#AE}T8ʐ vpU9zĮfh=%830!Y5f(7S! E&-8; *|ؖVsJa 8Yܘc9 #s(EȀo/oèpPlw U:m+YiS蓊/UyR}P,LFI8:wu̺T(Bb,V7u!8͏w4`GYRmF.U H/­!OR%a_"KeK#RPhTbLb5} >E2-B|-ۤ 6ݒfŽǞ,GŲ\4*$8n(cU l^^vV3J"y:H>Hrζ簫nKeCD_/؛t_d=vq?(Iu,u$ײe^&)e͎Ok:C1+rvG?fR07VC/ƁleevITX#* LSŀ 0!I/ OsQPC0ApNM3_57$"aY C'sڍx4( P{ fL$MA8θ='ȡW&"[4ܗ8{MʼF;D$$V$]k$"]p$ʞE33b!̐GRT`p60oe 8X5wG\C4#"ɑ\BKl&9 '` A\BߎoМ"9(Cy0Idv<{@(,X9T3 cBmU_(>V/P1_~ʸ9L,5sO3PԚxIK9y(dU-``opf#֌qQY@4 _%!b8g)H96^#R FL%,EAhETHgF,:J~AB<'#qܝKsm+m?k&ۻ*n5Bcm*$|0(imY$^Q3djjc‰N%D`2xd-+1sA13}[k$@Kqr|I o`A \C!n9U<6"ngx`h"0ZH۵s&#EKy/eVەHA+v#tI䝬G6kBM١FJ2Se<_!R3V:׵: u݇#%ЧFu%w]->M +^uvUJ-b0ڬAF^5F4J*v_哯 w0K+3D<Z4pPI554)1$5dۣЇaQeib!\N?/ [d{VAMڪ:s8A;̖fRU̠bk*#KRf5Ypb: ύK|?ş ;5@. i>d7&u)/Eg`A(O}|s%H +/ĤH%z(12\A' jiGx|+~ŗ'Aj8/BD THG1X9uS\XWs)R6HZ'}0#6ȗe8v/ЍX2b 슨>[ A 2 Qn`%yQZakB<`0MtOB3z0",Y^!a ͠[ A7CnCBVZ@RVBׅPBQR{f;ω3F7)C` (odo/[&p{IEiև좱)6,O@VU6 `C#B5&gڱgXG!_6$ÈL6!̨sBYkeI\7HwqgE2 Nreb~s7CDiǹxrқXvPR1׎"e'Y[yqW1Z>q92Itr!ɘ@oҚ5SHEM';-Aɤ-%?͹uFVh^hI$MDl g>"9/D=])%fsCNGO)6qV;u=?_EoDbMru2F9tC"+iLcK7:q1 T&l\_YR9qR< Y+Q;o.~.5i* ϟF^e2;`JC}xG$xx'qj֜a?kgvc_i<>SވdV~{=?2;b^=(rNKNqms X@-0S9BpKL e9,2SHW]9!} DPKz Id70WZ4?Ѡ1*fd( 2 ](3'c+] o(շrbQ_Њh@+<6P_ILm܇ZKwxP݈r ޕKų1ոqu]) :ѡ,[Ƴ_BCbʙ&"]Ҝ{bI Ph@wѹA!ʰ=h責Wv\dE5LI! cUfGU$Ѻ(D(>#=IX$f̀Y  m (8Jֺc*Xr%Ֆ_J͊pP*Z čMN" ඘ʣ Ǹ-dL/hEo1}|@R@G,L0@"\4*S x)@jK&lw=ig`L )gbiT$k*^qU/$4 l DArlP*3.UH#r"UBM ,ܱ[4D)% ґ@oEs"w7.FBPr N47,2u# B YF}C* p N(/(.bx8,'M:=T;Ȏ&b9f W,W ȿRw.#X6ǫ4z+Ȏ5]0(?AH X3;B. D`% ̡]ebuJ&*w"C+M-iD֪2q=>\x 1*PdMgB!7^Dot.!4͡l7- Nr6aHBN:@1%NvC.F~dt%4m/܃^SK$9rfra$ W4׼}H0B@1!x'|XV"# j" y{Sk;.肈A-#bI D*INAHt}NJhZ6o!sÍINoe+^-.:tul-e$>'"6aq߼OS>v; < D `VGɕ'_~'7=. >I^g8q@$$Q5%/Mg+[WNDāI35 & 5?!3q8HwLU3uY%n<ZW$4d9[CH6"\r7gz˱P}}X#`NUVkXn[i]T!|$V I1iH\? AfA8A*djo)sQ_vf1"ۛda&uxZR2COc527j:| qᐑ̠:L!df`~)9Pο8%Š=f^M#gm:k& b*aD]{('EήhW#N+5)?"+ڳu$~:Su C[;`(5Xw!uQ$).Pd2epu0pA䮀t=%C-U;uW-$%vQ>~"4A?lHAÁx܄3*j-XN%Q kG++\a߇mzuTa:ViڣNehs(㉏GK6Thwj䐐!KVR)C!G~(+ f;6ZLw6y>AS !0@7f>%DFf}6A A6 bgavvW'Q-QF_اHqt1|鬴EN؎Wmid=a4|CqD#Lƣ0r6zj> T"C%4|ePH+mSë́rEHlT_5׀2Ag? g'Vϒ ʒHA=n-K:,;NhEaܷ_ĐIadu 0IE񨸎="X&` ?`(\i (Val2;ȭT%YY01Y.oU|ETQc^VKoJGq'ۭ$OUu2G5l"zZWBvDw%B`1 C0+:+Mq\^Jq_l{3gTŋq83W,+ +dS9ŵS2B1L4²P5-YЈ"œs`G-un܃u~goIƓ-99&G!qz4ʔ΂K'4UpΥ;܊Ŧ"LbBǂshH4y-Ry lC8 #on9 [꧎v@5a<:iCZFyLKGyqQe֎J[:dҷ%_ɾU_tE) MDl(r ( шH34Nt5^"uA/zO8{xEVyZ4o譙2:-( i|-7H.F9=*"q7k/L9! e1w+xj . vv8=_(v8R0{y.'Z[Re6?)#HJƖzHg{!h]2XckL^TcCS#Q|/ }pk2E/OqNLOz!'^SR-̨??)+ )]M)LaQ\r"a$w>Dxxh][y`Id(Hakp cx7`p/xŻyP  6 x1wM._4?"D1ÈuSjbtU>K.4 2pIf6L㹬,s b9wy_WZAN>pU4D"y̶3D\ukJƃkq.Y*+37DB҉y~/ZR[H9wL| ^na?  sy2pBhAL nCʢ#(<;&Y9+Ùb JUdjl-(+kCMzIHe\\0<*Nt]Jexʚ:~kh+SFUT#`T=v4i&/`7;ꅞ3?cܹc }$ko,,:#Up lgM^eT4M$J!̒dYxEc\oVVdNNKkͯuu4kLdd8Ԟmӹ|Qc!|GES+RpTdnLqP&=|T@02H6W+^jK'"ϹJc[z$K[k2EigcH:Z1U &fjM" +WNٟ1UImn)ꛙt?J/.jI[w𨺬dxk?\TH5+:rIF޲Y#*I*+uNҜKX`#E-ќG/#)sjh|" Hʱt~$'R> _/3c f>hymt{PB1ɬ"p?YL1`pl˩8 ,&Rp< 0d"`B(*Όa m0@%v$Mo`>SS/I03L#\Kg}ˆ)!!n1pLyoD'`N@?|lKi_I`E hߗmW:31ßWy`ZH3Nrp@0mBO~Mp*20)`U@k/y;PJӬ?Đ&JiJiz9]!ΣͻTg!Gv}ϻNdq >|5?E+4SeF 9#L%5j4-Mk7l/I 7N)T:{6=dg_sIjk%qH9%kE;3Њ3KƼY`!;IoMU븥NMOҠuZ^j`h7ּ+TIHKQtQN65tEңSi׎dRsV; yk'vh<z=DP2Y  -wk ;imk4D>+ۋc(IZvQWk*&KXLPjT_eoCQe{ +HK?|6*ԊW?d-hM)t'j(#`Sl}e3r=0FK̶b{eC5'OW.-3 >HVI!5gMX~ltW9\ʥv1s8$+yŮM$/Ii\ɄѤ"w%) oR&88^hΎd (~RmqB !'$ԶIH/'., 2c>bZ[u־[(]J [5ZGjx-5MúrO#7:'s-Zy5`h;[ƌbOAg#hR5.j ?uѱ0n1HMnìeG^I!`Sh6ݰAKZrzt!9ľ˰Z6қz݌#zysIW]vu6lOB[= ¤]EETF/sTKT"]$]`OMXC!;֎i( L̖vDEJ kKv7*ƚ62&ݽGILI& 8B]sLO{B^œ!0-D0g"5$8^-M|(=XeUG]0T U*-GcQqN%#`venCfi#[lJ~-9YL<-+2?3?n9asTy%ք{yިXt]jp%3K2ytd,Z 4ND!Mlp[L:NaLg`+([DR!cuBa74:urqQ)LU](v۔w쨥NNܯ沤⃑R5щK+a7^Աn\(k+=T?ɉk;UPO/u U +NFϵjL[HV:, SQ%qRC,&DdL/Jv݈%VcyՇ]EJGJAJʁ^21=OKf&Y«Nn+OB""bB$imjHn>' 1ȫwja)IycyG:åݩ_YOUG ֘F$sy 1tuyqC1C$v?4T=P#.9+ tb9Kkd.>=VO+ok/O\oߧK+w/[kd@Hc@ME|qxx &P-˺ɲ\-u`q$le&AXm-NhFEGMCv2LErE;IJ}x$ܨz_r޺iqĚyk<ח-f> ͯSH1ז}vJɎ:d WįFLL*ϫe1l,tK冃ˣA1~绞}coWfLq'd5͛AcqX:Z]4݄equ/hϘ UIbZ: jsi&l+kW5G2n޼6Uؿxl)*GK!* D a@U)LJCϓBH2#u܇]+N7h$ #%p_5ZhTU+4hTBmEx&Պt$S$U߳~]oĒ*A|ЗߟĥOAc!ɮ:Hjdoo%uBQr[0>eN)k>]i<r㼸?Zg=*4b;E[k$Ɉ@FԚъYo,wv6Jۑ&BOe$pY1*k{!g5;`}PqCO^n HGV䩥iknH; zVA|#b rxnRJ\t\K.j*Vbn M;#]KTJaOpG*] AEb4-!k2j/^el&SP)KEb-Y n*^ |U2*G_qJLRy*@Sɕ#LPU<~$^4JBeC`2=]+ꮾBߢEy ȍD^kQ(+([;t%\$ `_䢕G BFNo9~iՑõ/\f /DQ/ڄoXʍev&ZaxCء %$`Xho7:U4SyIn-Hu ԥ*rHZeg)~ H~ OÅwI]8U ]Bum?PqsmoϒIϝ։at\)ZvڤJLRj- Y<~wW}0+\IEZ)o"d8A=a [nKԅzGRCmؔ{]ϳ")48i= 5e>gb^F>reTWaq$f,vJs5fL,HoSJA 3ZUS-44ʸEgL"=)˱We7`N>ѕW,EʶZr?4_jUn"\I^a~Rga=NM-n;R`~9"eY9UGH:6X#/EKB]LL9mJJꑷٕ\ErGSŦW5-9oqO6͢rQGX^̜KN.bm6~Q*me5_2̄q3G^B'S|[ K#;OovccY8cĩ43zG1x"V Erir|GyGR?Ѕ;S|EK"դ&R1aҢvRD- QHy8umռEBUZ 6\cЧa&1l$ߺ۽G\,񶏶V{߳r[S)m ~|G-H~ZUs0I!`R b޼P=|B2iQjFB_$P;Rk6XUuj@TO^mdB"Z(BNǭ5V^ڠ#wK|L~{Ji٬/ȴf"[^( d:)lV^YO}7mSLP>DqWĕ3feD_cDyFɥV=J8r֕BD!B ߄v+eQk]IIа۝N?UEy!޺A3߂Ԃ={n% %w3e zYJt%'Pqk&HvObN쯾$m6[!~J#u ~ ):jzBIg-a]OV.fRHٜ=9ء3@yxK&Jڛg$7u1ф|ŝp1l3?%M˨^ȽIS#=)QfV0p,z^R,`'uY$b܂Qռx ,Op+RV@?]/fED s/ З҉h nU AuLdBIҡN>=<ǟsva@mv,2 vQk|=Kh#K[%s#TwWcFK[d~СUJ#Fb)Ix3xYyJloXŞ~s?vw1"@VU)ilJ<)~' Z3UP6۔+F9~;SE?AE1ۓq`JeOu.豻yUd%$O(;ZWGfrqj@g]Yz ̚Y;}=FJȲ`3=+RA?CFR FZq,Sy'-A~9 l'.2cfAE  o8KWF;fM8a}QyԈ p٬EwihR61kԃP ϧ_rB%#=ϋJ`|:Rf vޘS}.rV}V~Ќ*+YITrM8m>hHYeުEe!0?5|; 4/{9-)C]܉C?)+^JR$di$ϛ)}+ t"vVSʺWwQL—Y(\X#UfIxxY $r2)ww&X^aL U )%܃SR6sz7FŞWj߇GfRmHV%JS^mOhGK$97w>-ɫMZn5c7%T@X2WyWtBUA4)psl&->O+UWϿ(B7s∠o s8Q#uPxbo/)VU ^;a#:NcݵeʹZMV6T̢[AoC"MEzmq#Idu;ۥ@ͦHj 2B@dfA~$CC5@-m]俲Yv%\:ˆ2{7hଐBȯR*BU+?d[ ,,q Tt"qԈi6!x5bW:K*n↞?GƦ2܏=SEuFv̤vĄm= nnI k0oqzh=_eD8Ud3Ca=a#Wʾ9ZD̔eɀIi?$i>P%^XւJ=2 7ٝd;YDB=]s0D ln ۴*pO]]_P 3iuBg DKvlj-P3U"7bEbZk1[ў>W[o 3~vJjA1Y&5VF A,Шaʼeu36.sл0 2'7t@.Jm $_FG7![I=EI!)ywy'p2H')//o1hvGAxُ/N}#İtbIFC&Psu)f)R),S* 傖`FѠDm{BMuڈXHV!̙őOc׸X%Q۾ 2#}mxY8z_&0%R 7ӵЋ$9Zp.ze8ADfh Lx\ǚAiae?i%O*"{)تɏU䬬  1Cn_|ٿ+G9ae8HF~f&$v#Ѻľ L+žu2Ǒ+( {|Qr NOä D*asSϒa]%lʗe!kD QT9T?Lw;yv}^/\ֻ`E;_8+mGE[U.T/;F[JKg}åm,J}4Q$WTbꞣ⩰^pHy_y/JRN5#g9&-DI=LfXطd*An!uF/Jc-F$s@Pa.+ـs>&UkIIeQ ⇪_&Ӊ2BüS{ }99vd,\<"}%#c!-K3방ȜBuWggcWWGVX/yT|KLRN{/H'rOIVBAUD>{ Q"D gJ9:3- U湖$(6$O g"P[lXSfvj-4> (ľr-؅XW`-Mitb#iC{-\nM5.ĝϺA@KP>Օ񈫹|6-,tPh=$߸fwSU!2t)yhiΙͿRG*KVfX-.4 +ܑOP:iNj|%;d?e{2FUoOP#ktVF~ rOtݲ˾R1&(k]" ĺNvU#oj1Gjob xN,?< BA뭕C>U,˂$˪1jBaJ %hQ(lJ /&[U.#Aw7/nBd#'gpԺUBOO|,ݚ QM)?9>ۤ̀ӨM%ZBǡ.r3';Ap دZ2dNue]_5SXe258PrˢpZr ) 5ڥRXKJH" R:_u_(zJwNImBDE%dө+*b6ist-⽢>XQ/]u?p>)C+y\2}u0~)$Ў.T^O9*>n4!N\M?JnbRב>}F6ʭD"VE'b?˕^$$u󺊜iK؉9^8F^ S %HSmH E "I;\OHZ'\5&J0V)EY=C(89 \BJ*&{>֗jEP35X.|Vm8WKuJ]P('',uR^7hX"aKzp.$OI}椴$n1nO4"1/z4rUh4Rh x{2Ɏc^F"?0DZKӀ(מ*~/B$O"~ՠǤV,._2bo[MX}9-H%]uMV˔.*uwC#䈡>w(Z|HEŶTv@PAo[}/goFn/RTLULW4JYg˵* ]j%,6] ijDc1ִ.EQHo߀,z}R&mVc=RQT~'-5o=Z͖?鎼`g}mz6EMLng\[!m(H}sy^Bq;L=19Qvptt()h5b'_sYnZ&w>OVl4Mb!szQ$K2ΎV zrRZnnQps_zaz#Vdz5OcQşb>{$[( kAV$ "Vr: /Y[LQMXtsmQ늀αId]gמ;agmK4PVVcp;{jQd#t* k<ƌkvMSt"|+8XEOu)Z"mRbbǂYFT)4)ZMPŐu,Y *r {G 6^h*ZςNv+__v1CT:ߋ?OqMx#lf7[JbLeS=,(ZSq]5!$% [馉IQIYx:9ZkR2|8SuR0 FN=6ԝ顟;,V_ÝY2I:z # |iR[$X14)DP)c.lj0 | ꊥ)[tŘ/:wڹB]hj +zQ%cgeF&OR.ra/Jfuj}o(VZs})bn(d#̧AdzeAE{ZuApf2]7=\ 5wLb/:ɵUx&P#vK$u1Z%Iy6dIw.\&@",M%Ϭc#WԔ0Tirl#8HLFF]ڀ|*K 9 q Pv[3 cPFK''׃dcvк$=艍+8U7_(;5"=#a,#e|S,kSz07'3ϋʩYlײP,%C V q`ic!}MZP(tyn#A8A dK9FF0?Ӑ'h" 7'V|4%p0ME i`ۙubia%_CeF +hIhȦԪכ[wj&㞏dC0 7CƲ 6,F'6wv}z6IN#MtEj\*j 黕WS$SPWsu$7::0F!'PL4;VdXBf/C,:NwW]Yax[dc .ezsUu] 6~4'>WGYdOZBHӌ1;-o#c`3xmmblLM&dު {Aбv) ˧jY2x޾[nhyB._蠉BT9i8MbIE)I e!6Ml: ~ `Ky\K[7[xU&%E%-u{˹K %e-ˌTRSs>9/CS5ÉV:J2AB[2c2p(hzuߺ%!/mPh&֞ 5V2Kf%TTI^hYSϵ5-$:$qe^ ,RZ֭V6 !`k1EVi!z+TO)|A&O2KE?fOq>L$PEfBFN(cWs(M** TaJn` y`#>]d9G \Vm'GΧz&qx뚶@_M]/h WUDD)Z Wژ\+:A#|}e"7 Es>0aƟkek!Jzl e'V/+GN+MlЪi8u-;FIe7/M8}-72d^%!./ҿ]38K+Z'V+2Eƀ@xb@+aWlHc[qQ|f!u&unvhG;S=5}k6xyQv,{olx+zF)ˀJ^LNaǯEotoR$SDʈLcDž~.:|$_)gʕԆ52qy3!k=Sh$V겗b&W=g|hN&[RVLVHC[ PvE02.Xm~:J]2 HU`MJ,j7k)v,i)COJa%G񌒇Ag#J :]5b0D vĠ(R'uPTmW<}-tk|Vz~ݛ F|=O>I{`1W2zUnDԔΪk mb&0~hy3GKbJ-?eo[AW+]䴻b'?k|9=GБt>3oB!:F/U3ҼU eG:cG1qY~c.o }PF6[8zugVNDR!9,DBj"ϊVh|>m:k"٩r&O{]-F'Z u:fwb9c`A ; CC5H}ØR. vmb[;Ҋoe|U[AT?Txo~o_-j0N5 qs#Z#ֱG jQpe!G%2y2twn{yv veY-Q-jN3TB~>,%V˧$7:HoFdmy"5Z)h&kr͌Bk`r : X͊ m`EÙ\DSdjN 8@KɐM-h)QC?+SO~$Hp+WwYg55S_(#1w.. V[5]H8b/>I7km@'}0&GMv.SsP7SV-]dLV^gC$|d}|1t>_JՒ2SafjN$0[T*W8{.YxcR')<8 *ZunBFH˞TɉbC ށ"Onp^r/j ݏ"^Z`VK q]$^D*^[;Z=WsZ9J~С$ouv 65<~ '9kƗYq$]̗G i.cPY$SGl.d:PYdI %(]B_' jZU8\O%Gڏ 5VZmIF$lI_$K?*#5.~u:f)H]9q|$brWPJʙr(ӐUQ_$3o?O!yٙ鵍z|(N-۔\5NkkeYQiY4W< 1&YUkp.&ׅ@7OZb9O cOaNeQ-gC!3tm5!P$rnюy MSK9v2ih$wlm'IS aGz21Jzzm,e*{ vEX!4(m<.)g*:|c`<i8o!kG+A״fr(C"W!5,ofDaܯS6BiPx%H(\Q$#׉v~BiIMl .[f$R @fu{yHƁ&X%%IkB=^@DhGWUl 1oVضI^c` `kX0 5`@)Q> %[dجp7ƃefr/F4 ]"1/"4Am1Pl3CAXb~5R}62z vENVmsXZ 62R98iAY(`g[s垒tchkB2c{I#DUhĸL#m]p`&$F"n@ lCHJ"; @Kf1>.S`P'+A2I cQ},Ng`%ATZ]"OQSJc t"SLĸ2F"ܸC'GS޸%АG'S8]`[^̀n>F i?6Ff%Nj}Ԋ(Y3Vުqj^$4}a:!A-g pZ@Od &%( 8_1}h!z@ 2@Ǡc#Le kӱ&E{%A-}RؙwaerH2ߑ:0 $jE *iIjB=n8ڔ A;ilaP famN$9+DaC.zA% .duUbq5+z,v dx= XD)WFL'.6ǰb2j\N2ύ&&!|e^0P%,'!8kC`_(PfL I?a'PMp)f?I XJ!dOvЊY1_itrI@Ip?Ւ@W&Ջr"OrA@Ij)KONIzl>< ngjB }CQSENUa**j91j{!ޏݪʱnn-z*rj(? b=X'M%EOZ#5iw@+ hvTgvN!6-o܎uCs/>E%u.6mU)j_ J8 _5ro^tw46d>%k݀ԊA/qeJ~"t=+b䵕Rў)fv/߄V=HEk G̯'9UNJr?V;[~1F`G.YV3VBA*g6QG)lR zWZy!X^,f,>%NMp'h?k钶o*Gojn9hIRjj]M$"(%Ic}e=bj0glm*$O;Ljrv1b!s!7lF"`:cnՍݬ2v%ѳ_*x*.(dw׊ʏ8y-f!UN1^헓_)z:12`MC2/f}XRN2&,; ڻZ5 o=p)lguC@2 wO.TwRجtSu1d3P}\Q.;?.5YekSQe;6bIw+u"D& DRx`/ԵXVIޜتf>QTi$d^HMmAi(Z)_62r"'wܗmVAX(wبoR$Sv8~UӸ&t !'`b}F;"crf W y6ngYT9WXcYb-IWo9-CQġVi7\z1QI>U\4srĝ($YOrxi\ɤo\~{ >Yk;h!0%;Kq@򙜈,!8l_c C-*[5"gѢ`b bᘬ~vF^"sR yF09)B9i2O~)s-9ʼn3n/!2 .M#i[Mؿksq]\A]sO,fV rdB؁Df7dx~"y M0[  *iB*5#%aR6K+FT@ڜ􈀝aTWl]gs t #+r'g/\ Np#S0`(D:b.z01ʬVDa_z$:d'161e/iĜ}$N6='D@:&J4S(\/mA*" a'vIuк*Iu)A$T~=buRBQr2hft Q#q2Q"Gm11qAـTfh>,^(<A3U) j ʷs1ATӰ"_VV$]W%ue)܂&]=g5󾵄]S|%$7lLs3Cp0Ez,s>Y:#$T$ [hA?xƘ )ݏ<q.z挙 W)0-:jAC2ŨҞt~Bth^f1`) R@Hz8avzbZHh3!!IQ,GXСjd j=lsD5qA4g=啷v}ռp`Pϥ3|̞)hzYЪǔ%kdX)VKhV~fxau*5$@-\2Dq"eυIaРddd$KbP< Btko9.V%Yߛmˤqh6,?hb -^0Ir(:}ŝsky i6J^$x4{L&iSys>EQS??vm_/kmZ͊mE܁ 0Úcec'm"j]*gl㚬q!n5:64Zpc!\r}*ux3I?"(u$'݇s!6YGK$RxsڥrjPUea'bw%&lqgZ2f#u΂a>F*,dm*Dd$[E]ToZ^''p29R˓46ōTKH1!608*% DBiBM6'bKB(;E/'"GXy<>[uesg#NɼxdK(mes[YN8Ȋ]|cQ#$Q4zWi5ڛQMo73N]ȕ]ar$r'bCVz쐘DZΏP,z;Bf1Q-N'rJTT* a1.e$z($ LĭYHiWq*sB1RlQ7[lDwF6r8^$؟Zx}qԓem[#K9*s緺} ;LrϹ0iן|]W4YwDsC{>9m[Whs;pPL.=GzhP\^0/+A)8Jءy׬BB/{|4x7>LԔI_>뭘X_gC2nYpm:=)ӿVF 4\^^P}RVR>iOm/籫A2j694b$|w'/#SV1\ƱV\ \ۍS9+M)f/vBQQK PV;###4 A@\1EQa/A}!$.53L!71LFA(ʳLF\ ] F@1I'O+ }Eqi HK>6"BqH!gR)gO,qIRɟ7ZE"#ieϩtJةA˄J y&D,u~!h*TBT2tLiE h'2PU$x!7?;2d,Z0-ѱ21iJݸVSez dX=PŦ3fܼNd-OBO&yC[r+?&z'<'R6E泹3uC >p0(TY񰼹ED)5@XeѠ"0CmíF WWB%P n褶l> \<g@M9$M’n+P{ 689Sՙ%7Ka+y?(c rZ; 24Zw=[ߔBdaEXBfMvKP|FP u@hN>I2JN @]@HU0DG4)'A^M皲 77!b%NJbQOc'a Ҝo;c כj"1cuĨEe:\Ժګ5bӁ\aqcI- ƠwwFv@H|-to!n37qQz%ߡqE ^No35hSH9JwzV{óv$>"ޕ溢ک*(BwZ%ӧl 9& Tlc ~ )>N bBH4818#aꦖ EyDG}OMjA t5)l{JL‘T{aJ[yϡ*|+~g@[Z\囩Xc(퀽iѢ51 #rw`2fm 1{; Y+D𗠍,$M򠛻SB"E1.B ԎBbH*LUu]Q@Np\FenQ{`\ԇcKKikPY2Dӕ5ls0f癨=r)4л<#k`ܲ+0V% ? %Z( .Azmݝ2Ptbgs_9O+l$t} &Ұ[ԟ6~t[y @P:$Y-W68S.3$= a(AO!,"xb2 ApM춴m;F1|/?>KX ?FTT9Ggf6tA)=tܺ u)\솦 pL{NlbYJ+ȕFH^M)?LQp'99hkOY$v* ONKB?$@p z?Š"_8ӥI9ဤXi~@&CH@24%P\e[~ || 84>V{T[򾆻FuAJ^G g#HA$R=K$4gknCyJ3J\okQjaG-yJZ!K+ "YKl|WU4hDŽ%^=a\MF?B_k \s.Qv͸UDHҵC #Dˋ4 h54{^*w1QqId/8^\Drز VV,pSr>4;7y=ST>㍊35vUgs^ j ˋ%LG3 DЩ.6_2kZ0%(WL0cs,p63d`aC61IH`ǢW n9v* &E 2{iU Zt2~g% HpDU 9YY XH?cz`m{!WO6r#HRoҥAѻ+NJiRT>%B;.DW踺_%Z[M)]~ |Q}LMu&#%Ue*F-eZ|ȰD.'a CM1fھ_&v.;%~I?%G߫3/uowoք)(b{a2 -ۇUj3FK!͗0jp7K(.mxZe3eκ4Sn'q埖1G.Ӓ m흴1| |CˏWZѷpq1B]T}݉}}pm3).|Ўu/j(IJ☾7k:\#8!QI4u2g@vL*lnSvޥ9r Z+ ؽbXZײ7t*QD1E5SLEжCP!鎼LNIhc;¿ɩjkV%WêAtn z̶\)UΤ*@39Yf7"d زoԺpzmr+륜 7{dk W.f`G!rM|RQKm3FeͿUYA@ʧR-ܛeʕz``: =M|̩ИiO6l[5ɖ*b|&|IUeD l)]]vM~r+"r2&* aY^/${Yhr-;@|U5DƬ XU/ !8(D~G*kJʔLve5zNOdKQO,q9E+Eqp;"_vKtK;)_T}eiDM 7hFv&8zLhEX]+:gĮ+'ӁRrY8oNVj Sq]6YH4y= +3H5[;)^U6ò4k{CE]ckЬԅ}Zr-.ZRĉ&l6wj"$vlFﬞ&Vm5|m-gFDk05ERve֍HNCōe:T^5@|Rq~YOgf6)xh[*H{jdq*mU[h^4/cYm=ufe }v_JyE_1#bͼMڶX*(Avvۆب*v[YDiH h/&넾Jfe>h) {5Q~EKoE %56uNB߬#f'sLE.|ߺǔZkJ윶#7hmLJ恮KfyJ``̎\@V5ew tC B1D=ן0ABw)(hQ1eeq̩Fx@"nK kcZfkch瑻z]4hȍŸ=VJ,3E2NAjZF+lHND6djJ-z)~<$9hFͪޘ4RJCMK_V*s,nZ[>ہ (ըՓ"Wki<򤟲-"ұ>n4+h`"P/YkYIHBg^(7onl@-N('Mzdrh:-#*#ԚN`s9ZZkU#h=!؈+(8[nnA5WI:tR?\ֹ(tdC!1ADS)swBUtJ27VքEG%}eck?X"S4Lֵ"0T5+HqVN'%W4H& )g](92(NTyP_=ڧI- +A ;]k&sR5KyFӴ:"N6UU$'8y,QB#X!|8R㝪.ƢD 4R2IiIyq[ oJd DSCkl ba|w-[9bQhx%"q JF͹rՓH,^ţpnAFGlT/I^?6vG?E3CYcFR:аЅ!{VrRwQRP"%v.Ag7Ԏ/\a/_DʩB5)&qE:re1[qwIgu1{he] E;otO%d*ahocy GA$Z?32)^DC5ډxJzmULb9bϟ$1Wq $葼]^8ҝ{gM d8<o%:FcFSS<<+-mޝ7RY'LpC+3>TG6 kO rblhvȝ,%V94C$tO9U+ Bsm!sRDO4&֖,abrrb.:Ɍ]zhtagpy-2013.1/test/deb-bug-438556.py0000644000175000017500000000042110700455506017411 0ustar andreasandreas00000000000000# former crash bug by Andreas Hemel import tagpy import tagpy.id3v2 fileref = tagpy.FileRef('la.mp3') file = fileref.file() tag = file.ID3v2Tag(True) frame = tagpy.id3v2.UniqueFileIdentifierFrame('blah', 'blah') tag.addFrame(frame) file.save() tagpy-2013.1/test/la.mp30000644000175000017500000007117411004164361016002 0ustar andreasandreas00000000000000ID3@ TIT2La-Aaah (Flappin' Booty Mix)TPE1AndreasTALB Tolle TunesTDRC2005TRCK1UFID blahblahUFID blahblahUFID blahblah,(4 ; n j@ B&`0 !2dɀ @2dɓ&L @dɓ&L2d @&L2dɆ @@!)ob><b HT00X;U y nO/: y J93SPNv;r|CB @TFU}ۭW(`{Vf3vezj]/9 CrJjӯ gXA3쵾kjx=-J,qKs *  8'  ,=61p*dB 860H:EtB!@$xh?|&#'A;±`Z:Ė26E"I#Kҙ]$V$8u٩L)E ȭt'cњ,oh cgXsݛ(_rL

f=L$G L"ZE 6AQ4V"X;T䦢UЄ`rl,TNiya Įɾw_M+jwlҍw>4 )8#0*w28#"1*#Pr y*m %%9ɀh 8 "< 5nfF]S4~ 5@+JB@"C10pBuN›F.HL?ZWN7T B1 9Mv*DĈt@%"xD>ǹ֕Uj&DT%Ɛ tF\h!Nhq EBv*J!']Jh&@ʔ8a$%-ll:'Exu~Kwk9~Ʊ{;'̪K~%&¢ͨNLh svP) .)ʱ& ܙP HC` Y"&'Pu,G lzh\F k2&Ae1e%'6u&F#TT{[/qU'(zJ䝌^;aqFP{,-CA#!3@c<uAP7 0` ` P $ @I3Y}$((C ju?tɄ2ǩiR<$ԭv.$U3\C6םudãfcm!bcKYAy581H@\L@= H?4,(!DA X#ΰ@0}t-޶!"&`0`p ['0/i ʼ%9@ &[a)+`hyDH4XyTȴ1ƉIzYs60ʛRڛ*`0lD0lEF^?{ͧY7ے!!L4PH ^ 0)/LH+rE!DRňS@33'<%  !aLSX&㴀lTNU.^( IFWیP!V#@̈ å99+(fE/6#|? 40֝VT&I* @ 3 L[JL̆dH%c!X4$&"BOP@iP!F KVJj0 PPPJ- 1an/ի7'e%FO2LTqT Q2N ¿f%Gc3^)f7*[:MVR-GEqGZ&&佄DN0ē∌>3D4TB" C7+qlq}A #8D% RPЙ0X\#}B #EVP09$ #B]>/sgʎ̩Cjո hS.^Bݨg/W[!U0ذÓs;5 Hu;hDgЈ  Ɯ-@ V;l,440!Ȉ0u|DI[  /:G@J LtE( \T-+b]2-bѓl殾ʆQ/ę#k!k2-mi;.^x`v#:w:RTD.iʫ&ܥ{Y .C{Sr ( 'hGzP!` D l4@ `.dlJBFUAGӉvQQjľnvA$S0(Dǐ>LFc_$D\.?, rJ6PTejcebW@uP90`HB &D cb$ d7£I`֓K}҄L/{san2BAD a HXDW4nKXKbqG8-53yjlԍP'iK7PCk!ƲҰZ簠iz/ zMj):ӭMGS ô8 . .`#flO4ni ¸"IܡV`LP*@;" jAJ[!q]3xĴLpԸ㋂]ijYA (0lJ:/8[~SE{e+8 ʴE git"u SE @SDӄ  I`H$"Y(J` <~@@AM3i%Yf! DRW^1Y΂.J氄&6F`.8BqP?9wce)|7 ղFMc 5BXRX}ͥm}|éU2F`dy@K#ІjI|O(Qh.b NAXкxJ,t*ah`c;1#C 9й+1T j4 t"A1PԳ+qe:$B2Qpi aW[š.Cd !{ S2b 40XFX8DLddV{ٱ%0/mؗ8s>L6bQ)7q$6:0w4nD0`afo&`Q@1)XeC4Idf+X YkN^̣49򗓜Y[}.s\ii@9ʉlvisv99C0 V0`]I0ɦ1 ^/ SPzB ffl KaL RPc30A"CoJ՜ܦrEtycaZSBU3kjC XDrRvkwwznW>,ZdwkH) \zÇ))t&  JpeR(./kd& `(5@ dpBW3WTơ0ᙠLMаn"(F'!r#K:")pˡʬ%*R1HaΫ R=ԃY;./qT)e8A!03ED)1e130% xI.Rǃ*8`a|J)|`P$0HI˘D QHX0k05?fS_Kc LGN/@M%+GHLq{ŏWZUwfyFOljZ@/=sMWZ_>TYO+oڔXWI F`BP AXK`  c cTf2Rd" 4s<DEc (d ّ*яHtO(sVQk@c:(!qMM'old}ˆOY>ΤP5 ݰDlxM.Oq˙*z^y`4BG< GMS!R(00P }pPx:b$ 6jp@)c0ܺW;!,¯sMXdSĬ|0/scK?8!ݫ3w}ܿ:(%o4)a\(荾>_/PPkc-V%,y`<3H271P0 J (f*"fPjV+T]#Ժ*4>XahX" V<0XC)cD (~2 HgpX (VFmQ#QV[zw"I|?GĹޝ4fNKVo٥?gH oyfEDz>44 h8`nv areU@~Z "#01F00Nf1 0=2;TL(H,8(k4Ws}n6!w sďhG1sr&^iOĤ=)0.fEݱIL\Ya]>V2_v{ͪj~< =ƠT] (/q"c޶aX1% '䘁yɂ` b !2 L Q XGF' "kj ɂc.i;؀煂D*h୬Rͫp΃: DA Y[]2ѧ81B*ubՓ^.',**sj^y^ro&B&fTBő°804/HAji*T`8 3Hƒ r HA$6L@HYiIQB' rnu" M@i8ĥ;:NJ% ݡbDbB(4WD[vLh2|\`~ #s_4Ůq LG[фbY5f @&0`S " Yd [PgP99`V<*آPIJ2٢!:C%ihCp>M*F,Q6K&cən2oŇcDW9 _ ԉY842<'2& b.+y4T$q0"3sFX. dΆ5tI$U +$  $cO/w9rN}*DKZۯUf#Ro\򠮖}G$Uݽ97e62dv>lY?=pKݫήƣS>Z|mvmҮv]mj9Fצ, QPU SBh %Rُ uK ~H[Q5a1f b4`PlTe^XY[I3T!KkgmTن=D Q,?TKmJ>PGs1wqQ=]T-D!IӇ"l 0m& H QƃYU6N/BĨ-z x.t` eg%hZ *L Q#<h nViĴƒD J-{X֭Vl rPx+9q*/$)9$~</Ge<[ESHqA@DouIv1U 12$ni $D!d <+G8F H:P $Hll)%7. zdRakh*qM(ޢli8C Fp>dB5 VUkT,$EN_5p:Zhptk.` yNa>f(hx p0Tj3y 0&1s0!85(BhaX@x#4NmBfܡ9@1r&'bfX_K܇YުKR[R;譍]zNCi^+YMU$(HC |9oQW~ޯ!ir"o6B+NT'HPUˠ"6d1@Fh F5@(Pd͡q@Ұ I4%!ƨaK(<,p(/Q hJXʲLE%fC(O&ٓ"͆pPf !ɪ%c%f{fʯu&X{{kz縙yep8L.\x6y;PQM$s0}90h5p3|0 2Hk (vѸZh)ߙx4Y,,*ґs5-id.uB!` pP%p#hmP8c3 *&^g[3p2wפSt̏򷖐*2,HSBM8لv4`)!|+];$YWe;EsBRVXd#VCeyi(+T6uݻRNTr9%kC ytDfL.;FfZJ To[ߡtֲSowԿs_~,( ̍@ FcN& &j 9 :`8-$d`B\ P0"zr40SYIyU3& `,pNyz @Ra>:y2̹~|\kNwk]b;32i̹*I 4|S9q^_֕pL I /ͤL\8\/xX2 ;$n  `2 Ja.S,i"e1fxTI܁$Wi̾s~y(٬>]Ηtzjon?gm M M  %Υ L%}EL^"abќoђ08 &`jLAy 1a){6{C*9"9&LvQTŜjVJIy5GgRrٹjϹtY廿'+uL|-~H;KnawCJ AD "HtIC 1&Lpœ$%A h2LQ IL(l0L@PA 1 !MP!շ(/dςeܙ|uYlj T7m"'{V g(~U5L՗M>&ϝ.ߎ]aJQ+B2AQ/t(1$V ŗxS36 E䶮{2ag}fs6t )~ sV(  `Ì Dʡ4] A) @TZ` yb ^H>D0:$HĄQij,MA:XjZQf9Cvٓ޻߰ d45܋'[nSXagTdyk+Gz?HD fq`agdZ,`F ``B &Jc~@t`` A!@ĢϮTrɛ&/hʽ*XϴYj_"҉ r*O^'TMCڑ1IOjTNP\D˜0;%4sk:7׫ķks~$-Dc|1{յܰ~QcP:3"s*!ɅT@ E@DbfDPb00dy.0e5fKnH5 Wj2I/(hi(@K #8rM7AW,px@rD>c'Hp/<]Hl1:㵹]T<%m=ET*|7O`QUBC "S03P}8AL  ~fR `Y( *1<8c%(/d3A}74`ĝHq85a%XƛB7]w@%_1 ,<643H!w3$BiR9d4C|') k(i8I}4OV~e1*I\,9`iز$pac` ٦0fzzfwM9-*4*`<S1 06e 00s0, `HC pED0CE8!LyԽ*Oe/Jܥ"lq 8KjpS o$G 'mtb $]HѢ)1R,y"tx~XPs5Iz'Ե#T4Bb:㏪떎{[8管#W4PmAڣhI^B'n:cAc`vJ8LBů^i0XZDtFxgu53d(<:ŴShyӖ=+z{:U0#`1?0t16!-0`$ A1a>T LF(؀ q\qeZM|4nU,/i &I 7V J @H|^$(4͔ҋF]MãMC >E B7(WGx@0K H )Ƌ$Ai o^mP)% fQkzmrdw09XAFU˧j*1[$;1`@32-%8ˁ&@'PwQ`TR0 IG"Kk;F `#VmWK)E4Xݘf @R^bI G`Ɋ: 0*08H2{U/޿Qs\?_{hmiI.+3rUH3L HG10*isႡ92 R9(8(bAc+500*p냚u,i Ψf!r,TaF'`X& cMd&H.u,a{'lVtdLڈ x20.)z@6xCR hzW[QH[u)K^ߟ =y+9Kj5~5ߚksW⪺zSXDRNDz"h-1 V'pYRWnW^k9eP'oW_Oj1B`pf`nn``P|[ ez`6r鎖e[a=0 Aڵw(i-2AEu"KJI+}  BoS0P!'}Fa*SqapC3KpYֱZ\J:1ʩ)V]C e'°LOPbKy5ӐdL @2LL(QNdha@C 8gx%P:0 12#Y@$ ɟXąݰ!F: ТtA,Ke:ۊǥH0Э5pHo <:H&s3Lm#F%Й~_̗j͌"t[9;gv>尊׆*ʓfe>UrKvUMuf`o C## @3|&`y"8_oEM..q6)Q%="j0<"`UFxMAWM29ےZ@/y&+`#҆"ǢiyU^cl\|BsZm!{ޫleBWگ?׶m(kHˮ e(R`~o`Fh(:`rFV Xa @Jj(D8.~)mR1I+`a$N09r.\h-$a6C>^>⵶ev!M_ ZC-y^̃ _ʏx0'Zsg6bզ6g ow!mo*n6E' fZd)QjIH`bx$=2GB2FƄk2NCE%1T &4u5u0.P@17h41MFHy½>הK1i'\Q -26s,GY>C6~["<IC$ *0c7,eU)eBYݎ9$_6I 1wࣘsIH_LɁ(" (p1/AxSfa)q~44&{ 9ƩdlK41  CkD\Ho;1/}y $iS2[2=V.خn^} s|@V-.f{nf8'2PiKUQZ P04+1ɀ0 1@)rG#FQG!"Q%ɱ1#ƴEASH0-Cj(=0UG$ (_L@(a( ԡEw*Jg1 EGŕnMyR|?(ziS6ᐫJ̒ffuou'E ZΒy7jnDT\{SB#!2- K3cCGXDpcs0FV(*Qg8'K0H*FYH^fm;KY\xOsxXl$F6rȝCVeXwѦ[*\(O6oPz{C}u څgPymzDԆ;C2EHLҀfLL <0@ *0ݐA=Q2N 8*%=BƼd S,@AiPsw X$X44ūh/D]h9^V\dCicJ`J$<5}.NcWs#^Ql2]_Xj/u(ת70*!Yqxg\cG4p \[L@xHL-11@rE$ɉ:Mokfw!&ZcVaSM^#>+U[3eSnc\Ӵ*y!5@ /UlCڟOUϽa.j{οS^s˷`qGv#V,6| 1 xF#!a`~`@`P3!Òal[בK2/i b%#Ʉ 'K p &Z@zVPf #((J,0c)/ @ZI` )%-^1uJv U"Q\(ƅ &T@vgs{߫}# ˒gʷ>k'VybP08)Q0070\60,0p" 鄀)`0@- RmfHFA7^VxpɁ1:HzHxh3E%qDvz ,OFM473&i)mZN9ӯ.jܳ$8}tqb"k Μ8(,6ͽALC\ቁ { y !8)V30nIոg x[G0./ * ܦ DB) OVH, !~ƀ]!P[ 5 A}d1HkȲ.(DvVP ZmCm O ʤ5{ku,Bܓzoe48Z4@+0'U _ h0 & bvdPf ``%a>3am2ǐD1ce >#d HQ٨$t5ZVfPޭ١g൵󕌠ĕ(ҺfHJ6HH7c)CL JC?6ɛm3i4Bɽ{u_Zno K]xUYSg`p`mڄ $WC49@87SCTր0`V`*`(+ O2N-P)1tHQaMaY `;Ze(u6_y#PJ6 mBrnI=n,SD%|iN)F^~$tIj֪uU ֚oZ~@hqL@|z`Nh`p0 B㉖%R L 1 `f -0 g00 ы.d1S7E|r?Lk)RA:$x˙B $FQP,먨/U.xP/k[վZqa l 7X0@*<[z002(e0(:10sYC GT3" ⃗1W2N )'pY8EJ Çߩ<&5BWN^Uf͹VIH )uFcIܞc{bL"; >6PuI:iTzыR)NS<;FùJrZ.RWݔ$ 2,h"xV A$ibႡh dBB2dþL}ٱ#Q!<4p?!F& HTJԱX%K̪̱Wt6܎Q uN@*=,A Xu.?MgŔ]?[˶5\*B!׹*&G61B5"5P`EA-1`! L`RO62db, шYW0N/JѪI!L>H$*6SD& &ǎ^;23^1G]D)n*7Pf寥ZM$ C h ?Hи3Lմ0$ ,*47bᮒdpT]| É}:gG:ȁ'n1\?nk p+(:c }IiZ%b$L 0I]|T%UW {:zV@SDMJ ?2s#A`Y.!(SyBPX(rH8U|m/hYuCPB"d:탛y,/moR*I! A>j΅ FLy":WR٘gm8ޝ+@4q ;m^Zzdt+q% 6Aȧ`6vg1Mm\{7MGoLE9뙦E)LVѭ_Ԙ`0|TۅH0 DB˺{ 41f.`d:[$J35s!P] $TIs16 8@jX0m,V䂂VلlҒ:hAr*,Җ9H*EYax;VmF}Ƒ7^6IU{W|\|MTGxT@cTezѫU , Š "pD¥I l(!!20A!42!8a`, _(S%`(xpόl:"G,ioJE!:*1  2ocï+6+}~F/%=w-&VU:&v;a xOj]O ˆ#efܵ~omnL?rl>fg`!xʶ޿eT}JM$ E'Q +00-LʌOϗaږ+? }u~um%ғm1ONզ?Uk=ݘhICgSli թX$0L.0,6=0H06A8koj ."Iz4ͦ Xs1 DH8Z(<*ҔYÀ- 8̊;҂ QPg: ,jN3$_D#  B02)SvXUeA$OH 1 AtX%4 L\p( L t9jFx%%a*m fE%ZP t`iVm-y&IC1"X0*^fbT 1`o@r#Q9%;,e6dYRgb$Bm,zR/H*(_^[x˼l'R׽{{igUZ)-pfH(`*]0_># 00 2($``+SPq[5Z}3#1 ad!Mh%g q!D2R) oXd5RX֊$$U s|N?%I:2ߙ%kray*[Z6/=*LpLzSUB@s 1LD[.XLD&<,Z) 5@Fk$c.i-C'%T3J ZVKHahDiM-}״0xp %894 ZXوm[[+V(~)ӆlgjWM}Niz#6Ő\BA0|d$ zIB L%D LUf,aNh,  ĉ&)*jmXp/ f lPaIPy 9`dqeRcM$*,ʏ1tB 9JV RFUL%x7ܥ U-YJ]Hh&zq|Z~k!~ߌU I5x)0Dr630)400=l80T58Bd3?6Ѱ 0(ViO0.z3%錵@_<PZd6@#Sh@1v (6iP/ 6ٜXTYj2`KGI bȃ"&|MvS%ZeIrPVG&M+\'{MjH) 蘵(wtE'g T!|ЎL@0p8Q5T[n>ouI#vN"|TjR`8wi^$6G't^ZFDSXy| :Ý+sE`mоyw b{E7alIcM^ÃK@tc)Z=ܬ2] c٘@R"jUL1Bс c L €)Z`͢RF"O.N)Q% 9vS-CEa~I+/xL<ѦFЈ Ԇ(Zkd( ,SN 0o9on,aT͸SY:왆Kf5uVΈ!Lػ&хiAiI( `S !`p80|sHPs@F)-D^TI(o#D匂֖}bv yԦW.Žj0)rA#DRb%!w2LI>R-w>Ye/Q܉I zF4ݣ GOJDw}67&vMӦZC(,521 qŦ#5'|PB0!:[=0 ߃_,.+)5fk,0_l-RCJ:*h"^4$ڱNYU:PKjq%0N!%z(aR=863ܓ@{iuqkx+ʺ؜I5ڌ!)`!BаD1`Py[ "C`0 *Wr^3T~^N3kZ@ޱ䕬PneYq߸JPy‚D؛Tv(KGa .2Z;=XFI'l9f?fNM x1Ju+fU-*&?y~٫6޴6!F:4j00{&iT)j/OTTsDbXt  AƓrQev&Ni+̵ďYev&D)E)C

C\1$A7i3^!v^;{vu 8o fa&b:$h5^$,1c *(P jn.H,I\-U&Zbҹd e4%y+H]0`oH8,t1) D&|7&VmZ˲nBbX)"*BH B1\Ie86Kjug-is^*v)9,ⒶrHn lJ)0(02bPip1fK`@oL 08QEFjhLA j XR]S)lҚ=ȩ_ȝ U?=cW:e?~aa/c{8p&&s8ugm: !ʯej)JBx!Pv>Zm52G yQ3:etpPrV8 A!QZ(4腅L8dsHAJ "--1%H nL6@k*,}eKlfJ578̵R 1INRe"JI%Ե֚KMrM6It]H#ugUZkuENĪKE"nnR U$ p |wp=wL  @ l KP 1̡ .@(pgFn 6K3Bn0`Th$(ʁ<&g +13'H` ;a@4L,َxuFR@@Q0,d)$l)x)oYBM˙qMZ̞{[YUf quFg9kiS(ϟ N# Ԧ yeYJۓ۫`(3";_2lp]AnrQܶ79t`Xz N;,K5F[pЍ:Bj;$VK5 4:K( \Ԇ86$p5S2ZA2)`TIXt R$`/pR]j=P"p*Gh  ,h=pl_h벿9 0̐pL=ZB]RI)e&*LAME3.96.1Ĥm_4LAME3.96.1 4TAGLa-Aaah (Flappin' Booty Mix)AndreasTolle Tunes2005tagpy-2013.1/test/mp3-framedumper.py0000644000175000017500000000040310564200273020331 0ustar andreasandreas00000000000000import tagpy import tagpy.mpeg f = tagpy.mpeg.File("la.mp3") t = f.ID3v2Tag() for frame_type in t.frameListMap().keys(): print frame_type frame_list = t.frameListMap()[frame_type] for frame in frame_list: print " %s" % frame.toString() tagpy-2013.1/test/tagprinter.py0000644000175000017500000000032010564200274017503 0ustar andreasandreas00000000000000import tagpy f = tagpy.FileRef("la.ogg") t = f.tag() print t.artist print t.title print t.album print t.year t.artist = "Andreas" t.title = "Laaa-ahh" t.album = "Shake what'cha got" t.year = 2006 f.save() tagpy-2013.1/test/tagrename0000755000175000017500000001246311166320213016651 0ustar andreasandreas00000000000000#! /usr/bin/env python import sys import os import os.path import re import tagpy import tagpy.mpeg import tagpy.ogg from optparse import OptionParser songs = sys.argv[1:] patterns = [ "${artist:7}-${album:7}/${track}-${title}.${ext}", "${artist}/${album}/${track}-${title}.${ext}", ] mix_pattern = "AAAMIX/${artist:7}-${title}.${ext}" opt_parse = OptionParser(usage="%prog [options] root_path") opt_parse.add_option("-p", "--pattern", dest="pattern", help="Use PATTERN as the rename pattern") opt_parse.add_option("-m", "--use-mix", action="store_true", dest="use_mix", help="Use aaamix directory") opt_parse.add_option("-M", "--no-use-mix", action="store_false", dest="use_mix", help="Do not use aaamix directory") (options, remaining_args) = opt_parse.parse_args() if options.pattern is not None: pattern = patterns[int(options.pattern)] else: for i, p in enumerate(patterns): print i, p print pattern = patterns[int(raw_input("Select pattern: "))] if options.use_mix is not None: use_mix = options.use_mix else: use_mix = raw_input("Use AAAMIX directory? [n]y?") == "y" def canonical_ext(song): if isinstance(song, tagpy.mpeg.File): aprops = song.audioProperties() if aprops is None: raise ValueError, "MPEG track %s has no audio properties" % song.name() return "mp%d" % aprops.layer elif isinstance(song, tagpy.ogg.File): return "ogg" else: raise ValueError, "unknown track type" def is_directory(name): dirstat = os.stat(name) import stat return stat.S_ISDIR(dirstat.st_mode) def force_us_ascii(str): result = "" for i in str: if 32 <= ord(i) < 128: result += chr(ord(i)) else: result += "_" return result def makedirs(name, mode=0777): # stolen and modified from os """makedirs(path [, mode=0777]) Super-mkdir; create a leaf directory and all intermediate ones. Works like mkdir, except that any intermediate path segment (not just the rightmost) will be created if it does not exist. This is recursive. """ head, tail = os.path.split(name) while not tail: head, tail = os.path.split(head) if head and tail and not os.path.exists(head): makedirs(head, mode) try: os.mkdir(name, mode) except OSError: pass def make_fn_suitable(str): return force_us_ascii(str .replace("/", "_") .replace("?", "_") .replace("*", "_") .replace("&", "_") .replace(":", "_") .replace("\"", "_") .replace("\'", "_") .replace("<", "_") .replace(">", "_") ) def expand_pattern(pattern, song): tag = song.tag() if tag is None: return None expr = re.compile(r"\$\{([a-z]+)(\:[0-9]+)?\}") def variable_function(match): meta_id = match.group(1) if meta_id == "artist": result = tag.artist elif meta_id == "album": result = tag.album elif meta_id == "title": result = tag.title elif meta_id == "ext": result = canonical_ext(song) elif meta_id == "track": result = "%02d" % tag.track else: raise ValueError, "invalid variable in pattern: "+meta_id if len(match.groups()) == 2 and match.group(2): result = result[:int(match.group(2)[1:])] return make_fn_suitable(result).strip() return expr.sub(variable_function, pattern) def get_new_dir_and_name(pattern, song): dest_name = expand_pattern(pattern, song) if dest_name is None: return None dest_dir = os.path.dirname(dest_name).lower() dest_basename = os.path.basename(dest_name) return dest_dir, os.path.join(dest_dir, dest_basename) # walk directory tree all_songs = [] for dirpath, dirnames, filenames in os.walk(remaining_args[0]): all_songs += [os.path.join(dirpath, filename) for filename in filenames] # find out all "canonical" renames, i.e. without mix directory dest_dir_counts = {} renames = [] for source_name in all_songs: try: song = tagpy.FileRef(source_name).file() except ValueError: print "WARNING: Not a media file:\n %s" % source_name continue dir_and_name = get_new_dir_and_name(pattern, song) if dir_and_name is not None: dest_dir, dest_name = get_new_dir_and_name(pattern, song) renames.append((source_name, dest_name, dest_dir)) dest_dir_counts[dest_dir] = 1 + dest_dir_counts.setdefault(dest_dir, 0) # judge mix directory and perform moves for source_name, dest_name, dest_dir in renames: if use_mix and dest_dir_counts[dest_dir] == 1: song = tagpy.FileRef(source_name).file() dest_dir, dest_name = get_new_dir_and_name(mix_pattern, song) try: if not is_directory(dest_dir): print "WARNING: %s exists, but is not a directory, skipping\n %s" % \ (dest_dir, dest_name) continue except OSError: makedirs(dest_dir) try: os.rename(source_name, dest_name) except OSError, e: print "Couldn't rename `%s' to `%s': %s" % (source_name, dest_name, e) tagpy-2013.1/test/la.ogg0000644000175000017500000004726211004401232016047 0ustar andreasandreas00000000000000OggSGr3qvorbisDwOggSGzi zvorbisXiph.Org libVorbis I 20040629ALBUM=Shake what'cha gotARTIST=Andreas DATE=2006TITLE=Laaa-ahhvorbis)BCV1L ŀАU`$)fI)(yHI)0c1c1c 4d( Ij9g'r9iN8 Q9 &cnkn)% Y@H!RH!b!b!r!r * 2 L2餓N:騣:(B -JL1Vc]|s9s9s BCV BdB!R)r 2ȀАU GI˱$O,Q53ESTMUUUUu]Wvevuv}Y[}Y[؅]aaaa}}} 4d #9)"9d ")Ifjihm˲,˲ iiiiiiifYeYeYeYeYeYeYeYeYeYeYeYeY@h*@@qq$ER$r, Y@R,r4Gs4sjQ'tUn~*PQ}6A`G_wz-a"|=:ZN+(qXQ1O(Ĝߨ,X$zgozQ!跜=)-tWࡪdY38и#óeh.yJP*8\6'DL>w9dG' !#7 vGd8Ƣy)VhD2t (F w_+vziluG!{qfrG%+H8=p @HkӇLt>^Tq| 4CtԥW!ٛHw WV'Ci)4peO+ĸZb@l .@!4 # ?0F-;AJH H.V$St),}һ1\䎍0kp+SshiqMAq[UP$N}Ziܭ<ߣLKʓLѲ,ĪMjƥ?ưV%}5` ֙~y|L޵Dépñ'[Fa9t6a@{փt "Э߬DMφ@g V{р`U_:gL`7r; ҿ4(EDhtǁp 'E!mQl:s̖ãoATzѭis:,iM1,˅RGOPA u ˥]X335~+ԮlW۬W' GNG)oa mSt'@|HA91>L 2ٚ[3..aҫ i֥?c]lB@j>!P4i%Z `[T֢͑tQFP.;JFީM>ؗi@~ДqTJ;vJ@:@va65|\R@C;w(nGjLhs*{mɻJ74Xt"q3S Kd4!O[TH˘|K"l8,U1^N!iU(bY,z; 9޹9Jy%0L"XfiЃWi(`ِw5g_-hnS/6K$5vⱨ#c}XND>EfcJX\="99R7k@[so,X͟CgZs9͖/Hl-$ij 7 o"wἎdNo5khyMVӚ,p9'F]XypbpgM9E6Tګ9}]oPoH t N3LVUh葽mAX*^ĸ4-i׌6zfb#9WXNjLorr10ʅ\MwRSVi ~)5`j SazW P4ET_@3:28|2lلTZ3t+..bUMMӒcjVwged;|t I[*3~h;"CSh qضW73R(mwt=Uj2Jk+|WIu)au }wb}g &FR{jzQCMI%3 e*&Y_>>P Pa7m"{kbBh| )KBdrNp:y'L}UݝTZS?T`eQ׏~ yV85_62-V DlR0 $+{WIOh;tgmG噲3* ,XKbNzl@zW `k@ #{!Z7Aޙ5xۋOć/-\4$$lIaXry2 YO=˙)Z(;5@Ѹj.vȘcI{[_q q56#|<=ׄ(5_D֜5wzqӰ)M K(n {[_) cdsoM2^va{-oүEKϴA-;GDWq׫Y^S|S`v9࠯$˥ciG b/U:5Hn/h/,[,_!;~*Bp1z7h&C<;:p\Sx-nf7_'P m '?s@*p_m_%֛Xz8۔@u'}>UE_NUJES1Y)VABa>%~]?qU;;,e9tMyv9/+p7 ޿)mH ɸRj:|ն\|bx/u/ h {z m]^ֽ~0$̇,j(p <ؐƴ$_yo X)r ,;S/ ]`E@p<0 ' D?%u$$9C͘QW)OAJC!hOc@rKRtyu4:9;8QcVRt6Ț)V߫meVP;7=Ÿ">` ' p4F-ku$B40WFz;d ĉo;A xšB *ٰEB"LfR #i)Z!1 uk)5v|h02: ի>Wpjj?oev(;#b)=1Q 8Ɂ81,3eU6ڕ~q#?,`44&q9g`Ҟͥ/x<'31Y5ֶYД7GkZz4r_}xoAȇh =A!aO?M߂SD҄r4 O4:Qul_=]}n玼A ?:K[y6o?Kd<f[2[Pwlr02[>z])*EY2+/Rg+6OggSG,,,34n'wͺRd=Y*jh &r<1iUg=C>wTMvaTͺJA go; v*:(rߢ!zIXgwȗMdfro8ۿZEWc%9tO=e;7iNjS Ĝ8&8_p@ts0ϧN49yVkJp4KF Qf(M&?mZ$5`vÏd$UWPPGY_X"tnoDm٬bZ}bO$Y[1# )-Cz;ZHM%l hj[%o4Qb9!AֽW3V_Ttf>55=}&|GsЁ~:5,`{A/(RL)v_ܺ -jķts=mܪ>f:>L ْZ~ MD/'({ !-Sb- ûb'xE}wzWl1~ UV'F1xM֢ ;:|T\F Jm@0`n*'sGbvr:t j#U*[v,-J-b0?gݰ偹@FP9{m0(ͤҟ 2faժD>z[pD##M8Š{2r]:{}ڀ6h6^E^٥6=>0]i 2N :$g mpH MS?pbeӪ+ؔO8"оp/5 ;TM(G{EX [%^I9~PT6~ ]bNJ~1A*m(r,X´Y8+WVxtf+5gRS[;EPFDn~e6{8b|_|Wa\Dp  M;ëS? h<2Be/F]@~0 !Br1q+%K 9E;2*V }ӊoSADI7fG& Hs8߭<țz"䴡RPҴC{] -ree%/pŷ;• ţkx/{ͰAhu% ͑4@=kYPR{BtrXnsXHX aftz*3@6)c΀APCH6:ɱw[&Ṕ (}-Q=w3DGp?׏YA@p"zN?LB'B"fx]ag;}fǻ{'v` Mv}҂ЀX!N&41Ŋ;X%x _F yeJ};{l6Z߳!\aXeǩ).X|ECl ӓ%*Px zFGr[6?ZPY]0i[%]ىEINb?0 Y;a9U[J1gye2bZۥB^I1,Zi" )bl~u%aiL6d&I$Nˌ J=K/o=͂…zLTV~;Ċ^x-2.!V-E+ɕDb\ q!a7B!]!T޸-ƘT 6mdӆ tlṡx'[M_ڴD9 }ɺٷ:u79>u-v5}>'kRL+&@gdǖ"ѳ'cƽDTӬ`Xe<ںPM*T9c򁸲Z}q .gmHS1X^k< b H^*dqtO~"20ܥF#9sRX=cW3,u&~5%v.f ]` Ec6Ak R @YrgGRHؤ{9`*U ?fROXώpJ@xGaf~"}ʼnfz߭Bg"5)}?Ji H{' ْrKҜ C&LJ+Vs$ OEpϘ&ozi״u lŸdn%ca&t^م#-ڊ@cϚЍUIJ) 0] ưֆ(Ced"u_I:2㹟<5JFW%~P 1a[yΠh~: + 8^2RSVloyƅ=S[x()2o:,l vgq-}D~( N.R־s5X/^-<" 8+&*@&y82 0=ʡ(߆L! zӊǫsHq{?TCS&u`mMiF<}{Ng"OOs"Op{YȮ[H [u͖xV/9ニ]Da,ι̋ڶ+ M>Ԍ?W0^-d15t`:x 2F[3gC1x-፫@\ݺHAږdrV~5`?@BzZB(Gt6N=m[ގA9C18T5'fTUBx2W &'9Ƶ0i/ZnTCde0Tc]?H];Zvt B y5%G<{sxTia4 Ȣa`VZ16]]]3SzuvcdgierQq{6OfdðrG,Gmnga ?xdE VDZOeGRTwu9 ըR@҅Ž|8x)nt/#Rx 6L<^euw;I k~6Va? @Ŧ'H" XSgΰ#AˁUt$v3N߲rL^+J _>޼d <x,pxlΘ_T3oXW !^44$=AoĂpU3!oQr`fjIYnmBc'#4}ʳođqҲJ x5%v&$fTX(qq` D#,p'ޕ%[սzbSdѷlzxeֺ|P=В6k zKKJ eg[`gЗ":j .%SnF! Xl^Jfŧ\@P xr@U=(t݅3.ɀS뎟c+h'r^-֑4!C`$DkpmW5ȡ2R'Z95Z)9Jn|"4SbH" m hYV$pyFaodkS*_CD'n;gZ d4?5ƥ:W.ޕFr>up3X^6/<2tJ+$ kExe(*4veʷU Pr8wU8hAl1bp8s&rUxDXi^,]#Ρlǝ-=eb9/Nb1PζqhC`W{5eA3*Ib >$ڟVF5nB3fВ"ܰ/OggSGtӊў%bN$d`Rp K'Ja5jr0ZQ18cl$PtI > X3X6TTzE棏Vj#L1"fz)1P?PaaߕH4FZJ0S@n&grF8?  ~)t(k{p#˿~H *Ft7FTF/]OX^H*O4 A.69eu񬨸fTa r .~tC]0 \nN9e(=w#nuYklu^u NiO%}5(k\w^ۂ0 gfes3@yz'7jWb|р m#9qZZHFLT敢k->d;ŨYj2tIJhK X 33D4>zJ5|$O0"N< K&E 9rWP Ԛi)&^ɍ6bM39X];0q#PK3=\5ZKn@c\:`Mڠ}:-ug#$9#ɴm9O, ugX֖sh:AjRV ͫ"tmw^κ;U sC]lNMz:"Wڭ,0&Mm7g2?? fpl4i^˚,bҒyQDqҮ$m XWy:t|'>OkyȄvjcR)y8 gjvjP.Pi$66b?WQp$qawtXU6M<#}:|yN3/ (:M+.{z]ƒʤn^Y1h^45ۯaќ>5q._=]I/֙&Ptg@.NbHS^t{6&2"@ =[,S"U(;'I't~|~TIH29A`^1[bL <k(YFDCIC9{[hUo}Z8+ъM \ ~) (J$ n*/ RAEqP~Jޝ 4y ~2{f$n,]v.ZIRE+u 0IYO@b7$D%)xKǍ?ya ]ABފ=Uh:dȡ^R[+]f~h'[h{sa1#IW"JR0"{ ~uyOZ@ J 1N1`If> w$2ޖ4H"Yzo߿ZBew/%;c<Noe/ 6P8Ym\ve%:9) ^ٟnk^sKV= @[Rn_G)j=l4ӎD|foq/8S13PcQ zPã>ٕ[@VQzNBg)R pW3a OLDk#y X:"La +Bͻn >?$@as߀YAGs0$q<>W@oq@HѴ.Ar~kcB#>ݑ@>d@>$[]Euk۹~ٕ֙x3x(~ #ۓ h@Rnm&+U%~.Xl5}S1Pֆ+=@tNdnP\{Jh+uBz|iNk'Z =$mD&5K){'XmpbFaN52uTVH@OG6"xFhـYц>{ѓ^e  0: |JSvcPdM*/5c}/eIxL_BnC- KY..}P ^As:[jp;qPL1v~ STsXG9S GiP(Ғv˵ v07|5y)TzL?M*b"\l4F\aeN~i,M @7I9 kZUZםa󣔲t;56Gc~ЃAsH_]MՉs5gd~fTf;n{ٮ#ю<2x*+Qm=n-<@I@)JMfD F""|O~w c6=4~hDiÁ㛁 >م%>HIv6p_D G m:23S?)MH= $|n5(t\ԙe*Z^lwP7ۆbrh'#^( ;Es '"zN3B.Z8Qkۣ2o1Ws6JF2` vl*<W es~UVe#GVs(6? ~5%zQ@xy`Cl&x yh43`W{&Վ\(-[)EMTJ#6 // Bx+8|grP#YlEȷV4#1ɚ8h?YLELxTVO@bxڄ ƫ :S"uWuairR¬W-n V/jSŅCiP~5L~@ ;B;(B38)`px,r.FQMQ?T&O)(XJ1K=~Hӵ6lEuu躒-I`|JbvyW?Hpr#f w{5 j-HM?|_㍙雹CnT5tK| 9d<|I>x/OC~.:P@MP=ڼLjc^T@Ha]*>k8YĽެJ.2A>meHJ Ӽh:?}Y9u\4 }wۂt(5Eq-GBMlQ^(/o~mFjyb&3:m˙zT66ljII+=bx8uo$j_{du#ۯ;;$@ "|`HV\B6khsWe k$cۓ$:l)(0Jp(J<.:^SpROqBv:XQtsebA8#"W`:C[w 0k9X>Tۑ~ov]IûW1%J%DIxܲ+Gk: ږ#vXo(= eyf2%ݦ_F+f4i,Pߜ7rV.ᰢp:c_ڻ6Z&OggSG|~6]v󙒣$h/:HHe5 ;-+ .E*H"D-@M S>wFk[C3ܭzuL~W@ TH\H?Y!1(:h0_GGs$cIѣN*UW[+q)vݖ+_hu)+tp!"w5*fidwd0_[Fd晎Kw$:=Cv9HS+NZ*!'^g'2.{=[,Gf%_4 fН4]RJPQr 6C,!anʸEC Pέ*3@ޠZH#ji-K]24@뿻2ZDֶR$g#{'u5Vfw*JW3Z#o륃'Xab RM 4!z:F,-*Aڽ~"o#ru2u[l2cEv!5*5Kf߇"1k~= ^þC#D X]EY3SO :$2ʐq6T—9[ 2Qi*C*ެqa0!l`wh⁒ ,y!Mzb֓4 I\F*-+;p8< JuǬʢ.CZ(_=>n6G ^5>%xc{] },6|V^>(O =i `׉eyzYOG%/`GQ^/0*^q/qǤXN4Tm>'Ѐn$:t*,B{ ^{?cg Ef~ C2тҝkYޘ{O؃?NH$5ʣЊp`.d?K;ʙaY-#6 ~1c}^r{|Vqff($S#T`G &x< ̮MwٴNHk}T\btx#~hM[{\EGVQ'A7ӥӜ]$.ބ\emڍfgpؕq00Y_16QM.?n7X̠M$&v[`@'!\<1ʘ :I$x(lOfC< 2un!j7|^ 6x}gC6IhPGuп]T̢̞V|!@R9? 8D{WZk *vV4yqۡ^{=JTiIw~9M0K1 ekV"}ke7Ō029]t|&&iO]Tř{IbtG'ig7W5#łSZ}[fҤcPS 2?'ɼ5]%?[fȠu^m>U6AāFT~1Qoϗ6Ll_;5E #22k&jvɦ2W_ |o.!'D#BM"଻|W2<ͼDw aTmK5n y2%%"(G2JNE+yvfkpHsBaT\isy\.T"W4HDicJ_@ Ȗ:Jv3~wgd~D=S4Ƭj %k j1ɰԸ< ? >)z`~"ՐXK597T:?7ɰOljYnTMJI=X+]m5BB)w@ջhI4 m8m|:vX?;ȣ3N7xWU2jl|4=ûB̹ ̽Fnϝv=hr ɴh400/ >hV U>l34hhў?[/t*d;6TUmZtqjVw7!;~)ַK] na[v '(4ʦHG ~#Y@#nO38"Uۑzi)el/A[Ep+ Vu՘AhW9:9RL~{I:`a|LQ,oƔs~I0JŨf F׺ycO5_6SWd2\ IϡogsUYp3+Mɹf!3db.08EK3b-*Q$gwgTTAl s*tc"Cjb^ e(kdoax=4y7;M^4-&cxM["tagpy-2013.1/tagpy/0000755000175000017500000000000012067051706015130 5ustar andreasandreas00000000000000tagpy-2013.1/tagpy/mpc.py0000644000175000017500000000222611004161167016254 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy TagTypes = _tagpy.mpc_TagTypes File = _tagpy.mpc_File tagpy-2013.1/tagpy/ogg/0000755000175000017500000000000012067051706015704 5ustar andreasandreas00000000000000tagpy-2013.1/tagpy/ogg/__init__.py0000644000175000017500000000230311004161342017777 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy FieldListMap = _tagpy.ogg_FieldListMap XiphComment = _tagpy.ogg_XiphComment File = _tagpy.ogg_File tagpy-2013.1/tagpy/ogg/vorbis.py0000644000175000017500000000217611004161365017561 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy File = _tagpy.ogg_vorbis_File tagpy-2013.1/tagpy/ogg/flac.py0000644000175000017500000000217411004161352017154 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy File = _tagpy.ogg_flac_File tagpy-2013.1/tagpy/__init__.py0000644000175000017500000000701312063225163017236 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner, Christoph Burgmer # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy from _tagpy import Tag, File, AudioProperties, StringType, ReadStyle #AudioProperties.ReadStyle = _tagpy.ReadStyle class FileTypeResolver(object): def createFile(self, fileName, readAudioProperties=True, audioPropertiesStyle=ReadStyle.Average): raise NotImplementedError class FileRef(object): fileTypeResolvers = [] def __init__(self, f, readAudioProperties=True, audioPropertiesStyle=ReadStyle.Average): if isinstance(f, FileRef): self._file = f._file elif isinstance(f, File): self._file = f else: self._file = FileRef.create(f, readAudioProperties, audioPropertiesStyle) def tag(self): return self._file.tag() def audioProperties(self): return self._file.audioProperties() def file(self): return self._file def save(self): return self._file.save() @classmethod def defaultFileExtensions(cls): return cls._getExtToModule.keys() def isNull(self): return not self._file or not self._file.isValid() @classmethod def addFileTypeResolver(cls, resolver): cls.fileTypeResolvers.insert(0, resolver) return resolver @staticmethod def _getExtToModule(): import tagpy.ogg.vorbis import tagpy.ogg.flac import tagpy.mpeg, tagpy.flac, tagpy.mpc #import tagpy.wavpack, tagpy.ogg.speex, tagpy.trueaudio return { "ogg": tagpy.ogg.vorbis, "mp3": tagpy.mpeg, "oga": tagpy.ogg.flac, "flac": tagpy.flac, "mpc": tagpy.mpc, #".wv": tagpy.wavpack, #".spx": tagpy.ogg.speex, #".tta": tagpy.trueaudio, } @classmethod def create(cls, fileName, readAudioProperties=True, audioPropertiesStyle=ReadStyle.Average): for resolver in cls.fileTypeResolvers: file = resolver.createFile(fileName, readAudioProperties, audioPropertiesStyle) if file: return file from os.path import splitext ext = splitext(fileName)[1][1:].lower() try: module = cls._getExtToModule()[ext] except KeyError: raise ValueError("unable to find file type") return module.File(fileName, readAudioProperties, audioPropertiesStyle) tagpy-2013.1/tagpy/id3v2.py0000644000175000017500000000401211437207526016431 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy FrameFactory = _tagpy.id3v2_FrameFactory Frame = _tagpy.id3v2_Frame FrameListMap = _tagpy.id3v2_FrameListMap FrameList = _tagpy.id3v2_FrameList Header = _tagpy.id3v2_Header ExtendedHeader = _tagpy.id3v2_ExtendedHeader Footer = _tagpy.id3v2_Footer Tag = _tagpy.id3v2_Tag AttachedPictureFrameType = _tagpy.id3v2_AttachedPictureFrame_Type AttachedPictureFrame = _tagpy.id3v2_AttachedPictureFrame CommentsFrame = _tagpy.id3v2_CommentsFrame PeakVolume = _tagpy.id3v2_PeakVolume ChannelType = _tagpy.id3v2_RelativeVolumeFrame_ChannelType RelativeVolumeFrame = _tagpy.id3v2_RelativeVolumeFrame TextIdentificationFrame = _tagpy.id3v2_TextIdentificationFrame UserTextIdentificationFrame = _tagpy.id3v2_UserTextIdentificationFrame UniqueFileIdentifierFrame = _tagpy.id3v2_UniqueFileIdentifierFrame UnkownFrame = _tagpy.id3v2_UnknownFrame UnknownFrame = _tagpy.id3v2_UnknownFrame UnsynchronizedLyricsFrame = _tagpy.id3v2_UnsynchronizedLyricsFrame tagpy-2013.1/tagpy/ape.py0000644000175000017500000000231011004161130016222 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy ItemTypes = _tagpy.ape_ItemTypes Item = _tagpy.ape_Item Footer = _tagpy.ape_Footer Tag = _tagpy.ape_Tag tagpy-2013.1/tagpy/mpeg.py0000644000175000017500000000015310564200274016425 0ustar andreasandreas00000000000000import _tagpy TagTypes = _tagpy.mpeg_TagTypes File = _tagpy.mpeg_File Properties = _tagpy.mpeg_Properties tagpy-2013.1/tagpy/flac.py0000644000175000017500000000217011004161143016372 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy File = _tagpy.flac_File tagpy-2013.1/tagpy/id3v1.py0000644000175000017500000000301111004161153016407 0ustar andreasandreas00000000000000# Copyright (c) 2006-2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import _tagpy def _genreList(): result = [] i = 0 next = _tagpy.id3v1_genre(i) while next: result.append(next) i += 1 next = _tagpy.id3v1_genre(i) return result _GenreList = _genreList() _GenreMap = dict([(v, k) for k, v in enumerate(_GenreList)]) genre = _tagpy.id3v1_genre def genreIndex(genre): return _GenreMap[genre] def genreList(): return _GenreList def genreMap(): return _GenreMap tagpy-2013.1/MANIFEST.in0000644000175000017500000000034311265663250015544 0ustar andreasandreas00000000000000include test/la.* include test/*.py include src/wrapper/common.hpp include test/tagrename include LICENSE include distribute_setup.py include configure.py include Makefile.in include aksetup_helper.py include README_SETUP.txt tagpy-2013.1/setup.cfg0000644000175000017500000000007312067051706015625 0ustar andreasandreas00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 tagpy-2013.1/LICENSE0000644000175000017500000000205211004160600014770 0ustar andreasandreas00000000000000Copyright (c) 2006-2008 Andreas Kloeckner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tagpy-2013.1/setup.py0000644000175000017500000001102312066400271015505 0ustar andreasandreas00000000000000#!/usr/bin/env python # -*- coding: latin-1 -*- # Copyright (c) 2006-2008 Andreas Kloeckner, Christoph Burgmer # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. def get_config_schema(): from aksetup_helper import ConfigSchema, Option, \ IncludeDir, LibraryDir, Libraries, BoostLibraries, \ Switch, StringListOption, make_boost_base_options return ConfigSchema(make_boost_base_options() + [ BoostLibraries("python"), IncludeDir("TAGLIB", []), LibraryDir("TAGLIB", []), Libraries("TAGLIB", ["tag"]), StringListOption("CXXFLAGS", [], help="Any extra C++ compiler options to include"), ]) def main(): import glob from aksetup_helper import hack_distutils, get_config, setup, Extension hack_distutils() conf = get_config(get_config_schema()) INCLUDE_DIRS = conf["TAGLIB_INC_DIR"] + conf["BOOST_INC_DIR"] LIBRARY_DIRS = conf["TAGLIB_LIB_DIR"] + conf["BOOST_LIB_DIR"] LIBRARIES = conf["TAGLIB_LIBNAME"] + conf["BOOST_PYTHON_LIBNAME"] setup(name="tagpy", version="2013.1", description="Python Bindings for TagLib", long_description=""" TagPy is a set of Python bindings for Scott Wheeler's `TagLib `_. It builds upon `Boost.Python `_, a wrapper generation library which is part of the renowned Boost set of C++ libraries. Just like TagLib, TagPy can: * read and write ID3 tags of version 1 and 2, with many supported frame types for version 2 (in MPEG Layer 2 and MPEG Layer 3, FLAC and MPC), * access Xiph Comments in Ogg Vorbis Files and Ogg Flac Files, * access APE tags in Musepack and MP3 files. All these features have their own specific interfaces, but TagLib's generic tag reading and writing mechanism is also supported. It comes with a bunch of examples. """, author="Andreas Kloeckner", author_email="inform@tiker.net", classifiers= [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Multimedia :: Sound/Audio", "Topic :: Multimedia :: Sound/Audio :: CD Audio :: CD Ripping", "Topic :: Multimedia :: Sound/Audio :: Editors", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities"], license = "MIT", url="http://mathema.tician.de/software/tagpy", packages=["tagpy", "tagpy.ogg"], ext_modules=[ Extension("_tagpy", ["src/wrapper/basics.cpp", "src/wrapper/id3.cpp", "src/wrapper/rest.cpp"], include_dirs=INCLUDE_DIRS, library_dirs=LIBRARY_DIRS, libraries=LIBRARIES, extra_compile_args=conf["CXXFLAGS"], ), ] ) if __name__ == '__main__': main()