meta.c 9.99 KB
Newer Older
1 2 3
/*****************************************************************************
 * meta.c : Metadata handling
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 1998-2004 VLC authors and VideoLAN
5 6 7 8 9
 * $Id$
 *
 * Authors: Antoine Cellerier <dionoea@videolan.org>
 *          Clément Stenac <zorglub@videolan.org
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
10 11 12
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
13 14 15 16
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
19
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
20 21 22
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24
 *****************************************************************************/

25 26 27 28
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

29 30
#include <assert.h>

31
#include <vlc_common.h>
Clément Stenac's avatar
Clément Stenac committed
32
#include <vlc_playlist.h>
33
#include <vlc_url.h>
34
#include <vlc_arrays.h>
35
#include <vlc_modules.h>
36
#include <vlc_charset.h>
37

38
#include "input_internal.h"
39
#include "../playlist/art.h"
40

41 42 43
struct vlc_meta_t
{
    char * ppsz_meta[VLC_META_TYPE_COUNT];
44

45
    vlc_dictionary_t extra_tags;
46

47 48 49
    int i_status;
};

50
/* FIXME bad name convention */
51
const char * vlc_meta_TypeToLocalizedString( vlc_meta_type_t meta_type )
52
{
53
    static const char posix_names[][18] =
54
    {
55 56 57 58 59 60 61 62 63 64 65 66
        [vlc_meta_Title]       = N_("Title"),
        [vlc_meta_Artist]      = N_("Artist"),
        [vlc_meta_Genre]       = N_("Genre"),
        [vlc_meta_Copyright]   = N_("Copyright"),
        [vlc_meta_Album]       = N_("Album"),
        [vlc_meta_TrackNumber] = N_("Track number"),
        [vlc_meta_Description] = N_("Description"),
        [vlc_meta_Rating]      = N_("Rating"),
        [vlc_meta_Date]        = N_("Date"),
        [vlc_meta_Setting]     = N_("Setting"),
        [vlc_meta_URL]         = N_("URL"),
        [vlc_meta_Language]    = N_("Language"),
67
        [vlc_meta_ESNowPlaying]= N_("Now Playing"),
68 69 70 71 72
        [vlc_meta_NowPlaying]  = N_("Now Playing"),
        [vlc_meta_Publisher]   = N_("Publisher"),
        [vlc_meta_EncodedBy]   = N_("Encoded by"),
        [vlc_meta_ArtworkURL]  = N_("Artwork URL"),
        [vlc_meta_TrackID]     = N_("Track ID"),
73 74 75 76 77 78
        [vlc_meta_TrackTotal]  = N_("Number of Tracks"),
        [vlc_meta_Director]    = N_("Director"),
        [vlc_meta_Season]      = N_("Season"),
        [vlc_meta_Episode]     = N_("Episode"),
        [vlc_meta_ShowName]    = N_("Show Name"),
        [vlc_meta_Actors]      = N_("Actors"),
79
        [vlc_meta_AlbumArtist] = N_("Album Artist"),
80
        [vlc_meta_DiscNumber]  = N_("Disc number")
81
    };
Rafaël Carré's avatar
Rafaël Carré committed
82

83 84
    assert (meta_type < (sizeof(posix_names) / sizeof(posix_names[0])));
    return vlc_gettext (posix_names[meta_type]);
Rafaël Carré's avatar
Rafaël Carré committed
85
};
86

87 88 89 90

/**
 * vlc_meta contructor.
 * vlc_meta_Delete() will free the returned pointer.
91
 */
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
vlc_meta_t *vlc_meta_New( void )
{
    vlc_meta_t *m = (vlc_meta_t*)malloc( sizeof(*m) );
    if( !m )
        return NULL;
    memset( m->ppsz_meta, 0, sizeof(m->ppsz_meta) );
    m->i_status = 0;
    vlc_dictionary_init( &m->extra_tags, 0 );
    return m;
}

/* Free a dictonary key allocated by strdup() in vlc_meta_AddExtra() */
static void vlc_meta_FreeExtraKey( void *p_data, void *p_obj )
{
    VLC_UNUSED( p_obj );
    free( p_data );
}

void vlc_meta_Delete( vlc_meta_t *m )
{
112
    for( int i = 0; i < VLC_META_TYPE_COUNT ; i++ )
113 114 115 116 117 118 119 120 121
        free( m->ppsz_meta[i] );
    vlc_dictionary_clear( &m->extra_tags, vlc_meta_FreeExtraKey, NULL );
    free( m );
}

