Created
March 27, 2022 04:09
-
-
Save 19wintersp/bf537acdbbcbaf9d6384514722b0cfc6 to your computer and use it in GitHub Desktop.
Code to generate a WebM file that will show differently depending on the player
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "weird-discord-video" | |
version = "0.1.0" | |
edition = "2021" | |
[dependencies] | |
anyhow = "^1.0" | |
argh = "^0.1" | |
webm-iterable = "^0.3" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[macro_use] extern crate anyhow; | |
use std::fs::File; | |
use std::process::Command; | |
use anyhow::Result; | |
use argh::FromArgs; | |
use webm_iterable::{ WebmIterator, WebmWriter }; | |
use webm_iterable::matroska_spec::{ Master, MatroskaSpec as Spec }; | |
/// Create videos that show differently on different platforms. | |
#[derive(FromArgs)] | |
struct Args { | |
/// video to show on android | |
#[argh(option)] | |
android_video: Option<String>, | |
/// video to show on firefox | |
#[argh(option)] | |
firefox_video: Option<String>, | |
/// video to show on chromium/electron app | |
#[argh(option)] | |
chromium_video: Option<String>, | |
/// video to show on all platforms | |
#[argh(option, short = 'v')] | |
video: Option<String>, | |
/// audio to play on android | |
#[argh(option)] | |
android_audio: Option<String>, | |
/// audio to play on firefox | |
#[argh(option)] | |
firefox_audio: Option<String>, | |
/// audio to play on chromium/electron app | |
#[argh(option)] | |
chromium_audio: Option<String>, | |
/// audio to play on all platforms | |
#[argh(option, short = 'a')] | |
audio: Option<String>, | |
/// path to output WEBM file | |
#[argh(option, short = 'o')] | |
output: Option<String>, | |
/// ffmpeg command to execute | |
#[argh(option)] | |
ffmpeg: Option<String>, | |
} | |
#[derive(Clone, Copy, Debug, PartialEq)] | |
enum Platform { | |
Android, | |
Firefox, | |
Chromium, | |
All, | |
} | |
fn main() { | |
if let Err(err) = kmain() { | |
eprintln!("error: {}", err); | |
std::process::exit(1); | |
} | |
} | |
fn kmain() -> Result<()> { | |
let args = argh::from_env::<Args>(); | |
if args.video.is_some() { | |
if | |
args.android_video.is_some() || | |
args.firefox_video.is_some() || | |
args.chromium_video.is_some() | |
{ | |
bail!("cannot have all and specific video"); | |
} | |
} | |
if args.audio.is_some() { | |
if | |
args.android_audio.is_some() || | |
args.firefox_audio.is_some() || | |
args.chromium_audio.is_some() | |
{ | |
bail!("cannot have all and specific audio"); | |
} | |
} | |
let output = args.output.unwrap_or("output.webm".into()); | |
let ffmpeg = args.ffmpeg.unwrap_or("ffmpeg".into()); | |
// create the basic video | |
let temp_output = { | |
let mut temp = std::env::temp_dir(); | |
temp.push("temp.webm"); | |
if temp.exists() { | |
std::fs::remove_file(&temp)?; | |
} | |
temp.to_string_lossy().to_string() | |
}; | |
let mut platforms = Vec::new(); | |
let mut command = Vec::new(); | |
if let Some(video) = args.android_video { | |
platforms.push(Platform::Android); | |
command.append(&mut vec![ "-i".into(), video ]); | |
} | |
if let Some(video) = args.firefox_video { | |
platforms.push(Platform::Firefox); | |
command.append(&mut vec![ "-i".into(), video ]); | |
} | |
if let Some(video) = args.chromium_video { | |
platforms.push(Platform::Chromium); | |
command.append(&mut vec![ "-i".into(), video ]); | |
} | |
if let Some(video) = args.video { | |
platforms.push(Platform::All); | |
command.append(&mut vec![ "-i".into(), video ]); | |
} | |
if let Some(audio) = args.android_audio { | |
platforms.push(Platform::Android); | |
command.append(&mut vec![ "-i".into(), audio ]); | |
} | |
if let Some(audio) = args.firefox_audio { | |
platforms.push(Platform::Firefox); | |
command.append(&mut vec![ "-i".into(), audio ]); | |
} | |
if let Some(audio) = args.chromium_audio { | |
platforms.push(Platform::Chromium); | |
command.append(&mut vec![ "-i".into(), audio ]); | |
} | |
if let Some(audio) = args.audio { | |
platforms.push(Platform::All); | |
command.append(&mut vec![ "-i".into(), audio ]); | |
} | |
command.append( | |
&mut (0..command.len() / 2) | |
.map(|index| vec![ "-map".into(), index.to_string() ]) | |
.collect::<Vec<_>>() | |
.concat() | |
); | |
command.push(temp_output.clone()); | |
let status = Command::new(ffmpeg) | |
.args(command) | |
.spawn()? | |
.wait()?; | |
if !status.success() { | |
bail!("ffmpeg exited with non-zero exit code"); | |
} | |
// screw up the video | |
let webm_input = File::open(temp_output)?; | |
let webm_output = File::create(output)?; | |
let track_start = Spec::Tracks(Master::Start); | |
let tag_input = WebmIterator::new(webm_input, &[track_start]); | |
let mut tag_output = WebmWriter::new(webm_output); | |
// by god i renounce this cursèd child of mine | |
for tag_result in tag_input { | |
let tag = tag_result?; | |
if let Spec::Tracks(master) = tag { | |
if let Master::Full(tag_tracks) = master { | |
tag_output.write(&Spec::Tracks(Master::Start))?; | |
for (i, track) in tag_tracks.iter().enumerate() { | |
if let Spec::TrackEntry(Master::Full(tags)) = track { | |
tag_output.write(&Spec::TrackEntry(Master::Start))?; | |
for tag in tags { | |
match tag { | |
Spec::TrackType(_) => { | |
if | |
platforms[i] == Platform::All || | |
platforms[i] == Platform::Chromium | |
{ | |
tag_output.write(tag)?; | |
} | |
}, | |
Spec::CodecId(id) => { | |
tag_output.write(tag)?; | |
let true_type = match &id[..2] { | |
"A_" => 2, | |
"V_" => 1, | |
_ => bail!("codec id is not video or audio"), | |
}; | |
let track_type = match platforms[i] { | |
Platform::Android => 3, | |
Platform::Firefox => true_type, | |
_ => continue, | |
}; | |
tag_output.write(&Spec::TrackType(track_type))?; | |
}, | |
Spec::Video(_) | Spec::Audio(_) => { | |
tag_output.write(tag)?; | |
if platforms[i] == Platform::Firefox { | |
tag_output.write(&Spec::TrackType(3))?; | |
} | |
}, | |
Spec::Language(_) => { | |
tag_output.write(tag)?; | |
if platforms[i] != Platform::Android { | |
tag_output.write(&Spec::FlagDefault(0))?; | |
} | |
}, | |
Spec::FlagDefault(_) => (), | |
_ => tag_output.write(tag)?, | |
} | |
} | |
tag_output.write(&Spec::TrackEntry(Master::End))?; | |
} else { | |
unreachable!() | |
} | |
} | |
tag_output.write(&Spec::Tracks(Master::End))?; | |
} else { | |
unreachable!() | |
} | |
} else { | |
tag_output.write(&tag)?; | |
} | |
} | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment