diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-02-27 14:40:15 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-02-27 14:40:15 +0100 |
| commit | c05bfcc2775f0e11db6e856bfcf06d0419c35d54 (patch) | |
| tree | ffd0e9fcf6b476a6198287085a514cfa7940c200 /server/src/ui | |
| parent | 4ba86694e393c61107e27c4127efc0455b329524 (diff) | |
| download | jellything-c05bfcc2775f0e11db6e856bfcf06d0419c35d54.tar jellything-c05bfcc2775f0e11db6e856bfcf06d0419c35d54.tar.bz2 jellything-c05bfcc2775f0e11db6e856bfcf06d0419c35d54.tar.zst | |
ui changed before object slices
Diffstat (limited to 'server/src/ui')
| -rw-r--r-- | server/src/ui/account/mod.rs | 37 | ||||
| -rw-r--r-- | server/src/ui/account/settings.rs | 17 | ||||
| -rw-r--r-- | server/src/ui/admin/import.rs | 28 | ||||
| -rw-r--r-- | server/src/ui/admin/log.rs | 19 | ||||
| -rw-r--r-- | server/src/ui/admin/mod.rs | 37 | ||||
| -rw-r--r-- | server/src/ui/admin/users.rs | 46 | ||||
| -rw-r--r-- | server/src/ui/home.rs | 97 | ||||
| -rw-r--r-- | server/src/ui/items.rs | 45 | ||||
| -rw-r--r-- | server/src/ui/player.rs | 90 |
9 files changed, 153 insertions, 263 deletions
diff --git a/server/src/ui/account/mod.rs b/server/src/ui/account/mod.rs index ab5093d..b106fc7 100644 --- a/server/src/ui/account/mod.rs +++ b/server/src/ui/account/mod.rs @@ -12,33 +12,37 @@ use crate::{ auth::{hash_password, login}, request_info::RequestInfo, ui::error::MyResult, - ui_responder::UiResponse, }; use anyhow::anyhow; use jellycommon::{ - jellyobject::{OBB, Path}, + jellyobject::Path, routes::{u_account_login, u_home}, *, }; use jellydb::{Filter, Query}; +use jellyui::components::login::{AccountLogin, AccountLogout, AccountSetPassword}; use rocket::{ Either, FromForm, form::{Contextual, Form}, get, http::{Cookie, CookieJar}, post, - response::{Flash, Redirect}, + response::{Flash, Redirect, content::RawHtml}, }; use serde::{Deserialize, Serialize}; #[get("/account/login")] -pub async fn r_account_login(ri: RequestInfo<'_>) -> UiResponse { - ri.respond_ui(OBB::new().with(VIEW_ACCOUNT_LOGIN, ())) +pub async fn r_account_login(ri: RequestInfo<'_>) -> RawHtml<String> { + ri.respond_ui(&AccountLogin { + ri: &ri.render_info(), + }) } #[get("/account/logout")] -pub fn r_account_logout(ri: RequestInfo<'_>) -> UiResponse { - ri.respond_ui(OBB::new().with(VIEW_ACCOUNT_LOGOUT, ())) +pub fn r_account_logout(ri: RequestInfo<'_>) -> RawHtml<String> { + ri.respond_ui(&AccountLogout { + ri: &ri.render_info(), + }) } #[derive(FromForm, Serialize, Deserialize)] @@ -60,7 +64,7 @@ pub fn r_account_login_post( ri: RequestInfo<'_>, jar: &CookieJar, form: Form<Contextual<LoginForm>>, -) -> MyResult<Either<Redirect, UiResponse>> { +) -> MyResult<Either<Redirect, RawHtml<String>>> { let form = match &form.value { Some(v) => v, None => return Err(MyError(anyhow!(format_form_error(form)))), @@ -88,18 +92,11 @@ pub fn r_account_login_post( Ok(()) })?; } else { - return Ok(Either::Right( - ri.respond_ui( - OBB::new().with( - VIEW_ACCOUNT_SET_PASSWORD, - OBB::new() - .with(SETPW_USERNAME, &form.username) - .with(SETPW_PASSWORD, &form.password) - .finish() - .as_object(), - ), - ), - )); + return Ok(Either::Right(ri.respond_ui(&AccountSetPassword { + ri: &ri.render_info(), + password: &form.password, + username: &form.username, + }))); } } diff --git a/server/src/ui/account/settings.rs b/server/src/ui/account/settings.rs index c1068f6..9a98c09 100644 --- a/server/src/ui/account/settings.rs +++ b/server/src/ui/account/settings.rs @@ -4,22 +4,20 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ use super::format_form_error; -use crate::{ - auth::hash_password, request_info::RequestInfo, ui::error::MyResult, ui_responder::UiResponse, -}; +use crate::{auth::hash_password, request_info::RequestInfo, ui::error::MyResult}; use anyhow::anyhow; use jellycommon::{ - jellyobject::{OBB, Object, ObjectBuffer, Path, Tag}, + jellyobject::{Object, ObjectBuffer, Path, Tag}, routes::u_account_settings, *, }; use jellydb::{Filter, Query}; -use jellyui::tr; +use jellyui::{components::user::UserSettings, tr}; use rocket::{ FromForm, form::{self, Contextual, Form, validate::len}, get, post, - response::{Flash, Redirect}, + response::{Flash, Redirect, content::RawHtml}, }; use std::ops::Range; @@ -38,9 +36,12 @@ fn option_len<'v>(value: &Option<String>, range: Range<usize>) -> form::Result<' } #[get("/account/settings")] -pub fn r_account_settings(ri: RequestInfo) -> MyResult<UiResponse> { +pub fn r_account_settings(ri: RequestInfo) -> MyResult<RawHtml<String>> { let user = ri.require_user()?; - Ok(ri.respond_ui(OBB::new().with(VIEW_USER_SETTINGS, user))) + Ok(ri.respond_ui(&UserSettings { + ri: &ri.render_info(), + user, + })) } #[post("/account/settings", data = "<form>")] diff --git a/server/src/ui/admin/import.rs b/server/src/ui/admin/import.rs index 78db4a4..e199de4 100644 --- a/server/src/ui/admin/import.rs +++ b/server/src/ui/admin/import.rs @@ -4,27 +4,23 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{request_info::RequestInfo, ui::error::MyResult, ui_responder::UiResponse}; -use jellycommon::{ - jellyobject::{OBB, ObjectBuffer}, - routes::u_admin_import, - *, -}; +use crate::{request_info::RequestInfo, ui::error::MyResult}; +use jellycommon::routes::u_admin_import; use jellyimport::{ ImportConfig, import_wrap, is_importing, reporting::{IMPORT_ERRORS, IMPORT_PROGRESS}, }; -use jellyui::tr; +use jellyui::{components::admin::AdminImport, tr}; use rocket::{ get, post, - response::{Flash, Redirect}, + response::{Flash, Redirect, content::RawHtml}, }; use rocket_ws::{Message, Stream, WebSocket}; use std::time::Duration; use tokio::{spawn, time::sleep}; #[get("/admin/import", rank = 2)] -pub async fn r_admin_import(ri: RequestInfo<'_>) -> MyResult<UiResponse> { +pub async fn r_admin_import(ri: RequestInfo<'_>) -> MyResult<RawHtml<String>> { ri.require_admin()?; let last_import_err = IMPORT_ERRORS.read().await.clone(); @@ -33,15 +29,11 @@ pub async fn r_admin_import(ri: RequestInfo<'_>) -> MyResult<UiResponse> { .map(|e| e.as_str()) .collect::<Vec<_>>(); - let mut data = ObjectBuffer::empty(); - if is_importing() { - data = data.as_object().insert(ADMIN_IMPORT_BUSY, ()); - } - data = data - .as_object() - .insert_multi(ADMIN_IMPORT_ERROR, &last_import_err); - - Ok(ri.respond_ui(OBB::new().with(VIEW_ADMIN_IMPORT, data.as_object()))) + Ok(ri.respond_ui(&AdminImport { + busy: is_importing(), + errors: &last_import_err, + ri: &ri.render_info(), + })) } #[post("/admin/import?<incremental>")] diff --git a/server/src/ui/admin/log.rs b/server/src/ui/admin/log.rs index 0965a25..168ec6a 100644 --- a/server/src/ui/admin/log.rs +++ b/server/src/ui/admin/log.rs @@ -8,7 +8,7 @@ use crate::{ request_info::RequestInfo, ui::error::MyResult, }; -use jellyui::{Scaffold, ServerLogPage, render_log_line}; +use jellyui::components::admin_log::{ServerLogPage, render_log_line}; use rocket::{get, response::content::RawHtml}; use rocket_ws::{Message, Stream, WebSocket}; use serde_json::json; @@ -21,18 +21,11 @@ pub fn r_admin_log(ri: RequestInfo, warnonly: bool) -> MyResult<RawHtml<String>> .map(|l| render_log_line(&l)) .collect::<Vec<_>>(); - Ok(RawHtml( - Scaffold { - class: "theme-purple", - main: ServerLogPage { - messages: &messages, - warnonly, - }, - ri: &ri.render_info(), - title: "Admin Log", - } - .to_string(), - )) + Ok(ri.respond_ui(&ServerLogPage { + ri: &ri.render_info(), + messages: &messages, + warnonly, + })) } #[get("/admin/log?stream&<warnonly>&<html>", rank = 1)] diff --git a/server/src/ui/admin/mod.rs b/server/src/ui/admin/mod.rs index 10037b5..6119b74 100644 --- a/server/src/ui/admin/mod.rs +++ b/server/src/ui/admin/mod.rs @@ -9,34 +9,21 @@ pub mod log; pub mod users; use super::error::MyResult; -use crate::{request_info::RequestInfo, ui_responder::UiResponse}; -use jellycommon::{ - jellyobject::{OBB, ObjectBuffer}, - *, -}; -use jellyui::tr; -use rocket::get; +use crate::request_info::RequestInfo; +use jellyui::components::admin::AdminDashboard; +use rocket::{get, response::content::RawHtml}; #[get("/admin/dashboard")] -pub async fn r_admin_dashboard(ri: RequestInfo<'_>) -> MyResult<UiResponse> { +pub async fn r_admin_dashboard(ri: RequestInfo<'_>) -> MyResult<RawHtml<String>> { ri.require_admin()?; - let mut db_debug = String::new(); - ri.state.database.transaction(&mut |txn| { - db_debug = txn.debug_info()?; - Ok(()) - })?; + // let mut db_debug = String::new(); + // ri.state.database.transaction(&mut |txn| { + // db_debug = txn.debug_info()?; + // Ok(()) + // })?; - let mut page = OBB::new(); - page.push(VIEW_TITLE, &*tr(ri.lang, "admin.dashboard.title")); - page.push(VIEW_ADMIN_DASHBOARD, ()); - page.push( - VIEW_ADMIN_INFO, - ObjectBuffer::new(&mut [ - (ADMIN_INFO_TITLE.0, &"Database Debug"), - (ADMIN_INFO_TEXT.0, &db_debug.as_str()), - ]) - .as_object(), - ); - Ok(ri.respond_ui(page)) + Ok(ri.respond_ui(&AdminDashboard { + ri: &ri.render_info(), + })) } diff --git a/server/src/ui/admin/users.rs b/server/src/ui/admin/users.rs index 172facc..85f241b 100644 --- a/server/src/ui/admin/users.rs +++ b/server/src/ui/admin/users.rs @@ -6,28 +6,29 @@ use std::str::FromStr; +use crate::{auth::hash_password, request_info::RequestInfo, ui::error::MyResult}; +use anyhow::anyhow; use base64::{Engine, prelude::BASE64_URL_SAFE}; use jellycommon::{ - jellyobject::{OBB, ObjectBufferBuilder, Path}, + jellyobject::{ObjectBufferBuilder, Path}, routes::u_admin_users, *, }; use jellydb::{Filter, Query}; -use jellyui::tr; +use jellyui::{ + components::admin::{AdminUser, AdminUserList}, + tr, +}; use rand::random; use rocket::{ FromForm, form::Form, get, post, - response::{Flash, Redirect}, -}; - -use crate::{ - auth::hash_password, request_info::RequestInfo, ui::error::MyResult, ui_responder::UiResponse, + response::{Flash, Redirect, content::RawHtml}, }; #[get("/admin/users")] -pub fn r_admin_users(ri: RequestInfo) -> MyResult<UiResponse> { +pub fn r_admin_users(ri: RequestInfo) -> MyResult<RawHtml<String>> { ri.require_admin()?; let mut users = Vec::new(); @@ -43,15 +44,10 @@ pub fn r_admin_users(ri: RequestInfo) -> MyResult<UiResponse> { Ok(()) })?; - let mut list = ObjectBufferBuilder::default(); - for u in users { - list.push(ADMIN_USER_LIST_ITEM, u.as_object()); - } - - let mut page = ObjectBufferBuilder::default(); - page.push(VIEW_TITLE, &*tr(ri.lang, "admin.users")); - page.push(VIEW_ADMIN_USER_LIST, list.finish().as_object()); - Ok(ri.respond_ui(page)) + Ok(ri.respond_ui(&AdminUserList { + ri: &ri.render_info(), + users: &users.iter().map(|u| u.as_object()).collect::<Vec<_>>(), + })) } #[derive(FromForm)] @@ -83,22 +79,26 @@ pub fn r_admin_new_user(ri: RequestInfo, form: Form<NewUser>) -> MyResult<Flash< } #[get("/admin/user/<name>")] -pub fn r_admin_user(ri: RequestInfo<'_>, name: &str) -> MyResult<UiResponse> { +pub fn r_admin_user(ri: RequestInfo<'_>, name: &str) -> MyResult<RawHtml<String>> { ri.require_admin()?; - let mut page = OBB::new(); + let mut user = None; ri.state.database.transaction(&mut |txn| { if let Some(row) = txn.query_single(Query { filter: Filter::Match(Path(vec![USER_LOGIN.0]), name.into()), ..Default::default() })? { - let user = txn.get(row)?.unwrap(); - page = OBB::new(); - page.push(VIEW_ADMIN_USER, user.as_object()); + user = Some(txn.get(row)?.unwrap()); } Ok(()) })?; + let Some(user) = user else { + Err(anyhow!("no such user"))? + }; - Ok(ri.respond_ui(page)) + Ok(ri.respond_ui(&AdminUser { + ri: &ri.render_info(), + user: user.as_object(), + })) } #[post("/admin/user/<name>/remove")] diff --git a/server/src/ui/home.rs b/server/src/ui/home.rs index 1a7da36..6cb6a77 100644 --- a/server/src/ui/home.rs +++ b/server/src/ui/home.rs @@ -4,77 +4,50 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use std::str::FromStr; - use super::error::MyResult; -use crate::{request_info::RequestInfo, ui_responder::UiResponse}; +use crate::request_info::RequestInfo; use anyhow::{Context, Result}; -use jellycommon::{ - jellyobject::{Object, ObjectBuffer, ObjectBufferBuilder}, - *, -}; +use jellycommon::jellyobject::{Object, ObjectBuffer}; use jellydb::Query; -use jellyui::tr; -use rocket::get; +use jellyui::components::home::HomeRow; +use rocket::{get, response::content::RawHtml}; +use std::str::FromStr; #[get("/home")] -pub fn r_home(ri: RequestInfo<'_>) -> MyResult<UiResponse> { +pub fn r_home(ri: RequestInfo<'_>) -> MyResult<RawHtml<String>> { ri.require_user()?; - let mut page = ObjectBufferBuilder::default(); - - page.push(VIEW_TITLE, &&*tr(ri.lang, "home")); - - page.push( - VIEW_NODE_LIST, - home_row( - &ri, - "home.bin.latest_video", - "FILTER (visi = visi AND kind = vide) SORT DESCENDING BY FIRST rldt", - )? - .as_object(), - ); - page.push( - VIEW_NODE_LIST, - home_row( - &ri, - "home.bin.latest_music", - "FILTER (visi = visi AND kind = musi) SORT DESCENDING BY FIRST rldt", - )? - .as_object(), - ); - page.push( - VIEW_NODE_LIST, - home_row_highlight( - &ri, - "home.bin.daily_random", - "FILTER (visi = visi AND kind = movi) SORT RANDOM", - )? - .as_object(), - ); - page.push( - VIEW_NODE_LIST, - home_row( - &ri, - "home.bin.max_rating", - "SORT DESCENDING BY FIRST rtng.imdb", - )? - .as_object(), - ); - page.push( - VIEW_NODE_LIST, - home_row_highlight( - &ri, - "home.bin.daily_random", - "FILTER (visi = visi AND kind = show) SORT RANDOM", - )? - .as_object(), - ); + let mut rows = Vec::new(); + rows.push(home_row( + &ri, + "home.bin.latest_video", + "FILTER (visi = visi AND kind = vide) SORT DESCENDING BY FIRST rldt", + )?); + rows.push(home_row( + &ri, + "home.bin.latest_music", + "FILTER (visi = visi AND kind = musi) SORT DESCENDING BY FIRST rldt", + )?); + rows.push(home_row_highlight( + &ri, + "home.bin.daily_random", + "FILTER (visi = visi AND kind = movi) SORT RANDOM", + )?); + rows.push(home_row( + &ri, + "home.bin.max_rating", + "SORT DESCENDING BY FIRST rtng.imdb", + )?); + rows.push(home_row_highlight( + &ri, + "home.bin.daily_random", + "FILTER (visi = visi AND kind = show) SORT RANDOM", + )?); - Ok(ri.respond_ui(page)) + Ok(ri.respond_ui(rows)) } -fn home_row(ri: &RequestInfo<'_>, title: &str, query: &str) -> Result<ObjectBuffer> { +fn home_row(ri: &RequestInfo<'_>, title: &str, query: &str) -> Result<HomeRow> { let q = Query::from_str(query).context("parse query")?; let mut res = ObjectBuffer::empty(); ri.state.database.transaction(&mut |txn| { @@ -96,7 +69,7 @@ fn home_row(ri: &RequestInfo<'_>, title: &str, query: &str) -> Result<ObjectBuff Ok(res) } -fn home_row_highlight(ri: &RequestInfo<'_>, title: &str, query: &str) -> Result<ObjectBuffer> { +fn home_row_highlight(ri: &RequestInfo<'_>, title: &str, query: &str) -> Result<HomeRow> { let q = Query::from_str(query).context("parse query")?; let mut res = ObjectBuffer::empty(); ri.state.database.transaction(&mut |txn| { diff --git a/server/src/ui/items.rs b/server/src/ui/items.rs index 286fc01..b800914 100644 --- a/server/src/ui/items.rs +++ b/server/src/ui/items.rs @@ -4,49 +4,56 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{request_info::RequestInfo, ui::error::MyResult, ui_responder::UiResponse}; +use crate::{request_info::RequestInfo, ui::error::MyResult}; use anyhow::anyhow; use base64::{Engine, prelude::BASE64_URL_SAFE}; use jellycommon::{ - jellyobject::{OBB, Path}, + jellyobject::{Object, Path}, *, }; use jellydb::{Filter, Query}; -use rocket::get; +use jellyui::components::items::Items; +use rocket::{get, response::content::RawHtml}; #[get("/items?<cont>")] -pub fn r_items(ri: RequestInfo, cont: Option<&str>) -> MyResult<UiResponse> { - let cont = cont +pub fn r_items(ri: RequestInfo, cont: Option<&str>) -> MyResult<RawHtml<String>> { + let cont_in = cont .map(|s| BASE64_URL_SAFE.decode(s)) .transpose() .map_err(|_| anyhow!("invalid contination token"))?; - let mut page = OBB::new(); + let mut items = Vec::new(); + let mut cont_out = None; ri.state.database.transaction(&mut |txn| { let rows = txn .query(Query { filter: Filter::Match(Path(vec![NO_KIND.0]), KIND_CHANNEL.into()), - continuation: cont.clone(), + continuation: cont_in.clone(), ..Default::default() })? .take(64) .collect::<Result<Vec<_>, _>>()?; - let mut list = OBB::new().with(NODELIST_DISPLAYSTYLE, NLSTYLE_GRID); - - let mut iterstate = Vec::new(); + items.clear(); + cont_out = None; for (r, is) in rows { let node = txn.get(r)?.unwrap(); - let nku = OBB::new().with(NKU_NODE, node.as_object()).finish(); - list.push(NODELIST_ITEM, nku.as_object()); - iterstate = is; + items.push(node); + cont_out = Some(is) } - list.push(NODELIST_CONTINUATION, &BASE64_URL_SAFE.encode(iterstate)); - - page = OBB::new(); - page.push(VIEW_NODE_LIST, list.finish().as_object()); - Ok(()) })?; - Ok(ri.respond_ui(page)) + + Ok(ri.respond_ui(&Items { + ri: &ri.render_info(), + items: &items + .iter() + .map(|node| Nku { + node: node.as_object(), + userdata: Object::EMPTY, + role: None, + }) + .collect::<Vec<_>>(), + cont: cont_out.map(|x| BASE64_URL_SAFE.encode(x)), + })) } diff --git a/server/src/ui/player.rs b/server/src/ui/player.rs index f0d6dea..1050abb 100644 --- a/server/src/ui/player.rs +++ b/server/src/ui/player.rs @@ -4,13 +4,14 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ use super::error::MyResult; -use crate::{request_info::RequestInfo, ui_responder::UiResponse}; +use crate::request_info::RequestInfo; use jellycommon::{ - jellyobject::{OBB, Object, Path}, + jellyobject::{Object, ObjectBuffer, Path}, *, }; use jellydb::{Filter, Query}; -use rocket::get; +use jellyui::components::node_page::Player; +use rocket::{get, response::content::RawHtml}; // fn jellynative_url(action: &str, seek: f64, secret: &str, node: &str, session: &str) -> String { // let protocol = if CONF.tls { "https" } else { "http" }; @@ -26,88 +27,27 @@ use rocket::get; // } #[get("/n/<slug>/player?<t>", rank = 4)] -pub fn r_player(ri: RequestInfo<'_>, t: Option<f64>, slug: &str) -> MyResult<UiResponse> { +pub fn r_player(ri: RequestInfo<'_>, t: Option<f64>, slug: &str) -> MyResult<RawHtml<String>> { ri.require_user()?; let _ = t; - let mut page = OBB::new(); + let mut node = ObjectBuffer::empty(); ri.state.database.transaction(&mut |txn| { if let Some(row) = txn.query_single(Query { filter: Filter::Match(Path(vec![NO_SLUG.0]), slug.into()), ..Default::default() })? { - let n = txn.get(row)?.unwrap(); - let nku = Object::EMPTY.insert(NKU_NODE, n.as_object()); - - page = OBB::new(); - let title = nku - .as_object() - .get(NKU_NODE) - .unwrap_or_default() - .get(NO_TITLE) - .unwrap_or_default(); - page.push(VIEW_TITLE, title); - page.push(VIEW_PLAYER, nku.as_object()); + node = txn.get(row)?.unwrap(); } Ok(()) })?; - Ok(ri.respond_ui(page)) + Ok(ri.respond_ui(&Player { + ri: &ri.render_info(), + nku: Nku { + node: node.as_object(), + userdata: Object::EMPTY, + role: None, + }, + })) } - -// pub fn player_conf<'a>(item: Arc<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 -// .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.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" }]; -// } -// }) -// } |