No issues found
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2012 Jonathan Matthew
4 *
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, or (at your option)
8 * any later version.
9 *
10 * The Rhythmbox authors hereby grant permission for non-GPL compatible
11 * GStreamer plugins to be used and distributed together with GStreamer
12 * and Rhythmbox. This permission is above and beyond the permissions granted
13 * by the GPL license by which Rhythmbox is covered. If you modify this code
14 * you may extend this exception to your version of the code, but you are not
15 * obligated to do so. If you do not wish to do so, delete this exception
16 * statement from your version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26 */
27
28 #include "config.h"
29
30 #include <glib/gi18n.h>
31 #include <gst/gst.h>
32 #include <gst/cdda/gstcddabasesrc.h>
33 #include <gio/gio.h>
34
35 #include "rb-audiocd-info.h"
36
37 static gboolean
38 read_gst_disc_info (RBAudioCDInfo *info, GError **error)
39 {
40 GstElement *source;
41 GstElement *sink;
42 GstElement *pipeline;
43 GstFormat format;
44 GstFormat out_format;
45 GstBus *bus;
46 gint64 num_tracks;
47 gboolean done;
48 int i;
49
50 source = gst_element_make_from_uri (GST_URI_SRC, "cdda://", NULL);
51 if (source == NULL) {
52 /* if cdparanoiasrc wasn't in base and installed by default
53 * everywhere, plugin install might be worth trying here.
54 */
55 g_set_error_literal (error,
56 GST_CORE_ERROR,
57 GST_CORE_ERROR_MISSING_PLUGIN,
58 _("Could not find a GStreamer CD source plugin"));
59 return FALSE;
60 }
61
62 g_object_set (source, "device", info->device, NULL);
63 pipeline = gst_pipeline_new (NULL);
64 sink = gst_element_factory_make ("fakesink", NULL);
65 gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
66 gst_element_link (source, sink);
67
68 if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
69 g_object_set (source, "paranoia-mode", 0, NULL);
70
71 gst_element_set_state (pipeline, GST_STATE_PAUSED);
72
73 bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
74 done = FALSE;
75 while (done == FALSE) {
76 GstMessage *msg;
77 GstTagList *tags;
78
79 msg = gst_bus_timed_pop (bus, 3 * GST_SECOND);
80 if (msg == NULL)
81 break;
82
83 switch (GST_MESSAGE_TYPE (msg)) {
84 case GST_MESSAGE_TAG:
85 gst_message_parse_tag (msg, &tags);
86
87 gst_tag_list_get_string (tags,
88 GST_TAG_CDDA_MUSICBRAINZ_DISCID,
89 &info->musicbrainz_disc_id);
90 gst_tag_list_get_string (tags,
91 GST_TAG_CDDA_MUSICBRAINZ_DISCID_FULL,
92 &info->musicbrainz_full_disc_id);
93
94 gst_tag_list_free (tags);
95 break;
96
97 case GST_MESSAGE_STATE_CHANGED:
98 if (GST_MESSAGE_SRC (msg) == GST_OBJECT (pipeline)) {
99 GstState oldstate;
100 GstState newstate;
101 GstState pending;
102
103 gst_message_parse_state_changed (msg, &oldstate, &newstate, &pending);
104 if (newstate == GST_STATE_PAUSED && pending == GST_STATE_VOID_PENDING)
105 done = TRUE;
106 }
107 break;
108 case GST_MESSAGE_ERROR:
109 gst_message_parse_error (msg, error, NULL);
110 done = TRUE;
111 break;
112 default:
113 break;
114 }
115
116 gst_message_unref (msg);
117 }
118
119 if (*error == NULL) {
120 format = gst_format_get_by_nick ("track");
121 out_format = format;
122 gst_element_query_duration (source, &out_format, &num_tracks);
123 info->num_tracks = num_tracks;
124
125 info->tracks = g_new0 (RBAudioCDTrack, num_tracks);
126 for (i = 0; i < num_tracks; i++) {
127 RBAudioCDTrack *track = &info->tracks[i];
128 GstCddaBaseSrcTrack *gst_track = &GST_CDDA_BASE_SRC (source)->tracks[i];
129 guint64 duration = 0;
130
131 track->is_audio = gst_track->is_audio;
132 track->track_num = gst_track->num;
133
134 gst_tag_list_get_uint64 (gst_track->tags, GST_TAG_DURATION, &duration);
135 track->duration = (duration / GST_MSECOND);
136 }
137 }
138
139 gst_element_set_state (pipeline, GST_STATE_NULL);
140 gst_object_unref (bus);
141 gst_object_unref (pipeline);
142 return (*error == NULL);
143 }
144
145 static void
146 read_gvfs_disc_info (RBAudioCDInfo *info)
147 {
148 GFile *cdda;
149 GFileInfo *fileinfo;
150 GFileEnumerator *tracks;
151 const char *attr;
152 char *uri;
153 char *dev;
154
155 dev = g_path_get_basename (info->device);
156 uri = g_strdup_printf ("cdda://%s", dev);
157 g_free (dev);
158
159 cdda = g_file_new_for_uri (uri);
160 g_free (uri);
161
162 fileinfo = g_file_query_info (cdda, "xattr::*", G_FILE_QUERY_INFO_NONE, NULL, NULL);
163 if (fileinfo == NULL) {
164 g_object_unref (cdda);
165 return;
166 }
167
168 attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.title");
169 if (attr != NULL) {
170 info->album = g_strdup (attr);
171 }
172 attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.artist");
173 if (attr != NULL) {
174 info->album_artist = g_strdup (attr);
175 }
176 attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.genre");
177 if (attr != NULL) {
178 info->genre = g_strdup (attr);
179 }
180
181 tracks = g_file_enumerate_children (cdda, G_FILE_ATTRIBUTE_STANDARD_NAME ",xattr::*", G_FILE_QUERY_INFO_NONE, NULL, NULL);
182 if (tracks != NULL) {
183 for (fileinfo = g_file_enumerator_next_file (tracks, NULL, NULL);
184 fileinfo != NULL;
185 fileinfo = g_file_enumerator_next_file (tracks, NULL, NULL)) {
186 const char *name;
187 const char *attr;
188 int track_num;
189
190 name = g_file_info_get_name (fileinfo);
191 if (name == NULL || sscanf (name, "Track %d.wav", &track_num) != 1) {
192 continue;
193 }
194
195 if (track_num < 1 || track_num > info->num_tracks) {
196 continue;
197 }
198 g_assert (track_num == info->tracks[track_num-1].track_num);
199
200 attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.title");
201 if (attr != NULL) {
202 info->tracks[track_num - 1].title = g_strdup (attr);
203 }
204 attr = g_file_info_get_attribute_string (fileinfo, "xattr::org.gnome.audio.artist");
205 if (attr != NULL) {
206 info->tracks[track_num - 1].artist = g_strdup (attr);
207 }
208 }
209 }
210 g_object_unref (tracks);
211
212 g_object_unref (cdda);
213 }
214
215 static void
216 audiocd_info_thread (GSimpleAsyncResult *result, GObject *object, GCancellable *cancellable)
217 {
218 RBAudioCDInfo *info;
219 GError *error = NULL;
220
221 info = g_simple_async_result_get_op_res_gpointer (result);
222
223 if (read_gst_disc_info (info, &error)) {
224 read_gvfs_disc_info (info);
225 } else {
226 rb_audiocd_info_free (info);
227 g_simple_async_result_set_op_res_gpointer (result, NULL, NULL);
228 g_simple_async_result_take_error (result, error);
229 }
230 }
231
232 void
233 rb_audiocd_info_free (RBAudioCDInfo *info)
234 {
235 int i;
236
237 g_free (info->device);
238 g_free (info->musicbrainz_disc_id);
239 g_free (info->musicbrainz_full_disc_id);
240 g_free (info->album);
241 g_free (info->genre);
242 g_free (info->album_artist);
243
244 for (i = 0; i < info->num_tracks; i++) {
245 g_free (info->tracks[i].artist);
246 g_free (info->tracks[i].title);
247 }
248 g_free (info->tracks);
249 g_free (info);
250 }
251
252 void
253 rb_audiocd_info_get (const char *device,
254 GCancellable *cancellable,
255 GAsyncReadyCallback callback,
256 gpointer user_data)
257 {
258 GSimpleAsyncResult *result;
259 RBAudioCDInfo *info;
260
261 result = g_simple_async_result_new (NULL, callback, user_data, rb_audiocd_info_get);
262 g_simple_async_result_set_check_cancellable (result, cancellable);
263
264 info = g_new0 (RBAudioCDInfo, 1);
265 info->device = g_strdup (device);
266 g_simple_async_result_set_op_res_gpointer (result, info, NULL);
267
268 g_simple_async_result_run_in_thread (result, audiocd_info_thread, G_PRIORITY_DEFAULT, cancellable);
269 }
270
271 RBAudioCDInfo *
272 rb_audiocd_info_finish (GAsyncResult *result,
273 GError **error)
274 {
275 g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, rb_audiocd_info_get),
276 NULL);
277 if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
278 return NULL;
279
280 return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
281 }