Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 81 additions & 1 deletion src/AudioObject.vala
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,88 @@ public class Music.AudioObject : Object {
public string artist { get; set; }
public string title { get; set; }
public int64 duration { get; set; default = 0; }
private static MetadataDiscoverer discoverer = new MetadataDiscoverer ();

public AudioObject (string uri) {
Object (uri: uri);
Object (
uri: uri,
title: uri
);
}

construct {
discoverer.request (this);
}

public void update_metadata (Gst.PbUtils.DiscovererInfo info) {
duration = (int64) info.get_duration ();

unowned Gst.TagList? tag_list = info.get_tags ();

string _title;
tag_list.get_string (Gst.Tags.TITLE, out _title);
if (_title != null) {
title = _title;
}

string _artist;
tag_list.get_string (Gst.Tags.ARTIST, out _artist);
if (_artist != null) {
artist = _artist;
} else if (_title != null) { // Don't set artist for files without tags
artist = _("Unknown");
}

var sample = get_cover_sample (tag_list);
if (sample != null) {
var buffer = sample.get_buffer ();

if (buffer != null) {
texture = Gdk.Texture.for_pixbuf (get_pixbuf_from_buffer (buffer));
}
}
}
Comment on lines +25 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These properties are all publicly settable so why do this here instead of in the MetadataDiscoverer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this probably should be moved into the MetadataDiscoverer. Sorry I didn't have much time the last 2 weeks, but I'll have a lot more time from wednesday onward to update this.


private Gst.Sample? get_cover_sample (Gst.TagList tag_list) {
Gst.Sample cover_sample = null;
Gst.Sample sample;
for (int i = 0; tag_list.get_sample_index (Gst.Tags.IMAGE, i, out sample); i++) {
var caps = sample.get_caps ();
unowned Gst.Structure caps_struct = caps.get_structure (0);
int image_type = Gst.Tag.ImageType.UNDEFINED;
caps_struct.get_enum ("image-type", typeof (Gst.Tag.ImageType), out image_type);
if (image_type == Gst.Tag.ImageType.UNDEFINED && cover_sample == null) {
cover_sample = sample;
} else if (image_type == Gst.Tag.ImageType.FRONT_COVER) {
return sample;
}
}

return cover_sample;
}

private Gdk.Pixbuf? get_pixbuf_from_buffer (Gst.Buffer buffer) {
Gst.MapInfo map_info;

if (!buffer.map (out map_info, Gst.MapFlags.READ)) {
warning ("Could not map memory buffer");
return null;
}

Gdk.Pixbuf pix = null;

try {
var loader = new Gdk.PixbufLoader ();

if (loader.write (map_info.data) && loader.close ()) {
pix = loader.get_pixbuf ();
}
} catch (Error err) {
warning ("Error processing image data: %s", err.message);
}

buffer.unmap (map_info);

return pix;
}
}
49 changes: 49 additions & 0 deletions src/MetadataDiscoverer.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[SingleInstance]
public class Music.MetadataDiscoverer : Object {
private Gst.PbUtils.Discoverer discoverer;
private HashTable<string, AudioObject> objects_to_update;

construct {
try {
discoverer = new Gst.PbUtils.Discoverer ((Gst.ClockTime) (5 * Gst.SECOND));
discoverer.discovered.connect (relay_metadata);
discoverer.finished.connect (discoverer.stop);
} catch (Error e) {
critical ("Unable to start Gstreamer Discoverer: %s", e.message);
}
objects_to_update = new HashTable<string, AudioObject> (str_hash, str_equal);
}

public void request (AudioObject audio) {
objects_to_update.insert (audio.uri, audio);
discoverer.start ();
discoverer.discover_uri_async (audio.uri);
}

private void relay_metadata (Gst.PbUtils.DiscovererInfo info, Error? err) {
string uri = info.get_uri ();
var audio_obj = objects_to_update.get (uri);
objects_to_update.remove (uri);
switch (info.get_result ()) {
case Gst.PbUtils.DiscovererResult.URI_INVALID:
critical ("Couldn't read metadata for '%s': invalid URI.", uri);
return;
case Gst.PbUtils.DiscovererResult.ERROR:
critical ("Couldn't read metadata for '%s': %s", uri, err.message);
return;
case Gst.PbUtils.DiscovererResult.TIMEOUT:
critical ("Couldn't read metadata for '%s': Discovery timed out.", uri);
return;
case Gst.PbUtils.DiscovererResult.BUSY:
critical ("Couldn't read metadata for '%s': Already discovering a file.", uri);
return;
case Gst.PbUtils.DiscovererResult.MISSING_PLUGINS:
critical ("Couldn't read metadata for '%s': Missing plugins.", uri);
return;
default:
break;
}

audio_obj.update_metadata (info);
}
}
128 changes: 0 additions & 128 deletions src/PlaybackManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public class Music.PlaybackManager : Object {
}

private dynamic Gst.Element playbin;
private Gst.PbUtils.Discoverer discoverer;
private uint progress_timer = 0;
private Settings settings;

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

try {
discoverer = new Gst.PbUtils.Discoverer ((Gst.ClockTime) (5 * Gst.SECOND));
discoverer.discovered.connect (update_metadata);
discoverer.finished.connect (discoverer.stop);
} catch (Error e) {
critical ("Unable to start Gstreamer Discoverer: %s", e.message);
}

