interactive: Host/View entry menu, clipboard copy, player picker
Bare `pixelpass` now opens a dialoguer-driven Host/View menu instead of going straight to host mode. Host path copies the ticket to the system clipboard via arboard with silent print-only fallback. View path prompts for the ticket, then after the local listener binds prompts mpv-vs-VLC and spawns it detached (setsid + null stdio) so the player survives pixelpass exiting. Headless invocations (`pixelpass <ticket>`, `pixelpass --repair`) unchanged. Per spec at ~/Documents/pixelpass-interactive-mode-spec.md.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
use anyhow::Result;
|
||||
use dialoguer::{Input, Select, theme::ColorfulTheme};
|
||||
use iroh_tickets::endpoint::EndpointTicket;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::cli::Cli;
|
||||
use crate::{host, viewer};
|
||||
|
||||
pub async fn run(cli: Cli) -> Result<()> {
|
||||
print_welcome();
|
||||
|
||||
let theme = ColorfulTheme::default();
|
||||
let choice = Select::with_theme(&theme)
|
||||
.with_prompt("What do you want to do?")
|
||||
.items(&[
|
||||
"Host (share my screen)",
|
||||
"View (watch someone else's screen)",
|
||||
])
|
||||
.default(0)
|
||||
.interact()?;
|
||||
|
||||
match choice {
|
||||
0 => host::run(cli.into_host_opts(true)).await,
|
||||
_ => {
|
||||
let ticket = prompt_ticket(&theme)?;
|
||||
viewer::run(ticket, cli.into_viewer_opts(true)).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_welcome() {
|
||||
eprintln!();
|
||||
eprintln!("Welcome to PixelPass.");
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
fn prompt_ticket(theme: &ColorfulTheme) -> Result<EndpointTicket> {
|
||||
loop {
|
||||
let raw: String = Input::with_theme(theme)
|
||||
.with_prompt("Paste the share code you received")
|
||||
.interact_text()?;
|
||||
match EndpointTicket::from_str(raw.trim()) {
|
||||
Ok(t) => return Ok(t),
|
||||
Err(_) => eprintln!("That doesn't look like a share code. Try again."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Player options the interactive viewer can launch.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Player {
|
||||
Mpv,
|
||||
Vlc,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn spawn(self, url: &str) -> std::io::Result<()> {
|
||||
match self {
|
||||
Player::Mpv => crate::common::process::spawn_detached(
|
||||
"mpv",
|
||||
&[
|
||||
"--profile=low-latency",
|
||||
"--untimed",
|
||||
"--audio-buffer=0.2",
|
||||
"--demuxer-max-bytes=2M",
|
||||
"--demuxer-readahead-secs=0.5",
|
||||
url,
|
||||
],
|
||||
),
|
||||
Player::Vlc => crate::common::process::spawn_detached(
|
||||
"vlc",
|
||||
&["--network-caching=200", "--live-caching=200", url],
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prompt_player() -> Result<Player> {
|
||||
let theme = ColorfulTheme::default();
|
||||
let choice = Select::with_theme(&theme)
|
||||
.with_prompt("Connected. Pick a player to launch")
|
||||
.items(&["mpv", "VLC"])
|
||||
.default(0)
|
||||
.interact()?;
|
||||
Ok(if choice == 0 { Player::Mpv } else { Player::Vlc })
|
||||
}
|
||||
Reference in New Issue
Block a user