cue_parser: new line based CUE sheet parser
To replace libcue, the unmaintained and crashy library.
This commit is contained in:
		
							
								
								
									
										327
									
								
								src/cue/cue_parser.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								src/cue/cue_parser.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,327 @@ | ||||
| /* | ||||
|  * Copyright (C) 2003-2011 The Music Player Daemon Project | ||||
|  * http://www.musicpd.org | ||||
|  * | ||||
|  * 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, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
| #include "cue_parser.h" | ||||
| #include "string_util.h" | ||||
| #include "song.h" | ||||
| #include "tag.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| struct cue_parser { | ||||
| 	enum { | ||||
| 		/** | ||||
| 		 * Parsing the CUE header. | ||||
| 		 */ | ||||
| 		HEADER, | ||||
|  | ||||
| 		/** | ||||
| 		 * Parsing a "FILE ... WAVE". | ||||
| 		 */ | ||||
| 		WAVE, | ||||
|  | ||||
| 		/** | ||||
| 		 * Ignore everything until the next "FILE". | ||||
| 		 */ | ||||
| 		IGNORE_FILE, | ||||
|  | ||||
| 		/** | ||||
| 		 * Parsing a "TRACK ... AUDIO". | ||||
| 		 */ | ||||
| 		TRACK, | ||||
|  | ||||
| 		/** | ||||
| 		 * Ignore everything until the next "TRACK". | ||||
| 		 */ | ||||
| 		IGNORE_TRACK, | ||||
| 	} state; | ||||
|  | ||||
| 	struct tag *tag; | ||||
|  | ||||
| 	char *filename; | ||||
|  | ||||
| 	struct song *current, *previous, *finished; | ||||
|  | ||||
| 	bool last_updated; | ||||
| }; | ||||
|  | ||||
| struct cue_parser * | ||||
| cue_parser_new(void) | ||||
| { | ||||
| 	struct cue_parser *parser = g_new(struct cue_parser, 1); | ||||
| 	parser->state = HEADER; | ||||
| 	parser->tag = tag_new(); | ||||
| 	parser->filename = NULL; | ||||
| 	parser->current = NULL; | ||||
| 	parser->previous = NULL; | ||||
| 	parser->finished = NULL; | ||||
| 	return parser; | ||||
| } | ||||
|  | ||||
| void | ||||
| cue_parser_free(struct cue_parser *parser) | ||||
| { | ||||
| 	tag_free(parser->tag); | ||||
| 	g_free(parser->filename); | ||||
|  | ||||
| 	if (parser->current != NULL) | ||||
| 		song_free(parser->current); | ||||
|  | ||||
| 	if (parser->finished != NULL) | ||||
| 		song_free(parser->finished); | ||||
|  | ||||
| 	g_free(parser); | ||||
| } | ||||
|  | ||||
| static const char * | ||||
| cue_next_word(char *p, char **pp) | ||||
| { | ||||
| 	assert(p >= *pp); | ||||
| 	assert(!g_ascii_isspace(*p)); | ||||
|  | ||||
| 	const char *word = p; | ||||
| 	while (*p != 0 && !g_ascii_isspace(*p)) | ||||
| 		++p; | ||||
|  | ||||
| 	*p = 0; | ||||
| 	*pp = p + 1; | ||||
| 	return word; | ||||
| } | ||||
|  | ||||
| static const char * | ||||
| cue_next_quoted(char *p, char **pp) | ||||
| { | ||||
| 	assert(p >= *pp); | ||||
| 	assert(p[-1] == '"'); | ||||
|  | ||||
| 	char *end = strchr(p, '"'); | ||||
| 	if (end == NULL) { | ||||
| 		/* syntax error - ignore it silently */ | ||||
| 		*pp = p + strlen(p); | ||||
| 		return p; | ||||
| 	} | ||||
|  | ||||
| 	*end = 0; | ||||
| 	*pp = end + 1; | ||||
|  | ||||
| 	return p; | ||||
| } | ||||
|  | ||||
| static const char * | ||||
| cue_next_token(char **pp) | ||||
| { | ||||
| 	char *p = strchug_fast(*pp); | ||||
| 	if (*p == 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	return cue_next_word(p, pp); | ||||
| } | ||||
|  | ||||
| static const char * | ||||
| cue_next_value(char **pp) | ||||
| { | ||||
| 	char *p = strchug_fast(*pp); | ||||
| 	if (*p == 0) | ||||
| 		return NULL; | ||||
|  | ||||
| 	if (*p == '"') | ||||
| 		return cue_next_quoted(p + 1, pp); | ||||
| 	else | ||||
| 		return cue_next_word(p, pp); | ||||
| } | ||||
|  | ||||
| static void | ||||
| cue_add_tag(struct tag *tag, enum tag_type type, char *p) | ||||
| { | ||||
| 	const char *value = cue_next_value(&p); | ||||
| 	if (value != NULL) | ||||
| 		tag_add_item(tag, type, value); | ||||
|  | ||||
| } | ||||
|  | ||||
| static void | ||||
| cue_parse_rem(char *p, struct tag *tag) | ||||
| { | ||||
| 	const char *type = cue_next_token(&p); | ||||
| 	if (type == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	enum tag_type type2 = tag_name_parse_i(type); | ||||
| 	if (type2 != TAG_NUM_OF_ITEM_TYPES) | ||||
| 		cue_add_tag(tag, type2, p); | ||||
| } | ||||
|  | ||||
| static struct tag * | ||||
| cue_current_tag(struct cue_parser *parser) | ||||
| { | ||||
| 	if (parser->state == HEADER) | ||||
| 		return parser->tag; | ||||
| 	else if (parser->state == TRACK) | ||||
| 		return parser->current->tag; | ||||
| 	else | ||||
| 		return NULL; | ||||
| } | ||||
|  | ||||
| static int | ||||
| cue_parse_position(const char *p) | ||||
| { | ||||
| 	char *endptr; | ||||
| 	unsigned long minutes = strtoul(p, &endptr, 10); | ||||
| 	if (endptr == p || *endptr != ':') | ||||
| 		return -1; | ||||
|  | ||||
| 	p = endptr + 1; | ||||
| 	unsigned long seconds = strtoul(p, &endptr, 10); | ||||
| 	if (endptr == p || *endptr != ':') | ||||
| 		return -1; | ||||
|  | ||||
| 	p = endptr + 1; | ||||
| 	unsigned long frames = strtoul(p, &endptr, 10); | ||||
| 	if (endptr == p || *endptr != 0) | ||||
| 		return -1; | ||||
|  | ||||
| 	return minutes * 60000 + seconds * 1000 + frames * 1000 / 75; | ||||
| } | ||||
|  | ||||
| static void | ||||
| cue_parser_feed2(struct cue_parser *parser, char *p) | ||||
| { | ||||
| 	assert(parser != NULL); | ||||
| 	assert(p != NULL); | ||||
|  | ||||
| 	const char *command = cue_next_token(&p); | ||||
| 	if (command == NULL) | ||||
| 		return; | ||||
|  | ||||
| 	if (strcmp(command, "REM") == 0) { | ||||
| 		struct tag *tag = cue_current_tag(parser); | ||||
| 		if (tag != NULL) | ||||
| 			cue_parse_rem(p, tag); | ||||
| 	} else if (strcmp(command, "PERFORMER") == 0) { | ||||
| 		struct tag *tag = cue_current_tag(parser); | ||||
| 		if (tag != NULL) | ||||
| 			cue_add_tag(tag, TAG_PERFORMER, p); | ||||
| 	} else if (strcmp(command, "TITLE") == 0) { | ||||
| 		if (parser->state == HEADER) | ||||
| 			cue_add_tag(parser->tag, TAG_ALBUM, p); | ||||
| 		else if (parser->state == TRACK) | ||||
| 			cue_add_tag(parser->current->tag, TAG_TITLE, p); | ||||
| 	} else if (strcmp(command, "FILE") == 0) { | ||||
| 		cue_parser_finish(parser); | ||||
|  | ||||
| 		const char *filename = cue_next_value(&p); | ||||
| 		if (filename == NULL) | ||||
| 			return; | ||||
|  | ||||
| 		const char *type = cue_next_token(&p); | ||||
| 		if (type == NULL) | ||||
| 			return; | ||||
|  | ||||
| 		if (strcmp(type, "WAVE") != 0) { | ||||
| 			parser->state = IGNORE_FILE; | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		parser->state = WAVE; | ||||
| 		g_free(parser->filename); | ||||
| 		parser->filename = g_strdup(filename); | ||||
| 	} else if (parser->state == IGNORE_FILE) { | ||||
| 		return; | ||||
| 	} else if (strcmp(command, "TRACK") == 0) { | ||||
| 		cue_parser_finish(parser); | ||||
|  | ||||
| 		const char *nr = cue_next_token(&p); | ||||
| 		if (nr == NULL) | ||||
| 			return; | ||||
|  | ||||
| 		const char *type = cue_next_token(&p); | ||||
| 		if (type == NULL) | ||||
| 			return; | ||||
|  | ||||
| 		if (strcmp(type, "AUDIO") != 0) { | ||||
| 			parser->state = IGNORE_TRACK; | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		parser->state = TRACK; | ||||
| 		parser->current = song_remote_new(parser->filename); | ||||
| 		assert(parser->current->tag == NULL); | ||||
| 		parser->current->tag = tag_dup(parser->tag); | ||||
| 		tag_add_item(parser->current->tag, TAG_TRACK, nr); | ||||
| 		parser->last_updated = false; | ||||
| 	} else if (parser->state == IGNORE_TRACK) { | ||||
| 		return; | ||||
| 	} else if (parser->state == TRACK && strcmp(command, "INDEX") == 0) { | ||||
| 		const char *nr = cue_next_token(&p); | ||||
| 		if (nr == NULL) | ||||
| 			return; | ||||
|  | ||||
| 		const char *position = cue_next_token(&p); | ||||
| 		if (position == NULL) | ||||
| 			return; | ||||
|  | ||||
| 		int position_ms = cue_parse_position(position); | ||||
| 		if (position_ms < 0) | ||||
| 			return; | ||||
|  | ||||
| 		if (!parser->last_updated && parser->previous != NULL && | ||||
| 		    parser->previous->start_ms < (unsigned)position_ms) { | ||||
| 			parser->last_updated = true; | ||||
| 			parser->previous->end_ms = position_ms; | ||||
| 			parser->previous->tag->time = | ||||
| 				(parser->previous->end_ms - parser->previous->start_ms + 500) / 1000; | ||||
| 		} | ||||
|  | ||||
| 		parser->current->start_ms = position_ms; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void | ||||
| cue_parser_feed(struct cue_parser *parser, const char *line) | ||||
| { | ||||
| 	assert(parser != NULL); | ||||
| 	assert(line != NULL); | ||||
|  | ||||
| 	char *allocated = g_strdup(line); | ||||
| 	cue_parser_feed2(parser, allocated); | ||||
| 	g_free(allocated); | ||||
| } | ||||
|  | ||||
| void | ||||
| cue_parser_finish(struct cue_parser *parser) | ||||
| { | ||||
| 	if (parser->finished != NULL) | ||||
| 		song_free(parser->finished); | ||||
|  | ||||
| 	parser->finished = parser->previous; | ||||
| 	parser->previous = parser->current; | ||||
| 	parser->current = NULL; | ||||
| } | ||||
|  | ||||
| struct song * | ||||
| cue_parser_get(struct cue_parser *parser) | ||||
| { | ||||
| 	assert(parser != NULL); | ||||
|  | ||||
| 	struct song *song = parser->finished; | ||||
| 	parser->finished = NULL; | ||||
| 	return song; | ||||
| } | ||||
							
								
								
									
										58
									
								
								src/cue/cue_parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/cue/cue_parser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
|  * Copyright (C) 2003-2011 The Music Player Daemon Project | ||||
|  * http://www.musicpd.org | ||||
|  * | ||||
|  * 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, write to the Free Software Foundation, Inc., | ||||
|  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  */ | ||||
|  | ||||
| #ifndef MPD_CUE_PARSER_H | ||||
| #define MPD_CUE_PARSER_H | ||||
|  | ||||
| #include "check.h" | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| struct cue_parser * | ||||
| cue_parser_new(void); | ||||
|  | ||||
| void | ||||
| cue_parser_free(struct cue_parser *parser); | ||||
|  | ||||
| /** | ||||
|  * Feed a text line from the CUE file into the parser.  Call | ||||
|  * cue_parser_get() after this to see if a song has been finished. | ||||
|  */ | ||||
| void | ||||
| cue_parser_feed(struct cue_parser *parser, const char *line); | ||||
|  | ||||
| /** | ||||
|  * Tell the parser that the end of the file has been reached.  Call | ||||
|  * cue_parser_get() after this to see if a song has been finished. | ||||
|  * This procedure must be done twice! | ||||
|  */ | ||||
| void | ||||
| cue_parser_finish(struct cue_parser *parser); | ||||
|  | ||||
| /** | ||||
|  * Check if a song was finished by the last cue_parser_feed() or | ||||
|  * cue_parser_finish() call. | ||||
|  * | ||||
|  * @return a song object that must be freed by the caller, or NULL if | ||||
|  * no song was finished at this time | ||||
|  */ | ||||
| struct song * | ||||
| cue_parser_get(struct cue_parser *parser); | ||||
|  | ||||
| #endif | ||||
| @@ -1,244 +0,0 @@ | ||||
| #include "config.h" | ||||
| #include "cue_tag.h" | ||||
| #include "tag.h" | ||||
|  | ||||
| #include <libcue/libcue.h> | ||||
| #include <assert.h> | ||||
|  | ||||
| static struct tag * | ||||
| cue_tag_cd(struct Cdtext *cdtext, struct Rem *rem) | ||||
| { | ||||
| 	struct tag *tag; | ||||
| 	char *tmp; | ||||
|  | ||||
| 	assert(cdtext != NULL); | ||||
|  | ||||
| 	tag = tag_new(); | ||||
|  | ||||
| 	tag_begin_add(tag); | ||||
|  | ||||
| 	/* TAG_ALBUM_ARTIST */ | ||||
| 	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ALBUM_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ALBUM_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ALBUM_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ALBUM_ARTIST, tmp); | ||||
|  | ||||
| 	/* TAG_ARTIST */ | ||||
| 	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ARTIST, tmp); | ||||
|  | ||||
| 	/* TAG_PERFORMER */ | ||||
| 	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_PERFORMER, tmp); | ||||
|  | ||||
| 	/* TAG_COMPOSER */ | ||||
| 	if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_COMPOSER, tmp); | ||||
|  | ||||
| 	/* TAG_ALBUM */ | ||||
| 	if ((tmp = cdtext_get(PTI_TITLE, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ALBUM, tmp); | ||||
|  | ||||
| 	/* TAG_GENRE */ | ||||
| 	if ((tmp = cdtext_get(PTI_GENRE, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_GENRE, tmp); | ||||
|  | ||||
| 	/* TAG_DATE */ | ||||
| 	if ((tmp = rem_get(REM_DATE, rem)) != NULL) | ||||
| 		tag_add_item(tag, TAG_DATE, tmp); | ||||
|  | ||||
| 	/* TAG_COMMENT */ | ||||
| 	if ((tmp = cdtext_get(PTI_MESSAGE, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_COMMENT, tmp); | ||||
|  | ||||
| 	/* TAG_DISC */ | ||||
| 	if ((tmp = cdtext_get(PTI_DISC_ID, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_DISC, tmp); | ||||
|  | ||||
| 	/* stream name, usually empty | ||||
| 	 * tag_add_item(tag, TAG_NAME,); | ||||
| 	 */ | ||||
|  | ||||
| 	/* REM MUSICBRAINZ entry? | ||||
| 	tag_add_item(tag, TAG_MUSICBRAINZ_ARTISTID,); | ||||
| 	tag_add_item(tag, TAG_MUSICBRAINZ_ALBUMID,); | ||||
| 	tag_add_item(tag, TAG_MUSICBRAINZ_ALBUMARTISTID,); | ||||
| 	tag_add_item(tag, TAG_MUSICBRAINZ_TRACKID,); | ||||
| 	*/ | ||||
|  | ||||
| 	tag_end_add(tag); | ||||
|  | ||||
| 	if (tag_is_empty(tag)) { | ||||
| 		tag_free(tag); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return tag; | ||||
| } | ||||
|  | ||||
| static struct tag * | ||||
| cue_tag_track(struct Cdtext *cdtext, struct Rem *rem) | ||||
| { | ||||
| 	struct tag *tag; | ||||
| 	char *tmp; | ||||
|  | ||||
| 	assert(cdtext != NULL); | ||||
|  | ||||
| 	tag = tag_new(); | ||||
|  | ||||
| 	tag_begin_add(tag); | ||||
|  | ||||
| 	/* TAG_ARTIST */ | ||||
| 	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_SONGWRITER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ARTIST, tmp); | ||||
|  | ||||
| 	else if ((tmp = cdtext_get(PTI_ARRANGER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_ARTIST, tmp); | ||||
|  | ||||
| 	/* TAG_TITLE */ | ||||
| 	if ((tmp = cdtext_get(PTI_TITLE, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_TITLE, tmp); | ||||
|  | ||||
| 	/* TAG_GENRE */ | ||||
| 	if ((tmp = cdtext_get(PTI_GENRE, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_GENRE, tmp); | ||||
|  | ||||
| 	/* TAG_DATE */ | ||||
| 	if ((tmp = rem_get(REM_DATE, rem)) != NULL) | ||||
| 		tag_add_item(tag, TAG_DATE, tmp); | ||||
|  | ||||
| 	/* TAG_COMPOSER */ | ||||
| 	if ((tmp = cdtext_get(PTI_COMPOSER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_COMPOSER, tmp); | ||||
|  | ||||
| 	/* TAG_PERFORMER */ | ||||
| 	if ((tmp = cdtext_get(PTI_PERFORMER, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_PERFORMER, tmp); | ||||
|  | ||||
| 	/* TAG_COMMENT */ | ||||
| 	if ((tmp = cdtext_get(PTI_MESSAGE, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_COMMENT, tmp); | ||||
|  | ||||
| 	/* TAG_DISC */ | ||||
| 	if ((tmp = cdtext_get(PTI_DISC_ID, cdtext)) != NULL) | ||||
| 		tag_add_item(tag, TAG_DISC, tmp); | ||||
|  | ||||
| 	tag_end_add(tag); | ||||
|  | ||||
| 	if (tag_is_empty(tag)) { | ||||
| 		tag_free(tag); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	return tag; | ||||
| } | ||||
|  | ||||
| struct tag * | ||||
| cue_tag(struct Cd *cd, unsigned tnum) | ||||
| { | ||||
| 	struct tag *cd_tag, *track_tag, *tag; | ||||
| 	struct Track *track; | ||||
|  | ||||
| 	assert(cd != NULL); | ||||
|  | ||||
| 	track = cd_get_track(cd, tnum); | ||||
| 	if (track == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* tag from CDtext info */ | ||||
| 	cd_tag = cue_tag_cd(cd_get_cdtext(cd), cd_get_rem(cd)); | ||||
|  | ||||
| 	/* tag from TRACKtext info */ | ||||
| 	track_tag = cue_tag_track(track_get_cdtext(track), | ||||
| 				  track_get_rem(track)); | ||||
|  | ||||
| 	tag = tag_merge_replace(cd_tag, track_tag); | ||||
| 	if (tag == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	/* Create a tag number */ | ||||
|  | ||||
| 	tag_clear_items_by_type(tag, TAG_TRACK); | ||||
|  | ||||
| 	char convert_uinttostring[8]; | ||||
| 	snprintf(convert_uinttostring, sizeof(convert_uinttostring), | ||||
| 		 "%02d/%02d", tnum, cd_get_ntrack(cd)); | ||||
| 	tag_add_item(tag, TAG_TRACK, convert_uinttostring); | ||||
|  | ||||
| 	tag->time = track_get_length(track) | ||||
| 	    - track_get_index(track, 1) | ||||
| 	    + track_get_zero_pre(track); | ||||
| 	track = cd_get_track(cd, tnum + 1); | ||||
| 	if (track != NULL) | ||||
| 		tag->time += track_get_index(track, 1) | ||||
| 		    - track_get_zero_pre(track); | ||||
| 	/* libcue returns the track duration in frames, and there are | ||||
| 	   75 frames per second; this formula rounds down */ | ||||
| 	tag->time = tag->time / 75; | ||||
|  | ||||
| 	return tag; | ||||
| } | ||||
|  | ||||
| struct tag * | ||||
| cue_tag_file(FILE *fp, unsigned tnum) | ||||
| { | ||||
| 	struct Cd *cd; | ||||
| 	struct tag *tag; | ||||
|  | ||||
| 	assert(fp != NULL); | ||||
|  | ||||
| 	if (tnum > 256) | ||||
| 		return NULL; | ||||
|  | ||||
| 	cd = cue_parse_file(fp); | ||||
| 	if (cd == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	tag = cue_tag(cd, tnum); | ||||
| 	cd_delete(cd); | ||||
|  | ||||
| 	return tag; | ||||
| } | ||||
|  | ||||
| struct tag * | ||||
| cue_tag_string(const char *str, unsigned tnum) | ||||
| { | ||||
| 	struct Cd *cd; | ||||
| 	struct tag *tag; | ||||
|  | ||||
| 	assert(str != NULL); | ||||
|  | ||||
| 	if (tnum > 256) | ||||
| 		return NULL; | ||||
|  | ||||
| 	cd = cue_parse_string(str); | ||||
| 	if (cd == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	tag = cue_tag(cd, tnum); | ||||
| 	cd_delete(cd); | ||||
|  | ||||
| 	return tag; | ||||
| } | ||||
| @@ -1,23 +0,0 @@ | ||||
| #ifndef MPD_CUE_TAG_H | ||||
| #define MPD_CUE_TAG_H | ||||
|  | ||||
| #include "check.h" | ||||
|  | ||||
| #ifdef HAVE_CUE /* libcue */ | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| struct tag; | ||||
| struct Cd; | ||||
|  | ||||
| struct tag * | ||||
| cue_tag(struct Cd *cd, unsigned tnum); | ||||
|  | ||||
| struct tag * | ||||
| cue_tag_file(FILE *file, unsigned tnum); | ||||
|  | ||||
| struct tag * | ||||
| cue_tag_string(const char *str, unsigned tnum); | ||||
|  | ||||
| #endif /* libcue */ | ||||
| #endif | ||||
| @@ -22,10 +22,11 @@ | ||||
| #include "playlist_plugin.h" | ||||
| #include "tag.h" | ||||
| #include "song.h" | ||||
| #include "cue/cue_tag.h" | ||||
| #include "cue/cue_parser.h" | ||||
| #include "input_stream.h" | ||||
| #include "text_input_stream.h" | ||||
|  | ||||
| #include <glib.h> | ||||
| #include <libcue/libcue.h> | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
|  | ||||
| @@ -35,32 +36,21 @@ | ||||
| struct cue_playlist { | ||||
| 	struct playlist_provider base; | ||||
|  | ||||
| 	struct Cd *cd; | ||||
|  | ||||
| 	unsigned next; | ||||
| 	struct input_stream *is; | ||||
| 	struct text_input_stream *tis; | ||||
| 	struct cue_parser *parser; | ||||
| }; | ||||
|  | ||||
| static struct playlist_provider * | ||||
| cue_playlist_open_uri(const char *uri, | ||||
| 		      G_GNUC_UNUSED GMutex *mutex, G_GNUC_UNUSED GCond *cond) | ||||
| cue_playlist_open_stream(struct input_stream *is) | ||||
| { | ||||
| 	struct cue_playlist *playlist; | ||||
| 	FILE *file; | ||||
| 	struct Cd *cd; | ||||
|  | ||||
| 	file = fopen(uri, "rt"); | ||||
| 	if (file == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	cd = cue_parse_file(file); | ||||
| 	fclose(file); | ||||
| 	if (cd == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	playlist = g_new(struct cue_playlist, 1); | ||||
| 	struct cue_playlist *playlist = g_new(struct cue_playlist, 1); | ||||
| 	playlist_provider_init(&playlist->base, &cue_playlist_plugin); | ||||
| 	playlist->cd = cd; | ||||
| 	playlist->next = 1; | ||||
|  | ||||
| 	playlist->is = is; | ||||
| 	playlist->tis = text_input_stream_new(is); | ||||
| 	playlist->parser = cue_parser_new(); | ||||
|  | ||||
|  | ||||
| 	return &playlist->base; | ||||
| } | ||||
| @@ -70,7 +60,8 @@ cue_playlist_close(struct playlist_provider *_playlist) | ||||
| { | ||||
| 	struct cue_playlist *playlist = (struct cue_playlist *)_playlist; | ||||
|  | ||||
| 	cd_delete(playlist->cd); | ||||
| 	cue_parser_free(playlist->parser); | ||||
| 	text_input_stream_free(playlist->tis); | ||||
| 	g_free(playlist); | ||||
| } | ||||
|  | ||||
| @@ -78,45 +69,21 @@ static struct song * | ||||
| cue_playlist_read(struct playlist_provider *_playlist) | ||||
| { | ||||
| 	struct cue_playlist *playlist = (struct cue_playlist *)_playlist; | ||||
| 	struct Track *track; | ||||
| 	struct tag *tag; | ||||
| 	const char *filename; | ||||
| 	struct song *song; | ||||
|  | ||||
| 	track = cd_get_track(playlist->cd, playlist->next); | ||||
| 	if (track == NULL) | ||||
| 		return NULL; | ||||
| 	struct song *song = cue_parser_get(playlist->parser); | ||||
| 	if (song != NULL) | ||||
| 		return song; | ||||
|  | ||||
| 	tag = cue_tag(playlist->cd, playlist->next); | ||||
| 	if (tag == NULL) | ||||
| 		return NULL; | ||||
|  | ||||
| 	++playlist->next; | ||||
|  | ||||
| 	filename = track_get_filename(track); | ||||
| 	if (*filename == 0 || filename[0] == '.' || | ||||
| 	    strchr(filename, '/') != NULL) { | ||||
| 		/* unsafe characters found, bail out */ | ||||
| 		tag_free(tag); | ||||
| 		return NULL; | ||||
| 	const char *line; | ||||
| 	while ((line = text_input_stream_read(playlist->tis)) != NULL) { | ||||
| 		cue_parser_feed(playlist->parser, line); | ||||
| 		song = cue_parser_get(playlist->parser); | ||||
| 		if (song != NULL) | ||||
| 			return song; | ||||
| 	} | ||||
|  | ||||
| 	song = song_remote_new(filename); | ||||
| 	song->tag = tag; | ||||
| 	song->start_ms = ((track_get_start(track) | ||||
| 			   + track_get_index(track, 1) | ||||
| 			   - track_get_zero_pre(track)) * 1000) / 75; | ||||
|  | ||||
| 	/* append pregap of the next track to the end of this one */ | ||||
| 	track = cd_get_track(playlist->cd, playlist->next); | ||||
| 	if (track != NULL) | ||||
| 		song->end_ms = ((track_get_start(track) | ||||
| 				 + track_get_index(track, 1) | ||||
| 				 - track_get_zero_pre(track)) * 1000) / 75; | ||||
| 	else | ||||
| 		song->end_ms = 0; | ||||
|  | ||||
| 	return song; | ||||
| 	cue_parser_finish(playlist->parser); | ||||
| 	return cue_parser_get(playlist->parser); | ||||
| } | ||||
|  | ||||
| static const char *const cue_playlist_suffixes[] = { | ||||
| @@ -132,7 +99,7 @@ static const char *const cue_playlist_mime_types[] = { | ||||
| const struct playlist_plugin cue_playlist_plugin = { | ||||
| 	.name = "cue", | ||||
|  | ||||
| 	.open_uri = cue_playlist_open_uri, | ||||
| 	.open_stream = cue_playlist_open_stream, | ||||
| 	.close = cue_playlist_close, | ||||
| 	.read = cue_playlist_read, | ||||
|  | ||||
|   | ||||
| @@ -54,9 +54,7 @@ static const struct playlist_plugin *const playlist_plugins[] = { | ||||
| #ifdef ENABLE_LASTFM | ||||
| 	&lastfm_playlist_plugin, | ||||
| #endif | ||||
| #ifdef HAVE_CUE | ||||
| 	&cue_playlist_plugin, | ||||
| #endif | ||||
| #ifdef HAVE_FLAC | ||||
| 	&flac_playlist_plugin, | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann