Skip to content

Commit

Permalink
health check and upload speed features
Browse files Browse the repository at this point in the history
  • Loading branch information
webbdays committed Aug 16, 2024
1 parent 6371907 commit 568d907
Show file tree
Hide file tree
Showing 16 changed files with 369 additions and 26 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ specta = "=2.0.0-rc.19"
tauri-specta = { version = "=2.0.0-rc.14", features = ["derive", "typescript"] }
specta-typescript = "0.0.6"
dirs = "5.0.1"
atomic_float = "1.0.0"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = [
Expand Down
50 changes: 49 additions & 1 deletion apps/desktop/src-tauri/src/app/commands.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use std::sync::atomic::Ordering;

use tauri::{Emitter, Manager, Window};
use tauri_plugin_oauth::start;

use crate::{HEALTH_CHECK, UPLOAD_SPEED};

#[tauri::command]
#[specta::specta]
pub async fn start_server(window: Window) -> Result<u16, String> {
Expand Down Expand Up @@ -110,5 +114,49 @@ pub fn make_webview_transparent(app_handle: tauri::AppHandle, label: String) ->
}
}
#[cfg(not(target_os = "macos"))]
"This command is only available on macOS."
{
Err("This command is only available on macOS.".to_string())
}
}

#[tauri::command]
#[specta::specta]
pub fn get_health_check_status() -> bool {
let health = HEALTH_CHECK.load(Ordering::Relaxed);
return health;
}

#[tauri::command]
#[specta::specta]
pub fn get_upload_speed() -> f64 {
let upload_speed = UPLOAD_SPEED.load(Ordering::Relaxed);
return upload_speed;
}

#[cfg(test)]
mod tests {
use super::{get_health_check_status, get_upload_speed, HEALTH_CHECK, UPLOAD_SPEED};
use std::sync::atomic::Ordering;

#[test]
fn test_get_health_check_status() {
// example 1
HEALTH_CHECK.store(true, Ordering::Relaxed);
assert_eq!(get_health_check_status(), true);

// example 2
HEALTH_CHECK.store(false, Ordering::Relaxed);
assert_eq!(get_health_check_status(), false);
}

#[test]
fn test_get_upload_speed() {
// example 1
UPLOAD_SPEED.store(10.5, Ordering::Relaxed);
assert_eq!(get_upload_speed(), 10.5);

// example 2
UPLOAD_SPEED.store(20.7, Ordering::Relaxed);
assert_eq!(get_upload_speed(), 20.7);
}
}
53 changes: 51 additions & 2 deletions apps/desktop/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use reqwest::multipart::{Form, Part};
use sentry_tracing::EventFilter;
use specta_typescript::Typescript;
use std::path::PathBuf;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Instant;
use std::vec;
use std::{path::PathBuf, sync::atomic::AtomicBool};
use atomic_float::AtomicF64;

