plex-api 0.0.12

Library for communication with Plex server. Work in progress, not ready for any use. See github for details.
Documentation
use plex_api::{
    device::Device,
    library::{Item, MetadataItem, Transcodable},
    media_container::server::library::{AudioCodec, ContainerFormat, VideoCodec},
    transcode::{QueueItemStatus, VideoTranscodeOptions},
    HttpClientBuilder, MyPlexBuilder,
};
use rpassword::prompt_password;
use std::{
    io::{stdin, stdout, BufRead, Write},
    path::PathBuf,
};
use tokio::{
    fs::OpenOptions,
    io::BufWriter,
    time::{sleep, Duration},
};
use tokio_util::compat::TokioAsyncReadCompatExt;

async fn download<M>(media: M)
where
    M: Transcodable<Options = VideoTranscodeOptions> + MetadataItem,
{
    let mut entry = media
        .queue_download(
            VideoTranscodeOptions {
                bitrate: 4000,
                width: 1280,
                height: 720,
                containers: vec![ContainerFormat::Mp4],
                video_codecs: vec![VideoCodec::H264],
                audio_codecs: vec![AudioCodec::Ac3],
                ..Default::default()
            },
            None,
        )
        .await
        .unwrap();

    loop {
        match entry.status() {
            QueueItemStatus::Deciding | QueueItemStatus::Waiting => {
                sleep(Duration::from_millis(200)).await;
                entry.update().await.unwrap();
            }
            QueueItemStatus::Processing => {
                sleep(Duration::from_millis(1000)).await;
                entry.update().await.unwrap();
            }
            QueueItemStatus::Available => {
                println!("\nDownload available!:\n{:#?}\n", entry);
                break;
            }
            QueueItemStatus::Error => {
                println!("\nTranscode errored\n");
                break;
            }
            QueueItemStatus::Expired => {
                println!("\nTranscode expired\n");
                break;
            }
        }
    }

    let target = PathBuf::from(format!(
        "{}.{}",
        media.metadata().title,
        entry.container().await.unwrap()
    ));

    let file = OpenOptions::new()
        .append(true)
        .create(true)
        .open(&target)
        .await
        .unwrap();

    let mut writer = BufWriter::new(file).compat();

    entry.download(&mut writer, ..).await.unwrap();

    eprintln!("Item {}: Downloaded", media.metadata().key);

    entry.delete().await.unwrap();
}

#[tokio::main]
async fn main() {
    let mut pargs = pico_args::Arguments::from_env();

    let token = std::env::var("PLEX_API_AUTH_TOKEN")
        .unwrap_or_else(|_| prompt_password("Token: ").unwrap());
    stdout().flush().unwrap();

    let client_identifier = pargs
        .opt_value_from_str("--client-id")
        .unwrap()
        .unwrap_or_else(|| "dqueueexample".to_string());

    let myplex = MyPlexBuilder::default()
        .set_client(
            HttpClientBuilder::generic()
                .set_x_plex_client_identifier(&client_identifier)
                .build()
                .unwrap(),
        )
        .set_token(token)
        .build()
        .await
        .unwrap();

    let device_manager = myplex.device_manager().unwrap();
    let devices = device_manager.devices().await.unwrap();

    if devices.is_empty() {
        eprintln!("You have no devices");
        return;
    }

    let devices = devices
        .into_iter()
        .filter(|device| device.is_server())
        .collect::<Vec<Device>>();

    let device = {
        if devices.len() == 1 {
            devices.first().unwrap()
        } else {
            println!("Please selected the server you want to connect to:");
            for (idx, d) in devices.iter().enumerate() {
                println!("{}. {}", idx + 1, d.name());
            }
            let idx = stdin().lock().lines().next().unwrap().unwrap();
            let idx: usize = idx.parse().unwrap();
            if idx > devices.len() + 1 || idx < 1 {
                eprintln!("Don't be like that");
                return;
            }
            devices.get(idx - 1).unwrap()
        }
    };

    let server = match device.connect().await.unwrap() {
        plex_api::device::DeviceConnection::Server(srv) => srv,
        _ => panic!("HOW?"),
    };

    let queue = server.download_queue().await.unwrap();

    if pargs.contains("--clear") {
        for item in queue.items().await.unwrap() {
            item.delete().await.unwrap();
        }
    }

    eprintln!("Current queue: {:#?}", queue.items().await.unwrap());

    print!("Item ID: ");
    stdout().flush().unwrap();

    let identifier = stdin().lock().lines().next().unwrap().unwrap();

    let item = server.item_by_id(&identifier).await.unwrap();

    match item {
        Item::Movie(m) => {
            download(m).await;
        }
        Item::Episode(e) => {
            download(e).await;
        }
        _ => eprintln!("Unsupported item type"),
    }
}