Skip to content

Commit afd600b

Browse files
committed
Handle metadata discovery at creation of AudioObjects
1 parent abb1801 commit afd600b

File tree

4 files changed

+131
-128
lines changed

4 files changed

+131
-128
lines changed

src/AudioObject.vala

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,88 @@ public class Music.AudioObject : Object {
99
public string artist { get; set; }
1010
public string title { get; set; }
1111
public int64 duration { get; set; default = 0; }
12+
private static MetadataDiscoverer discoverer = new MetadataDiscoverer();
1213

1314
public AudioObject (string uri) {
14-
Object (uri: uri);
15+
Object (
16+
uri: uri,
17+
title: uri
18+
);
19+
}
20+
21+
construct {
22+
discoverer.request (this);
23+
}
24+
25+
public void update_metadata (Gst.PbUtils.DiscovererInfo info) {
26+
duration = (int64) info.get_duration ();
27+
28+
unowned Gst.TagList? tag_list = info.get_tags ();
29+
30+
string _title;
31+
tag_list.get_string (Gst.Tags.TITLE, out _title);
32+
if (_title != null) {
33+
title = _title;
34+
}
35+
36+
string _artist;
37+
tag_list.get_string (Gst.Tags.ARTIST, out _artist);
38+
if (_artist != null) {
39+
artist = _artist;
40+
} else if (_title != null) { // Don't set artist for files without tags
41+
artist = _("Unknown");
42+
}
43+
44+
var sample = get_cover_sample (tag_list);
45+
if (sample != null) {
46+
var buffer = sample.get_buffer ();
47+
48+
if (buffer != null) {
49+
texture = Gdk.Texture.for_pixbuf (get_pixbuf_from_buffer (buffer));
50+
}
51+
}
52+
}
53+
54+
private Gst.Sample? get_cover_sample (Gst.TagList tag_list) {
55+
Gst.Sample cover_sample = null;
56+
Gst.Sample sample;
57+
for (int i = 0; tag_list.get_sample_index (Gst.Tags.IMAGE, i, out sample); i++) {
58+
var caps = sample.get_caps ();
59+
unowned Gst.Structure caps_struct = caps.get_structure (0);
60+
int image_type = Gst.Tag.ImageType.UNDEFINED;
61+
caps_struct.get_enum ("image-type", typeof (Gst.Tag.ImageType), out image_type);
62+
if (image_type == Gst.Tag.ImageType.UNDEFINED && cover_sample == null) {
63+
cover_sample = sample;
64+
} else if (image_type == Gst.Tag.ImageType.FRONT_COVER) {
65+
return sample;
66+
}
67+
}
68+
69+
return cover_sample;
70+
}
71+
72+
private Gdk.Pixbuf? get_pixbuf_from_buffer (Gst.Buffer buffer) {
73+
Gst.MapInfo map_info;
74+
75+
if (!buffer.map (out map_info, Gst.MapFlags.READ)) {
76+
warning ("Could not map memory buffer");
77+
return null;
78+
}
79+
80+
Gdk.Pixbuf pix = null;
81+
82+
try {
83+
var loader = new Gdk.PixbufLoader ();
84+
85+
if (loader.write (map_info.data) && loader.close ()) {
86+
pix = loader.get_pixbuf ();
87+
}
88+
} catch (Error err) {
89+
warning ("Error processing image data: %s", err.message);
90+
}
91+
92+
buffer.unmap (map_info);
93+
94+
return pix;
1595
}
1696
}