/**
 * vlc_meta has two kinds of meta, the one in a table, and the one in a
 * dictionary.
 * FIXME - Why don't we merge those two?
122
 */
123 124 125 126

void vlc_meta_Set( vlc_meta_t *p_meta, vlc_meta_type_t meta_type, const char *psz_val )
{
    free( p_meta->ppsz_meta[meta_type] );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
127
    assert( psz_val == NULL || IsUTF8( psz_val ) );
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    p_meta->ppsz_meta[meta_type] = psz_val ? strdup( psz_val ) : NULL;
}

const char *vlc_meta_Get( const vlc_meta_t *p_meta, vlc_meta_type_t meta_type )
{
    return p_meta->ppsz_meta[meta_type];
}

void vlc_meta_AddExtra( vlc_meta_t *m, const char *psz_name, const char *psz_value )
{
    char *psz_oldvalue = (char *)vlc_dictionary_value_for_key( &m->extra_tags, psz_name );
    if( psz_oldvalue != kVLCDictionaryNotFound )
        vlc_dictionary_remove_value_for_key( &m->extra_tags, psz_name,
                                            vlc_meta_FreeExtraKey, NULL );
    vlc_dictionary_insert( &m->extra_tags, psz_name, strdup(psz_value) );
}

const char * vlc_meta_GetExtra( const vlc_meta_t *m, const char *psz_name )
{
    return (char *)vlc_dictionary_value_for_key(&m->extra_tags, psz_name);
}

unsigned vlc_meta_GetExtraCount( const vlc_meta_t *m )
{
    return vlc_dictionary_keys_count(&m->extra_tags);
}

char** vlc_meta_CopyExtraNames( const vlc_meta_t *m )
{
    return vlc_dictionary_all_keys(&m->extra_tags);
}

/**
 * vlc_meta status (see vlc_meta_status_e)
 */
163
int vlc_meta_GetStatus( vlc_meta_t *m )
164 165 166 167
{
    return m->i_status;
}

168
void vlc_meta_SetStatus( vlc_meta_t *m, int status )
169 170 171 172 173 174 175 176 177 178 179 180
{
    m->i_status = status;
}


/**
 * Merging meta
 */
void vlc_meta_Merge( vlc_meta_t *dst, const vlc_meta_t *src )
{
    if( !dst || !src )
        return;
181

182
    for( int i = 0; i < VLC_META_TYPE_COUNT; i++ )
183 184 185 186 187 188 189
    {
        if( src->ppsz_meta[i] )
        {
            free( dst->ppsz_meta[i] );
            dst->ppsz_meta[i] = strdup( src->ppsz_meta[i] );
        }
    }
190

191
    /* XXX: If speed up are needed, it is possible */
192
    char **ppsz_all_keys = vlc_dictionary_all_keys( &src->extra_tags );
193
    for( int i = 0; ppsz_all_keys && ppsz_all_keys[i]; i++ )
194 195 196
    {
        /* Always try to remove the previous value */
        vlc_dictionary_remove_value_for_key( &dst->extra_tags, ppsz_all_keys[i], vlc_meta_FreeExtraKey, NULL );
197

198 199 200 201 202 203 204 205
        void *p_value = vlc_dictionary_value_for_key( &src->extra_tags, ppsz_all_keys[i] );
        vlc_dictionary_insert( &dst->extra_tags, ppsz_all_keys[i], strdup( (const char*)p_value ) );
        free( ppsz_all_keys[i] );
    }
    free( ppsz_all_keys );
}


206 207
void input_ExtractAttachmentAndCacheArt( input_thread_t *p_input,
                                         const char *name )
208
{
209
    input_item_t *p_item = input_priv(p_input)->p_item;
210

211
    if( input_item_IsArtFetched( p_item ) )
212 213 214
    {   /* XXX Weird, we should not end up with attachment:// art URL
         * unless there is a race condition */
        msg_Warn( p_input, "art already fetched" );
215 216
        if( likely(playlist_FindArtInCache( p_item ) == VLC_SUCCESS) )
            return;
217 218 219
    }

    /* */
220
    input_attachment_t *p_attachment = NULL;
221 222

    vlc_mutex_lock( &p_item->lock );
223
    for( int i_idx = 0; i_idx < input_priv(p_input)->i_attachment; i_idx++ )
224
    {
225
        input_attachment_t *a = input_priv(p_input)->attachment[i_idx];
226 227

        if( !strcmp( a->psz_name, name ) )
228
        {
229
            p_attachment = vlc_input_attachment_Duplicate( a );
230 231 232
            break;
        }
    }
233 234
    vlc_mutex_unlock( &p_item->lock );

235
    if( p_attachment == NULL )
236
    {
237 238
        msg_Warn( p_input, "art attachment %s not found", name );
        return;
239 240
    }

241 242
    /* */
    const char *psz_type = NULL;
243

244
    if( !strcmp( p_attachment->psz_mime, "image/jpeg" ) )
245
        psz_type = ".jpg";
246
    else if( !strcmp( p_attachment->psz_mime, "image/png" ) )
247
        psz_type = ".png";
François Cartegnie's avatar
François Cartegnie committed
248 249
    else if( !strcmp( p_attachment->psz_mime, "image/x-pict" ) )
        psz_type = ".pct";
250

251
    playlist_SaveArt( VLC_OBJECT(p_input), p_item,
252
                      p_attachment->p_data, p_attachment->i_data, psz_type );
253
    vlc_input_attachment_Delete( p_attachment );
254
}
255

256 257 258
int input_item_WriteMeta( vlc_object_t *obj, input_item_t *p_item )
{
    meta_export_t *p_export =
259
        vlc_custom_create( obj, sizeof( *p_export ), "meta writer" );
260 261 262 263 264 265 266 267
    if( p_export == NULL )
        return VLC_ENOMEM;
    p_export->p_item = p_item;

    int type;
    vlc_mutex_lock( &p_item->lock );
    type = p_item->i_type;
    vlc_mutex_unlock( &p_item->lock );
268 269
    if( type != ITEM_TYPE_FILE )
        goto error;
270

271
    char *psz_uri = input_item_GetURI( p_item );
272
    p_export->psz_file = vlc_uri2path( psz_uri );
273 274 275 276 277
    if( p_export->psz_file == NULL )
        msg_Err( p_export, "cannot write meta to remote media %s", psz_uri );
    free( psz_uri );
    if( p_export->psz_file == NULL )
        goto error;
278 279 280 281 282 283

    module_t *p_mod = module_need( p_export, "meta writer", NULL, false );
    if( p_mod )
        module_unneed( p_export, p_mod );
    vlc_object_release( p_export );
    return VLC_SUCCESS;
284 285 286 287

error:
    vlc_object_release( p_export );
    return VLC_EGENERIC;
288
}
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324

void vlc_audio_replay_gain_MergeFromMeta( audio_replay_gain_t *p_dst,
                                          const vlc_meta_t *p_meta )
{
    const char * psz_value;

    if( !p_meta )
        return;

    if( (psz_value = vlc_meta_GetExtra(p_meta, "REPLAYGAIN_TRACK_GAIN")) ||
        (psz_value = vlc_meta_GetExtra(p_meta, "RG_RADIO")) )
    {
        p_dst->pb_gain[AUDIO_REPLAY_GAIN_TRACK] = true;
        p_dst->pf_gain[AUDIO_REPLAY_GAIN_TRACK] = us_atof( psz_value );
    }

    if( (psz_value = vlc_meta_GetExtra(p_meta, "REPLAYGAIN_TRACK_PEAK" )) ||
             (psz_value = vlc_meta_GetExtra(p_meta, "RG_PEAK" )) )
    {
        p_dst->pb_peak[AUDIO_REPLAY_GAIN_TRACK] = true;
        p_dst->pf_peak[AUDIO_REPLAY_GAIN_TRACK] = us_atof( psz_value );
    }

    if( (psz_value = vlc_meta_GetExtra(p_meta, "REPLAYGAIN_ALBUM_GAIN" )) ||
             (psz_value = vlc_meta_GetExtra(p_meta, "RG_AUDIOPHILE" )) )
    {
        p_dst->pb_gain[AUDIO_REPLAY_GAIN_ALBUM] = true;
        p_dst->pf_gain[AUDIO_REPLAY_GAIN_ALBUM] = us_atof( psz_value );
    }

    if( (psz_value = vlc_meta_GetExtra(p_meta, "REPLAYGAIN_ALBUM_PEAK" )) )
    {
        p_dst->pb_peak[AUDIO_REPLAY_GAIN_ALBUM] = true;
        p_dst->pf_peak[AUDIO_REPLAY_GAIN_ALBUM] = us_atof( psz_value );
    }
}