mirror of
https://codeberg.org/simonrepp/hyper8.git
synced 2026-08-14 13:45:27 +02:00
Extract aspect ratio abstraction into own module
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
// SPDX-FileCopyrightText: 2025 Simon Repp
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
const SPECIAL_ASPECT_RATIOS: [(f32, &str); 10] = [
|
||||
(2.37, "64:27"),
|
||||
(1.78, "16:9"),
|
||||
(1.66, "5:3"),
|
||||
(1.5, "3:2"),
|
||||
(1.33, "4:3"),
|
||||
(0.75, "3:4"),
|
||||
(0.67, "2:3"),
|
||||
(0.6, "3:5"),
|
||||
(0.56, "9:16"),
|
||||
(0.42, "27:64")
|
||||
];
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AspectRatio {
|
||||
/// Representation stores the user-supplied input (e.g. 16:9) while value stores
|
||||
/// the numeric result of dividing width parts by height parts (e.g. 1.78).
|
||||
Custom {
|
||||
representation: String,
|
||||
value: f32
|
||||
},
|
||||
// 16:9
|
||||
Default
|
||||
}
|
||||
|
||||
impl AspectRatio {
|
||||
/// For some common, handpicked aspect ratios we try to automatically display
|
||||
/// them to the user (e.g. 16:9 instead of 1.7777778 or such).
|
||||
pub fn approximate_representation(numeric_ratio: f32) -> String {
|
||||
// Could be adjusted up/down if we feel our approximations are too
|
||||
// strictly/loosely determined.
|
||||
const APPROXIMATION_TOLERANCE: f32 = 0.01;
|
||||
|
||||
for ratio in SPECIAL_ASPECT_RATIOS {
|
||||
if (ratio.0 - numeric_ratio).abs() <= APPROXIMATION_TOLERANCE {
|
||||
return ratio.1.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
format!("{numeric_ratio:.2}")
|
||||
}
|
||||
|
||||
/// Parse a representation such as '1.78', '16:9' or '16/9' into an
|
||||
/// [AspectRatio] struct or return an error if the format is
|
||||
/// unsupported.
|
||||
pub fn from_representation(representation: &str) -> Result<AspectRatio, String> {
|
||||
if let Ok(ratio) = representation.parse::<f32>() {
|
||||
let aspect_ratio = AspectRatio::Custom {
|
||||
representation: representation.to_owned(),
|
||||
value: ratio
|
||||
};
|
||||
|
||||
return Ok(aspect_ratio);
|
||||
}
|
||||
|
||||
let mut parts = representation.split(|c| c == '/' || c == ':');
|
||||
if let (Some(dividend), Some(divisor), None) = (parts.next(), parts.next(), parts.next()) {
|
||||
if let (Ok(dividend), Ok(divisor)) = (dividend.parse::<f32>(), divisor.parse::<f32>()) {
|
||||
let aspect_ratio = AspectRatio::Custom {
|
||||
representation: representation.to_owned(),
|
||||
value: dividend / divisor
|
||||
};
|
||||
|
||||
return Ok(aspect_ratio);
|
||||
}
|
||||
}
|
||||
|
||||
let error = format!("'{representation}' is not understood, allowed aspect formats are e.g. '1.78', '16:9' or '16/9')");
|
||||
Err(error)
|
||||
}
|
||||
|
||||
/// Return the raw aspect ratio number ("width parts divided by height parts").
|
||||
pub fn numeric(&self) -> f32 {
|
||||
match self {
|
||||
AspectRatio::Default => 16.0/9.0,
|
||||
AspectRatio::Custom { value, .. } => *value
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the aspect ratio as it was supplied by the user (i.e. in a
|
||||
/// usually more meaningful/understandable representation such as 16:9
|
||||
/// instead of 1.78).
|
||||
pub fn representation(&self) -> &str {
|
||||
match self {
|
||||
AspectRatio::Default => "16:9",
|
||||
AspectRatio::Custom { representation, .. } => representation
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1050,8 +1050,8 @@ impl Collection {
|
||||
subcollections
|
||||
}
|
||||
|
||||
/// Returns all public videos in the collection, i.e. those that are not
|
||||
/// marked as offline or unlisted.
|
||||
/// Returns all public videos (= not marked as offline or unlisted)
|
||||
/// contained directly in the collection.
|
||||
pub fn public_videos(&self) -> impl Iterator<Item = &Arc<Video>> {
|
||||
self.videos.iter().filter(|video| video.public_local())
|
||||
}
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@ use clap::Parser;
|
||||
use sys_locale::get_locale;
|
||||
|
||||
mod args;
|
||||
mod aspect_ratio;
|
||||
mod banner;
|
||||
mod browser_support;
|
||||
mod build;
|
||||
@@ -46,6 +47,7 @@ mod watcher;
|
||||
mod workers;
|
||||
|
||||
use args::Args;
|
||||
use aspect_ratio::AspectRatio;
|
||||
use banner::Banner;
|
||||
use cache::{Cache, PosterAssetsCached, PosterSourceCached, VideoMetaCached};
|
||||
use collection::Collection;
|
||||
@@ -67,7 +69,7 @@ use poster_assets::{PosterAsset, PosterAssets};
|
||||
use poster_file::PosterFile;
|
||||
use poster_meta::PosterMeta;
|
||||
use processing::{Job, JobKind, Processing, Progress};
|
||||
use site::{AspectRatio, Site, SiteContent};
|
||||
use site::{Site, SiteContent};
|
||||
use site_path::SitePath;
|
||||
use site_url::SiteUrl;
|
||||
use subtitles::SubtitleFile;
|
||||
|
||||
+1
-80
@@ -8,6 +8,7 @@ use std::sync::Arc;
|
||||
use indoc::indoc;
|
||||
|
||||
use crate::{
|
||||
AspectRatio,
|
||||
Context,
|
||||
Collection,
|
||||
Font,
|
||||
@@ -37,15 +38,6 @@ const SITE_OPTIONS: &[&str] = &[
|
||||
"theme"
|
||||
];
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AspectRatio {
|
||||
Custom {
|
||||
representation: String,
|
||||
value: f32
|
||||
},
|
||||
Default
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Site {
|
||||
pub base_url: Option<SiteUrl>,
|
||||
@@ -77,77 +69,6 @@ pub enum SiteContent {
|
||||
Video(Arc<Video>)
|
||||
}
|
||||
|
||||
impl AspectRatio {
|
||||
/// For some common, handpicked aspect ratios we try to automatically display
|
||||
/// them to the user (e.g. 16:9 instead of 1.7777778 or such).
|
||||
pub fn approximate_representation(numeric_ratio: f32) -> String {
|
||||
const RATIOS: [(f32, &str); 10] = [
|
||||
(2.37, "64:27"),
|
||||
(1.78, "16:9"),
|
||||
(1.66, "5:3"),
|
||||
(1.5, "3:2"),
|
||||
(1.33, "4:3"),
|
||||
(0.75, "3:4"),
|
||||
(0.67, "2:3"),
|
||||
(0.6, "3:5"),
|
||||
(0.56, "9:16"),
|
||||
(0.42, "27:64")
|
||||
];
|
||||
|
||||
// Could be adjusted up/down if we feel our approximations are too
|
||||
// strictly/loosely determined.
|
||||
const TOLERANCE: f32 = 0.01;
|
||||
|
||||
for ratio in RATIOS {
|
||||
if (ratio.0 - numeric_ratio).abs() <= TOLERANCE {
|
||||
return ratio.1.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
format!("{numeric_ratio:.2}")
|
||||
}
|
||||
|
||||
pub fn from_representation(representation: &str) -> Result<AspectRatio, String> {
|
||||
if let Ok(ratio) = representation.parse::<f32>() {
|
||||
let aspect_ratio = AspectRatio::Custom {
|
||||
representation: representation.to_owned(),
|
||||
value: ratio
|
||||
};
|
||||
|
||||
return Ok(aspect_ratio);
|
||||
}
|
||||
|
||||
let mut parts = representation.split(|c| c == '/' || c == ':');
|
||||
if let (Some(dividend), Some(divisor), None) = (parts.next(), parts.next(), parts.next()) {
|
||||
if let (Ok(dividend), Ok(divisor)) = (dividend.parse::<f32>(), divisor.parse::<f32>()) {
|
||||
let aspect_ratio = AspectRatio::Custom {
|
||||
representation: representation.to_owned(),
|
||||
value: dividend / divisor
|
||||
};
|
||||
|
||||
return Ok(aspect_ratio);
|
||||
}
|
||||
}
|
||||
|
||||
let error = format!("'{representation}' is not understood, allowed aspect formats are e.g. '1.78', '16:9' or '16/9')");
|
||||
Err(error)
|
||||
}
|
||||
|
||||
pub fn numeric(&self) -> f32 {
|
||||
match self {
|
||||
AspectRatio::Default => 16.0/9.0,
|
||||
AspectRatio::Custom { value, .. } => *value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn representation(&self) -> &str {
|
||||
match self {
|
||||
AspectRatio::Default => "16:9",
|
||||
AspectRatio::Custom { representation, .. } => representation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Site {
|
||||
pub fn absolute_url_dir_unchecked(&self, dir: &str, site_path: &SitePath) -> String {
|
||||
self.base_url
|
||||
|
||||
Reference in New Issue
Block a user