song.c 4.71 KB
Newer Older
Warren Dukes's avatar
Warren Dukes committed
1
/* the Music Player Daemon (MPD)
2
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
Warren Dukes's avatar
Warren Dukes committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "song.h"
#include "ls.h"
#include "directory.h"
22
#include "mapper.h"
23
#include "path.h"
24
#include "playlist.h"
25
#include "decoder_list.h"
26
#include "decoder_api.h"
27
#include "tag_id3.h"
28

29 30
#include <glib.h>

Max Kellermann's avatar
Max Kellermann committed
31 32 33 34 35
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

36
static struct song *
37
song_alloc(const char *url, struct directory *parent)
38
{
39
	size_t urllen;
40
	struct song *song;
41 42 43 44

	assert(url);
	urllen = strlen(url);
	assert(urllen);
45
	song = g_malloc(sizeof(*song) - sizeof(song->url) + urllen + 1);
46 47 48

	song->tag = NULL;
	memcpy(song->url, url, urllen + 1);
Max Kellermann's avatar
Max Kellermann committed
49
	song->parent = parent;
50 51 52

	return song;
}
53

54
struct song *
55 56 57 58 59 60 61 62
song_remote_new(const char *url)
{
	return song_alloc(url, NULL);
}

struct song *
song_file_new(const char *path, struct directory *parent)
{
63
	assert((parent == NULL) == (*path == '/'));
64 65 66 67 68 69

	return song_alloc(path, parent);
}

struct song *
song_file_load(const char *path, struct directory *parent)
Avuton Olrich's avatar
Avuton Olrich committed
70
{
71
	struct song *song;
72
	bool ret;
Warren Dukes's avatar
Warren Dukes committed
73

74
	assert((parent == NULL) == (*path == '/'));
75 76
	assert(!uri_has_scheme(path));
	assert(strchr(path, '\n') == NULL);
Warren Dukes's avatar
Warren Dukes committed
77

78 79
	song = song_file_new(path, parent);

80
	//in archive ?
81
	if (parent != NULL && parent->device == DEVICE_INARCHIVE) {
82 83 84 85
		ret = song_file_update_inarchive(song);
	} else {
		ret = song_file_update(song);
	}
86
	if (!ret) {
Max Kellermann's avatar
Max Kellermann committed
87
		song_free(song);
88
		return NULL;
89 90
	}

Warren Dukes's avatar
Warren Dukes committed
91 92 93
	return song;
}

94
void
Max Kellermann's avatar
Max Kellermann committed
95
song_free(struct song *song)
Avuton Olrich's avatar
Avuton Olrich committed
96 97
{
	if (song->tag)
98
		tag_free(song->tag);
99
	g_free(song);
100 101
}

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
/**
 * Attempts to load APE or ID3 tags from the specified file.
 */
static struct tag *
tag_load_fallback(const char *path)
{
	struct tag *tag = tag_ape_load(path);
	if (tag == NULL)
		tag = tag_id3_load(path);
	return tag;
}

/**
 * The decoder plugin failed to load any tags: fall back to the APE or
 * ID3 tag loader.
 */
static struct tag *
tag_fallback(const char *path, struct tag *tag)
{
	struct tag *fallback = tag_load_fallback(path);

	if (fallback != NULL) {
		/* tag was successfully loaded: copy the song
		   duration, and destroy the old (empty) tag */
		fallback->time = tag->time;
		tag_free(tag);
		return fallback;
	} else
		/* no APE/ID3 tag found: return the empty tag */
		return tag;
}

134
bool
Max Kellermann's avatar
Max Kellermann committed
135
song_file_update(struct song *song)
Avuton Olrich's avatar
Avuton Olrich committed
136
{
137
	const char *suffix;
138
	char *path_fs;
139
	const struct decoder_plugin *plugin;
140
	struct stat st;
141

142
	assert(song_is_file(song));
Warren Dukes's avatar
Warren Dukes committed
143

144 145
	/* check if there's a suffix and a plugin */

146
	suffix = uri_get_suffix(song->url);
147 148 149 150 151 152 153
	if (suffix == NULL)
		return false;

	plugin = decoder_plugin_from_suffix(suffix, false);
	if (plugin == NULL)
		return false;

154
	path_fs = map_song_fs(song);
155 156
	if (path_fs == NULL)
		return false;
157

158 159
	if (song->tag != NULL) {
		tag_free(song->tag);
160 161
		song->tag = NULL;
	}
162

163 164
	if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) {
		g_free(path_fs);
165
		return false;
166
	}
167 168 169

	song->mtime = st.st_mtime;

170
	do {
171
		song->tag = plugin->tag_dup(path_fs);
172 173 174 175 176
		if (song->tag != NULL)
			break;

		plugin = decoder_plugin_from_suffix(suffix, true);
	} while (plugin != NULL);
177

178 179 180
	if (song->tag != NULL && tag_is_empty(song->tag))
		song->tag = tag_fallback(path_fs, song->tag);

181
	g_free(path_fs);
182
	return song->tag != NULL;
Warren Dukes's avatar
Warren Dukes committed
183 184
}

185 186 187
bool
song_file_update_inarchive(struct song *song)
{
188
	const char *suffix;
189 190 191 192
	const struct decoder_plugin *plugin;

	assert(song_is_file(song));

193 194
	/* check if there's a suffix and a plugin */

195
	suffix = uri_get_suffix(song->url);
196
	if (suffix == NULL)
197 198
		return false;

199 200 201 202 203
	plugin = decoder_plugin_from_suffix(suffix, false);
	if (plugin == NULL)
		return false;

	if (song->tag != NULL)
204
		tag_free(song->tag);
205

206 207 208
	//accept every file that has music suffix
	//because we dont support tag reading throught
	//input streams
209 210 211
	song->tag = tag_new();

	return true;
212 213
}

214
char *
215
song_get_uri(const struct song *song)
Avuton Olrich's avatar
Avuton Olrich committed
216
{
217
	assert(song != NULL);
218
	assert(*song->url);
219

220
	if (!song_in_database(song) || directory_is_root(song->parent))
221
		return g_strdup(song->url);
Eric Wong's avatar
Eric Wong committed
222
	else
223 224
		return g_strconcat(directory_get_path(song->parent),
				   "/", song->url, NULL);
225
}