297 lines
6.8 KiB
C
297 lines
6.8 KiB
C
/*
|
|
* Copyright 2008-2013 Various Authors
|
|
* Copyright 2004-2007 Timo Hirvonen
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "comment.h"
|
|
#include "xmalloc.h"
|
|
#include "utils.h"
|
|
#include "uchar.h"
|
|
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
|
|
static int is_various_artists(const char *a)
|
|
{
|
|
return strcasecmp(a, "Various Artists") == 0 ||
|
|
strcasecmp(a, "Various") == 0 ||
|
|
strcasecmp(a, "VA") == 0 ||
|
|
strcasecmp(a, "V/A") == 0;
|
|
}
|
|
|
|
int track_is_compilation(const struct keyval *comments)
|
|
{
|
|
const char *c, *a, *aa;
|
|
|
|
c = keyvals_get_val(comments, "compilation");
|
|
if (c && is_freeform_true(c))
|
|
return 1;
|
|
|
|
c = keyvals_get_val(comments, "partofacompilation");
|
|
if (c && is_freeform_true(c))
|
|
return 1;
|
|
|
|
aa = keyvals_get_val(comments, "albumartist");
|
|
if (aa && is_various_artists(aa))
|
|
return 1;
|
|
|
|
a = keyvals_get_val(comments, "artist");
|
|
if (a && is_various_artists(a))
|
|
return 1;
|
|
|
|
if (aa && a && !u_strcase_equal(aa, a))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int track_is_va_compilation(const struct keyval *comments)
|
|
{
|
|
const char *c, *a, *aa;
|
|
|
|
aa = keyvals_get_val(comments, "albumartist");
|
|
if (aa)
|
|
return is_various_artists(aa);
|
|
|
|
a = keyvals_get_val(comments, "artist");
|
|
if (a && is_various_artists(a))
|
|
return 1;
|
|
|
|
c = keyvals_get_val(comments, "compilation");
|
|
if (c && is_freeform_true(c))
|
|
return 1;
|
|
|
|
c = keyvals_get_val(comments, "partofacompilation");
|
|
if (c && is_freeform_true(c))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *comments_get_albumartist(const struct keyval *comments)
|
|
{
|
|
const char *val = keyvals_get_val(comments, "albumartist");
|
|
|
|
if (!val || strcmp(val, "") == 0)
|
|
val = keyvals_get_val(comments, "artist");
|
|
|
|
return val;
|
|
}
|
|
|
|
const char *comments_get_artistsort(const struct keyval *comments)
|
|
{
|
|
const char *val;
|
|
|
|
if (track_is_va_compilation(comments))
|
|
return NULL;
|
|
|
|
val = keyvals_get_val(comments, "albumartistsort");
|
|
if (!track_is_compilation(comments)) {
|
|
if (!val || strcmp(val, "") == 0)
|
|
val = keyvals_get_val(comments, "artistsort");
|
|
}
|
|
|
|
if (!val || strcmp(val, "") == 0)
|
|
return NULL;
|
|
|
|
return val;
|
|
}
|
|
|
|
int comments_get_int(const struct keyval *comments, const char *key)
|
|
{
|
|
const char *val;
|
|
long int ival;
|
|
|
|
val = keyvals_get_val(comments, key);
|
|
if (val == NULL)
|
|
return -1;
|
|
while (*val && !(*val >= '0' && *val <= '9'))
|
|
val++;
|
|
if (str_to_int(val, &ival) == -1)
|
|
return -1;
|
|
return ival;
|
|
}
|
|
|
|
int comments_get_signed_int(const struct keyval *comments, const char *key, long int *ival)
|
|
{
|
|
const char *val;
|
|
|
|
val = keyvals_get_val(comments, key);
|
|
if (val == NULL)
|
|
return -1;
|
|
while (*val && !(*val == '+' || *val == '-' || (*val >= '0' && *val <= '9')))
|
|
val++;
|
|
return str_to_int(val, ival);
|
|
}
|
|
|
|
double comments_get_double(const struct keyval *comments, const char *key)
|
|
{
|
|
const char *val;
|
|
char *end;
|
|
double d;
|
|
|
|
val = keyvals_get_val(comments, key);
|
|
if (!val || strcmp(val, "") == 0)
|
|
goto error;
|
|
|
|
d = strtod(val, &end);
|
|
if (val == end)
|
|
goto error;
|
|
|
|
return d;
|
|
|
|
error:
|
|
return strtod("NAN", NULL);
|
|
}
|
|
|
|
/* Return date as an integer in the form YYYYMMDD, for sorting purposes.
|
|
* This function is not year 10000 compliant. */
|
|
int comments_get_date(const struct keyval *comments, const char *key)
|
|
{
|
|
const char *val;
|
|
char *endptr;
|
|
int year, month, day;
|
|
long int ival;
|
|
|
|
val = keyvals_get_val(comments, key);
|
|
if (val == NULL)
|
|
return -1;
|
|
|
|
year = strtol(val, &endptr, 10);
|
|
/* Looking for a four-digit number */
|
|
if (year < 1000 || year > 9999)
|
|
return -1;
|
|
ival = year * 10000;
|
|
|
|
if (*endptr == '-' || *endptr == ' ' || *endptr == '/') {
|
|
month = strtol(endptr+1, &endptr, 10);
|
|
if (month < 1 || month > 12)
|
|
return ival;
|
|
ival += month * 100;
|
|
}
|
|
|
|
if (*endptr == '-' || *endptr == ' ' || *endptr == '/') {
|
|
day = strtol(endptr+1, &endptr, 10);
|
|
if (day < 1 || day > 31)
|
|
return ival;
|
|
ival += day;
|
|
}
|
|
|
|
|
|
return ival;
|
|
}
|
|
|
|
static const char *interesting[] = {
|
|
"artist", "album", "title", "tracknumber", "discnumber", "totaldiscs", "genre",
|
|
"date", "compilation", "partofacompilation", "albumartist", "artistsort", "albumartistsort",
|
|
"albumsort",
|
|
"originaldate",
|
|
"r128_track_gain",
|
|
"r128_album_gain",
|
|
"replaygain_track_gain",
|
|
"replaygain_track_peak",
|
|
"replaygain_album_gain",
|
|
"replaygain_album_peak",
|
|
"musicbrainz_trackid",
|
|
"comment",
|
|
"bpm",
|
|
"arranger", "composer", "conductor", "lyricist", "performer",
|
|
"remixer", "label", "publisher", "work", "opus",
|
|
"subtitle", "media",
|
|
NULL
|
|
};
|
|
|
|
static struct {
|
|
const char *old;
|
|
const char *new;
|
|
} key_map[] = {
|
|
{ "album_artist", "albumartist" },
|
|
{ "album artist", "albumartist" },
|
|
{ "disc", "discnumber" },
|
|
{ "part", "discnumber" },
|
|
{ "partnumber", "discnumber" },
|
|
{ "disctotal", "totaldiscs" },
|
|
{ "tempo", "bpm" },
|
|
{ "track", "tracknumber" },
|
|
{ "WM/Year", "date" },
|
|
{ "WM/ArtistSortOrder", "artistsort" },
|
|
{ "WM/AlbumArtistSortOrder", "albumartistsort" },
|
|
{ "WM/AlbumSortOrder", "albumsort" },
|
|
{ "WM/OriginalReleaseYear", "originaldate" },
|
|
{ "WM/Media", "media" },
|
|
{ "sourcemedia", "media" },
|
|
{ "MusicBrainz Track Id", "musicbrainz_trackid" },
|
|
{ "version", "subtitle" },
|
|
/* ffmpeg id3 */
|
|
{ "artist-sort", "artistsort" },
|
|
{ "TSO2", "albumartistsort" },
|
|
{ "album-sort", "albumsort" },
|
|
/* ffmpeg mp4 */
|
|
{ "sort_artist", "artistsort" },
|
|
{ "sort_album_artist", "albumartistsort" },
|
|
{ "sort_album", "albumsort" },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
static const char *fix_key(const char *key)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; interesting[i]; i++) {
|
|
if (!strcasecmp(key, interesting[i]))
|
|
return interesting[i];
|
|
}
|
|
for (i = 0; key_map[i].old; i++) {
|
|
if (!strcasecmp(key, key_map[i].old))
|
|
return key_map[i].new;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int comments_add(struct growing_keyvals *c, const char *key, char *val)
|
|
{
|
|
if (!strcasecmp(key, "songwriter")) {
|
|
int r = comments_add_const(c, "lyricist", val);
|
|
return comments_add(c, "composer", val) && r;
|
|
}
|
|
|
|
key = fix_key(key);
|
|
if (!key) {
|
|
free(val);
|
|
return 0;
|
|
}
|
|
|
|
if (!strcmp(key, "tracknumber") || !strcmp(key, "discnumber")) {
|
|
char *slash = strchr(val, '/');
|
|
if (slash)
|
|
*slash = 0;
|
|
}
|
|
|
|
/* don't add duplicates */
|
|
if (keyvals_get_val_growing(c, key)) {
|
|
free(val);
|
|
return 0;
|
|
}
|
|
|
|
keyvals_add(c, key, val);
|
|
return 1;
|
|
}
|
|
|
|
int comments_add_const(struct growing_keyvals *c, const char *key, const char *val)
|
|
{
|
|
return comments_add(c, key, xstrdup(val));
|
|
}
|