diff --git a/audio/spotify/Cargo.toml b/audio/spotify/Cargo.toml index d5c1ce91..fbbd7237 100644 --- a/audio/spotify/Cargo.toml +++ b/audio/spotify/Cargo.toml @@ -16,6 +16,7 @@ librespot = { version = "0.4", default-features = false } tokio = "1.0" futures = "0.3" anyhow = "1.0" +url = "2.3" [lib] name = "gstspotify" diff --git a/audio/spotify/README.md b/audio/spotify/README.md index aa8424dc..d6f84c88 100644 --- a/audio/spotify/README.md +++ b/audio/spotify/README.md @@ -21,4 +21,10 @@ The `spotifyaudiosrc` element can be used to play a song from Spotify using its ``` gst-launch-1.0 spotifyaudiosrc username=$USERNAME password=$PASSWORD track=spotify:track:3i3P1mGpV9eRlfKccjDjwi ! oggdemux ! vorbisdec ! audioconvert ! autoaudiosink +``` + +The element also implements an URI handler which accepts credentials and cache settings as URI parameters: + +```console +gst-launch-1.0 playbin3 uri=spotify:track:3i3P1mGpV9eRlfKccjDjwi?username=$USERNAME\&password=$PASSWORD\&cache-credentials=cache\&cache-files=cache ``` \ No newline at end of file diff --git a/audio/spotify/src/spotifyaudiosrc/imp.rs b/audio/spotify/src/spotifyaudiosrc/imp.rs index ee0f643a..87ed8877 100644 --- a/audio/spotify/src/spotifyaudiosrc/imp.rs +++ b/audio/spotify/src/spotifyaudiosrc/imp.rs @@ -83,6 +83,7 @@ impl ObjectSubclass for SpotifyAudioSrc { const NAME: &'static str = "GstSpotifyAudioSrc"; type Type = super::SpotifyAudioSrc; type ParentType = gst_base::BaseSrc; + type Interfaces = (gst::URIHandler,); } impl ObjectImpl for SpotifyAudioSrc { @@ -413,3 +414,45 @@ impl Sink for BufferSink { Ok(()) } } + +impl URIHandlerImpl for SpotifyAudioSrc { + const URI_TYPE: gst::URIType = gst::URIType::Src; + + fn protocols() -> &'static [&'static str] { + &["spotify"] + } + + fn uri(&self) -> Option { + let settings = self.settings.lock().unwrap(); + + if settings.track.is_empty() { + None + } else { + Some(settings.track.clone()) + } + } + + fn set_uri(&self, uri: &str) -> Result<(), glib::Error> { + gst::debug!(CAT, imp: self, "set URI: {}", uri); + + let url = url::Url::parse(uri) + .map_err(|e| glib::Error::new(gst::URIError::BadUri, &format!("{:?}", e)))?; + + // allow to configure auth and cache settings from the URI + for (key, value) in url.query_pairs() { + match key.as_ref() { + "username" | "password" | "cache-credentials" | "cache-files" => { + self.instance().set_property(&key, value.as_ref()); + } + _ => { + gst::warning!(CAT, imp: self, "unsupported query: {}={}", key, value); + } + } + } + + self.instance() + .set_property("track", format!("{}:{}", url.scheme(), url.path())); + + Ok(()) + } +} diff --git a/audio/spotify/src/spotifyaudiosrc/mod.rs b/audio/spotify/src/spotifyaudiosrc/mod.rs index cfed1572..4cb41423 100644 --- a/audio/spotify/src/spotifyaudiosrc/mod.rs +++ b/audio/spotify/src/spotifyaudiosrc/mod.rs @@ -12,7 +12,7 @@ use gst::prelude::*; mod imp; glib::wrapper! { - pub struct SpotifyAudioSrc(ObjectSubclass) @extends gst_base::BaseSrc, gst::Element, gst::Object; + pub struct SpotifyAudioSrc(ObjectSubclass) @extends gst_base::BaseSrc, gst::Element, gst::Object, @implements gst::URIHandler; } pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> { diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index fac159d2..f627adea 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -5565,6 +5565,9 @@ "GInitiallyUnowned", "GObject" ], + "interfaces": [ + "GstURIHandler" + ], "klass": "Source/Audio", "long-name": "Spotify source", "pad-templates": {