src/MetadataDiscoverer.vala

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
[SingleInstance]
2+
public class Music.MetadataDiscoverer : Object {
3+
private Gst.PbUtils.Discoverer discoverer;
4+
private HashTable<string, AudioObject> objects_to_update;
5+
6+
construct {
7+
try {
8+
discoverer = new Gst.PbUtils.Discoverer ((Gst.ClockTime) (5 * Gst.SECOND));
9+
discoverer.discovered.connect (relay_metadata);
10+
discoverer.finished.connect (discoverer.stop);
11+
} catch (Error e) {
12+
critical ("Unable to start Gstreamer Discoverer: %s", e.message);
13+
}
14+
objects_to_update = new HashTable<string, AudioObject> (str_hash, str_equal);
15+
}
16+
17+
public void request (AudioObject audio) {
18+
objects_to_update.insert (audio.uri, audio);
19+
discoverer.start ();
20+
discoverer.discover_uri_async (audio.uri);
21+
}
22+
23+
private void relay_metadata (Gst.PbUtils.DiscovererInfo info, Error? err) {
24+
string uri = info.get_uri ();
25+
var audio_obj = objects_to_update.get (uri);
26+
objects_to_update.remove (uri);
27+
switch (info.get_result ()) {
28+
case Gst.PbUtils.DiscovererResult.URI_INVALID:
29+
critical ("Couldn't read metadata for '%s': invalid URI.", uri);
30+
return;
31+
case Gst.PbUtils.DiscovererResult.ERROR:
32+
critical ("Couldn't read metadata for '%s': %s", uri, err.message);
33+
return;
34+
case Gst.PbUtils.DiscovererResult.TIMEOUT:
35+
critical ("Couldn't read metadata for '%s': Discovery timed out.", uri);
36+
return;
37+
case Gst.PbUtils.DiscovererResult.BUSY:
38+
critical ("Couldn't read metadata for '%s': Already discovering a file.", uri);
39+
return;
40+
case Gst.PbUtils.DiscovererResult.MISSING_PLUGINS:
41+
critical ("Couldn't read metadata for '%s': Missing plugins.", uri);
42+
return;
43+
default:
44+
break;
45+
}
46+
47+
audio_obj.update_metadata (info);
48+
}
49+
}

src/PlaybackManager.vala

Lines changed: 0 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ public class Music.PlaybackManager : Object {
2121
}
2222

2323
private dynamic Gst.Element playbin;
24-
private Gst.PbUtils.Discoverer discoverer;
2524
private uint progress_timer = 0;
2625
private Settings settings;
2726

