add automatic conversion
This commit is contained in:
parent
a20d450ebe
commit
b3d96fb069
4 changed files with 170 additions and 14 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1,3 @@
|
||||||
/target
|
/target
|
||||||
|
/vid*
|
||||||
|
/aud*
|
||||||
|
|
36
Cargo.lock
generated
36
Cargo.lock
generated
|
@ -193,6 +193,12 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color_quant"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "command_attr"
|
name = "command_attr"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -542,6 +548,16 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gif"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06"
|
||||||
|
dependencies = [
|
||||||
|
"color_quant",
|
||||||
|
"weezl",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.14"
|
version = "0.3.14"
|
||||||
|
@ -1051,6 +1067,18 @@ dependencies = [
|
||||||
"pnet_base",
|
"pnet_base",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "png"
|
||||||
|
version = "0.17.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f0e7f4c94ec26ff209cee506314212639d6c91b80afb82984819fafce9df01c"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"crc32fast",
|
||||||
|
"flate2",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "poly1305"
|
name = "poly1305"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
|
@ -1082,7 +1110,9 @@ name = "projbotv3"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form-data-builder",
|
"form-data-builder",
|
||||||
|
"gif",
|
||||||
"openssl",
|
"openssl",
|
||||||
|
"png",
|
||||||
"serenity",
|
"serenity",
|
||||||
"songbird",
|
"songbird",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -2003,6 +2033,12 @@ dependencies = [
|
||||||
"webpki",
|
"webpki",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -11,3 +11,5 @@ serenity = {version="0.11.5", features=["builder", "cache", "client", "framework
|
||||||
songbird = {version="0.3.0", features=["driver", "serenity-rustls"]}
|
songbird = {version="0.3.0", features=["driver", "serenity-rustls"]}
|
||||||
openssl = "0.10.42"
|
openssl = "0.10.42"
|
||||||
form-data-builder = "1.0.1"
|
form-data-builder = "1.0.1"
|
||||||
|
png = "0.17.6"
|
||||||
|
gif = "0.11.4"
|
||||||
|
|
144
src/main.rs
144
src/main.rs
|
@ -1,15 +1,19 @@
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
fs::{self, OpenOptions},
|
fs::{self, File, OpenOptions},
|
||||||
io::{Cursor, Read, Write},
|
io::{Cursor, Read, Write},
|
||||||
net::{Shutdown, TcpStream},
|
net::{Shutdown, TcpStream},
|
||||||
sync::Arc,
|
path::Path,
|
||||||
time::{Duration, SystemTime},
|
process::{self, Stdio},
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
time::{Duration, SystemTime}, thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use form_data_builder::FormData;
|
use form_data_builder::FormData;
|
||||||
|
use gif::Encoder;
|
||||||
use openssl::ssl::{Ssl, SslContext, SslMethod, SslStream};
|
use openssl::ssl::{Ssl, SslContext, SslMethod, SslStream};
|
||||||
|
use png::Decoder;
|
||||||
use serenity::{
|
use serenity::{
|
||||||
async_trait,
|
async_trait,
|
||||||
framework::StandardFramework,
|
framework::StandardFramework,
|
||||||
|
@ -134,6 +138,8 @@ impl Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_frames(message: Message, ctx: Context) {
|
async fn send_frames(message: Message, ctx: Context) {
|
||||||
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
let mut v: Vec<Frame> = Vec::new();
|
let mut v: Vec<Frame> = Vec::new();
|
||||||
let dir = fs::read_dir("vid_encoded").expect("unable to read dir");
|
let dir = fs::read_dir("vid_encoded").expect("unable to read dir");
|
||||||
let dir: Vec<_> = dir.collect();
|
let dir: Vec<_> = dir.collect();
|
||||||
|
@ -161,7 +167,6 @@ async fn send_frames(message: Message, ctx: Context) {
|
||||||
.expect("voice: unable to initialize songbird");
|
.expect("voice: unable to initialize songbird");
|
||||||
let c0: Arc<Mutex<Option<ChannelId>>> = Arc::new(Mutex::new(None));
|
let c0: Arc<Mutex<Option<ChannelId>>> = Arc::new(Mutex::new(None));
|
||||||
let c1 = c0.clone();
|
let c1 = c0.clone();
|
||||||
//thread::spawn(move || {tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async move {
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let message = message;
|
let message = message;
|
||||||
let ctx = ctx;
|
let ctx = ctx;
|
||||||
|
@ -176,8 +181,6 @@ async fn send_frames(message: Message, ctx: Context) {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("discord: unable to send");
|
.expect("discord: unable to send");
|
||||||
println!("starting to send in {}@{}", n.id.0, message.channel_id.0);
|
|
||||||
//thread::spawn(move || {tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async move {
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let sa = unix_millis();
|
let sa = unix_millis();
|
||||||
println!("voice: init");
|
println!("voice: init");
|
||||||
|
@ -185,6 +188,7 @@ async fn send_frames(message: Message, ctx: Context) {
|
||||||
.create_channel(http, |c| c.name("ProjBotV3-Sound").kind(ChannelType::Voice))
|
.create_channel(http, |c| c.name("ProjBotV3-Sound").kind(ChannelType::Voice))
|
||||||
.await
|
.await
|
||||||
.expect("voice: unable to create channel");
|
.expect("voice: unable to create channel");
|
||||||
|
let api_time = unix_millis() - sa;
|
||||||
*c0.lock().await = Some(channel.id);
|
*c0.lock().await = Some(channel.id);
|
||||||
println!("voice: joining");
|
println!("voice: joining");
|
||||||
let (handler, err) = songbird.join(guild_id, channel.id).await;
|
let (handler, err) = songbird.join(guild_id, channel.id).await;
|
||||||
|
@ -192,7 +196,7 @@ async fn send_frames(message: Message, ctx: Context) {
|
||||||
panic!("voice: error {e}");
|
panic!("voice: error {e}");
|
||||||
}
|
}
|
||||||
println!("voice: loading");
|
println!("voice: loading");
|
||||||
let handle = handler.lock().await.play_source(
|
let handle = handler.lock().await.play_only_source(
|
||||||
songbird::ffmpeg("aud_encoded")
|
songbird::ffmpeg("aud_encoded")
|
||||||
.await
|
.await
|
||||||
.expect("voice: unable to load"),
|
.expect("voice: unable to load"),
|
||||||
|
@ -200,12 +204,11 @@ async fn send_frames(message: Message, ctx: Context) {
|
||||||
handle.make_playable().unwrap();
|
handle.make_playable().unwrap();
|
||||||
handle.pause().expect("voice: unable to pause");
|
handle.pause().expect("voice: unable to pause");
|
||||||
handle.set_volume(1.0).unwrap();
|
handle.set_volume(1.0).unwrap();
|
||||||
println!("voice: waiting for video");
|
println!("voice: waiting for video [api_time={api_time}]");
|
||||||
tokio::time::sleep(Duration::from_millis(5000 - (unix_millis() - sa))).await;
|
tokio::time::sleep(Duration::from_millis(5000 - (unix_millis() - sa) + (api_time * 2))).await;
|
||||||
println!("voice: playing");
|
println!("voice: playing");
|
||||||
handle.play().expect("voice: unable to play");
|
handle.play().expect("voice: unable to play");
|
||||||
println!("{:?}", handle.get_info().await);
|
});
|
||||||
}); //});
|
|
||||||
let mut sa = unix_millis();
|
let mut sa = unix_millis();
|
||||||
let mut to_compensate_for = 0;
|
let mut to_compensate_for = 0;
|
||||||
while let Some(mut frame) = v.next() {
|
while let Some(mut frame) = v.next() {
|
||||||
|
@ -293,6 +296,7 @@ async fn send_frames(message: Message, ctx: Context) {
|
||||||
println!("vid: completing");
|
println!("vid: completing");
|
||||||
frame.complete_send();
|
frame.complete_send();
|
||||||
}
|
}
|
||||||
|
tokio::time::sleep(Duration::from_millis(5000)).await;
|
||||||
n.delete(&ctx.http)
|
n.delete(&ctx.http)
|
||||||
.await
|
.await
|
||||||
.expect("discord: unable to delete message");
|
.expect("discord: unable to delete message");
|
||||||
|
@ -301,7 +305,7 @@ async fn send_frames(message: Message, ctx: Context) {
|
||||||
.await
|
.await
|
||||||
.expect("discord: unable to delete voice channel");
|
.expect("discord: unable to delete voice channel");
|
||||||
}
|
}
|
||||||
}); //});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Handler;
|
struct Handler;
|
||||||
|
@ -310,12 +314,10 @@ struct Handler;
|
||||||
impl EventHandler for Handler {
|
impl EventHandler for Handler {
|
||||||
async fn message(&self, ctx: Context, message: Message) {
|
async fn message(&self, ctx: Context, message: Message) {
|
||||||
if message.guild_id == None {
|
if message.guild_id == None {
|
||||||
println!("DM");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if message.content == "!play" {
|
if message.content == "!play" {
|
||||||
println!("hi");
|
|
||||||
send_frames(message, ctx).await;
|
send_frames(message, ctx).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,6 +325,120 @@ impl EventHandler for Handler {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
if !Path::new("vid_encoded/").is_dir() {
|
||||||
|
println!("encode: encoding video...");
|
||||||
|
fs::create_dir("vid").expect("encode: unable to modify files");
|
||||||
|
let mut command = process::Command::new("ffmpeg")
|
||||||
|
.args([
|
||||||
|
"-i",
|
||||||
|
"vid.mp4",
|
||||||
|
"-vf",
|
||||||
|
"fps=fps=25",
|
||||||
|
"-deadline",
|
||||||
|
"realtime",
|
||||||
|
"vid_25fps.mp4",
|
||||||
|
])
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.spawn()
|
||||||
|
.expect("encode: unable to find or run ffmpeg");
|
||||||
|
command.wait().expect("encode: ffmpeg failed: mp4->mp4");
|
||||||
|
let mut command = process::Command::new("ffmpeg")
|
||||||
|
.args([
|
||||||
|
"-i",
|
||||||
|
"vid_25fps.mp4",
|
||||||
|
"-vf",
|
||||||
|
"scale=240:180,setsar=1:1",
|
||||||
|
"-deadline",
|
||||||
|
"realtime",
|
||||||
|
"vid/%0d.png",
|
||||||
|
])
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.spawn()
|
||||||
|
.expect("encode: unable to find or run ffmpeg");
|
||||||
|
command.wait().expect("encode: ffmpeg failed: mp4->png");
|
||||||
|
fs::remove_file("vid_25fps.mp4").expect("encode: rm vid_25fps.mp4 failed");
|
||||||
|
let mut command = process::Command::new("ffmpeg")
|
||||||
|
.args(["-i", "vid.mp4", "-deadline", "realtime", "aud.opus"])
|
||||||
|
.stdin(Stdio::inherit())
|
||||||
|
.stdout(Stdio::inherit())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.spawn()
|
||||||
|
.expect("encode: unable to find or run ffmpeg");
|
||||||
|
command.wait().expect("encode: ffmpeg failed: mp4->opus");
|
||||||
|
fs::rename("aud.opus", "aud_encoded")
|
||||||
|
.expect("encode: unable to move aud.opus to aud_encoded");
|
||||||
|
|
||||||
|
fs::create_dir("vid_encoded").expect("encode: unable to modify files");
|
||||||
|
let dir: Vec<_> = fs::read_dir("vid")
|
||||||
|
.expect("encode: unable to read files")
|
||||||
|
.collect();
|
||||||
|
let dir = dir.len();
|
||||||
|
let running = Arc::new(Mutex::new(0));
|
||||||
|
println!("encode: encoding gifs...");
|
||||||
|
for n in 0..((dir as f32 / (25.0 * 5.0)).ceil() as usize) {
|
||||||
|
let running = running.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
*running.lock().unwrap() += 1;
|
||||||
|
let mut image = File::create(format!("vid_encoded/{n}"))
|
||||||
|
.expect("encode: unable to create gif file");
|
||||||
|
let mut encoder = Some(
|
||||||
|
Encoder::new(&mut image, 240, 180, &[]).expect("encode: unable to create gif"),
|
||||||
|
);
|
||||||
|
encoder
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.write_extension(gif::ExtensionData::new_control_ext(
|
||||||
|
4,
|
||||||
|
gif::DisposalMethod::Any,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
.expect("encode: unable to write extension data");
|
||||||
|
encoder
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.set_repeat(gif::Repeat::Finite(0))
|
||||||
|
.expect("encode: unable to set repeat");
|
||||||
|
println!("encode: encoding {n}...");
|
||||||
|
for i in (n * (25 * 5) + 1)..=dir {
|
||||||
|
let decoder = Decoder::new(
|
||||||
|
File::open(format!("vid/{}.png", i))
|
||||||
|
.expect(format!("encode: unable to read vid/{}.png", i).as_str()),
|
||||||
|
);
|
||||||
|
let mut reader = decoder
|
||||||
|
.read_info()
|
||||||
|
.expect(format!("encode: invalid ffmpeg output in vid/{}.png", i).as_str());
|
||||||
|
let mut buf: Vec<u8> = vec![0; reader.output_buffer_size()];
|
||||||
|
let info = reader
|
||||||
|
.next_frame(&mut buf)
|
||||||
|
.expect(format!("encode: invalid ffmpeg output in vid/{}.png", i).as_str());
|
||||||
|
let bytes = &mut buf[..info.buffer_size()];
|
||||||
|
let mut frame = gif::Frame::from_rgb(240, 180, &mut *bytes);
|
||||||
|
frame.delay = 4;
|
||||||
|
encoder
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.write_frame(&frame)
|
||||||
|
.expect("encode: unable to encode frame to gif");
|
||||||
|
if i / (25 * 5) != n {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*running.lock().unwrap() -= 1;
|
||||||
|
println!("encode: encoded {n}");
|
||||||
|
});
|
||||||
|
thread::sleep(Duration::from_millis(5000));
|
||||||
|
}
|
||||||
|
while *running.lock().unwrap() != 0 {
|
||||||
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
|
}
|
||||||
|
println!("encode: done");
|
||||||
|
}
|
||||||
|
|
||||||
let framework = StandardFramework::new().configure(|c| c.prefix("!"));
|
let framework = StandardFramework::new().configure(|c| c.prefix("!"));
|
||||||
let mut client = Client::builder(
|
let mut client = Client::builder(
|
||||||
env::args()
|
env::args()
|
||||||
|
|
Loading…
Add table
Reference in a new issue