Gregory Petrosyan
2011-10-01 09:55:05 UTC
When adding a file, check for the CUE sheet with the same name. If it exists,
add entries from it instead of the original file.
TODO:
- sometimes screen garbled due to libcue
- cache refresh etc.: will it work?
Signed-off-by: Gregory Petrosyan <***@gmail.com>
---
Makefile | 12 ++-
cmus.c | 2 +-
configure | 11 ++-
cue.c | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cue_utils.c | 76 +++++++++++
cue_utils.h | 29 ++++
input.c | 3 +
job.c | 34 +++++-
track_info.c | 5 +
utils.h | 7 +-
10 files changed, 578 insertions(+), 7 deletions(-)
create mode 100644 cue.c
create mode 100644 cue_utils.c
create mode 100644 cue_utils.h
diff --git a/Makefile b/Makefile
index 7e00434..9fa7ef9 100644
--- a/Makefile
+++ b/Makefile
@@ -18,12 +18,13 @@ include scripts/lib.mk
CFLAGS += -D_FILE_OFFSET_BITS=64
-CMUS_LIBS = $(PTHREAD_LIBS) $(NCURSES_LIBS) $(ICONV_LIBS) $(DL_LIBS) $(DISCID_LIBS) -lm $(COMPAT_LIBS)
+CMUS_LIBS = $(PTHREAD_LIBS) $(NCURSES_LIBS) $(ICONV_LIBS) $(DL_LIBS) $(DISCID_LIBS) $(CUE_LIBS) -lm $(COMPAT_LIBS)
input.o main.o ui_curses.o pulse.lo: .version
input.o main.o ui_curses.o pulse.lo: CFLAGS += -DVERSION=\"$(VERSION)\"
main.o server.o: CFLAGS += -DDEFAULT_PORT=3000
discid.o: CFLAGS += $(DISCID_CFLAGS)
+cue_utils.o: CFLAGS += $(CUE_CFLAGS)
.version: Makefile
@test "`cat $@ 2> /dev/null`" = "$(VERSION)" && exit 0; \
@@ -32,7 +33,7 @@ discid.o: CFLAGS += $(DISCID_CFLAGS)
# programs {{{
cmus-y := \
ape.o browser.o buffer.o cache.o cmdline.o cmus.o command_mode.o comment.o \
- channelmap.o convert.lo debug.o discid.o editable.o expr.o filters.o \
+ channelmap.o convert.lo cue_utils.o debug.o discid.o editable.o expr.o filters.o \
format_print.o gbuf.o glob.o help.o history.o http.o id3.o input.o job.o \
keys.o keyval.o lib.o load_dir.o locking.o mergesort.o misc.o options.o \
output.o pcm.o pl.o play_queue.o player.o \
@@ -76,6 +77,7 @@ wav-objs := wav.lo
mp4-objs := mp4.lo
aac-objs := aac.lo
ffmpeg-objs := ffmpeg.lo
+cue-objs := cue.lo
ip-$(CONFIG_CDIO) += cdio.so
ip-$(CONFIG_FLAC) += flac.so
@@ -88,7 +90,7 @@ ip-$(CONFIG_WAVPACK) += wavpack.so
ip-$(CONFIG_WAV) += wav.so
ip-$(CONFIG_MP4) += mp4.so
ip-$(CONFIG_AAC) += aac.so
-ip-$(CONFIG_FFMPEG) += ffmpeg.so
+ip-$(CONFIG_CUE) += cue.so
$(cdio-objs): CFLAGS += $(CDIO_CFLAGS) $(CDDB_CFLAGS)
$(flac-objs): CFLAGS += $(FLAC_CFLAGS)
@@ -101,6 +103,7 @@ $(wavpack-objs): CFLAGS += $(WAVPACK_CFLAGS)
$(mp4-objs): CFLAGS += $(MP4_CFLAGS)
$(aac-objs): CFLAGS += $(AAC_CFLAGS)
$(ffmpeg-objs): CFLAGS += $(FFMPEG_CFLAGS)
+$(cue-objs): CFLAGS += $(CUE_CFLAGS)
cdio.so: $(cdio-objs) $(libcmus-y)
$(call cmd,ld_dl,$(CDIO_LIBS) $(CDDB_LIBS))
@@ -138,6 +141,9 @@ aac.so: $(aac-objs) $(libcmus-y)
ffmpeg.so: $(ffmpeg-objs) $(libcmus-y)
$(call cmd,ld_dl,$(FFMPEG_LIBS))
+cue.so: $(cue-objs) $(libcmus-y)
+ $(call cmd,ld_dl,$(CUE_LIBS))
+
# }}}
# output plugins {{{
diff --git a/cmus.c b/cmus.c
index d156f06..e9cf9b0 100644
--- a/cmus.c
+++ b/cmus.c
@@ -125,7 +125,7 @@ enum file_type cmus_detect_ft(const char *name, char **ret)
char *absolute;
struct stat st;
- if (is_http_url(name)) {
+ if (is_http_url(name) || is_cue_url(name)) {
*ret = xstrdup(name);
return FILE_TYPE_URL;
}
diff --git a/configure b/configure
index 327654d..cac4d32 100755
--- a/configure
+++ b/configure
@@ -181,6 +181,13 @@ check_mad()
return $?
}
+check_cue()
+{
+ pkg_config CUE "libcue >= 1.3"
+ return $?
+}
+
+
mikmod_code="
#include <mikmod.h>
int main() {
@@ -407,6 +414,7 @@ Optional Features: y/n
CONFIG_MP4 MPEG-4 AAC (.mp4, .m4a, .m4b) [auto]
CONFIG_AAC AAC (.aac, audio/aac, audio/aacp) [auto]
CONFIG_FFMPEG FFMPEG (.shn, .wma) [auto]
+ CONFIG_CUE CUE sheets (.cue) [auto]
CONFIG_ROAR native RoarAudio output [auto]
CONFIG_PULSE native PulseAudio output [auto]
CONFIG_ALSA ALSA [auto]
@@ -465,6 +473,7 @@ check check_wavpack CONFIG_WAVPACK
check check_mp4 CONFIG_MP4
check check_aac CONFIG_AAC
check check_ffmpeg CONFIG_FFMPEG
+check check_cue CONFIG_CUE
# nothing to check, just validate the variable values
check true CONFIG_TREMOR
check true CONFIG_WAV
@@ -506,7 +515,7 @@ config_header config/iconv.h HAVE_ICONV
config_header config/xmalloc.h HAVE_STRDUP HAVE_STRNDUP
makefile_vars bindir datadir libdir mandir exampledir
-makefile_vars CONFIG_CDIO CONFIG_FLAC CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MPC CONFIG_VORBIS CONFIG_WAVPACK CONFIG_WAV CONFIG_MP4 CONFIG_AAC CONFIG_FFMPEG
+makefile_vars CONFIG_CDIO CONFIG_FLAC CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MPC CONFIG_VORBIS CONFIG_WAVPACK CONFIG_WAV CONFIG_MP4 CONFIG_AAC CONFIG_FFMPEG CONFIG_CUE
makefile_vars CONFIG_ROAR CONFIG_PULSE CONFIG_ALSA CONFIG_AO CONFIG_ARTS CONFIG_OSS CONFIG_SUN CONFIG_WAVEOUT
generate_config_mk
diff --git a/cue.c b/cue.c
new file mode 100644
index 0000000..a5fa73e
--- /dev/null
+++ b/cue.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * 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 "ip.h"
+#include "debug.h"
+#include "input.h"
+#include "utils.h"
+#include "comment.h"
+#include "xmalloc.h"
+
+#include <libcue/libcue.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+
+
+struct cue_private {
+ struct input_plugin *child;
+
+ char *cue_filename;
+ int track_n;
+
+ double start_offset;
+ double current_offset;
+ double end_offset;
+};
+
+
+static int __parse_cue_url(const char *url, char **filename, int *track_n)
+{
+ const char *slash;
+ long n;
+
+ if (!is_cue_url(url))
+ return 1;
+
+ url += 6;
+
+ slash = strrchr(url, '/');
+ if (!slash)
+ return 1;
+
+ if (str_to_int(slash + 1, &n) != 0)
+ return 1;
+
+ *filename = xstrndup(url, slash - url);
+ *track_n = n;
+ return 0;
+}
+
+
+static double __to_seconds(long v)
+{
+ const int FRAMES_IN_SECOND = 75;
+
+ return (double)v / FRAMES_IN_SECOND;
+}
+
+
+static char *__make_absolute_path(const char *abs_filename, const char *rel_filename)
+{
+ char *s;
+ const char *slash;
+ char buf[4096] = {0};
+
+ slash = strrchr(abs_filename, '/');
+ if (slash == NULL)
+ return xstrdup(rel_filename);
+
+ s = xstrndup(abs_filename, slash - abs_filename);
+ snprintf(buf, sizeof buf, "%s/%s", s, rel_filename);
+
+ free(s);
+ return xstrdup(buf);
+}
+
+
+static int cue_open(struct input_plugin_data *ip_data)
+{
+ int rc;
+ FILE *cue;
+ Cd *cd;
+ Track *t;
+ char *child_filename;
+ struct cue_private *priv;
+
+ priv = xnew(struct cue_private, 1);
+
+ rc = __parse_cue_url(ip_data->filename, &priv->cue_filename, &priv->track_n);
+ if (rc) {
+ rc = -IP_ERROR_INVALID_URI;
+ goto url_parse_failed;
+ }
+
+ cue = fopen(priv->cue_filename, "r");
+ if (cue == NULL) {
+ rc = -IP_ERROR_ERRNO;
+ goto cue_open_failed;
+ }
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_parse_failed;
+ }
+
+ t = cd_get_track(cd, priv->track_n);
+ if (t == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_read_failed;
+ }
+
+ child_filename = track_get_filename(t);
+ if (child_filename == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_read_failed;
+ }
+ child_filename = __make_absolute_path(priv->cue_filename, child_filename);
+
+ priv->child = ip_new(child_filename);
+ free(child_filename);
+
+ rc = ip_open(priv->child);
+ if (rc)
+ goto ip_open_failed;
+
+ ip_setup(priv->child);
+
+ priv->start_offset = __to_seconds(track_get_start(t));
+ priv->current_offset = priv->start_offset;
+
+ rc = ip_seek(priv->child, priv->start_offset);
+ if (rc)
+ goto ip_open_failed;
+
+ if (track_get_length(t) != 0)
+ priv->end_offset = priv->start_offset + __to_seconds(track_get_length(t));
+ else
+ priv->end_offset = ip_duration(priv->child);
+
+ ip_data->fd = open(ip_get_filename(priv->child), O_RDONLY);
+ if (ip_data->fd == -1)
+ goto ip_open_failed;
+
+ ip_data->private = priv;
+ ip_data->sf = ip_get_sf(priv->child);
+ ip_get_channel_map(priv->child, ip_data->channel_map);
+
+ fclose(cue);
+ cd_delete(cd);
+ return 0;
+
+ip_open_failed:
+ ip_delete(priv->child);
+
+cue_read_failed:
+ cd_delete(cd);
+
+cue_parse_failed:
+ fclose(cue);
+
+cue_open_failed:
+ free(priv->cue_filename);
+
+url_parse_failed:
+ free(priv);
+
+ return rc;
+}
+
+
+static int cue_close(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ close(ip_data->fd);
+ ip_data->fd = -1;
+
+ ip_delete(priv->child);
+ free(priv->cue_filename);
+
+ free(priv);
+ ip_data->private = NULL;
+
+ return 0;
+}
+
+
+static int cue_read(struct input_plugin_data *ip_data, char *buffer, int count)
+{
+ int rc;
+ sample_format_t sf;
+ double len;
+ double rem_len;
+ struct cue_private *priv = ip_data->private;
+
+ if (priv->current_offset >= priv->end_offset)
+ return 0;
+
+ rc = ip_read(priv->child, buffer, count);
+ if (rc <= 0)
+ return rc;
+
+ sf = ip_get_sf(priv->child);
+ len = (double)rc / sf_get_second_size(sf);
+
+ rem_len = priv->end_offset - priv->current_offset;
+ priv->current_offset += len;
+
+ if (priv->current_offset >= priv->end_offset)
+ rc = (int)(rem_len * sf_get_rate(sf)) * sf_get_frame_size(sf);
+
+ return rc;
+}
+
+
+static int cue_seek(struct input_plugin_data *ip_data, double offset)
+{
+ struct cue_private *priv = ip_data->private;
+ double new_offset = priv->start_offset + offset;
+
+ if (new_offset > priv->end_offset)
+ new_offset = priv->end_offset;
+
+ priv->current_offset = new_offset;
+
+ return ip_seek(priv->child, new_offset);
+}
+
+
+static int cue_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
+{
+ int rc;
+ FILE *cue;
+ Cd *cd;
+ Rem *cd_rem;
+ Cdtext *cd_cdtext;
+ Track *t;
+ Rem *track_rem;
+ Cdtext *track_cdtext;
+ char *val;
+ char buf[32] = {0};
+ GROWING_KEYVALS(c);
+ struct cue_private *priv = ip_data->private;
+
+ cue = fopen(priv->cue_filename, "r");
+ if (cue == NULL) {
+ rc = -IP_ERROR_ERRNO;
+ goto cue_open_failed;
+ }
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_parse_failed;
+ }
+
+ t = cd_get_track(cd, priv->track_n);
+ if (t == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto get_track_failed;
+ }
+
+ snprintf(buf, sizeof buf, "%d", priv->track_n);
+ comments_add(&c, "tracknumber", xstrdup(buf));
+
+ cd_rem = cd_get_rem(cd);
+ cd_cdtext = cd_get_cdtext(cd);
+ track_rem = track_get_rem(t);
+ track_cdtext = track_get_cdtext(t);
+
+ val = cdtext_get(PTI_TITLE, track_cdtext);
+ if (val != NULL)
+ comments_add(&c, "title", xstrdup(val));
+
+ val = cdtext_get(PTI_TITLE, cd_cdtext);
+ if (val != NULL)
+ comments_add(&c, "album", xstrdup(val));
+
+ val = cdtext_get(PTI_PERFORMER, track_cdtext);
+ if (val != NULL)
+ comments_add(&c, "artist", xstrdup(val));
+
+ val = cdtext_get(PTI_PERFORMER, cd_cdtext);
+ if (val != NULL)
+ comments_add(&c, "albumartist", xstrdup(val));
+
+ val = rem_get(REM_DATE, track_rem);
+ if (val != NULL) {
+ comments_add(&c, "date", xstrdup(val));
+ } else {
+ val = rem_get(REM_DATE, cd_rem);
+ if (val != NULL)
+ comments_add(&c, "date", xstrdup(val));
+ }
+
+ /*
+ * TODO:
+ * - replaygain REMs
+ * - genre?
+ */
+
+ keyvals_terminate(&c);
+ *comments = c.keyvals;
+
+ cd_delete(cd);
+ fclose(cue);
+ return 0;
+
+get_track_failed:
+ cd_delete(cd);
+
+cue_parse_failed:
+ fclose(cue);
+
+cue_open_failed:
+ return rc;
+}
+
+
+static int cue_duration(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return priv->end_offset - priv->start_offset;
+}
+
+
+static long cue_bitrate(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_bitrate(priv->child);
+}
+
+
+static long cue_current_bitrate(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_current_bitrate(priv->child);
+}
+
+
+static char *cue_codec(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_codec(priv->child);
+}
+
+
+static char *cue_codec_profile(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_codec_profile(priv->child);
+}
+
+
+static int cue_set_option(int key, const char *val)
+{
+ return -IP_ERROR_NOT_OPTION;
+}
+
+
+static int cue_get_option(int key, char **val)
+{
+ return -IP_ERROR_NOT_OPTION;
+}
+
+
+const struct input_plugin_ops ip_ops = {
+ .open = cue_open,
+ .close = cue_close,
+ .read = cue_read,
+ .seek = cue_seek,
+ .read_comments = cue_read_comments,
+ .duration = cue_duration,
+ .bitrate = cue_bitrate,
+ .bitrate_current = cue_current_bitrate,
+ .codec = cue_codec,
+ .codec_profile = cue_codec_profile,
+ .set_option = cue_set_option,
+ .get_option = cue_get_option
+};
+
+const int ip_priority = 50;
+const char * const ip_extensions[] = { "cue", NULL };
+const char * const ip_mime_types[] = { "application/x-cue", NULL };
+const char * const ip_options[] = { NULL };
diff --git a/cue_utils.c b/cue_utils.c
new file mode 100644
index 0000000..4c040b0
--- /dev/null
+++ b/cue_utils.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * 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 "cue_utils.h"
+#include "xmalloc.h"
+
+#include <libcue/libcue.h>
+
+#include <stdio.h>
+
+
+char *associated_cue(const char *filename)
+{
+ char *f;
+ char buf[4096] = {0};
+ const char *dot = strrchr(filename, '.');
+
+ if (dot == NULL)
+ return NULL;
+
+ f = xstrndup(filename, dot - filename);
+ snprintf(buf, sizeof buf, "%s.cue", f);
+
+ free(f);
+ return xstrdup(buf);
+}
+
+
+int cue_get_ntracks(const char *filename)
+{
+ int n;
+ FILE *cue;
+ Cd *cd;
+
+ cue = fopen(filename, "r");
+ if (cue == NULL)
+ return -1;
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ fclose(cue);
+ return -1;
+ }
+
+ n = cd_get_ntrack(cd);
+
+ cd_delete(cd);
+ fclose(cue);
+
+ return n;
+}
+
+
+char *construct_cue_url(const char *cue_filename, int track_n)
+{
+ char buf[4096] = {0};
+
+ snprintf(buf, sizeof buf, "cue://%s/%d", cue_filename, track_n);
+
+ return xstrdup(buf);
+}
diff --git a/cue_utils.h b/cue_utils.h
new file mode 100644
index 0000000..41407a4
--- /dev/null
+++ b/cue_utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * 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/>.
+ */
+
+
+#ifndef __CUE_UTILS_H__
+#define __CUE_UTILS_H__
+
+
+char *associated_cue(const char *filename);
+int cue_get_ntracks(const char *filename);
+char *construct_cue_url(const char *cue_filename, int track_n);
+
+
+#endif
diff --git a/input.c b/input.c
index 0019aee..48f105e 100644
--- a/input.c
+++ b/input.c
@@ -541,6 +541,9 @@ int ip_open(struct input_plugin *ip)
if (is_cdda_url(ip->data.filename)) {
ip->ops = get_ops_by_mime_type("x-content/audio-cdda");
rc = ip->ops ? ip->ops->open(&ip->data) : 1;
+ } else if (is_cue_url(ip->data.filename)) {
+ ip->ops = get_ops_by_mime_type("application/x-cue");
+ rc = ip->ops ? ip->ops->open(&ip->data) : 1;
} else
rc = open_file(ip);
}
diff --git a/job.c b/job.c
index 187a8e0..289c7b6 100644
--- a/job.c
+++ b/job.c
@@ -33,6 +33,7 @@
#include "player.h"
#include "discid.h"
#include "xstrjoin.h"
+#include "cue_utils.h"
#include <string.h>
#include <unistd.h>
@@ -63,10 +64,15 @@ static void add_ti(struct track_info *ti)
ti_buffer[ti_buffer_fill++] = ti;
}
+static int add_file_cue(const char *filename);
+
static void add_file(const char *filename)
{
struct track_info *ti;
+ if (!is_cue_url(filename) && add_file_cue(filename))
+ return;
+
cache_lock();
ti = cache_get_ti(filename);
cache_unlock();
@@ -75,6 +81,32 @@ static void add_file(const char *filename)
add_ti(ti);
}
+static int add_file_cue(const char *filename)
+{
+ int n_tracks;
+ char *url;
+ char *cue_filename;
+
+ cue_filename = associated_cue(filename);
+ if (cue_filename == NULL)
+ return 0;
+
+ n_tracks = cue_get_ntracks(cue_filename);
+ if (n_tracks <= 0) {
+ free(cue_filename);
+ return 0;
+ }
+
+ for (int i = 1; i <= n_tracks; ++i) {
+ url = construct_cue_url(cue_filename, i);
+ add_file(url);
+ free(url);
+ }
+
+ free(cue_filename);
+ return 1;
+}
+
static void add_url(const char *url)
{
add_file(url);
@@ -204,7 +236,7 @@ static int handle_line(void *data, const char *line)
if (worker_cancelling())
return 1;
- if (is_http_url(line)) {
+ if (is_http_url(line) || is_cue_url(line)) {
add_url(line);
} else {
char *absolute = path_absolute_cwd(line, data);
diff --git a/track_info.c b/track_info.c
index 2109a4d..0e582b0 100644
--- a/track_info.c
+++ b/track_info.c
@@ -71,6 +71,11 @@ void track_info_set_comments(struct track_info *ti, struct keyval *comments) {
ti->artistsort = comments_get_artistsort(comments);
ti->is_va_compilation = track_is_va_compilation(comments);
+ if (ti->artist == NULL && ti->albumartist != NULL) {
+ /* best guess */
+ ti->artist = ti->albumartist;
+ }
+
if (track_info_has_tag(ti) && ti->title == NULL) {
/* best guess */
ti->title = path_basename(ti->filename);
diff --git a/utils.h b/utils.h
index f3cb45b..d3aa75f 100644
--- a/utils.h
+++ b/utils.h
@@ -149,9 +149,14 @@ static inline int is_cdda_url(const char *name)
return strncmp(name, "cdda://", 7) == 0;
}
+static inline int is_cue_url(const char *name)
+{
+ return strncmp(name, "cue://", 6) == 0;
+}
+
static inline int is_url(const char *name)
{
- return is_http_url(name) || is_cdda_url(name);
+ return is_http_url(name) || is_cdda_url(name) || is_cue_url(name);
}
static inline int is_freeform_true(const char *c)
add entries from it instead of the original file.
TODO:
- sometimes screen garbled due to libcue
- cache refresh etc.: will it work?
Signed-off-by: Gregory Petrosyan <***@gmail.com>
---
Makefile | 12 ++-
cmus.c | 2 +-
configure | 11 ++-
cue.c | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
cue_utils.c | 76 +++++++++++
cue_utils.h | 29 ++++
input.c | 3 +
job.c | 34 +++++-
track_info.c | 5 +
utils.h | 7 +-
10 files changed, 578 insertions(+), 7 deletions(-)
create mode 100644 cue.c
create mode 100644 cue_utils.c
create mode 100644 cue_utils.h
diff --git a/Makefile b/Makefile
index 7e00434..9fa7ef9 100644
--- a/Makefile
+++ b/Makefile
@@ -18,12 +18,13 @@ include scripts/lib.mk
CFLAGS += -D_FILE_OFFSET_BITS=64
-CMUS_LIBS = $(PTHREAD_LIBS) $(NCURSES_LIBS) $(ICONV_LIBS) $(DL_LIBS) $(DISCID_LIBS) -lm $(COMPAT_LIBS)
+CMUS_LIBS = $(PTHREAD_LIBS) $(NCURSES_LIBS) $(ICONV_LIBS) $(DL_LIBS) $(DISCID_LIBS) $(CUE_LIBS) -lm $(COMPAT_LIBS)
input.o main.o ui_curses.o pulse.lo: .version
input.o main.o ui_curses.o pulse.lo: CFLAGS += -DVERSION=\"$(VERSION)\"
main.o server.o: CFLAGS += -DDEFAULT_PORT=3000
discid.o: CFLAGS += $(DISCID_CFLAGS)
+cue_utils.o: CFLAGS += $(CUE_CFLAGS)
.version: Makefile
@test "`cat $@ 2> /dev/null`" = "$(VERSION)" && exit 0; \
@@ -32,7 +33,7 @@ discid.o: CFLAGS += $(DISCID_CFLAGS)
# programs {{{
cmus-y := \
ape.o browser.o buffer.o cache.o cmdline.o cmus.o command_mode.o comment.o \
- channelmap.o convert.lo debug.o discid.o editable.o expr.o filters.o \
+ channelmap.o convert.lo cue_utils.o debug.o discid.o editable.o expr.o filters.o \
format_print.o gbuf.o glob.o help.o history.o http.o id3.o input.o job.o \
keys.o keyval.o lib.o load_dir.o locking.o mergesort.o misc.o options.o \
output.o pcm.o pl.o play_queue.o player.o \
@@ -76,6 +77,7 @@ wav-objs := wav.lo
mp4-objs := mp4.lo
aac-objs := aac.lo
ffmpeg-objs := ffmpeg.lo
+cue-objs := cue.lo
ip-$(CONFIG_CDIO) += cdio.so
ip-$(CONFIG_FLAC) += flac.so
@@ -88,7 +90,7 @@ ip-$(CONFIG_WAVPACK) += wavpack.so
ip-$(CONFIG_WAV) += wav.so
ip-$(CONFIG_MP4) += mp4.so
ip-$(CONFIG_AAC) += aac.so
-ip-$(CONFIG_FFMPEG) += ffmpeg.so
+ip-$(CONFIG_CUE) += cue.so
$(cdio-objs): CFLAGS += $(CDIO_CFLAGS) $(CDDB_CFLAGS)
$(flac-objs): CFLAGS += $(FLAC_CFLAGS)
@@ -101,6 +103,7 @@ $(wavpack-objs): CFLAGS += $(WAVPACK_CFLAGS)
$(mp4-objs): CFLAGS += $(MP4_CFLAGS)
$(aac-objs): CFLAGS += $(AAC_CFLAGS)
$(ffmpeg-objs): CFLAGS += $(FFMPEG_CFLAGS)
+$(cue-objs): CFLAGS += $(CUE_CFLAGS)
cdio.so: $(cdio-objs) $(libcmus-y)
$(call cmd,ld_dl,$(CDIO_LIBS) $(CDDB_LIBS))
@@ -138,6 +141,9 @@ aac.so: $(aac-objs) $(libcmus-y)
ffmpeg.so: $(ffmpeg-objs) $(libcmus-y)
$(call cmd,ld_dl,$(FFMPEG_LIBS))
+cue.so: $(cue-objs) $(libcmus-y)
+ $(call cmd,ld_dl,$(CUE_LIBS))
+
# }}}
# output plugins {{{
diff --git a/cmus.c b/cmus.c
index d156f06..e9cf9b0 100644
--- a/cmus.c
+++ b/cmus.c
@@ -125,7 +125,7 @@ enum file_type cmus_detect_ft(const char *name, char **ret)
char *absolute;
struct stat st;
- if (is_http_url(name)) {
+ if (is_http_url(name) || is_cue_url(name)) {
*ret = xstrdup(name);
return FILE_TYPE_URL;
}
diff --git a/configure b/configure
index 327654d..cac4d32 100755
--- a/configure
+++ b/configure
@@ -181,6 +181,13 @@ check_mad()
return $?
}
+check_cue()
+{
+ pkg_config CUE "libcue >= 1.3"
+ return $?
+}
+
+
mikmod_code="
#include <mikmod.h>
int main() {
@@ -407,6 +414,7 @@ Optional Features: y/n
CONFIG_MP4 MPEG-4 AAC (.mp4, .m4a, .m4b) [auto]
CONFIG_AAC AAC (.aac, audio/aac, audio/aacp) [auto]
CONFIG_FFMPEG FFMPEG (.shn, .wma) [auto]
+ CONFIG_CUE CUE sheets (.cue) [auto]
CONFIG_ROAR native RoarAudio output [auto]
CONFIG_PULSE native PulseAudio output [auto]
CONFIG_ALSA ALSA [auto]
@@ -465,6 +473,7 @@ check check_wavpack CONFIG_WAVPACK
check check_mp4 CONFIG_MP4
check check_aac CONFIG_AAC
check check_ffmpeg CONFIG_FFMPEG
+check check_cue CONFIG_CUE
# nothing to check, just validate the variable values
check true CONFIG_TREMOR
check true CONFIG_WAV
@@ -506,7 +515,7 @@ config_header config/iconv.h HAVE_ICONV
config_header config/xmalloc.h HAVE_STRDUP HAVE_STRNDUP
makefile_vars bindir datadir libdir mandir exampledir
-makefile_vars CONFIG_CDIO CONFIG_FLAC CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MPC CONFIG_VORBIS CONFIG_WAVPACK CONFIG_WAV CONFIG_MP4 CONFIG_AAC CONFIG_FFMPEG
+makefile_vars CONFIG_CDIO CONFIG_FLAC CONFIG_MAD CONFIG_MIKMOD CONFIG_MODPLUG CONFIG_MPC CONFIG_VORBIS CONFIG_WAVPACK CONFIG_WAV CONFIG_MP4 CONFIG_AAC CONFIG_FFMPEG CONFIG_CUE
makefile_vars CONFIG_ROAR CONFIG_PULSE CONFIG_ALSA CONFIG_AO CONFIG_ARTS CONFIG_OSS CONFIG_SUN CONFIG_WAVEOUT
generate_config_mk
diff --git a/cue.c b/cue.c
new file mode 100644
index 0000000..a5fa73e
--- /dev/null
+++ b/cue.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * 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 "ip.h"
+#include "debug.h"
+#include "input.h"
+#include "utils.h"
+#include "comment.h"
+#include "xmalloc.h"
+
+#include <libcue/libcue.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+
+
+struct cue_private {
+ struct input_plugin *child;
+
+ char *cue_filename;
+ int track_n;
+
+ double start_offset;
+ double current_offset;
+ double end_offset;
+};
+
+
+static int __parse_cue_url(const char *url, char **filename, int *track_n)
+{
+ const char *slash;
+ long n;
+
+ if (!is_cue_url(url))
+ return 1;
+
+ url += 6;
+
+ slash = strrchr(url, '/');
+ if (!slash)
+ return 1;
+
+ if (str_to_int(slash + 1, &n) != 0)
+ return 1;
+
+ *filename = xstrndup(url, slash - url);
+ *track_n = n;
+ return 0;
+}
+
+
+static double __to_seconds(long v)
+{
+ const int FRAMES_IN_SECOND = 75;
+
+ return (double)v / FRAMES_IN_SECOND;
+}
+
+
+static char *__make_absolute_path(const char *abs_filename, const char *rel_filename)
+{
+ char *s;
+ const char *slash;
+ char buf[4096] = {0};
+
+ slash = strrchr(abs_filename, '/');
+ if (slash == NULL)
+ return xstrdup(rel_filename);
+
+ s = xstrndup(abs_filename, slash - abs_filename);
+ snprintf(buf, sizeof buf, "%s/%s", s, rel_filename);
+
+ free(s);
+ return xstrdup(buf);
+}
+
+
+static int cue_open(struct input_plugin_data *ip_data)
+{
+ int rc;
+ FILE *cue;
+ Cd *cd;
+ Track *t;
+ char *child_filename;
+ struct cue_private *priv;
+
+ priv = xnew(struct cue_private, 1);
+
+ rc = __parse_cue_url(ip_data->filename, &priv->cue_filename, &priv->track_n);
+ if (rc) {
+ rc = -IP_ERROR_INVALID_URI;
+ goto url_parse_failed;
+ }
+
+ cue = fopen(priv->cue_filename, "r");
+ if (cue == NULL) {
+ rc = -IP_ERROR_ERRNO;
+ goto cue_open_failed;
+ }
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_parse_failed;
+ }
+
+ t = cd_get_track(cd, priv->track_n);
+ if (t == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_read_failed;
+ }
+
+ child_filename = track_get_filename(t);
+ if (child_filename == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_read_failed;
+ }
+ child_filename = __make_absolute_path(priv->cue_filename, child_filename);
+
+ priv->child = ip_new(child_filename);
+ free(child_filename);
+
+ rc = ip_open(priv->child);
+ if (rc)
+ goto ip_open_failed;
+
+ ip_setup(priv->child);
+
+ priv->start_offset = __to_seconds(track_get_start(t));
+ priv->current_offset = priv->start_offset;
+
+ rc = ip_seek(priv->child, priv->start_offset);
+ if (rc)
+ goto ip_open_failed;
+
+ if (track_get_length(t) != 0)
+ priv->end_offset = priv->start_offset + __to_seconds(track_get_length(t));
+ else
+ priv->end_offset = ip_duration(priv->child);
+
+ ip_data->fd = open(ip_get_filename(priv->child), O_RDONLY);
+ if (ip_data->fd == -1)
+ goto ip_open_failed;
+
+ ip_data->private = priv;
+ ip_data->sf = ip_get_sf(priv->child);
+ ip_get_channel_map(priv->child, ip_data->channel_map);
+
+ fclose(cue);
+ cd_delete(cd);
+ return 0;
+
+ip_open_failed:
+ ip_delete(priv->child);
+
+cue_read_failed:
+ cd_delete(cd);
+
+cue_parse_failed:
+ fclose(cue);
+
+cue_open_failed:
+ free(priv->cue_filename);
+
+url_parse_failed:
+ free(priv);
+
+ return rc;
+}
+
+
+static int cue_close(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ close(ip_data->fd);
+ ip_data->fd = -1;
+
+ ip_delete(priv->child);
+ free(priv->cue_filename);
+
+ free(priv);
+ ip_data->private = NULL;
+
+ return 0;
+}
+
+
+static int cue_read(struct input_plugin_data *ip_data, char *buffer, int count)
+{
+ int rc;
+ sample_format_t sf;
+ double len;
+ double rem_len;
+ struct cue_private *priv = ip_data->private;
+
+ if (priv->current_offset >= priv->end_offset)
+ return 0;
+
+ rc = ip_read(priv->child, buffer, count);
+ if (rc <= 0)
+ return rc;
+
+ sf = ip_get_sf(priv->child);
+ len = (double)rc / sf_get_second_size(sf);
+
+ rem_len = priv->end_offset - priv->current_offset;
+ priv->current_offset += len;
+
+ if (priv->current_offset >= priv->end_offset)
+ rc = (int)(rem_len * sf_get_rate(sf)) * sf_get_frame_size(sf);
+
+ return rc;
+}
+
+
+static int cue_seek(struct input_plugin_data *ip_data, double offset)
+{
+ struct cue_private *priv = ip_data->private;
+ double new_offset = priv->start_offset + offset;
+
+ if (new_offset > priv->end_offset)
+ new_offset = priv->end_offset;
+
+ priv->current_offset = new_offset;
+
+ return ip_seek(priv->child, new_offset);
+}
+
+
+static int cue_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
+{
+ int rc;
+ FILE *cue;
+ Cd *cd;
+ Rem *cd_rem;
+ Cdtext *cd_cdtext;
+ Track *t;
+ Rem *track_rem;
+ Cdtext *track_cdtext;
+ char *val;
+ char buf[32] = {0};
+ GROWING_KEYVALS(c);
+ struct cue_private *priv = ip_data->private;
+
+ cue = fopen(priv->cue_filename, "r");
+ if (cue == NULL) {
+ rc = -IP_ERROR_ERRNO;
+ goto cue_open_failed;
+ }
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto cue_parse_failed;
+ }
+
+ t = cd_get_track(cd, priv->track_n);
+ if (t == NULL) {
+ rc = -IP_ERROR_FILE_FORMAT;
+ goto get_track_failed;
+ }
+
+ snprintf(buf, sizeof buf, "%d", priv->track_n);
+ comments_add(&c, "tracknumber", xstrdup(buf));
+
+ cd_rem = cd_get_rem(cd);
+ cd_cdtext = cd_get_cdtext(cd);
+ track_rem = track_get_rem(t);
+ track_cdtext = track_get_cdtext(t);
+
+ val = cdtext_get(PTI_TITLE, track_cdtext);
+ if (val != NULL)
+ comments_add(&c, "title", xstrdup(val));
+
+ val = cdtext_get(PTI_TITLE, cd_cdtext);
+ if (val != NULL)
+ comments_add(&c, "album", xstrdup(val));
+
+ val = cdtext_get(PTI_PERFORMER, track_cdtext);
+ if (val != NULL)
+ comments_add(&c, "artist", xstrdup(val));
+
+ val = cdtext_get(PTI_PERFORMER, cd_cdtext);
+ if (val != NULL)
+ comments_add(&c, "albumartist", xstrdup(val));
+
+ val = rem_get(REM_DATE, track_rem);
+ if (val != NULL) {
+ comments_add(&c, "date", xstrdup(val));
+ } else {
+ val = rem_get(REM_DATE, cd_rem);
+ if (val != NULL)
+ comments_add(&c, "date", xstrdup(val));
+ }
+
+ /*
+ * TODO:
+ * - replaygain REMs
+ * - genre?
+ */
+
+ keyvals_terminate(&c);
+ *comments = c.keyvals;
+
+ cd_delete(cd);
+ fclose(cue);
+ return 0;
+
+get_track_failed:
+ cd_delete(cd);
+
+cue_parse_failed:
+ fclose(cue);
+
+cue_open_failed:
+ return rc;
+}
+
+
+static int cue_duration(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return priv->end_offset - priv->start_offset;
+}
+
+
+static long cue_bitrate(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_bitrate(priv->child);
+}
+
+
+static long cue_current_bitrate(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_current_bitrate(priv->child);
+}
+
+
+static char *cue_codec(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_codec(priv->child);
+}
+
+
+static char *cue_codec_profile(struct input_plugin_data *ip_data)
+{
+ struct cue_private *priv = ip_data->private;
+
+ return ip_codec_profile(priv->child);
+}
+
+
+static int cue_set_option(int key, const char *val)
+{
+ return -IP_ERROR_NOT_OPTION;
+}
+
+
+static int cue_get_option(int key, char **val)
+{
+ return -IP_ERROR_NOT_OPTION;
+}
+
+
+const struct input_plugin_ops ip_ops = {
+ .open = cue_open,
+ .close = cue_close,
+ .read = cue_read,
+ .seek = cue_seek,
+ .read_comments = cue_read_comments,
+ .duration = cue_duration,
+ .bitrate = cue_bitrate,
+ .bitrate_current = cue_current_bitrate,
+ .codec = cue_codec,
+ .codec_profile = cue_codec_profile,
+ .set_option = cue_set_option,
+ .get_option = cue_get_option
+};
+
+const int ip_priority = 50;
+const char * const ip_extensions[] = { "cue", NULL };
+const char * const ip_mime_types[] = { "application/x-cue", NULL };
+const char * const ip_options[] = { NULL };
diff --git a/cue_utils.c b/cue_utils.c
new file mode 100644
index 0000000..4c040b0
--- /dev/null
+++ b/cue_utils.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * 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 "cue_utils.h"
+#include "xmalloc.h"
+
+#include <libcue/libcue.h>
+
+#include <stdio.h>
+
+
+char *associated_cue(const char *filename)
+{
+ char *f;
+ char buf[4096] = {0};
+ const char *dot = strrchr(filename, '.');
+
+ if (dot == NULL)
+ return NULL;
+
+ f = xstrndup(filename, dot - filename);
+ snprintf(buf, sizeof buf, "%s.cue", f);
+
+ free(f);
+ return xstrdup(buf);
+}
+
+
+int cue_get_ntracks(const char *filename)
+{
+ int n;
+ FILE *cue;
+ Cd *cd;
+
+ cue = fopen(filename, "r");
+ if (cue == NULL)
+ return -1;
+
+ cd = cue_parse_file(cue);
+ if (cd == NULL) {
+ fclose(cue);
+ return -1;
+ }
+
+ n = cd_get_ntrack(cd);
+
+ cd_delete(cd);
+ fclose(cue);
+
+ return n;
+}
+
+
+char *construct_cue_url(const char *cue_filename, int track_n)
+{
+ char buf[4096] = {0};
+
+ snprintf(buf, sizeof buf, "cue://%s/%d", cue_filename, track_n);
+
+ return xstrdup(buf);
+}
diff --git a/cue_utils.h b/cue_utils.h
new file mode 100644
index 0000000..41407a4
--- /dev/null
+++ b/cue_utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2008-2011 Various Authors
+ * Copyright (C) 2011 Gregory Petrosyan
+ *
+ * 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/>.
+ */
+
+
+#ifndef __CUE_UTILS_H__
+#define __CUE_UTILS_H__
+
+
+char *associated_cue(const char *filename);
+int cue_get_ntracks(const char *filename);
+char *construct_cue_url(const char *cue_filename, int track_n);
+
+
+#endif
diff --git a/input.c b/input.c
index 0019aee..48f105e 100644
--- a/input.c
+++ b/input.c
@@ -541,6 +541,9 @@ int ip_open(struct input_plugin *ip)
if (is_cdda_url(ip->data.filename)) {
ip->ops = get_ops_by_mime_type("x-content/audio-cdda");
rc = ip->ops ? ip->ops->open(&ip->data) : 1;
+ } else if (is_cue_url(ip->data.filename)) {
+ ip->ops = get_ops_by_mime_type("application/x-cue");
+ rc = ip->ops ? ip->ops->open(&ip->data) : 1;
} else
rc = open_file(ip);
}
diff --git a/job.c b/job.c
index 187a8e0..289c7b6 100644
--- a/job.c
+++ b/job.c
@@ -33,6 +33,7 @@
#include "player.h"
#include "discid.h"
#include "xstrjoin.h"
+#include "cue_utils.h"
#include <string.h>
#include <unistd.h>
@@ -63,10 +64,15 @@ static void add_ti(struct track_info *ti)
ti_buffer[ti_buffer_fill++] = ti;
}
+static int add_file_cue(const char *filename);
+
static void add_file(const char *filename)
{
struct track_info *ti;
+ if (!is_cue_url(filename) && add_file_cue(filename))
+ return;
+
cache_lock();
ti = cache_get_ti(filename);
cache_unlock();
@@ -75,6 +81,32 @@ static void add_file(const char *filename)
add_ti(ti);
}
+static int add_file_cue(const char *filename)
+{
+ int n_tracks;
+ char *url;
+ char *cue_filename;
+
+ cue_filename = associated_cue(filename);
+ if (cue_filename == NULL)
+ return 0;
+
+ n_tracks = cue_get_ntracks(cue_filename);
+ if (n_tracks <= 0) {
+ free(cue_filename);
+ return 0;
+ }
+
+ for (int i = 1; i <= n_tracks; ++i) {
+ url = construct_cue_url(cue_filename, i);
+ add_file(url);
+ free(url);
+ }
+
+ free(cue_filename);
+ return 1;
+}
+
static void add_url(const char *url)
{
add_file(url);
@@ -204,7 +236,7 @@ static int handle_line(void *data, const char *line)
if (worker_cancelling())
return 1;
- if (is_http_url(line)) {
+ if (is_http_url(line) || is_cue_url(line)) {
add_url(line);
} else {
char *absolute = path_absolute_cwd(line, data);
diff --git a/track_info.c b/track_info.c
index 2109a4d..0e582b0 100644
--- a/track_info.c
+++ b/track_info.c
@@ -71,6 +71,11 @@ void track_info_set_comments(struct track_info *ti, struct keyval *comments) {
ti->artistsort = comments_get_artistsort(comments);
ti->is_va_compilation = track_is_va_compilation(comments);
+ if (ti->artist == NULL && ti->albumartist != NULL) {
+ /* best guess */
+ ti->artist = ti->albumartist;
+ }
+
if (track_info_has_tag(ti) && ti->title == NULL) {
/* best guess */
ti->title = path_basename(ti->filename);
diff --git a/utils.h b/utils.h
index f3cb45b..d3aa75f 100644
--- a/utils.h
+++ b/utils.h
@@ -149,9 +149,14 @@ static inline int is_cdda_url(const char *name)
return strncmp(name, "cdda://", 7) == 0;
}
+static inline int is_cue_url(const char *name)
+{
+ return strncmp(name, "cue://", 6) == 0;
+}
+
static inline int is_url(const char *name)
{
- return is_http_url(name) || is_cdda_url(name);
+ return is_http_url(name) || is_cdda_url(name) || is_cue_url(name);
}
static inline int is_freeform_true(const char *c)
--
1.7.4.4
1.7.4.4