@@ -46,14 +45,6 @@ public class Music.PlaybackManager : Object {
4645
bus.add_watch (0, bus_callback);
4746
bus.enable_sync_message_emission ();
4847

49-
try {
50-
discoverer = new Gst.PbUtils.Discoverer ((Gst.ClockTime) (5 * Gst.SECOND));
51-
discoverer.discovered.connect (update_metadata);
52-
discoverer.finished.connect (discoverer.stop);
53-
} catch (Error e) {
54-
critical ("Unable to start Gstreamer Discoverer: %s", e.message);
55-
}
56-
5748
queue_liststore.items_changed.connect (() => {
5849
var shuffle_action_action = (SimpleAction) GLib.Application.get_default ().lookup_action (Application.ACTION_SHUFFLE);
5950
has_items = queue_liststore.get_n_items () > 0;
@@ -91,22 +82,10 @@ public class Music.PlaybackManager : Object {
9182

9283
// Files[] must not contain any null entries
9384
public void queue_files (File[] files) {
94-
discoverer.start ();
9585
int invalids = 0;
9686
foreach (unowned var file in files) {
9787
if (file.query_exists () && "audio" in ContentType.guess (file.get_uri (), null, null)) {
9888
var audio_object = new AudioObject (file.get_uri ());
99-
100-
string? basename = file.get_basename ();
101-
102-
if (basename != null) {
103-
audio_object.title = basename;
104-
} else {
105-
audio_object.title = audio_object.uri;
106-
}
107-
108-
discoverer.discover_uri_async (audio_object.uri);
109-
11089
queue_liststore.append (audio_object);
11190
} else {
11291
invalids++;
@@ -151,69 +130,6 @@ public class Music.PlaybackManager : Object {
151130
queue_liststore.remove_all ();
152131
}
153132

154-
private void update_metadata (Gst.PbUtils.DiscovererInfo info, Error? err) {
155-
string uri = info.get_uri ();
156-
switch (info.get_result ()) {
157-
case Gst.PbUtils.DiscovererResult.URI_INVALID:
158-
critical ("Couldn't read metadata for '%s': invalid URI.", uri);
159-
return;
160-
case Gst.PbUtils.DiscovererResult.ERROR:
161-
critical ("Couldn't read metadata for '%s': %s", uri, err.message);
162-
return;
163-
case Gst.PbUtils.DiscovererResult.TIMEOUT:
164-
critical ("Couldn't read metadata for '%s': Discovery timed out.", uri);
165-
return;
166-
case Gst.PbUtils.DiscovererResult.BUSY:
167-
critical ("Couldn't read metadata for '%s': Already discovering a file.", uri);
168-
return;
169-
case Gst.PbUtils.DiscovererResult.MISSING_PLUGINS:
170-
critical ("Couldn't read metadata for '%s': Missing plugins.", uri);
171-
return;
172-
default:
173-
break;
174-
}
175-
176-
EqualFunc<string> equal_func = (a, b) => {
177-
return ((AudioObject) a).uri == ((AudioObject) b).uri;
178-
};
179-
180-
var temp_audio_object = new AudioObject (uri);
181-
182-
uint position = -1;
183-
queue_liststore.find_with_equal_func (temp_audio_object, equal_func, out position);
184-
185-
if (position != -1) {
186-
var audio_object = (AudioObject) queue_liststore.get_item (position);
187-
audio_object.duration = (int64) info.get_duration ();
188-
189-
unowned Gst.TagList? tag_list = info.get_tags ();
190-
191-
string _title;
192-
tag_list.get_string (Gst.Tags.TITLE, out _title);
193-
if (_title != null) {
194-
audio_object.title = _title;
195-
}
196-
197-
string _artist;
198-
tag_list.get_string (Gst.Tags.ARTIST, out _artist);
199-
if (_artist != null) {
200-
audio_object.artist = _artist;
201-
} else if (_title != null) { // Don't set artist for files without tags
202-
audio_object.artist = _("Unknown");
203-
}
204-
205-
var sample = get_cover_sample (tag_list);
206-
if (sample != null) {
207-
var buffer = sample.get_buffer ();
208-
209-
if (buffer != null) {
210-
audio_object.texture = Gdk.Texture.for_pixbuf (get_pixbuf_from_buffer (buffer));
211-
}
212-
}
213-
} else {
214-
critical ("Couldn't find '%s' in queue", uri);
215-
}
216-
}
217133

218134
private bool bus_callback (Gst.Bus bus, Gst.Message message) {
219135
switch (message.type) {
@@ -397,47 +313,4 @@ public class Music.PlaybackManager : Object {
397313
previous_action.set_enabled (previous_sensitive);
398314

399315
}
400-
401-
private Gst.Sample? get_cover_sample (Gst.TagList tag_list) {
402-
Gst.Sample cover_sample = null;
403-
Gst.Sample sample;
404-
for (int i = 0; tag_list.get_sample_index (Gst.Tags.IMAGE, i, out sample); i++) {
405-
var caps = sample.get_caps ();
406-
unowned Gst.Structure caps_struct = caps.get_structure (0);
407-
int image_type = Gst.Tag.ImageType.UNDEFINED;
408-
caps_struct.get_enum ("image-type", typeof (Gst.Tag.ImageType), out image_type);
409-
if (image_type == Gst.Tag.ImageType.UNDEFINED && cover_sample == null) {
410-
cover_sample = sample;
411-
} else if (image_type == Gst.Tag.ImageType.FRONT_COVER) {
412-
return sample;
413-
}
414-
}
415-
416-
return cover_sample;
417-
}
418-
419-
private Gdk.Pixbuf? get_pixbuf_from_buffer (Gst.Buffer buffer) {
420-
Gst.MapInfo map_info;
421-
422-
if (!buffer.map (out map_info, Gst.MapFlags.READ)) {
423-
warning ("Could not map memory buffer");
424-
return null;
425-
}
426-
427-
Gdk.Pixbuf pix = null;
428-
429-
try {
430-
var loader = new Gdk.PixbufLoader ();
431-
432-
if (loader.write (map_info.data) && loader.close ()) {
433-
pix = loader.get_pixbuf ();
434-
}
435-
} catch (Error err) {
436-
warning ("Error processing image data: %s", err.message);
437-
}
438-
439-
buffer.unmap (map_info);
440-
441-
return pix;
442-
}
443316
}

src/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ sources = [
33
'AudioObject.vala',
44
'MainWindow.vala',
55
'PlaybackManager.vala',
6+
'MetadataDiscoverer.vala',
67
'DBus/MprisPlayer.vala',
78
'DBus/MprisRoot.vala',
89
'Views/NowPlayingView.vala',

0 commit comments

Comments
 (0)