Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e42a922507 | ||
|
|
8fc2d11c5f | ||
|
|
11832e911b | ||
|
|
18e6b974c0 | ||
|
|
08e464daaf | ||
|
|
5d22adcaa4 |
@@ -13,23 +13,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
NODE_VERSION: "20"
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
steps:
|
||||
- name: Determine tag
|
||||
id: tag
|
||||
run: |
|
||||
TAG="${{ inputs.tag }}"
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=$(git ls-remote --tags --sort=-v:refname origin 'refs/tags/v*' | head -1 | sed 's|.*refs/tags/||')
|
||||
fi
|
||||
echo "Building for tag: ${TAG}"
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
- name: Show tag
|
||||
run: echo "Building for tag: ${RELEASE_TAG}"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ steps.tag.outputs.tag }}
|
||||
ref: ${{ inputs.tag }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -58,7 +49,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get install -y jq
|
||||
REPO_API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
||||
TAG="${{ steps.tag.outputs.tag }}"
|
||||
TAG="${RELEASE_TAG}"
|
||||
echo "Release tag: ${TAG}"
|
||||
|
||||
echo "Waiting for release ${TAG} to be available..."
|
||||
|
||||
@@ -13,23 +13,14 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
NODE_VERSION: "20"
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
steps:
|
||||
- name: Determine tag
|
||||
id: tag
|
||||
run: |
|
||||
TAG="${{ inputs.tag }}"
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=$(git ls-remote --tags --sort=-v:refname origin 'refs/tags/v*' | head -1 | sed 's|.*refs/tags/||')
|
||||
fi
|
||||
echo "Building for tag: ${TAG}"
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
- name: Show tag
|
||||
run: echo "Building for tag: ${RELEASE_TAG}"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ steps.tag.outputs.tag }}
|
||||
ref: ${{ inputs.tag }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
@@ -56,7 +47,7 @@ jobs:
|
||||
run: |
|
||||
which jq || brew install jq
|
||||
REPO_API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
||||
TAG="${{ steps.tag.outputs.tag }}"
|
||||
TAG="${RELEASE_TAG}"
|
||||
echo "Release tag: ${TAG}"
|
||||
|
||||
echo "Waiting for release ${TAG} to be available..."
|
||||
|
||||
@@ -13,23 +13,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PYTHON_VERSION: "3.11"
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
steps:
|
||||
- name: Determine tag
|
||||
id: tag
|
||||
run: |
|
||||
TAG="${{ inputs.tag }}"
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=$(git ls-remote --tags --sort=-v:refname origin 'refs/tags/sidecar-v*' | head -1 | sed 's|.*refs/tags/||')
|
||||
fi
|
||||
echo "Building for tag: ${TAG}"
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
- name: Show tag
|
||||
run: echo "Building for tag: ${RELEASE_TAG}"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ steps.tag.outputs.tag }}
|
||||
ref: ${{ inputs.tag }}
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
@@ -75,7 +66,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get install -y jq
|
||||
REPO_API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
||||
TAG="${{ steps.tag.outputs.tag }}"
|
||||
TAG="${RELEASE_TAG}"
|
||||
|
||||
echo "Waiting for sidecar release ${TAG} to be available..."
|
||||
for i in $(seq 1 30); do
|
||||
|
||||
@@ -13,23 +13,14 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
PYTHON_VERSION: "3.11"
|
||||
RELEASE_TAG: ${{ inputs.tag }}
|
||||
steps:
|
||||
- name: Determine tag
|
||||
id: tag
|
||||
run: |
|
||||
TAG="${{ inputs.tag }}"
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=$(git ls-remote --tags --sort=-v:refname origin 'refs/tags/sidecar-v*' | head -1 | sed 's|.*refs/tags/||')
|
||||
fi
|
||||
echo "Building for tag: ${TAG}"
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
- name: Show tag
|
||||
run: echo "Building for tag: ${RELEASE_TAG}"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ steps.tag.outputs.tag }}
|
||||
ref: ${{ inputs.tag }}
|
||||
|
||||
- name: Install uv
|
||||
run: |
|
||||
@@ -66,7 +57,7 @@ jobs:
|
||||
run: |
|
||||
which jq || brew install jq
|
||||
REPO_API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
|
||||
TAG="${{ steps.tag.outputs.tag }}"
|
||||
TAG="${RELEASE_TAG}"
|
||||
|
||||
echo "Waiting for sidecar release ${TAG} to be available..."
|
||||
for i in $(seq 1 30); do
|
||||
|
||||
@@ -159,9 +159,7 @@ jobs:
|
||||
echo " Deleting release ${TAG} (ID: ${ID})..."
|
||||
curl -s -X DELETE -H "Authorization: token ${BUILD_TOKEN}" \
|
||||
"${REPO_API}/releases/${ID}"
|
||||
|
||||
# Also delete the tag
|
||||
curl -s -X DELETE -H "Authorization: token ${BUILD_TOKEN}" \
|
||||
"${REPO_API}/tags/${TAG}"
|
||||
# Keep the git tag -- only delete the release (assets).
|
||||
# Deleting tags breaks builds that haven't checked out yet.
|
||||
done
|
||||
echo "Cleanup complete"
|
||||
|
||||
@@ -166,9 +166,7 @@ jobs:
|
||||
echo " Deleting sidecar release ${TAG} (ID: ${ID})..."
|
||||
curl -s -X DELETE -H "Authorization: token ${BUILD_TOKEN}" \
|
||||
"${REPO_API}/releases/${ID}"
|
||||
|
||||
# Also delete the tag
|
||||
curl -s -X DELETE -H "Authorization: token ${BUILD_TOKEN}" \
|
||||
"${REPO_API}/tags/${TAG}"
|
||||
# Keep the git tag -- only delete the release (assets).
|
||||
# Deleting tags breaks builds that haven't checked out yet.
|
||||
done
|
||||
echo "Cleanup complete"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "local-transcription",
|
||||
"private": true,
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "local-transcription"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
description = "A standalone desktop application for real-time speech-to-text transcription using Whisper models"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "local-transcription"
|
||||
version = "2.0.1"
|
||||
version = "2.0.3"
|
||||
description = "Real-time speech-to-text transcription for streamers"
|
||||
authors = ["Local Transcription Contributors"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -29,9 +29,9 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.manage(sidecar::ManagedSidecar(Mutex::new(
|
||||
.manage(sidecar::ManagedSidecar(std::sync::Arc::new(Mutex::new(
|
||||
sidecar::SidecarManager::new(),
|
||||
)))
|
||||
))))
|
||||
.setup(|app| {
|
||||
let resource_dir = app
|
||||
.path()
|
||||
|
||||
@@ -555,7 +555,7 @@ impl SidecarManager {
|
||||
|
||||
fn build_dev_command(&self) -> Result<std::process::Command, String> {
|
||||
let mut cmd = std::process::Command::new("python");
|
||||
cmd.args(["-m", "backend.main_headless"]);
|
||||
cmd.args(["-u", "-m", "backend.main_headless"]); // -u = unbuffered
|
||||
|
||||
// Try to find the project root (parent of src-tauri)
|
||||
if let Some(dirs) = DIRS.get() {
|
||||
@@ -568,6 +568,7 @@ impl SidecarManager {
|
||||
}
|
||||
}
|
||||
|
||||
cmd.env("PYTHONUNBUFFERED", "1");
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
@@ -583,27 +584,51 @@ impl SidecarManager {
|
||||
bin.parent()
|
||||
.ok_or("Cannot determine sidecar parent dir")?,
|
||||
);
|
||||
// Force unbuffered stdout so the ready event is sent immediately.
|
||||
// PyInstaller frozen executables buffer stdout when piped.
|
||||
cmd.env("PYTHONUNBUFFERED", "1");
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
fn wait_for_ready(stdout: std::process::ChildStdout) -> Result<u16, String> {
|
||||
let reader = std::io::BufReader::new(stdout);
|
||||
let timeout = std::time::Duration::from_secs(120);
|
||||
let start = std::time::Instant::now();
|
||||
use std::sync::mpsc;
|
||||
|
||||
for line in reader.lines() {
|
||||
if start.elapsed() > timeout {
|
||||
return Err("Timed out waiting for sidecar ready event".into());
|
||||
}
|
||||
let line = line.map_err(|e| format!("IO error reading stdout: {e}"))?;
|
||||
if let Ok(evt) = serde_json::from_str::<ReadyEvent>(&line) {
|
||||
if evt.event == "ready" {
|
||||
return Ok(evt.port);
|
||||
let timeout = std::time::Duration::from_secs(120);
|
||||
|
||||
// Read stdout in a background thread so we can enforce a real timeout.
|
||||
// BufReader::lines() blocks indefinitely if no data arrives.
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let reader = std::io::BufReader::new(stdout);
|
||||
for line in reader.lines() {
|
||||
match line {
|
||||
Ok(line) => {
|
||||
eprintln!("[sidecar-stdout] {}", line);
|
||||
if let Ok(evt) = serde_json::from_str::<ReadyEvent>(&line) {
|
||||
if evt.event == "ready" {
|
||||
let _ = tx.send(Ok(evt.port));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = tx.send(Err(format!("IO error reading stdout: {e}")));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ignore other lines (e.g. log output)
|
||||
}
|
||||
Err("Sidecar process exited before sending ready event".into())
|
||||
let _ = tx.send(Err(
|
||||
"Sidecar process exited before sending ready event".into(),
|
||||
));
|
||||
});
|
||||
|
||||
rx.recv_timeout(timeout).unwrap_or_else(|_| {
|
||||
Err(format!(
|
||||
"Timed out after {}s waiting for sidecar ready event",
|
||||
timeout.as_secs()
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +637,8 @@ impl SidecarManager {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Wrapper so we can store `SidecarManager` in Tauri's managed state.
|
||||
pub struct ManagedSidecar(pub Mutex<SidecarManager>);
|
||||
/// Uses Arc so it can be cloned into background threads for async commands.
|
||||
pub struct ManagedSidecar(pub std::sync::Arc<Mutex<SidecarManager>>);
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_sidecar_port(state: tauri::State<'_, ManagedSidecar>) -> Result<Option<u16>, String> {
|
||||
@@ -628,12 +654,16 @@ pub fn get_sidecar_port(state: tauri::State<'_, ManagedSidecar>) -> Result<Optio
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn start_sidecar(state: tauri::State<'_, ManagedSidecar>) -> Result<u16, String> {
|
||||
let mut mgr = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|e| format!("Lock error: {e}"))?;
|
||||
mgr.ensure_running()
|
||||
pub async fn start_sidecar(state: tauri::State<'_, ManagedSidecar>) -> Result<u16, String> {
|
||||
let mgr = state.0.clone();
|
||||
// Run blocking sidecar launch in a background thread so it doesn't
|
||||
// freeze the Tauri UI while waiting for the ready event (up to 120s).
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut mgr = mgr.lock().map_err(|e| format!("Lock error: {e}"))?;
|
||||
mgr.ensure_running()
|
||||
})
|
||||
.await
|
||||
.map_err(|e| format!("Task join error: {e}"))?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"productName": "Local Transcription",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.3",
|
||||
"identifier": "net.anhonesthost.local-transcription",
|
||||
"build": {
|
||||
"frontendDist": "../dist",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Version information for Local Transcription."""
|
||||
|
||||
__version__ = "2.0.1"
|
||||
__version_info__ = (2, 0, 1)
|
||||
__version__ = "2.0.3"
|
||||
__version_info__ = (2, 0, 3)
|
||||
|
||||
# Version history:
|
||||
# 1.4.0 - Auto-update feature:
|
||||
|
||||
Reference in New Issue
Block a user