queue_liststore.items_changed.connect (() => {
var shuffle_action_action = (SimpleAction) GLib.Application.get_default ().lookup_action (Application.ACTION_SHUFFLE);
has_items = queue_liststore.get_n_items () > 0;
Expand Down Expand Up @@ -91,22 +82,10 @@ public class Music.PlaybackManager : Object {

// Files[] must not contain any null entries
public void queue_files (File[] files) {
discoverer.start ();
int invalids = 0;
foreach (unowned var file in files) {
if (file.query_exists () && "audio" in ContentType.guess (file.get_uri (), null, null)) {
var audio_object = new AudioObject (file.get_uri ());

string? basename = file.get_basename ();

if (basename != null) {
audio_object.title = basename;
} else {
audio_object.title = audio_object.uri;
}

discoverer.discover_uri_async (audio_object.uri);

queue_liststore.append (audio_object);
} else {
invalids++;
Expand Down Expand Up @@ -162,70 +141,6 @@ public class Music.PlaybackManager : Object {
queue_liststore.remove (position);
}

private void update_metadata (Gst.PbUtils.DiscovererInfo info, Error? err) {
string uri = info.get_uri ();
switch (info.get_result ()) {
case Gst.PbUtils.DiscovererResult.URI_INVALID:
critical ("Couldn't read metadata for '%s': invalid URI.", uri);
return;
case Gst.PbUtils.DiscovererResult.ERROR:
critical ("Couldn't read metadata for '%s': %s", uri, err.message);
return;
case Gst.PbUtils.DiscovererResult.TIMEOUT:
critical ("Couldn't read metadata for '%s': Discovery timed out.", uri);
return;
case Gst.PbUtils.DiscovererResult.BUSY:
critical ("Couldn't read metadata for '%s': Already discovering a file.", uri);
return;
case Gst.PbUtils.DiscovererResult.MISSING_PLUGINS:
critical ("Couldn't read metadata for '%s': Missing plugins.", uri);
return;
default:
break;
}

EqualFunc<string> equal_func = (a, b) => {
return ((AudioObject) a).uri == ((AudioObject) b).uri;
};

var temp_audio_object = new AudioObject (uri);

uint position = -1;
queue_liststore.find_with_equal_func (temp_audio_object, equal_func, out position);

if (position != -1) {
var audio_object = (AudioObject) queue_liststore.get_item (position);
audio_object.duration = (int64) info.get_duration ();

unowned Gst.TagList? tag_list = info.get_tags ();

string _title;
tag_list.get_string (Gst.Tags.TITLE, out _title);
if (_title != null) {
audio_object.title = _title;
}

string _artist;
tag_list.get_string (Gst.Tags.ARTIST, out _artist);
if (_artist != null) {
audio_object.artist = _artist;
} else if (_title != null) { // Don't set artist for files without tags
audio_object.artist = _("Unknown");
}

var sample = get_cover_sample (tag_list);
if (sample != null) {
var buffer = sample.get_buffer ();

if (buffer != null) {
audio_object.texture = Gdk.Texture.for_pixbuf (get_pixbuf_from_buffer (buffer));
}
}
} else {
critical ("Couldn't find '%s' in queue", uri);
}
}

private bool bus_callback (Gst.Bus bus, Gst.Message message) {
switch (message.type) {
case Gst.MessageType.EOS:
Expand Down Expand Up @@ -408,47 +323,4 @@ public class Music.PlaybackManager : Object {
previous_action.set_enabled (previous_sensitive);

}

private Gst.Sample? get_cover_sample (Gst.TagList tag_list) {
Gst.Sample cover_sample = null;
Gst.Sample sample;
for (int i = 0; tag_list.get_sample_index (Gst.Tags.IMAGE, i, out sample); i++) {
var caps = sample.get_caps ();
unowned Gst.Structure caps_struct = caps.get_structure (0);
int image_type = Gst.Tag.ImageType.UNDEFINED;
caps_struct.get_enum ("image-type", typeof (Gst.Tag.ImageType), out image_type);
if (image_type == Gst.Tag.ImageType.UNDEFINED && cover_sample == null) {
cover_sample = sample;
} else if (image_type == Gst.Tag.ImageType.FRONT_COVER) {
return sample;
}
}

return cover_sample;
}

private Gdk.Pixbuf? get_pixbuf_from_buffer (Gst.Buffer buffer) {
Gst.MapInfo map_info;

if (!buffer.map (out map_info, Gst.MapFlags.READ)) {
warning ("Could not map memory buffer");
return null;
}

Gdk.Pixbuf pix = null;

try {
var loader = new Gdk.PixbufLoader ();

if (loader.write (map_info.data) && loader.close ()) {
pix = loader.get_pixbuf ();
}
} catch (Error err) {
warning ("Error processing image data: %s", err.message);
}

buffer.unmap (map_info);

return pix;
}
}
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ sources = [
'AudioObject.vala',
'MainWindow.vala',
'PlaybackManager.vala',
'MetadataDiscoverer.vala',
'DBus/MprisPlayer.vala',
'DBus/MprisRoot.vala',
'Views/NowPlayingView.vala',
Expand Down