Skip to content

Commit

Permalink
Merge pull request #25 from bruceunx/fix/workflow
Browse files Browse the repository at this point in the history
Fix/workflow
  • Loading branch information
bruceunx authored Dec 30, 2024
2 parents 5fd104f + 339dcbb commit 3b97763
Show file tree
Hide file tree
Showing 16 changed files with 415 additions and 67 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ Welcome to Video Maestro! 🚀 (formerly known as Video Manager), your ultimate
## 📸 Screen Shot

<p align="center">
<img src="docs/screenshot3.png" alt="Video Maestro Screenshot" width="600">
<img src="docs/shot2.png" alt="Video Maestro Screenshot" width="600">
</p>

<p align="center">
<img src="docs/screenshot2.png" alt="Video Maestro Screenshot" width="600">
<img src="docs/shot1.png" alt="Video Maestro Screenshot" width="600">
</p>

## ✨ Features
Expand Down
Binary file removed docs/screenshot1.png
Binary file not shown.
Binary file removed docs/screenshot2.png
Binary file not shown.
Binary file removed docs/screenshot3.png
Binary file not shown.
Binary file removed docs/screenshot4.png
Binary file not shown.
Binary file added docs/shot1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/shot2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions src-tauri/src/gemini.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use serde::Deserialize;

#[derive(Deserialize)]
struct GeminiResponse {
candidates: Vec<GeminiCandidate>,
}

#[derive(Deserialize)]
#[allow(non_snake_case)]
struct GeminiCandidate {
content: GeminiContent,
// finishReason: Option<String>,
}

#[derive(Deserialize)]
struct GeminiContent {
parts: Vec<GeminiPart>,
}

#[derive(Deserialize)]
struct GeminiPart {
text: String,
}

pub fn parse_gemini(chunk: &str) -> Result<String, String> {
let gemini_response: GeminiResponse =
serde_json::from_str(chunk).map_err(|e| format!("JSON parse error: {}", e))?;

if let Some(candidate) = gemini_response.candidates.first() {
if let Some(part) = candidate.content.parts.first() {
Ok(part.text.clone())
} else {
Err("No parts found in Gemini response".to_string())
}
} else {
Err("No candidates found in Gemini response".to_string())
}
}
1 change: 1 addition & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ pub mod webvtt;
use dotenv::dotenv;
use tauri::{Emitter, Manager};
mod db;
mod gemini;
mod setting;
mod utils;
mod whisper;
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/setting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub struct AppSettings {
pub api_key: Option<String>,
pub ai_url: Option<String>,
pub ai_model_name: Option<String>,
pub whisper_api_key: Option<String>,
pub whisper_url: Option<String>,
pub whisper_model_name: Option<String>,
pub proxy: Option<String>,
Expand Down
61 changes: 32 additions & 29 deletions src-tauri/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ pub fn transform_subtitles_to_segments(subtitles: Vec<SubtitleEntry>) -> Vec<Seg
}

pub fn transform_segments_to_chunks(description: &str, segments: Vec<Segment>) -> Vec<String> {
let mut chunks = Vec::new();
let segments: Vec<Segment> = segments
.into_iter()
.filter(|segment| !segment.text.trim().is_empty())
.collect();

let mut current_string = String::new();
let mut chunks = Vec::new();

let mut timelines = parse_timeline(description);
timelines.sort_by_key(|e| e.timestamp);
Expand Down Expand Up @@ -77,41 +80,41 @@ pub fn transform_segments_to_chunks(description: &str, segments: Vec<Segment>) -
}

for timeline in timelines {
if current_string.len() + timeline.content.len() > 3000 {
chunks.push(current_string.clone());
current_string.clear();
};

current_string.push_str(&timeline.content);
chunks.push(timeline.content);
}
if !current_string.is_empty() {
chunks.push(current_string);
}

return chunks;
}

let mut end_time = 0.0;
for segment in segments {
if current_string.len() + segment.text.len() > 3000 {
chunks.push(current_string.clone());
current_string.clear();
};

if current_string.len() + segment.text.len() > 2000 && segment.start - end_time > 7.0 {
chunks.push(current_string.clone());
current_string.clear();
}

current_string.push_str(&segment.text);
end_time = segment.end;
chunks.push(format!(
"{} - {}",
convert_microseconds_to_time(segment.start as u64),
segment.text
));
}
chunks
}

