about summary refs log tree commit diff stats
path: root/libmpv2/examples/events.rs
blob: 8f7c79f83286292236069733f42107ddcc036b2d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// yt - A fully featured command line YouTube client
//
// Copyright (C) 2024 Benedikt Peetz <benedikt.peetz@b-peetz.de>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This file is part of Yt.
//
// You should have received a copy of the License along with this program.
// If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.

use libmpv2::{events::*, mpv_node::MpvNode, *};

use std::{collections::HashMap, env, thread, time::Duration};

const VIDEO_URL: &str = "https://www.youtube.com/watch?v=VLnWf1sQkjY";

fn main() -> Result<()> {
    let path = env::args()
        .nth(1)
        .unwrap_or_else(|| String::from(VIDEO_URL));

    // Create an `Mpv` and set some properties.
    let mpv = Mpv::with_initializer(|init| {
        init.set_property("vo", "null")?;
        Ok(())
    })
    .unwrap();
    mpv.set_property("volume", 15)?;

    let mut ev_ctx = EventContext::new(mpv.ctx);
    ev_ctx.disable_deprecated_events()?;
    ev_ctx.observe_property("volume", Format::Int64, 0)?;
    ev_ctx.observe_property("demuxer-cache-state", Format::Node, 0)?;

    crossbeam::scope(|scope| {
        scope.spawn(|_| {
            mpv.command("loadfile", &[&path, "append-play"]).unwrap();

            thread::sleep(Duration::from_secs(3));

            mpv.set_property("volume", 25).unwrap();

            thread::sleep(Duration::from_secs(5));

            // Trigger `Event::EndFile`.
            mpv.command("playlist-next", &["force"]).unwrap();
        });
        scope.spawn(move |_| loop {
            let ev = ev_ctx.wait_event(600.).unwrap_or(Err(Error::Null));

            match ev {
                Ok(Event::EndFile(r)) => {
                    println!("Exiting! Reason: {:?}", r);
                    break;
                }

                Ok(Event::PropertyChange {
                    name: "demuxer-cache-state",
                    change: PropertyData::Node(mpv_node),
                    ..
                }) => {
                    let ranges = seekable_ranges(mpv_node);
                    println!("Seekable ranges updated: {:?}", ranges);
                }
                Ok(e) => println!("Event triggered: {:?}", e),
                Err(e) => println!("Event errored: {:?}", e),
            }
        });
    })
    .unwrap();
    Ok(())
}

fn seekable_ranges(demuxer_cache_state: MpvNode) -> Vec<(f64, f64)> {
    let mut res = Vec::new();
    let props = demuxer_cache_state
        .map()
        .unwrap()
        .collect::<HashMap<_, _>>();
    let ranges = props
        .get("seekable-ranges")
        .unwrap()
        .clone()
        .array()
        .unwrap();
    for node in ranges {
        let range = node.map().unwrap().collect::<HashMap<_, _>>();
        let start = range.get("start").unwrap().f64().unwrap();
        let end = range.get("end").unwrap().f64().unwrap();
        res.push((start, end));
    }
    res
}