use tauri::{
tray::{MouseButton, MouseButtonState},
Emitter, Manager,
Expand Down Expand Up @@ -34,6 +39,9 @@ use ffmpeg_sidecar::{

use winit::monitor::{MonitorHandle, VideoMode};

static UPLOAD_SPEED: AtomicF64 = AtomicF64::new(0.0);
static HEALTH_CHECK: AtomicBool = AtomicBool::new(false);

fn main() {
let _ = fix_path_env::fix();

Expand Down Expand Up @@ -101,6 +109,37 @@ fn main() {
Ok(())
}

async fn perform_health_check_and_calculate_upload_speed() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let sample_screen_recording = vec![0u8; 1_000_000];

let health_check_url_base: &'static str = dotenvy_macro::dotenv!("NEXT_PUBLIC_URL");
let health_check_url = format!("{}/api/health-check", health_check_url_base);

let form = Form::new().part(
"file",
Part::bytes(sample_screen_recording.clone())
.file_name("sample_screen_recording.webm")
.mime_str("video/webm")?,
);
let start_time = Instant::now();
let resp = client.post(health_check_url).multipart(form).send().await?;
let time_elapsed = start_time.elapsed();

let is_success = resp.status().is_success();
HEALTH_CHECK.store(is_success, Ordering::Relaxed);

if is_success {
let upload_speed = (sample_screen_recording.len() as f64 / time_elapsed.as_secs_f64()) / 1250000.0;
UPLOAD_SPEED.store(upload_speed, Ordering::Relaxed);
tracing::debug!("Health check successful. Upload speed: {} Mbps", upload_speed);
} else {
tracing::debug!("Health check failed.");
}

Ok(())
}

if let Err(error) = handle_ffmpeg_installation() {
tracing::error!(error);
// TODO: UI message instead
Expand Down Expand Up @@ -141,7 +180,9 @@ fn main() {
reset_microphone_permissions,
reset_camera_permissions,
close_webview,
make_webview_transparent
make_webview_transparent,
get_health_check_status,
get_upload_speed
]);

#[cfg(debug_assertions)] // <- Only export on non-release builds
Expand All @@ -156,6 +197,14 @@ fn main() {
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.invoke_handler(specta_builder.invoke_handler())
.setup(move |app| {
tracing::info!("Setting up application...");

tauri::async_runtime::spawn(async {
if let Err(error) = perform_health_check_and_calculate_upload_speed().await {
tracing::error!("Health check and upload speed calculation failed: {}", error);
}
});

let handle = app.handle();

if let Some(main_window) = app.get_webview_window("main") {
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop/src-tauri/src/media/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use tracing::Level;
use crate::{
app::config,
recording::RecordingOptions,
utils::{create_named_pipe, ffmpeg_path_as_str},
utils::{create_named_pipe, ffmpeg_path_as_str}, UPLOAD_SPEED,
};

mod audio;
Expand Down Expand Up @@ -108,6 +108,7 @@ impl MediaRecorder {
max_screen_width,
max_screen_height,
self.should_stop.clone(),
VideoCapturer::get_dynamic_resolution(UPLOAD_SPEED.load(Ordering::Relaxed)),
);
let adjusted_width = video_capturer.frame_width;
let adjusted_height = video_capturer.frame_height;
Expand Down
23 changes: 20 additions & 3 deletions apps/desktop/src-tauri/src/media/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,20 @@ pub struct VideoCapturer {
impl VideoCapturer {
pub const FPS: u32 = 30;

pub fn new(_width: usize, _height: usize, should_stop: SharedFlag) -> VideoCapturer {
pub fn new(
_width: usize,
_height: usize,
should_stop: SharedFlag,
resolution: Resolution,
) -> VideoCapturer {
let mut capturer = Capturer::new(Options {
fps: Self::FPS,
target: None,
show_cursor: true,
show_highlight: true,
excluded_targets: None,
output_type: FrameType::BGRAFrame,
output_resolution: Resolution::Captured,
output_resolution: resolution,
crop_area: None,
});

Expand Down Expand Up @@ -229,4 +234,16 @@ impl VideoCapturer {
let _ = pipe.sync_all().await;
}
}
}

pub fn get_dynamic_resolution(upload_speed: f64) -> Resolution {
match upload_speed {
speed if speed >= 60.0 => Resolution::Captured,
speed if speed >= 50.0 => Resolution::_4320p,
speed if speed >= 25.0 => Resolution::_2160p,
speed if speed >= 15.0 => Resolution::_1440p,
speed if speed >= 8.0 => Resolution::_1080p,
speed if speed >= 5.0 => Resolution::_720p,
_ => Resolution::_480p,
}
}
}
3 changes: 3 additions & 0 deletions apps/desktop/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { authFetch } from "@/utils/auth/helpers";
import { commands } from "@/utils/commands";
import { setTrayMenu } from "@/utils/tray";
import { useMediaDevices } from "@/utils/recording/MediaDeviceContext";
import { UploadSpeed } from "@/components/upload-speed";

export const dynamic = "force-static";

Expand Down Expand Up @@ -127,6 +128,7 @@ export default function CameraPage() {
<div id="app" data-tauri-drag-region style={{ borderRadius: "16px" }}>
<WindowActions />
<Recorder />
<UploadSpeed />
</div>
);
}
Expand All @@ -149,6 +151,7 @@ export default function CameraPage() {
) : (
<Recorder />
)}
<UploadSpeed />
</>
) : (
<SignIn />
Expand Down
38 changes: 21 additions & 17 deletions apps/desktop/src/components/WindowActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { Home } from "@/components/icons/Home";
import { openLinkInBrowser } from "@/utils/helpers";
import { HealthCheckStatus } from "./health";

export const WindowActions = () => {
const actionButtonBase = "w-3 h-3 bg-gray-500 rounded-full m-0 p-0 block";
Expand Down Expand Up @@ -41,23 +42,26 @@ export const WindowActions = () => {
<span className={actionButtonBase}></span>
</div>
</div>
<div className="flex">
<button
onClick={async () => {
if (window.fathom !== undefined) {
window.fathom.trackEvent("home_clicked");
}
await openLinkInBrowser(
`${process.env.NEXT_PUBLIC_URL}/dashboard`
);
}}
className="p-1.5 bg-transparent hover:bg-gray-200 rounded-full transition-all"
>
<Home className="w-5 h-5" />
</button>
{/* <button className="p-1.5 bg-transparent hover:bg-gray-200 rounded-full transition-all">
<Settings className="w-5 h-5" />
</button> */}
<div className="flex space-x-2">
<HealthCheckStatus/>
<div className="flex">
<button
onClick={async () => {
if (window.fathom !== undefined) {
window.fathom.trackEvent("home_clicked");
}
await openLinkInBrowser(
`${process.env.NEXT_PUBLIC_URL}/dashboard`
);
}}
className="p-1.5 bg-transparent hover:bg-gray-200 rounded-full transition-all"
>
<Home className="w-5 h-5" />
</button>
{/* <button className="p-1.5 bg-transparent hover:bg-gray-200 rounded-full transition-all">
<Settings className="w-5 h-5" />
</button> */}
</div>
</div>
</div>
</div>
Expand Down
48 changes: 48 additions & 0 deletions apps/desktop/src/components/health.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useState } from 'react';
import { useHealthCheck } from '../utils/hooks/useHealthCheck';
import { Dialog, DialogContent, DialogDescription, DialogTitle, DialogFooter } from '@cap/ui';

export const HealthCheckStatus: React.FC = () => {
const { isHealthy, message } = useHealthCheck();
const [showMessage, setShowMessage] = useState(false);

const handleClick = () => {
setShowMessage(true);
};

return (
<div className="flex items-center space-x-2">
<button
onClick={handleClick}
className={`w-4 h-4 rounded-full ${isHealthy ? 'bg-green-500' : 'bg-red-500'}`}
aria-label="Health Check Status"
title={isHealthy ? "System is healthy" : "System health issue detected"}
/>
<Dialog
open={showMessage}
onOpenChange={setShowMessage}
>
<DialogContent>
<DialogTitle>Health Check Status</DialogTitle>
<DialogDescription>
{message}
{!isHealthy && (
<p className="text-sm mt-2">
If you are still having an issue, please contact our
<a href="/support" className="text-blue-500 hover:underline ml-1">support</a>.
</p>
)}
</DialogDescription>
<DialogFooter>
<button
onClick={() => setShowMessage(false)}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
>
Close
</button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
};
Loading

0 comments on commit 568d907

Please sign in to comment.