aboutsummaryrefslogtreecommitdiff
path: root/server/src/routes/ui/player.rs
blob: 0e877490648577e5792eb34da9fec538ff676fbd (plain)
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
    This file is part of jellything (https://codeberg.org/metamuffin/jellything)
    which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
    Copyright (C) 2023 metamuffin <metamuffin.org>
*/
use super::{account::session::Session, layout::LayoutPage};
use crate::{
    database::Database,
    routes::{
        stream::stream_uri,
        ui::{
            assets::{rocket_uri_macro_r_item_assets, AssetRole},
            error::MyResult,
            layout::DynLayoutPage,
        },
    },
    uri,
};
use anyhow::anyhow;
use jellycommon::{Node, SourceTrackKind};
use markup::DynRender;
use rocket::{get, FromForm, State};

pub fn player_uri(id: &str) -> String {
    format!("/player/{}", id)
}

#[derive(FromForm, Default, Clone, Debug)]
pub struct PlayerConfig {
    pub a: Option<u64>,
    pub v: Option<u64>,
    pub s: Option<u64>,
    pub webm: bool,
}

#[get("/player/<id>?<conf..>", rank = 4)]
pub fn r_player(
    _sess: Session,
    db: &State<Database>,
    id: String,
    conf: PlayerConfig,
) -> MyResult<DynLayoutPage<'_>> {
    let item = db.node.get(&id)?.ok_or(anyhow!("node does not exist"))?;
    let tracks = None
        .into_iter()
        .chain(conf.v.into_iter())
        .chain(conf.a.into_iter())
        .chain(conf.s.into_iter())
        .collect::<Vec<_>>();

    let conf = player_conf(item.clone(), !tracks.is_empty())?;
    Ok(LayoutPage {
        title: item.public.title.to_owned(),
        class: Some("player"),
        content: markup::new! {
            @if tracks.is_empty() {
                img.backdrop[src=uri!(r_item_assets(&id, AssetRole::Backdrop)).to_string()];
            } else {
                video[src=stream_uri(&id, &tracks, true), controls]{}
            }
            @conf
        },
        show_back: true,
        ..Default::default()
    })
}

pub fn player_conf<'a>(item: Node, playing: bool) -> anyhow::Result<DynRender<'a>> {
    let mut audio_tracks = vec![];
    let mut video_tracks = vec![];
    let mut sub_tracks = vec![];
    let tracks = item
        .public
        .media
        .clone()
        .ok_or(anyhow!("node does not have media"))?
        .tracks
        .clone();
    for (tid, track) in tracks.into_iter().enumerate() {
        match &track.kind {
            SourceTrackKind::Audio { .. } => audio_tracks.push((tid, track)),
            SourceTrackKind::Video { .. } => video_tracks.push((tid, track)),
            SourceTrackKind::Subtitles { .. } => sub_tracks.push((tid, track)),
        }
    }

    Ok(markup::new! {
        form.playerconf[method = "GET", action = ""] {
            h2 { "Select tracks for " @item.public.title }

            fieldset.video {
                legend { "Video" }
                @for (i, (tid, track)) in video_tracks.iter().enumerate() {
                    input[type="radio", id=tid, name="v", value=tid, checked=i==0];
                    label[for=tid] { @format!("{track}") } br;
                }
                input[type="radio", id="v-none", name="v", value=""];
                label[for="v-none"] { "No video" }
            }

            fieldset.audio {
                legend { "Audio" }
                @for (i, (tid, track)) in audio_tracks.iter().enumerate() {
                    input[type="radio", id=tid, name="a", value=tid, checked=i==0];
                    label[for=tid] { @format!("{track}") } br;
                }
                input[type="radio", id="a-none", name="a", value=""];
                label[for="a-none"] { "No audio" }
            }

            fieldset.subtitles {
                legend { "Subtitles" }
                @for (_i, (tid, track)) in sub_tracks.iter().enumerate() {
                    input[type="radio", id=tid, name="s", value=tid];
                    label[for=tid] { @format!("{track}") } br;
                }
                input[type="radio", id="s-none", name="s", value="", checked=true];
                label[for="s-none"] { "No subtitles" }
            }

            input[type="submit", value=if playing { "Change tracks" } else { "Start playback" }];
        }
    })
}