parent
90fdea7686
commit
cfc76f12ed
|
@ -0,0 +1,122 @@
|
|||
A loader for ID3v2 picture tags in audio files -*- text -*-
|
||||
----------------------------------------------
|
||||
|
||||
INTRODUCTION
|
||||
------------
|
||||
|
||||
ID3v2 is a format used to embed metadata within popular audio formats like
|
||||
mp3. ID3v2 officially describes a wide range of metadata that could possibly be
|
||||
associated with some audio/music, like Artist, Album, Composer etc. (apart from
|
||||
being extensible to allow nonstandard arbitrary metadata as well) These pieces
|
||||
of data are held as `frames' in an ID3v2 tag in the audio file. One such type of
|
||||
frame is the `Associated Picture' (APIC) frame used to store pictures associated
|
||||
with the audio (like say, the picture of the lead artist). There could be
|
||||
multiple such frames in a file. Visit http://www.id3.org for more information.
|
||||
|
||||
Well, I suppose that makes the purpose of this image loader clear. This loader
|
||||
depends on the libid3tag library (atleast version 0.15). This is a helper
|
||||
library for the MAD, a GPL'd high quality MPEG audio decoder, and can be found
|
||||
in the same place as MAD. (http://www.underbit.com/products/mad)
|
||||
|
||||
KEY SYNTAX
|
||||
----------
|
||||
|
||||
The filename to be passed to the loader, is as in imlib2 convention, of the form
|
||||
`filename.mp3:key'. The key is a comma separated list of `parameter=value'
|
||||
pairs, spaces are significant. Any item without an `=' is treated as the value
|
||||
for the parameter `index'. In case of repetition, the last occurrence is
|
||||
used. The supported parameters are:
|
||||
|
||||
+----------+--------------------------------------------------------------------+
|
||||
| index |Numeric value. 1-based index of picture frame to be extracted. |
|
||||
| |Defaults to 1, the first frame. |
|
||||
+----------+--------------------------------------------------------------------+
|
||||
| |An image loaded by the loader can be made to contain a `next' image |
|
||||
| |tag containing the full filename:key string of some other image in |
|
||||
| |the file, when this parameter in non-zero. In such a case, the next |
|
||||
| traverse |tag points to `index+N'th image of the file, where N is the value |
|
||||
| |given here. Common values include `1', for the next image, and `-1' |
|
||||
| |for the previous image. Default is `1' if there is no key for the |
|
||||
| |image to be loaded, and `0' otherwise. |
|
||||
+----------+--------------------------------------------------------------------+
|
||||
| |An opaque integer got from the `context' tag of an existing |
|
||||
| |image. See the section below on the tags provided with each |
|
||||
| |image. `tag' here refers to data which can be attached to any imlib |
|
||||
| |image, and is not to be confused with ID3 tags. The context of an |
|
||||
| |existing image passed as a parameter indicates that both images come|
|
||||
| |from the same file. This saves some work for the loader in such a |
|
||||
| context |case. This parameter is normally not necessary, as such a case |
|
||||
| |usually occurs when the filename of both the images are the same, |
|
||||
| |and only the key differs, and this case is taken care of by the |
|
||||
| |loader automatically. Obscure cases of the same tag for different |
|
||||
| |filenames (like, symlinks for example) can be taken care by this |
|
||||
| |parameter. A context is valid as long as any of the images using it |
|
||||
| |are valid. |
|
||||
+----------+--------------------------------------------------------------------+
|
||||
|
||||
TAGS OUTPUT
|
||||
-----------
|
||||
|
||||
+---------------+---------------------------------------------------------------+
|
||||
| index |1-based index of picture frame in the file which the image |
|
||||
| |refers to. |
|
||||
+---------------+---------------------------------------------------------------+
|
||||
| count |The total number of picture frames present in the file to which|
|
||||
| |this picture frame belongs. |
|
||||
+---------------+---------------------------------------------------------------+
|
||||
| |An opaque id corresponding to the ID3v2 tag from which the |
|
||||
| context |picture came. Images from the same tag have the same |
|
||||
| |context. See the context parameter above for a possible use. |
|
||||
+---------------+---------------------------------------------------------------+
|
||||
| |A filename:key string of a related image in the same ID3v2 tag,|
|
||||
| |as required by the `traverse' argument passed for the |
|
||||
| next |file. This imlib tag is absent when traverse is 0, and it's |
|
||||
| |value is NULL when no image frame satisfying the given traverse|
|
||||
| |value is present (like traverse=1 called with the last image |
|
||||
| |frame). |
|
||||
+---------------+---------------------------------------------------------------+
|
||||
| mime-type |The mime type of the picture rendered. |
|
||||
+---------------+---------------------------------------------------------------+
|
||||
| id3-picture |A string describing the picture type, amongst a set of |
|
||||
| -type |predefined types described by the ID3v2 standard. |
|
||||
+---------------+---------------------------------------------------------------+
|
||||
| |An integer between 0-3 describing the text encoding used by the|
|
||||
| |id3-description imlib tag. The string in the tag gives the |
|
||||
| |corresponding encoding name. The possible values are: |
|
||||
| +---+-----------------------------------------------------------+
|
||||
|id3-description| 0 | ISO-8859-1 |
|
||||
|-text-encoding +---+-----------------------------------------------------------+
|
||||
| | 1 | UTF-16 encoded Unicode with BOM |
|
||||
| +---+-----------------------------------------------------------+
|
||||
| | 2 | UTF-16BE encoded Unicode without BOM |
|
||||
| +---+-----------------------------------------------------------+
|
||||
| | 3 | UTF-8 encoded Unicode |
|
||||
+---------------+---+-----------------------------------------------------------+
|
||||
| |A string describing the image, as given in the ID3 tag. Note |
|
||||
| |that this is different from any comment that might be present |
|
||||
|id3-description|in the image itself, as allowed by the image format. The |
|
||||
| |encoding used for the string is given by the imlib tag |
|
||||
| |id3-description-text-encoding. |
|
||||
+---------------+---------------------------------------------------------------+
|
||||
|
||||
Eg: to process all images in a file, the following pseudocode should work:
|
||||
|
||||
loadname = `foo.mp3' // with no key
|
||||
do {
|
||||
image = load-image (loadname)
|
||||
// do whatever you want with the image
|
||||
loadname = imlib-tag (image, `next')
|
||||
} while (loadname != NULL)
|
||||
|
||||
|
||||
PERFORMANCE ISSUES
|
||||
------------------
|
||||
|
||||
The loader parses picture data from audio files and caches them, as long as any
|
||||
of the images using the data exist. This is useful when there are many images in
|
||||
the same file, as loading subsequent images use this cache. So, caching is
|
||||
turned off when there is only one image in the file, and turned on
|
||||
otherwise. When memory consumption is of importance, developers likely to use
|
||||
this loader frequently need to keep this in mind when deciding on the imlib
|
||||
cache size - with caching on, as long as the image remains in use or in cache,
|
||||
this parsed data stays along.
|
|
@ -9,31 +9,286 @@
|
|||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "image.h"
|
||||
|
||||
#include <id3tag.h>
|
||||
|
||||
int extract_pic (struct id3_frame* frame, int dest, char* ext, int extlen)
|
||||
#if ! defined (__STDC_VERSION__) || __STDC_VERSION__ < 199901L
|
||||
# if __GNUC__ >= 2
|
||||
# define inline __inline__
|
||||
# else
|
||||
# define inline
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define UNLIKELY(exp) __builtin_expect ((exp), 0)
|
||||
#else
|
||||
# define UNLIKELY(exp) (exp)
|
||||
#endif
|
||||
|
||||
typedef struct context
|
||||
{
|
||||
int id;
|
||||
char* filename;
|
||||
struct id3_tag* tag;
|
||||
int refcount;
|
||||
struct context* next;
|
||||
} context;
|
||||
|
||||
static context* id3_ctxs = NULL;
|
||||
|
||||
static inline struct id3_frame*
|
||||
id3_tag_get_frame (struct id3_tag* tag, size_t index)
|
||||
{
|
||||
return tag->frames[index];
|
||||
}
|
||||
|
||||
static inline size_t id3_tag_get_numframes (struct id3_tag* tag)
|
||||
{
|
||||
return tag->nframes;
|
||||
}
|
||||
|
||||
static inline char const* id3_frame_id (struct id3_frame* frame)
|
||||
{
|
||||
return frame->id;
|
||||
}
|
||||
|
||||
static context* context_create (const char* filename)
|
||||
{
|
||||
context* node = (context*) malloc (sizeof (context));
|
||||
context *ptr, *last;
|
||||
int last_id = INT_MAX;
|
||||
node->refcount = 1;
|
||||
{
|
||||
struct id3_file* file;
|
||||
struct id3_tag* tag;
|
||||
unsigned int i;
|
||||
file = id3_file_open (filename, ID3_FILE_MODE_READONLY);
|
||||
if (! file) {
|
||||
fprintf (stderr, "Unable to open tagged file %s: %s\n",
|
||||
filename, strerror (errno));
|
||||
id3_file_close (file);
|
||||
goto fail_free;
|
||||
}
|
||||
tag = id3_file_tag (file);
|
||||
if (! tag) {
|
||||
fprintf (stderr,
|
||||
"Unable to find ID3v2 tags in file %s\n",
|
||||
filename);
|
||||
id3_file_close (file);
|
||||
goto fail_free;
|
||||
}
|
||||
node->tag = id3_tag_new ();
|
||||
for (i = 0; i < id3_tag_get_numframes (tag); i ++)
|
||||
if (! strcmp (id3_frame_id
|
||||
(id3_tag_get_frame (tag, i)), "APIC"))
|
||||
id3_tag_attachframe (node->tag,
|
||||
id3_tag_get_frame (tag, i));
|
||||
id3_file_close (file);
|
||||
}
|
||||
node->filename = strdup (filename);
|
||||
if (! id3_ctxs) {
|
||||
node->id = 1;
|
||||
node->next = NULL;
|
||||
id3_ctxs = node;
|
||||
return node;
|
||||
}
|
||||
ptr = id3_ctxs;
|
||||
last = NULL;
|
||||
while (UNLIKELY (ptr && (ptr->id + 1) >= last_id)) {
|
||||
last_id = ptr->id;
|
||||
last = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
/* Paranoid! this can occur only if there are INT_MAX contexts :) */
|
||||
if (UNLIKELY (ptr == NULL)) {
|
||||
fprintf (stderr, "Too many open ID3 contexts\n");
|
||||
goto fail_close;
|
||||
}
|
||||
node->id = ptr->id + 1;
|
||||
if (UNLIKELY (last != NULL)) {
|
||||
node->next = last->next;
|
||||
last->next = node;
|
||||
} else {
|
||||
node->next = id3_ctxs;
|
||||
id3_ctxs = node;
|
||||
}
|
||||
return node;
|
||||
|
||||
fail_close:
|
||||
free (node->filename);
|
||||
id3_tag_delete (node->tag);
|
||||
fail_free:
|
||||
free (node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void context_destroy (context* ctx)
|
||||
{
|
||||
id3_tag_delete (ctx->tag);
|
||||
free (ctx->filename);
|
||||
free (ctx);
|
||||
}
|
||||
|
||||
static inline void context_addref (context* ctx)
|
||||
{
|
||||
ctx->refcount ++;
|
||||
}
|
||||
|
||||
static context* context_get (int id)
|
||||
{
|
||||
context* ptr = id3_ctxs;
|
||||
while (ptr) {
|
||||
if (ptr->id == id) {
|
||||
context_addref (ptr);
|
||||
return ptr;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
fprintf (stderr, "No context by handle %d found\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static context* context_get_by_name (const char* name)
|
||||
{
|
||||
context* ptr = id3_ctxs;
|
||||
while (ptr) {
|
||||
if (! strcmp (name, ptr->filename)) {
|
||||
context_addref (ptr);
|
||||
return ptr;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void context_delref (context* ctx)
|
||||
{
|
||||
ctx->refcount --;
|
||||
if (ctx->refcount <= 0) {
|
||||
context *last = NULL, *ptr = id3_ctxs;
|
||||
while (ptr) {
|
||||
if (ptr == ctx) {
|
||||
if (last)
|
||||
last->next = ctx->next;
|
||||
else
|
||||
id3_ctxs = ctx->next;
|
||||
context_destroy (ctx);
|
||||
return;
|
||||
}
|
||||
last = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int str2int (char* str, int old)
|
||||
{
|
||||
long index;
|
||||
errno = 0;
|
||||
index = strtol (str, NULL, 10);
|
||||
return ((errno || index > INT_MAX) ? old : (int)index);
|
||||
}
|
||||
|
||||
static size_t str2uint (char* str, size_t old)
|
||||
{
|
||||
unsigned long index;
|
||||
errno = 0;
|
||||
index = strtoul (str, NULL, 10);
|
||||
return ((errno || index > UINT_MAX) ? old : (size_t)index);
|
||||
}
|
||||
|
||||
static void destructor_data (ImlibImage* im, void* data)
|
||||
{
|
||||
free (data);
|
||||
}
|
||||
|
||||
static void destructor_context (ImlibImage* im, void* data)
|
||||
{
|
||||
context_delref ((context*)data);
|
||||
}
|
||||
|
||||
typedef struct lopt
|
||||
{
|
||||
context* ctx;
|
||||
size_t index;
|
||||
int traverse;
|
||||
char cache_level;
|
||||
} lopt;
|
||||
|
||||
static char get_options (lopt* opt, ImlibImage* im)
|
||||
{
|
||||
size_t handle = 0, index = 0, traverse = 0;
|
||||
context* ctx;
|
||||
|
||||
if (im->key) {
|
||||
char* key = strdup (im->key);
|
||||
char* tok = strtok (key, ",");
|
||||
traverse = 0;
|
||||
while (tok) {
|
||||
char* value = strchr (tok, '=');
|
||||
if (! value) {
|
||||
value = tok;
|
||||
tok = "index";
|
||||
} else {
|
||||
*value = '\0';
|
||||
value ++;
|
||||
}
|
||||
if (! strcasecmp (tok, "index"))
|
||||
index = str2uint (value, index);
|
||||
else if (! strcasecmp (tok, "context"))
|
||||
handle = str2uint (value, handle);
|
||||
else if (! strcasecmp (tok, "traverse"))
|
||||
traverse = str2int (value, traverse);
|
||||
tok = strtok (NULL, ",");
|
||||
}
|
||||
free (key);
|
||||
} else
|
||||
traverse = 1;
|
||||
|
||||
if (! handle) {
|
||||
ImlibImageTag* htag = __imlib_GetTag (im, "context");
|
||||
if (htag && htag->val)
|
||||
handle = htag->val;
|
||||
}
|
||||
if (handle)
|
||||
ctx = context_get (handle);
|
||||
else if (! (ctx = context_get_by_name (im->real_file)) &&
|
||||
! (ctx = context_create (im->real_file)))
|
||||
return 0;
|
||||
|
||||
if (! index) {
|
||||
ImlibImageTag* htag = __imlib_GetTag (im, "index");
|
||||
if (htag && htag->val)
|
||||
index = htag->val;
|
||||
}
|
||||
if (index < 0 || index > id3_tag_get_numframes (ctx->tag) ||
|
||||
(index == 0 && id3_tag_get_numframes (ctx->tag) < 1)) {
|
||||
if (index)
|
||||
fprintf (stderr, "No picture frame # %d found\n", index);
|
||||
context_delref (ctx);
|
||||
return 0;
|
||||
}
|
||||
if (! index)
|
||||
index = 1;
|
||||
|
||||
opt->ctx = ctx;
|
||||
opt->index = index;
|
||||
opt->traverse = traverse;
|
||||
opt->cache_level = (id3_tag_get_numframes (ctx->tag) > 1 ? 1 : 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int extract_pic (struct id3_frame* frame, int dest)
|
||||
{
|
||||
union id3_field* field;
|
||||
unsigned char const * data;
|
||||
long length;
|
||||
int done = 0;
|
||||
|
||||
field = id3_frame_field (frame, 1);
|
||||
data = id3_field_getlatin1 (field);
|
||||
if (! data) {
|
||||
fprintf (stderr, "No mime type data found for image frame\n");
|
||||
return 0;
|
||||
}
|
||||
if (strncmp (data, "image/", 6)) {
|
||||
fprintf (stderr,
|
||||
"Picture frame with unknown mime-type %s found\n",
|
||||
data);
|
||||
return 0;
|
||||
}
|
||||
strncpy (ext, data + 6, extlen);
|
||||
field = id3_frame_field (frame, 4);
|
||||
data = id3_field_getbinarydata (field, &length);
|
||||
if (! data) {
|
||||
|
@ -54,85 +309,229 @@ int extract_pic (struct id3_frame* frame, int dest, char* ext, int extlen)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Loader for ID3v2 tags in audio files.
|
||||
* ID3v2 allows for 'Attached Picture' frames to be embedded in the file.
|
||||
* A numeric key is supported, and indicates the zero-based frame index
|
||||
* to be extracted, in case more than one such frame is found.
|
||||
* Defaults to 0, or the first picture frame.
|
||||
*/
|
||||
#define EXT_LEN 14
|
||||
|
||||
static char get_loader (lopt* opt, ImlibLoader** loader)
|
||||
{
|
||||
union id3_field* field;
|
||||
unsigned char const * data;
|
||||
char ext[EXT_LEN + 2];
|
||||
|
||||
ext[EXT_LEN + 1] = '\0';
|
||||
ext[0] = '.';
|
||||
|
||||
field = id3_frame_field (id3_tag_get_frame (opt->ctx->tag,
|
||||
opt->index - 1), 1);
|
||||
data = id3_field_getlatin1 (field);
|
||||
if (! data) {
|
||||
fprintf (stderr, "No mime type data found for image frame\n");
|
||||
return 0;
|
||||
}
|
||||
if (strncasecmp (data, "image/", 6)) {
|
||||
if (! strcmp (data, "-->")) {
|
||||
*loader = NULL;
|
||||
return 1;
|
||||
}
|
||||
fprintf (stderr,
|
||||
"Picture frame with unknown mime-type \'%s\' found\n",
|
||||
data);
|
||||
return 0;
|
||||
}
|
||||
strncpy (ext + 1, data + 6, EXT_LEN);
|
||||
if (! (*loader = __imlib_FindBestLoaderForFile (ext, 0))) {
|
||||
fprintf (stderr, "No loader found for extension %s\n", ext);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char* id3_pic_types [] = {
|
||||
/* $00 */ "Other",
|
||||
/* $01 */ "32x32 pixels file icon",
|
||||
/* $02 */ "Other file icon",
|
||||
/* $03 */ "Cover (front)",
|
||||
/* $04 */ "Cover (back)",
|
||||
/* $05 */ "Leaflet page",
|
||||
/* $06 */ "Media",
|
||||
/* $07 */ "Lead artist/lead performer/soloist",
|
||||
/* $08 */ "Artist/performer",
|
||||
/* $09 */ "Conductor",
|
||||
/* $0A */ "Band/Orchestra",
|
||||
/* $0B */ "Composer",
|
||||
/* $0C */ "Lyricist/text writer",
|
||||
/* $0D */ "Recording Location",
|
||||
/* $0E */ "During recording",
|
||||
/* $0F */ "During performance",
|
||||
/* $10 */ "Movie/video screen capture",
|
||||
/* $11 */ "A bright coloured fish",
|
||||
/* $12 */ "Illustration",
|
||||
/* $13 */ "Band/artist logotype",
|
||||
/* $14 */ "Publisher/Studio logotype"
|
||||
};
|
||||
|
||||
static char* id3_text_encodings [] = {
|
||||
/* $00 */ "ISO-8859-1",
|
||||
/* $01 */ "UTF-16 encoded Unicode with BOM",
|
||||
/* $02 */ "UTF-16BE encoded Unicode without BOM",
|
||||
/* $03 */ "UTF-8 encoded Unicode"
|
||||
};
|
||||
|
||||
static void write_tags (ImlibImage* im, lopt* opt)
|
||||
{
|
||||
struct id3_frame* frame
|
||||
= id3_tag_get_frame (opt->ctx->tag, opt->index - 1);
|
||||
union id3_field* field;
|
||||
int num_data;
|
||||
char* data;
|
||||
|
||||
if ((field = id3_frame_field (frame, 1)) &&
|
||||
(data = (char*) id3_field_getlatin1 (field)))
|
||||
__imlib_AttachTag (im, "mime-type", 0,
|
||||
strdup (data), destructor_data);
|
||||
if ((field = id3_frame_field (frame, 3)) &&
|
||||
(data = (char*) id3_field_getstring (field))) {
|
||||
size_t length;
|
||||
char* dup;
|
||||
id3_ucs4_t* ptr = (id3_ucs4_t*)data;
|
||||
while (*ptr)
|
||||
ptr ++;
|
||||
length = (ptr - (id3_ucs4_t*)data + 1) * sizeof (id3_ucs4_t);
|
||||
dup = (char*) malloc (length);
|
||||
memcpy (dup, data, length);
|
||||
__imlib_AttachTag (im, "id3-description", 0,
|
||||
dup, destructor_data);
|
||||
}
|
||||
if (field = id3_frame_field (frame, 0))
|
||||
__imlib_AttachTag (im, "id3-description-text-encoding",
|
||||
(num_data = (int)
|
||||
id3_field_gettextencoding (field)),
|
||||
num_data < sizeof (id3_text_encodings) ?
|
||||
id3_text_encodings[num_data] : NULL, NULL);
|
||||
if (field = id3_frame_field (frame, 2))
|
||||
__imlib_AttachTag (im, "id3-picture-type",
|
||||
(num_data = id3_field_getint (field)),
|
||||
num_data < sizeof (id3_pic_types) ?
|
||||
id3_pic_types[num_data] : NULL, NULL);
|
||||
__imlib_AttachTag (im, "count", id3_tag_get_numframes (opt->ctx->tag),
|
||||
NULL, NULL);
|
||||
if (opt->cache_level) {
|
||||
context_addref (opt->ctx);
|
||||
__imlib_AttachTag (im, "context", opt->ctx->id,
|
||||
opt->ctx, destructor_context);
|
||||
}
|
||||
__imlib_AttachTag (im, "index", opt->index, NULL, NULL);
|
||||
if (opt->traverse) {
|
||||
char* buf = NULL;
|
||||
if ((opt->index + opt->traverse)
|
||||
<= id3_tag_get_numframes (opt->ctx->tag)
|
||||
&& (opt->index + opt->traverse) > 0) {
|
||||
buf = (char*) malloc
|
||||
((strlen (im->real_file) + 50) * sizeof (char));
|
||||
sprintf (buf, "%s:index=%d,traverse=%d", im->real_file,
|
||||
opt->index + opt->traverse, opt->traverse);
|
||||
}
|
||||
__imlib_AttachTag (im, "next", 0, buf, destructor_data);
|
||||
}
|
||||
}
|
||||
|
||||
char load (ImlibImage *im, ImlibProgressFunction progress,
|
||||
char progress_granularity, char immediate_load)
|
||||
{
|
||||
ImlibLoader *loader;
|
||||
char *file, tmp[] = "/tmp/imlib2_loader_id3-XXXXXX", *p;
|
||||
char real_ext[16];
|
||||
int res, dest, pic_index = 0;
|
||||
struct id3_file* tagfile;
|
||||
struct id3_tag* tag;
|
||||
struct id3_frame* frame;
|
||||
lopt opt;
|
||||
int res;
|
||||
|
||||
assert (im);
|
||||
|
||||
p = strrchr(im->real_file, '.');
|
||||
if (! (p && p != im->real_file && !strcmp (p + 1, "mp3"))) {
|
||||
if (! get_options (&opt, im))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (im->key)
|
||||
pic_index = atoi (im->key);
|
||||
if (! get_loader (&opt, &loader))
|
||||
goto fail_context;
|
||||
|
||||
tagfile = id3_file_open (im->real_file, ID3_FILE_MODE_READONLY);
|
||||
if (! tagfile) {
|
||||
fprintf (stderr, "Unable to open tagged file %s: %s",
|
||||
im->real_file, strerror (errno));
|
||||
return 0;
|
||||
}
|
||||
tag = id3_file_tag (tagfile);
|
||||
if (! tag) {
|
||||
fprintf (stderr, "Unable to find ID3v2 tags in file\n");
|
||||
return 0;
|
||||
}
|
||||
frame = id3_tag_findframe (tag, "APIC", pic_index);
|
||||
if (! frame) {
|
||||
fprintf (stderr, "No picture frame # %d found\n", pic_index);
|
||||
return 0;
|
||||
}
|
||||
if (loader) {
|
||||
char *ofile, tmp[] = "/tmp/imlib2_loader_id3-XXXXXX";
|
||||
int dest;
|
||||
|
||||
if ((dest = mkstemp (tmp)) < 0) {
|
||||
fprintf (stderr, "Unable to create a temporary file\n");
|
||||
id3_file_close (tagfile);
|
||||
return 0;
|
||||
}
|
||||
if ((dest = mkstemp (tmp)) < 0) {
|
||||
fprintf (stderr, "Unable to create a temporary file\n");
|
||||
goto fail_context;
|
||||
}
|
||||
res = extract_pic (id3_tag_get_frame (opt.ctx->tag,
|
||||
opt.index - 1), dest);
|
||||
close (dest);
|
||||
|
||||
real_ext[15] = '\0';
|
||||
real_ext[0] = '.';
|
||||
res = extract_pic (frame, dest, real_ext + 1, 14);
|
||||
close (dest);
|
||||
id3_file_close (tagfile);
|
||||
if (! res) {
|
||||
unlink (tmp);
|
||||
goto fail_context;
|
||||
}
|
||||
|
||||
ofile = im->real_file;
|
||||
im->real_file = strdup (tmp);
|
||||
res = loader->load (im, progress,
|
||||
progress_granularity, immediate_load);
|
||||
free (im->real_file);
|
||||
im->real_file = ofile;
|
||||
|
||||
if (!res) {
|
||||
unlink (tmp);
|
||||
return 0;
|
||||
} else {
|
||||
/* The tag actually provides a image url rather than image data.
|
||||
* Practically, dunno if such a tag exists on earth :)
|
||||
* Here's the code anyway...
|
||||
*/
|
||||
union id3_field* field;
|
||||
long length;
|
||||
char const* data;
|
||||
char *url, *file, *ofile;
|
||||
|
||||
field = id3_frame_field
|
||||
(id3_tag_get_frame (opt.ctx->tag, opt.index - 1), 4);
|
||||
data = (char const*) id3_field_getbinarydata (field, &length);
|
||||
if (! data || ! length) {
|
||||
fprintf (stderr, "No link image URL present\n");
|
||||
goto fail_context;
|
||||
}
|
||||
url = (char*) malloc ((length + 1) * sizeof (char));
|
||||
strncpy (url, data, length);
|
||||
url[length] = '\0';
|
||||
file = (strncmp (url, "file://", 7) ? url : url + 7);
|
||||
if (! (loader = __imlib_FindBestLoaderForFile (file, 0))) {
|
||||
fprintf (stderr, "No loader found for file %s\n", file);
|
||||
free (url);
|
||||
goto fail_context;
|
||||
}
|
||||
ofile = im->real_file;
|
||||
im->real_file = file;
|
||||
res = loader->load (im, progress,
|
||||
progress_granularity, immediate_load);
|
||||
if (! im->loader)
|
||||
__imlib_AttachTag (im, "id3-link-url", 0,
|
||||
url, destructor_data);
|
||||
else
|
||||
free (url);
|
||||
im->real_file = ofile;
|
||||
}
|
||||
|
||||
if (!(loader = __imlib_FindBestLoaderForFile (real_ext, 0))) {
|
||||
fprintf (stderr, "No loader found for extension %s\n", real_ext);
|
||||
unlink (tmp);
|
||||
return 0;
|
||||
if (! im->loader)
|
||||
write_tags (im, &opt);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (! im->loader) {
|
||||
ImlibImageTag* cur = im->tags;
|
||||
fprintf (stderr, "Tags for file %s:\n", im->file);
|
||||
while (cur) {
|
||||
fprintf (stderr, "\t%s: (%d) %s\n", cur->key,
|
||||
cur->val, (char*) cur->data);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* remember the original filename */
|
||||
file = strdup (im->real_file);
|
||||
context_delref (opt.ctx);
|
||||
return res;
|
||||
|
||||
free (im->real_file);
|
||||
im->real_file = strdup (tmp);
|
||||
loader->load (im, progress, progress_granularity, immediate_load);
|
||||
|
||||
free (im->real_file);
|
||||
im->real_file = file;
|
||||
unlink (tmp);
|
||||
|
||||
return 1;
|
||||
fail_context:
|
||||
context_delref (opt.ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void formats (ImlibLoader *l)
|
||||
|
|
Loading…
Reference in New Issue