doc: Remove superfluous comment already described in footnotes.
[mpd-mk.git] / src / song.c
blob76c25f44f74f94b029579a0b6f5c208d22ee32cc
1 /*
2 * Copyright (C) 2003-2009 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "song.h"
21 #include "uri.h"
22 #include "directory.h"
23 #include "mapper.h"
24 #include "decoder_list.h"
25 #include "decoder_plugin.h"
26 #include "tag_ape.h"
27 #include "tag_id3.h"
28 #include "tag.h"
30 #include <glib.h>
32 #include <assert.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <string.h>
38 static struct song *
39 song_alloc(const char *url, struct directory *parent)
41 size_t urllen;
42 struct song *song;
44 assert(url);
45 urllen = strlen(url);
46 assert(urllen);
47 song = g_malloc(sizeof(*song) - sizeof(song->url) + urllen + 1);
49 song->tag = NULL;
50 memcpy(song->url, url, urllen + 1);
51 song->parent = parent;
52 song->mtime = 0;
54 return song;
57 struct song *
58 song_remote_new(const char *url)
60 return song_alloc(url, NULL);
63 struct song *
64 song_file_new(const char *path, struct directory *parent)
66 assert((parent == NULL) == (*path == '/'));
68 return song_alloc(path, parent);
71 struct song *
72 song_file_load(const char *path, struct directory *parent)
74 struct song *song;
75 bool ret;
77 assert((parent == NULL) == (*path == '/'));
78 assert(!uri_has_scheme(path));
79 assert(strchr(path, '\n') == NULL);
81 song = song_file_new(path, parent);
83 //in archive ?
84 if (parent != NULL && parent->device == DEVICE_INARCHIVE) {
85 ret = song_file_update_inarchive(song);
86 } else {
87 ret = song_file_update(song);
89 if (!ret) {
90 song_free(song);
91 return NULL;
94 return song;
97 void
98 song_free(struct song *song)
100 if (song->tag)
101 tag_free(song->tag);
102 g_free(song);
106 * Attempts to load APE or ID3 tags from the specified file.
108 static struct tag *
109 tag_load_fallback(const char *path)
111 struct tag *tag = tag_ape_load(path);
112 if (tag == NULL)
113 tag = tag_id3_load(path);
114 return tag;
118 * The decoder plugin failed to load any tags: fall back to the APE or
119 * ID3 tag loader.
121 static struct tag *
122 tag_fallback(const char *path, struct tag *tag)
124 struct tag *fallback = tag_load_fallback(path);
126 if (fallback != NULL) {
127 /* tag was successfully loaded: copy the song
128 duration, and destroy the old (empty) tag */
129 fallback->time = tag->time;
130 tag_free(tag);
131 return fallback;
132 } else
133 /* no APE/ID3 tag found: return the empty tag */
134 return tag;
137 bool
138 song_file_update(struct song *song)
140 const char *suffix;
141 char *path_fs;
142 const struct decoder_plugin *plugin;
143 struct stat st;
145 assert(song_is_file(song));
147 /* check if there's a suffix and a plugin */
149 suffix = uri_get_suffix(song->url);
150 if (suffix == NULL)
151 return false;
153 plugin = decoder_plugin_from_suffix(suffix, false);
154 if (plugin == NULL)
155 return false;
157 path_fs = map_song_fs(song);
158 if (path_fs == NULL)
159 return false;
161 if (song->tag != NULL) {
162 tag_free(song->tag);
163 song->tag = NULL;
166 if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) {
167 g_free(path_fs);
168 return false;
171 song->mtime = st.st_mtime;
173 do {
174 song->tag = plugin->tag_dup(path_fs);
175 if (song->tag != NULL)
176 break;
178 plugin = decoder_plugin_from_suffix(suffix, true);
179 } while (plugin != NULL);
181 if (song->tag != NULL && tag_is_empty(song->tag))
182 song->tag = tag_fallback(path_fs, song->tag);
184 g_free(path_fs);
185 return song->tag != NULL;
188 bool
189 song_file_update_inarchive(struct song *song)
191 const char *suffix;
192 const struct decoder_plugin *plugin;
194 assert(song_is_file(song));
196 /* check if there's a suffix and a plugin */
198 suffix = uri_get_suffix(song->url);
199 if (suffix == NULL)
200 return false;
202 plugin = decoder_plugin_from_suffix(suffix, false);
203 if (plugin == NULL)
204 return false;
206 if (song->tag != NULL)
207 tag_free(song->tag);
209 //accept every file that has music suffix
210 //because we dont support tag reading throught
211 //input streams
212 song->tag = tag_new();
214 return true;
217 char *
218 song_get_uri(const struct song *song)
220 assert(song != NULL);
221 assert(*song->url);
223 if (!song_in_database(song) || directory_is_root(song->parent))
224 return g_strdup(song->url);
225 else
226 return g_strconcat(directory_get_path(song->parent),
227 "/", song->url, NULL);