summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJokler <jokler@protonmail.com>2020-01-07 06:48:43 +0100
committerJokler <jokler@protonmail.com>2020-01-07 06:48:43 +0100
commitdaa895e3be120ba640ca29d7653a03b6a547f4a0 (patch)
tree2312ffc926afe064334b5152441d0c71bb5f286e
parent6ff7cd94cf612e7eb2d54d2a3b754a97899b6a94 (diff)
downloadpokebot-daa895e3be120ba640ca29d7653a03b6a547f4a0.tar.gz
pokebot-daa895e3be120ba640ca29d7653a03b6a547f4a0.zip
Add playlist feature and default channel flag
-rw-r--r--Cargo.lock59
-rw-r--r--Cargo.toml18
-rw-r--r--src/main.rs62
-rw-r--r--src/playlist.rs68
-rw-r--r--src/state.rs292
5 files changed, 372 insertions, 127 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 591627d..ec0ab8f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -106,7 +106,7 @@ name = "backtrace-sys"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -208,7 +208,7 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.0.48"
+version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -258,7 +258,7 @@ name = "clear_on_drop"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -550,7 +550,7 @@ name = "flakebi-ring"
version = "0.16.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1159,7 +1159,7 @@ name = "libgit2-sys"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1170,7 +1170,7 @@ name = "libz-sys"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1281,6 +1281,17 @@ dependencies = [
]
[[package]]
+name = "mio-named-pipes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "mio-uds"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1302,6 +1313,15 @@ dependencies = [
]
[[package]]
+name = "miow"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "muldiv"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1427,7 +1447,7 @@ version = "0.9.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
"vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1571,10 +1591,10 @@ dependencies = [
"gstreamer-app 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gstreamer-audio 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-process 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
"tsclientlib 0.1.0 (git+https://github.com/Jokler/tsclientlib/?branch=fix-poke)",
@@ -2388,6 +2408,24 @@ dependencies = [
]
[[package]]
+name = "tokio-process"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "tokio-reactor"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2997,7 +3035,7 @@ dependencies = [
"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
"checksum bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10004c15deb332055f7a4a208190aed362cf9a7c2f6ab70a305fba50e1105f38"
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
-"checksum cc 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76"
+"checksum cc 1.0.49 (registry+https://github.com/rust-lang/crates.io-index)" = "e450b8da92aa6f274e7c6437692f9f2ce6d701fb73bacfcf87897b3f89a4c20e"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum chashmap 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff41a3c2c1e39921b9003de14bf0439c7b63a9039637c291e1a64925d8ddfa45"
"checksum chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
@@ -3110,8 +3148,10 @@ dependencies = [
"checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599"
"checksum miniz_oxide 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625"
"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
+"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125"
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
+"checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226"
"checksum muldiv 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204"
"checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
@@ -3231,6 +3271,7 @@ dependencies = [
"checksum tokio-executor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ca6df436c42b0c3330a82d855d2ef017cd793090ad550a6bc2184f4b933532ab"
"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af"
"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926"
+"checksum tokio-process 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afbd6ef1b8cc2bd2c2b580d882774d443ebb1c6ceefe35ba9ea4ab586c89dbe8"
"checksum tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146"
"checksum tokio-signal 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "dd6dc5276ea05ce379a16de90083ec80836440d5ef8a6a39545a3207373b8296"
"checksum tokio-sync 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "d06554cce1ae4a50f42fba8023918afa931413aded705b560e29600ccf7c6d76"
diff --git a/Cargo.toml b/Cargo.toml
index acbd620..4191c69 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,17 +9,21 @@ edition = "2018"
[dependencies]
tsclientlib = { git = "https://github.com/Jokler/tsclientlib/", branch = "fix-poke" }
tsproto-packets = { git = "https://github.com/Jokler/tsclientlib/", branch = "fix-poke" }
+log = "0.4.8"
+toml = "0.5.5"
structopt = "0.2"
-tokio = "0.1.0"
+
+tokio = "0.1"
+tokio-process = "0.2.4"
+tokio-signal = "0.2"
+# Changes enabled features on tsclientlib to fix errors on it
tokio2 = { package = "tokio", version = "0.2.0", features = ["tcp", "io-util"] }
+
+futures01 = { package = "futures", version = "0.1.21" }
futures-preview = { version = "=0.3.0-alpha.19", features = ["compat"] }
-futures01 = { package = "futures", version = "0.1" }
-tokio-signal = "0.2.7"
-log = "0.4.8"
-gstreamer = "0.15.0"
futures-util = "0.3.1"
+
+gstreamer = "0.15.0"
gstreamer-app = "0.15.0"
gstreamer-audio = "0.15.0"
byte-slice-cast = "0.3.5"
-serde = "1.0.104"
-toml = "0.5.5"
diff --git a/src/main.rs b/src/main.rs
index 7cb0aff..6d6c00e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,17 +7,18 @@ use futures::{
future::{FutureExt, TryFutureExt},
};
-use futures01::{future::Future, stream::Stream, sync::mpsc};
+use futures01::future::Future;
use structopt::clap::AppSettings;
use structopt::StructOpt;
use tsclientlib::{
- events::Event, ChannelId, ConnectOptions, Connection, ConnectionLock, DisconnectOptions,
- Event::ConEvents, Identity, MessageTarget,
+ events::Event, ConnectOptions, Connection, ConnectionLock, Event::ConEvents,
+ Identity, MessageTarget,
};
use log::error;
+mod playlist;
mod state;
use state::State;
@@ -39,6 +40,12 @@ struct Args {
)]
id_path: Option<PathBuf>,
#[structopt(
+ short = "c",
+ long = "channel",
+ help = "The channel the bot should connect to"
+ )]
+ default_channel: Option<String>,
+ #[structopt(
short = "v",
long = "verbose",
help = "Print the content of all packets",
@@ -70,7 +77,7 @@ async fn async_main() {
Identity::create().expect("Failed to create id")
};
- let con_config = ConnectOptions::new(args.address)
+ let mut con_config = ConnectOptions::new(args.address)
.version(tsclientlib::Version::Linux_3_3_2)
.name(String::from("PokeBot"))
.identity(id)
@@ -78,13 +85,17 @@ async fn async_main() {
.log_packets(args.verbose >= 2)
.log_udp_packets(args.verbose >= 3);
+ if let Some(channel) = args.default_channel {
+ con_config = con_config.channel(channel);
+ }
+
//let (disconnect_send, disconnect_recv) = mpsc::unbounded();
let conn = Connection::new(con_config).compat().await.unwrap();
- let mut state = State::new(conn.clone());
+ let state = State::new(conn.clone());
{
let packet = conn.lock().server.set_subscribed(true);
- conn.send_packet(packet).compat().await;
+ conn.send_packet(packet).compat().await.unwrap();
}
//con.add_on_disconnect(Box::new( || {
//disconnect_send.unbounded_send(()).unwrap()
@@ -104,19 +115,19 @@ async fn async_main() {
loop {
state.poll().await;
}
- let ctrl_c = tokio_signal::ctrl_c().flatten_stream();
+ //let ctrl_c = tokio_signal::ctrl_c().flatten_stream();
//let dc_fut = disconnect_recv.into_future().compat().fuse();
//let ctrlc_fut = ctrl_c.into_future().compat().fuse();
//ctrlc_fut.await.map_err(|(e, _)| e).unwrap();
- conn.disconnect(DisconnectOptions::new())
- .compat()
- .await
- .unwrap();
+ //conn.disconnect(DisconnectOptions::new())
+ //.compat()
+ //.await
+ //.unwrap();
// TODO Should not be required
- std::process::exit(0);
+ //std::process::exit(0);
}
fn handle_event<'a>(state: &State, conn: &ConnectionLock<'a>, event: &Event) {
@@ -158,7 +169,13 @@ fn handle_event<'a>(state: &State, conn: &ConnectionLock<'a>, event: &Event) {
conn.to_mut().set_name("PokeBot - Loading").map_err(|_| ()),
);
let trimmed = url[5..url.len() - 6].to_owned();
- state.add_audio(trimmed);
+ let inner_state = state.clone();
+ tokio::spawn(
+ async move { inner_state.add_audio(trimmed).await }
+ .unit_error()
+ .boxed()
+ .compat(),
+ );
} else {
invalid = true;
}
@@ -175,12 +192,29 @@ fn handle_event<'a>(state: &State, conn: &ConnectionLock<'a>, event: &Event) {
}
Some("volume") => {
if let Ok(volume) = f64::from_str(tokens[1]) {
- state.volume(volume / 100.0);
+ if 0.0 <= volume && volume <= 100.0 {
+ state.volume(volume);
+ } else {
+ tokio::spawn(
+ conn.to_mut()
+ .send_message(
+ MessageTarget::Channel,
+ "Volume must be between 0 and 100",
+ )
+ .map_err(|_| ()),
+ );
+ }
}
}
Some("play") => {
state.play();
}
+ Some("skip") => {
+ state.next();
+ }
+ Some("clear") => {
+ state.clear();
+ }
Some("pause") => {
state.pause();
}
diff --git a/src/playlist.rs b/src/playlist.rs
new file mode 100644
index 0000000..e0df816
--- /dev/null
+++ b/src/playlist.rs
@@ -0,0 +1,68 @@
+pub struct Playlist {
+ data: Vec<Option<AudioRequest>>,
+ read: usize,
+ write: usize,
+ is_full: bool,
+}
+
+impl Playlist {
+ pub fn new() -> Self {
+ Self {
+ data: Vec::with_capacity(3),
+ read: 0,
+ write: 0,
+ is_full: false,
+ }
+ }
+
+ pub fn push(&mut self, req: AudioRequest) -> bool {
+ if self.is_full {
+ return false;
+ }
+
+ if self.data.len() < self.data.capacity() {
+ self.data.push(Some(req));
+ } else {
+ self.data[self.write] = Some(req);
+ }
+
+ self.write = (self.write + 1) % self.data.capacity();
+
+ if self.write == self.read {
+ self.is_full = true;
+ }
+
+ true
+ }
+
+ pub fn is_empty(&self) -> bool {
+ !self.is_full && self.write == self.read
+ }
+
+ pub fn is_full(&self) -> bool {
+ self.is_full
+ }
+
+ pub fn pop(&mut self) -> Option<AudioRequest> {
+ if self.is_empty() {
+ None
+ } else {
+ self.read += 1;
+ self.is_full = false;
+ self.data[self.read].take()
+ }
+ }
+
+ pub fn clear(&mut self) {
+ self.data.clear();
+ self.read = 0;
+ self.write = 0;
+ self.is_full = false;
+ }
+}
+
+#[derive(Clone, Debug)]
+pub struct AudioRequest {
+ pub title: String,
+ pub address: String,
+}
diff --git a/src/state.rs b/src/state.rs
index dfccd9a..8c6a290 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,27 +1,26 @@
-use std::error::Error;
-use std::process::{Command, Stdio};
-use std::sync::Arc;
-
-use futures::{
- compat::Future01CompatExt,
- future::{FutureExt, TryFutureExt},
-};
-use futures01::{future::Future, sink::Sink};
+use std::process::Command;
+use std::sync::{Arc, Mutex};
+use futures::compat::Future01CompatExt;
+use futures01::{future::Future, sink::Sink};
use futures_util::stream::StreamExt;
-use tsclientlib::{Connection, MessageTarget, DisconnectOptions};
+use tokio_process::CommandExt;
+
+use tsclientlib::{Connection, DisconnectOptions, MessageTarget};
use gst::prelude::*;
use gstreamer as gst;
use gstreamer_app as gst_app;
-use gstreamer_audio as gst_audio;
use byte_slice_cast::*;
+use crate::playlist::{AudioRequest, Playlist};
+
#[derive(Clone)]
pub struct State {
conn: Arc<Connection>,
pipeline: Arc<gst::Pipeline>,
+ playlist: Arc<Mutex<Playlist>>,
}
impl State {
@@ -68,6 +67,16 @@ impl State {
};
if is_audio {
+ let prev_volume = if let Some(volume) = pipeline.get_by_name("volume") {
+ volume
+ .get_property("volume")
+ .expect("Can get volume property")
+ .get_some::<f64>()
+ .unwrap_or(0.1)
+ } else {
+ 0.1
+ };
+
let names = [
"audio-converter",
"volume",
@@ -78,8 +87,10 @@ impl State {
for element in pipeline.iterate_elements() {
if let Ok(element) = element {
if names.contains(&&*element.get_name()) {
- element.set_state(gst::State::Null);
- pipeline.remove_many(&[&element]);
+ element.set_state(gst::State::Null).unwrap();
+ pipeline
+ .remove_many(&[&element])
+ .expect("Can remove element");
}
}
}
@@ -92,9 +103,12 @@ impl State {
let opus_enc = gst::ElementFactory::make("opusenc", Some("opus-encoder")).unwrap();
let sink = gst::ElementFactory::make("appsink", Some("app-sink")).unwrap();
- sink.set_property("async", &false);
+ sink.set_property("async", &false)
+ .expect("Can make app-sink async");
- volume.set_property("volume", &0.2);
+ volume
+ .set_property("volume", &prev_volume)
+ .expect("Can change volume");
{
let elements = &[&convert, &volume, &resample, &opus_enc, &sink];
@@ -109,7 +123,7 @@ impl State {
let appsink = sink
.dynamic_cast::<gst_app::AppSink>()
- .expect("Sink element is expected to be an appsink!");
+ .expect("Sink is an Appsink");
appsink.set_caps(Some(&gst::Caps::new_simple("audio/x-opus", &[])));
@@ -144,7 +158,7 @@ impl State {
.get_packet_sink()
.send(packet)
.map(|_| ())
- .map_err(|e| println!("Failed to send voice packet"));
+ .map_err(|e| println!("Failed to send voice packet: {}", e));
tokio::run(send_packet);
@@ -165,68 +179,77 @@ impl State {
Self {
conn: conn_arc,
pipeline: Arc::new(pipeline),
+ playlist: Arc::new(Mutex::new(Playlist::new())),
}
}
- pub fn add_audio<'a>(&self, uri: String) {
+ pub async fn add_audio(&self, url: String) {
+ if self
+ .playlist
+ .lock()
+ .expect("Mutex was not poisoned")
+ .is_full()
+ {
+ self.send_message(MessageTarget::Channel, "Playlist is full");
+ return;
+ }
+
let ytdl_args = [
"--no-playlist",
"-f",
"bestaudio/best",
"-g",
- &uri,
+ "--get-filename",
"-o",
- "-",
+ "%(title)s",
+ &url,
];
- let ytdl_output = Command::new("youtube-dl")
+ let output = Command::new("youtube-dl")
.args(&ytdl_args)
- .stdin(Stdio::null())
- .output()
- .unwrap();
+ .output_async()
+ .compat()
+ .await
+ .expect("youtube-dl is runnable");
- if ytdl_output.status.success() == false {
- tokio::spawn(
- self.conn
- .lock()
- .to_mut()
- .set_name("PokeBot")
- .map_err(|_| println!("Failed to change name")),
- );
- tokio::spawn(
- self.conn
- .lock()
- .to_mut()
- .send_message(MessageTarget::Channel, "Failed to load url")
- .map_err(|_| println!("Failed to change name")),
- );
+ if output.status.success() == false {
+ self.set_name("PokeBot");
+ self.send_message(MessageTarget::Channel, "Failed to load url");
return;
}
- let dl_url: &str = &String::from_utf8(ytdl_output.stderr).unwrap();
-
- self.pipeline
- .set_state(gst::State::Ready)
- .expect("Can set state to ready");
-
- let http_src = self
- .pipeline
- .get_by_name("http-source")
- .expect("Http source should be registered");
- http_src
- .set_property("location", &dl_url)
- .expect("Can set location on http_dl");
- self.pipeline
- .set_state(gst::State::Playing)
- .expect("Can set state to playing");
-
- tokio::spawn(
- self.conn
+ let output_string = String::from_utf8(output.stdout).unwrap();
+ let output_lines = output_string.lines().collect::<Vec<_>>();
+ let address = output_lines[0];
+ let title = output_lines[1];
+
+ let req = AudioRequest {
+ title: title.to_owned(),
+ address: address.to_owned(),
+ };
+
+ if gst::State::Null == self.pipeline.get_state(gst::ClockTime(None)).1 {
+ self.set_name("PokeBot - Playing");
+ self.start_audio(req);
+ } else {
+ self.set_name("PokeBot - Playing");
+
+ let title = req.title.clone();
+ if self
+ .playlist
.lock()
- .to_mut()
- .set_name("PokeBot - Playing")
- .map_err(|_| println!("Failed to change name")),
- );
+ .expect("Mutex was not poisoned")
+ .push(req)
+ == false
+ {
+ self.send_message(MessageTarget::Channel, "Playlist is full");
+ } else {
+ self.send_message(
+ MessageTarget::Channel,
+ &format!("Added '{}' to the playlist", title),
+ );
+ }
+ }
}
pub async fn poll(&self) {
@@ -239,8 +262,6 @@ impl State {
while let Some(msg) = messages.next().await {
use gst::MessageView;
- // Determine whether we want to quit: on EOS or error message
- // we quit, otherwise simply continue.
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
@@ -256,39 +277,96 @@ impl State {
};
}
- tokio::spawn(
- self.conn
- .lock()
- .to_mut()
- .set_name("PokeBot")
- .map_err(|_| println!("Failed to change name")),
- );
+ self.next();
+ }
+ pub fn start_audio(&self, req: AudioRequest) {
self.pipeline
.set_state(gst::State::Null)
.expect("Can set state to null");
+
+ let http_src = self
+ .pipeline
+ .get_by_name("http-source")
+ .expect("Http source should be registered");
+
+ http_src
+ .set_property("location", &&req.address)
+ .expect("Can set location on http_dl");
+
+ self.pipeline
+ .set_state(gst::State::Playing)
+ .expect("Can set state to playing");
+
+ self.set_description(&format!("Currently playing: {}", req.title));
}
pub fn volume(&self, volume: f64) {
- self.pipeline
- .get_by_name("volume")
- .expect("Volume filter should be registered")
- .set_property("volume", &volume)
- .expect("can change volume");
+ if let Some(vol_filter) = self.pipeline.get_by_name("volume") {
+ vol_filter
+ .set_property("volume", &(10.0f64.powf(volume / 50.0 - 2.0)))
+ .expect("can change volume");
+
+ // TODO Reflect volume in name
+ }
}
pub fn play(&self) {
+ let http_src = self
+ .pipeline
+ .get_by_name("http-source")
+ .expect("Http source should be registered");
+
+ if http_src
+ .get_property("location")
+ .ok()
+ .and_then(|v| v.get::<String>().ok().and_then(|s| s.map(|s| s.is_empty())))
+ .unwrap_or(true)
+ {
+ if self
+ .playlist
+ .lock()
+ .expect("Mutex was not poisoned")
+ .is_empty()
+ {
+ self.send_message(MessageTarget::Channel, "There is nothing to play");
+ return;
+ }
+ }
+
self.pipeline
.set_state(gst::State::Playing)
.expect("can play");
- tokio::spawn(
- self.conn
- .lock()
- .to_mut()
- .set_name("PokeBot - Playing")
- .map_err(|_| println!("Failed to change name")),
- );
+ self.set_name("PokeBot - Playing");
+ }
+
+ pub fn next(&self) {
+ if let Some(req) = self.playlist.lock().expect("Mutex was not poisoned").pop() {
+ self.start_audio(req);
+ } else {
+ self.pipeline
+ .set_state(gst::State::Null)
+ .expect("Can set state to null");
+
+ let http_src = self
+ .pipeline
+ .get_by_name("http-source")
+ .expect("Http source should be registered");
+
+ http_src
+ .set_property("location", &"")
+ .expect("Can set location on http_dl");
+
+ self.set_name("PokeBot");
+ }
+ }
+
+ pub fn clear(&self) {
+ self.playlist
+ .lock()
+ .expect("Mutex was not poisoned")
+ .clear();
}
pub fn pause(&self) {
@@ -296,13 +374,7 @@ impl State {
.set_state(gst::State::Paused)
.expect("can pause");
- tokio::spawn(
- self.conn
- .lock()
- .to_mut()
- .set_name("PokeBot - Paused")
- .map_err(|_| println!("Failed to change name")),
- );
+ self.set_name("PokeBot - Paused");
}
pub fn stop(&self) {
@@ -310,13 +382,7 @@ impl State {
.set_state(gst::State::Ready)
.expect("can stop");
- tokio::spawn(
- self.conn
- .lock()
- .to_mut()
- .set_name("PokeBot - Stopped")
- .map_err(|_| println!("Failed to change name")),
- );
+ self.set_name("PokeBot - Stopped");
}
pub async fn disconnect(&self) {
@@ -325,4 +391,36 @@ impl State {
.compat()
.await;
}
+
+ pub fn send_message(&self, target: MessageTarget, message: &str) {
+ tokio::spawn(
+ self.conn
+ .lock()
+ .to_mut()
+ .send_message(target, message)
+ .map_err(|e| println!("Failed to send message: {}", e)),
+ );
+ }
+
+ pub fn set_name(&self, name: &str) {
+ tokio::spawn(
+ self.conn
+ .lock()
+ .to_mut()
+ .set_name(name)
+ .map_err(|e| println!("Failed to change name: {}", e)),
+ );
+ }
+
+ pub fn set_description(&self, desc: &str) {
+ tokio::spawn(
+ self.conn
+ .lock()
+ .to_mut()
+ .get_client(&self.conn.lock().own_client)
+ .expect("Can get myself")
+ .set_description(desc)
+ .map_err(|e| println!("Failed to change description: {}", e)),
+ );
+ }
}