if !current_string.is_empty() {
chunks.push(current_string);
}
fn convert_microseconds_to_time(microseconds: u64) -> String {
let total_seconds = microseconds / 1_000;
let minutes = total_seconds / 60;
let seconds = total_seconds % 60;
format!("{:02}:{:02}", minutes, seconds)
}

chunks
pub fn transform_segment_to_string(segments: Vec<Segment>) -> String {
let mut content = String::new();
for segment in segments {
content.push_str(
format!(
"{} - {}\n",
convert_microseconds_to_time(segment.start as u64),
segment.text
)
.as_ref(),
);
}
content
}

#[cfg(test)]
Expand Down
332 changes: 302 additions & 30 deletions src-tauri/src/whisper.rs

Large diffs are not rendered by default.

4 changes: 1 addition & 3 deletions src/components/LanguageSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from "react";
import * as Select from "@radix-ui/react-select";
import { ChevronDownIcon, CheckIcon, Languages } from "lucide-react";
import { CheckIcon, ChevronDownIcon, Languages } from "lucide-react";

interface Language {
code: string;
Expand All @@ -15,8 +15,6 @@ const LANGUAGES: Language[] = [
{ code: "es", name: "Español" },
{ code: "fr", name: "Français" },
{ code: "de", name: "Deutsch" },
{ code: "ar", name: "العربية" },
{ code: "ru", name: "Русский" },
];

interface LanguageSelectorProps {
Expand Down
37 changes: 35 additions & 2 deletions src/components/SettingsModal.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as React from "react";
import * as Dialog from "@radix-ui/react-dialog";
import { Settings, X, Eye, EyeOff } from "lucide-react";
import { Eye, EyeOff, Settings, X } from "lucide-react";
import { useSettings } from "store/SettingsProvider";
import { SettingsType } from "types/settings";
import type { SettingsType } from "types/settings";

const SettingsModal: React.FC = () => {
const [isOpen, setIsOpen] = React.useState(false);
const [showApiKey, setShowApiKey] = React.useState(false);
const [showWhsperApiKey, setWhisperShowApiKey] = React.useState(false);

const { settings: saveSettings, updateSettings } = useSettings();
const [settings, setSettings] = React.useState<SettingsType>(saveSettings);
Expand Down Expand Up @@ -135,6 +136,38 @@ const SettingsModal: React.FC = () => {
/>
</div>

<div className="relative">
<label
htmlFor="whisperApiKey"
className="block text-sm font-medium text-gray-700 mb-1"
>
Whisper API Key
</label>
<div className="flex items-center">
<input
type={showWhsperApiKey ? "text" : "password"}
id="whisperApiKey"
name="whisperApiKey"
value={settings.whisperApiKey || ""}
onChange={handleInputChange}
className="flex-grow px-3 py-2 border border-gray-300 rounded-md shadow-sm
focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Enter your Whisper API key"
/>
<button
type="button"
onClick={() => setWhisperShowApiKey(!showWhsperApiKey)}
className="ml-2 text-gray-500 hover:text-gray-700"
>
{showWhsperApiKey ? (
<EyeOff className="w-5 h-5" />
) : (
<Eye className="w-5 h-5" />
)}
</button>
</div>
</div>

<div>
<label
htmlFor="whisperUrl"
Expand Down
3 changes: 2 additions & 1 deletion src/store/SettingsProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as React from "react";
import { SettingsType } from "types/settings";
import type { SettingsType } from "types/settings";

import { invoke } from "@tauri-apps/api/core";

const defaultSettings: SettingsType = {
apiKey: null,
aiUrl: null,
aiModelName: null,
whisperApiKey: null,
whisperUrl: null,
whisperModelName: null,
proxy: null,
Expand Down
1 change: 1 addition & 0 deletions src/types/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface SettingsType {
apiKey: string | null;
aiUrl: string | null;
aiModelName: string | null;
whisperApiKey: string | null;
whisperUrl: string | null;
whisperModelName: string | null;
proxy: string | null;
Expand Down

0 comments on commit 3b97763

Please sign in to comment.