halfway-decent (i hope) Python extension modules.
+++ /dev/null
-extern "C" {
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-
-};
-
-#include <attachedpictureframe.h>
-#include <id3v2tag.h>
-#include <mpegfile.h>
-
-int main(int argc, char *argv[])
-{
- struct stat st;
- stat(argv[2], &st);
- int fd = open(argv[2], O_RDONLY);
- char *m = (char *)malloc(st.st_size);
- read(fd, m, st.st_size);
-
- TagLib::MPEG::File f(argv[1]);
- TagLib::ID3v2::AttachedPictureFrame *p;
-
- p = new TagLib::ID3v2::AttachedPictureFrame;
- p->setMimeType("image/jpeg");
- p->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
- p->setPicture(TagLib::ByteVector((const char *)m, st.st_size));
- f.ID3v2Tag()->addFrame(p);
-
- f.save();
-
- return 0;
-}
--- /dev/null
+#include <FLAC/metadata.h>
+
+#include <Python.h>
+
+static PyObject *flac_error;
+
+static PyObject *
+flac_pictures(char *fn)
+{
+ FLAC__Metadata_Chain *chain;
+ FLAC__Metadata_Iterator *iterator;
+ FLAC__StreamMetadata *block;
+ PyObject *tuple, *list;
+
+ chain = FLAC__metadata_chain_new();
+ if (chain == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ if(!FLAC__metadata_chain_read(chain, fn)) {
+ PyObject *s = PyString_FromFormat("metadata_chain_read (%d)",
+ FLAC__metadata_chain_status(chain));
+ if (s != NULL) {
+ PyErr_SetObject(flac_error, s);
+ }
+ return NULL;
+ }
+
+ iterator = FLAC__metadata_iterator_new();
+ if (iterator == NULL) {
+ return PyErr_NoMemory();
+ }
+
+ FLAC__metadata_iterator_init(iterator, chain);
+
+ list = PyList_New(0);
+ if (list == NULL) {
+ return NULL;
+ }
+
+ do {
+ /* This can fail, but the documentation doesn't say how, why,
+ * or how to react. metaflac/operations.c just prints a
+ * useless error message and keeps going. */
+ block = FLAC__metadata_iterator_get_block(iterator);
+ if (block == NULL) {
+ PyErr_SetString(flac_error,
+ "metadata_iterator_get_block returned NULL");
+ return NULL;
+ }
+
+ if (block->type != FLAC__METADATA_TYPE_PICTURE) {
+ continue;
+ }
+
+ tuple = Py_BuildValue("(s#issiiii)",
+ block->data.picture.data,
+ block->data.picture.data_length,
+ block->data.picture.type,
+ block->data.picture.mime_type,
+ block->data.picture.description,
+ block->data.picture.width,
+ block->data.picture.height,
+ block->data.picture.depth,
+ block->data.picture.colors);
+ if (tuple == NULL) {
+ return NULL;
+ }
+
+ if (PyList_Append(list, tuple) == -1) {
+ return NULL;
+ }
+ } while(FLAC__metadata_iterator_next(iterator));
+
+ FLAC__metadata_iterator_delete(iterator);
+ FLAC__metadata_chain_delete(chain);
+
+ return list;
+}
+
+static PyObject *
+get_pictures(PyObject *self, PyObject *args)
+{
+ char *fn;
+
+ if (!PyArg_ParseTuple(args, "s", &fn)) {
+ return NULL;
+ }
+
+ return flac_pictures(fn);
+}
+
+static
+PyMethodDef methods[] = {
+ {"get_pictures", get_pictures, METH_VARARGS,
+ "document me"},
+
+ {NULL, NULL, 0, NULL}
+};
+
+static void
+export_constants(PyObject *m)
+{
+ PyObject *n;
+
+#define foo(x) \
+ n = PyInt_FromLong(FLAC__STREAM_METADATA_ ## x); \
+ if (n == NULL || PyModule_AddObject(m, #x, n) == -1) return;
+
+ foo(PICTURE_TYPE_OTHER);
+ foo(PICTURE_TYPE_FILE_ICON_STANDARD);
+ foo(PICTURE_TYPE_FILE_ICON);
+ foo(PICTURE_TYPE_FRONT_COVER);
+ foo(PICTURE_TYPE_BACK_COVER);
+ foo(PICTURE_TYPE_LEAFLET_PAGE);
+ foo(PICTURE_TYPE_MEDIA);
+ foo(PICTURE_TYPE_LEAD_ARTIST);
+ foo(PICTURE_TYPE_ARTIST);
+ foo(PICTURE_TYPE_CONDUCTOR);
+ foo(PICTURE_TYPE_BAND);
+ foo(PICTURE_TYPE_COMPOSER);
+ foo(PICTURE_TYPE_LYRICIST);
+ foo(PICTURE_TYPE_RECORDING_LOCATION);
+ foo(PICTURE_TYPE_DURING_RECORDING);
+ foo(PICTURE_TYPE_DURING_PERFORMANCE);
+ foo(PICTURE_TYPE_VIDEO_SCREEN_CAPTURE);
+ foo(PICTURE_TYPE_FISH);
+ foo(PICTURE_TYPE_ILLUSTRATION);
+ foo(PICTURE_TYPE_BAND_LOGOTYPE);
+ foo(PICTURE_TYPE_PUBLISHER_LOGOTYPE);
+ foo(PICTURE_TYPE_UNDEFINED);
+
+#undef foo
+}
+
+PyMODINIT_FUNC
+initflac(void)
+{
+ PyObject *m = Py_InitModule("flac", methods);
+ if (m == NULL) {
+ return;
+ }
+
+ flac_error = PyErr_NewException("org.diplodocus.flac.error", NULL, NULL);
+ if (flac_error == NULL) {
+ return;
+ }
+ PyModule_AddObject(m, "error", flac_error);
+
+ export_constants(m);
+}
-from distutils.core import setup
+import os
+
+from distutils.core import Extension, setup
+from subprocess import Popen, PIPE
+
+taglib_config = os.getenv('TAGLIB_CONFIG', 'taglib-config')
+taglib_cflags = Popen([taglib_config, '--cflags'],
+ stdout=PIPE).communicate()[0].strip().split()
+taglib_libs = Popen([taglib_config, '--libs'],
+ stdout=PIPE).communicate()[0].strip().split()
setup(name='flac-archive',
version='6',
'fa-flacd',
'fa-rip',
'flac2mp3',
- ]
+ ],
+
+ ext_modules=[
+ Extension(name='org.diplodocus.flac', sources=['flac.c'],
+ extra_link_args=['-lFLAC'],
+ ),
+ Extension(name='org.diplodocus.taglib', sources=['taglib.cc'],
+ extra_compile_args=taglib_cflags,
+ extra_link_args=taglib_libs
+ ),
+ ],
)
+++ /dev/null
-diff -Naur -x '*~' flac-1.1.3-beta2/src/metaflac/.gdb_history mine/src/metaflac/.gdb_history
---- flac-1.1.3-beta2/src/metaflac/.gdb_history 1970-01-01 00:00:00.000000000 +0000
-+++ mine/src/metaflac/.gdb_history 2006-10-28 08:24:22.000000000 +0000
-@@ -0,0 +1,2 @@
-+set height 0
-+set width 0
-diff -Naur -x '*~' flac-1.1.3-beta2/src/metaflac/operations.c mine/src/metaflac/operations.c
---- flac-1.1.3-beta2/src/metaflac/operations.c 2006-09-28 18:18:45.000000000 +0000
-+++ mine/src/metaflac/operations.c 2006-10-28 08:39:01.000000000 +0000
-@@ -359,6 +359,7 @@
- case OP__EXPORT_CUESHEET_TO:
- ok = do_shorthand_operation__cuesheet(filename, chain, operation, needs_write);
- break;
-+ case OP__EXPORT_PICTURE:
- case OP__IMPORT_PICTURE:
- ok = do_shorthand_operation__picture(filename, chain, operation, needs_write);
- break;
-diff -Naur -x '*~' flac-1.1.3-beta2/src/metaflac/operations_shorthand_picture.c mine/src/metaflac/operations_shorthand_picture.c
---- flac-1.1.3-beta2/src/metaflac/operations_shorthand_picture.c 2006-10-10 00:09:39.000000000 +0000
-+++ mine/src/metaflac/operations_shorthand_picture.c 2006-10-29 08:15:36.000000000 +0000
-@@ -16,6 +16,9 @@
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-+#include <fcntl.h>
-+#include <unistd.h>
-+
- #if HAVE_CONFIG_H
- # include <config.h>
- #endif
-@@ -29,11 +32,35 @@
-
- static FLAC__bool import_pic_from(const char *filename, FLAC__StreamMetadata **picture, const char *specification, FLAC__bool *needs_write);
-
-+/* Write all of buf, even if write(2) is interrupted. */
-+static ssize_t
-+full_write(int fd, const char *buf, size_t nbytes)
-+{
-+ ssize_t r, w = 0;
-+
-+ /* Loop until nbytes of buf have been written. */
-+ while (w < nbytes) {
-+ /* Keep trying until write succeeds without interruption. */
-+ do {
-+ r = write(fd, buf + w, nbytes - w);
-+ } while (r < 0 && errno == EINTR);
-+
-+ if (r < 0) {
-+ return r;
-+ }
-+
-+ w += r;
-+ }
-+
-+ return w;
-+}
-+
- FLAC__bool do_shorthand_operation__picture(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write)
- {
- FLAC__bool ok = true, has_type1 = false, has_type2 = false;
- FLAC__StreamMetadata *picture = 0;
- FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
-+ FLAC__StreamMetadata *block;
-
- if(0 == iterator)
- die("out of memory allocating iterator");
-@@ -54,6 +81,43 @@
- }
- }
- break;
-+ case OP__EXPORT_PICTURE:
-+ do {
-+ block = FLAC__metadata_iterator_get_block(iterator);
-+ ok &= (0 != block);
-+ if(!ok) {
-+ fprintf(stderr, "%s: ERROR: couldn't get block from chain\n", filename);
-+ continue;
-+ }
-+ if (block->type != FLAC__METADATA_TYPE_PICTURE)
-+ continue;
-+ if (block->data.picture.type != FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER)
-+ continue;
-+ int fd = open(operation->argument.specification.value, O_WRONLY | O_EXCL | O_CREAT, 0644);
-+ if (fd < 0) {
-+ perror("open");
-+ ok = 0;
-+ break;
-+ }
-+
-+ if(0 != block->data.picture.data)
-+ if (full_write(fd, block->data.picture.data, block->data.picture.data_length) != block->data.picture.data_length) {
-+ perror("write");
-+ ok = 0;
-+ break;
-+ }
-+ if (fsync(fd) < 0) {
-+ perror("fsync");
-+ ok = 0;
-+ break;
-+ }
-+ if (close(fd) < 0) {
-+ perror("close");
-+ ok = 0;
-+ break;
-+ }
-+ } while(ok && FLAC__metadata_iterator_next(iterator));
-+ break;
- default:
- ok = false;
- FLAC__ASSERT(0);
-diff -Naur -x '*~' flac-1.1.3-beta2/src/metaflac/options.c mine/src/metaflac/options.c
---- flac-1.1.3-beta2/src/metaflac/options.c 2006-09-26 00:52:23.000000000 +0000
-+++ mine/src/metaflac/options.c 2006-10-28 10:47:20.000000000 +0000
-@@ -72,6 +72,7 @@
- { "export-tags-to", 1, 0, 0 },
- { "import-cuesheet-from", 1, 0, 0 },
- { "export-cuesheet-to", 1, 0, 0 },
-+ { "stupid-export-picture", 1, 0, 0 },
- { "import-picture", 1, 0, 0 },
- { "add-seekpoint", 1, 0, 0 },
- { "add-replay-gain", 0, 0, 0 },
-@@ -547,6 +548,14 @@
- ok = false;
- }
- }
-+ else if(0 == strcmp(opt, "stupid-export-picture")) {
-+ op = append_shorthand_operation(options, OP__EXPORT_PICTURE);
-+ FLAC__ASSERT(0 != option_argument);
-+ if(!parse_string(option_argument, &(op->argument.specification.value))) {
-+ fprintf(stderr, "ERROR (--%s): missing specification\n", opt);
-+ ok = false;
-+ }
-+ }
- else if(0 == strcmp(opt, "import-picture")) {
- op = append_shorthand_operation(options, OP__IMPORT_PICTURE);
- FLAC__ASSERT(0 != option_argument);
-diff -Naur -x '*~' flac-1.1.3-beta2/src/metaflac/options.h mine/src/metaflac/options.h
---- flac-1.1.3-beta2/src/metaflac/options.h 2006-09-25 15:21:49.000000000 +0000
-+++ mine/src/metaflac/options.h 2006-10-28 07:50:44.000000000 +0000
-@@ -60,6 +60,7 @@
- OP__EXPORT_VC_TO,
- OP__IMPORT_CUESHEET_FROM,
- OP__EXPORT_CUESHEET_TO,
-+ OP__EXPORT_PICTURE,
- OP__IMPORT_PICTURE,
- OP__ADD_SEEKPOINT,
- OP__ADD_REPLAY_GAIN,
--- /dev/null
+#include <attachedpictureframe.h>
+#include <id3v2tag.h>
+#include <mpegfile.h>
+
+extern "C" {
+
+#include <errno.h>
+
+#include <Python.h>
+
+static PyObject *taglib_error;
+
+static PyObject *
+taglib_apic(const char *fn, const char *image, int len, const char *type)
+{
+ TagLib::ByteVector *bv;
+ TagLib::ID3v2::AttachedPictureFrame *p;
+ TagLib::MPEG::File *f;
+ TagLib::ID3v2::Tag *t;
+
+ bv = new TagLib::ByteVector (image, len);
+ if (bv == 0) {
+ return PyErr_NoMemory();
+ }
+
+ p = new TagLib::ID3v2::AttachedPictureFrame;
+ if (p == 0) {
+ return PyErr_NoMemory();
+ }
+
+ /* Um, look at all these void-returning functions. I'm sure they
+ * can fail, i just have no way to detect it... */
+ p->setMimeType(type);
+ p->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
+ p->setPicture(*bv);
+
+ /* GAH! Opening the file is entangled with allocating the object;
+ * if this fails, am i out of memory, or is there a problem with
+ * the file? Hate C++... */
+ f = new TagLib::MPEG::File (fn);
+ if (f == 0) {
+ return PyErr_NoMemory();
+ }
+
+ t = f->ID3v2Tag(true);
+ if (t == 0) {
+ PyErr_SetString(taglib_error, "taglib failed to return ID3v2");
+ return 0;
+ }
+
+ // another void-returning function that i bet fails
+ t->addFrame(p);
+
+ errno = 0;
+ if (!f->save()) {
+ if (errno != 0) {
+ PyErr_SetFromErrnoWithFilename(PyExc_IOError, (char *)fn);
+ } else {
+ PyErr_SetString(taglib_error, "taglib failed to save");
+ }
+ return 0;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *
+add_apic_frame_to_mp3(PyObject *self, PyObject *args)
+{
+ const char *fn, *image, *type;
+ int len;
+
+ if (!PyArg_ParseTuple(args, "ss#s", &fn, &image, &len, &type)) {
+ return 0;
+ }
+
+ return taglib_apic(fn, image, len, type);
+}
+
+static
+PyMethodDef methods[] = {
+ {"add_apic_frame_to_mp3", add_apic_frame_to_mp3, METH_VARARGS,
+ "document me"},
+ {0, 0, 0, 0}
+};
+
+PyMODINIT_FUNC
+inittaglib(void)
+{
+ PyObject *m = Py_InitModule("taglib", methods);
+ if (m == NULL) {
+ return;
+ }
+
+ taglib_error = PyErr_NewException("org.diplodocus.taglib.error", 0, 0);
+ if (taglib_error == 0) {
+ return;
+ }
+ PyModule_AddObject(m, "error", taglib_error);
+}
+
+}