From bbdcde3f0f74cb9ab68c7f45fa99a8cb253f73a6 Mon Sep 17 00:00:00 2001 From: Lucas Oskorep Date: Tue, 27 Oct 2026 11:27:30 -0400 Subject: [PATCH] Rename project, crates, and binaries to open-360-link --- Cargo.lock | 108 +++++++++--------- Cargo.toml | 10 +- README.md | 77 ++++++------- assets/insta360-link-controller.desktop | 14 --- assets/open-360-link.desktop | 14 +++ ...-link-controller.svg => open-360-link.svg} | 0 .../Cargo.toml | 8 +- .../src/main.rs | 6 +- .../Cargo.toml | 8 +- .../src/main.rs | 84 +++++++------- .../src/right_panel.rs | 2 +- .../src/state.rs | 31 +++-- .../src/theme.rs | 0 .../src/transport.rs | 2 +- .../src/video_engine.rs | 2 +- .../Cargo.toml | 4 +- .../src/codec.rs | 0 .../src/controller.rs | 0 .../src/ids.rs | 0 .../src/lib.rs | 0 .../src/transport.rs | 0 .../src/types.rs | 0 .../tests/protocol_integration.rs | 2 +- .../Cargo.toml | 8 +- .../src/lib.rs | 2 +- .../src/main.rs | 10 +- .../tests/server_integration.rs | 4 +- mise.toml | 30 ++--- 28 files changed, 219 insertions(+), 207 deletions(-) delete mode 100644 assets/insta360-link-controller.desktop create mode 100644 assets/open-360-link.desktop rename assets/{insta360-link-controller.svg => open-360-link.svg} (100%) rename crates/{insta360-link-cli => open-360-link-cli}/Cargo.toml (53%) rename crates/{insta360-link-cli => open-360-link-cli}/src/main.rs (99%) rename crates/{insta360-link-gui => open-360-link-gui}/Cargo.toml (60%) rename crates/{insta360-link-gui => open-360-link-gui}/src/main.rs (98%) rename crates/{insta360-link-gui => open-360-link-gui}/src/right_panel.rs (99%) rename crates/{insta360-link-gui => open-360-link-gui}/src/state.rs (96%) rename crates/{insta360-link-gui => open-360-link-gui}/src/theme.rs (100%) rename crates/{insta360-link-gui => open-360-link-gui}/src/transport.rs (99%) rename crates/{insta360-link-gui => open-360-link-gui}/src/video_engine.rs (99%) rename crates/{insta360-link-protocol => open-360-link-protocol}/Cargo.toml (64%) rename crates/{insta360-link-protocol => open-360-link-protocol}/src/codec.rs (100%) rename crates/{insta360-link-protocol => open-360-link-protocol}/src/controller.rs (100%) rename crates/{insta360-link-protocol => open-360-link-protocol}/src/ids.rs (100%) rename crates/{insta360-link-protocol => open-360-link-protocol}/src/lib.rs (100%) rename crates/{insta360-link-protocol => open-360-link-protocol}/src/transport.rs (100%) rename crates/{insta360-link-protocol => open-360-link-protocol}/src/types.rs (100%) rename crates/{insta360-link-protocol => open-360-link-protocol}/tests/protocol_integration.rs (99%) rename crates/{insta360-link-server => open-360-link-server}/Cargo.toml (58%) rename crates/{insta360-link-server => open-360-link-server}/src/lib.rs (99%) rename crates/{insta360-link-server => open-360-link-server}/src/main.rs (91%) rename crates/{insta360-link-server => open-360-link-server}/tests/server_integration.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index c1baad2..2dbbb3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1829,60 +1829,6 @@ dependencies = [ "hashbrown 0.17.1", ] -[[package]] -name = "insta360-link-cli" -version = "0.0.0" -dependencies = [ - "clap", - "insta360-link-protocol", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "insta360-link-gui" -version = "0.0.0" -dependencies = [ - "eframe", - "egui", - "image", - "insta360-link-protocol", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "insta360-link-protocol" -version = "0.0.0" -dependencies = [ - "bitflags 2.13.2", - "libc", - "nusb", - "serde", - "serde_json", - "thiserror 2.0.21", - "tracing", -] - -[[package]] -name = "insta360-link-server" -version = "0.0.0" -dependencies = [ - "axum", - "clap", - "insta360-link-protocol", - "serde", - "serde_json", - "tokio", - "tower-http", - "tracing", - "tracing-subscriber", -] - [[package]] name = "io-kit-sys" version = "0.4.1" @@ -2623,6 +2569,60 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "open-360-link-cli" +version = "0.0.0" +dependencies = [ + "clap", + "open-360-link-protocol", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "open-360-link-gui" +version = "0.0.0" +dependencies = [ + "eframe", + "egui", + "image", + "open-360-link-protocol", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "open-360-link-protocol" +version = "0.0.0" +dependencies = [ + "bitflags 2.13.2", + "libc", + "nusb", + "serde", + "serde_json", + "thiserror 2.0.21", + "tracing", +] + +[[package]] +name = "open-360-link-server" +version = "0.0.0" +dependencies = [ + "axum", + "clap", + "open-360-link-protocol", + "serde", + "serde_json", + "tokio", + "tower-http", + "tracing", + "tracing-subscriber", +] + [[package]] name = "orbclient" version = "0.3.55" diff --git a/Cargo.toml b/Cargo.toml index 438c5b9..73f022e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [workspace] resolver = "2" members = [ - "crates/insta360-link-protocol", - "crates/insta360-link-cli", - "crates/insta360-link-gui", - "crates/insta360-link-server", + "crates/open-360-link-protocol", + "crates/open-360-link-cli", + "crates/open-360-link-gui", + "crates/open-360-link-server", ] [workspace.package] @@ -12,7 +12,7 @@ version = "0.0.0" edition = "2021" license = "MIT OR Apache-2.0" authors = ["Lucas Oskorep "] -repository = "https://github.com/lucasoskorep/insta360-link-controller" +repository = "https://github.com/lucasoskorep/open-360-link" [workspace.dependencies] thiserror = "2.0" diff --git a/README.md b/README.md index 83eae48..5f4e1e6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Insta360 Link Open Controller +# Open 360 Link A pure-Rust, cross-platform (**Linux**, **Windows**, **macOS**) hardware controller suite for the **Insta360 Link**, **Insta360 Link 2**, **Insta360 Link 2C**, and **Insta360 Link 2 Pro (PUC3)** 4K AI webcams. @@ -12,10 +12,10 @@ Built by reverse-engineering the proprietary USB & UVC Extension Unit (`XU1` and | Crate / Binary | Description | | :--- | :--- | -| **`insta360-link-protocol`** | Core Rust library implementing USB device discovery, UVC VideoControl descriptor parsing, standard UVC `CT_*`/`PU_*` controls, proprietary `XU1`/`XU2` codecs, `NusbTransport` (pure-Rust USB via `nusb`), and a stateful `MockCameraTransport` simulator. | -| **`insta360-link-ctl`** (`insta360-link-cli`) | Command-line tool for scripting PTZ movement, presets, video modes (`Normal`, `AutoTracking`, `WhiteBoard`, `OverheadShot`, `DeskView`, `AutoFraming`, `SmartWhiteBoard`), exposure/ISO, white balance, audio beamforming modes, filters, and extended hardware switches. | -| **`insta360-link-server`** | Asynchronous HTTP REST & WebSocket daemon (`axum` + `tokio`) for remote control, Stream Deck / OBS integration, and real-time state synchronization. | -| **`insta360-link-gui`** | Native cross-platform desktop GUI (`egui` / `eframe`) featuring a virtual gimbal PTZ joystick, preset slots, AI mode switcher, image adjustment sliders, audio & filter controls, and built-in simulated camera mode. | +| **`open-360-link-protocol`** | Core Rust library implementing USB device discovery, UVC VideoControl descriptor parsing, standard UVC `CT_*`/`PU_*` controls, proprietary `XU1`/`XU2` codecs, `NusbTransport` (pure-Rust USB via `nusb`), and a stateful `MockCameraTransport` simulator. | +| **`open-360-link-ctl`** (`open-360-link-cli`) | Command-line tool for scripting PTZ movement, presets, video modes (`Normal`, `AutoTracking`, `WhiteBoard`, `OverheadShot`, `DeskView`, `AutoFraming`, `SmartWhiteBoard`), exposure/ISO, white balance, audio beamforming modes, filters, and extended hardware switches. | +| **`open-360-link-server`** | Asynchronous HTTP REST & WebSocket daemon (`axum` + `tokio`) for remote control, Stream Deck / OBS integration, and real-time state synchronization. | +| **`open-360-link-gui`** | Native cross-platform desktop GUI (`egui` / `eframe`) featuring a virtual gimbal PTZ joystick, AI mode switcher, image adjustment sliders, audio & filter controls, and live camera preview/recording. | --- @@ -61,9 +61,9 @@ cargo build --release ``` The compiled binaries will be placed in `target/release/`: -- `target/release/insta360-link-ctl` -- `target/release/insta360-link-server` -- `target/release/insta360-link-gui` +- `target/release/open-360-link-ctl` +- `target/release/open-360-link-server` +- `target/release/open-360-link-gui` --- @@ -71,12 +71,12 @@ The compiled binaries will be placed in `target/release/`: ### Linux (`V4L2` & `uvcvideo`) -On Linux, `insta360-link-protocol` communicates directly with the primary V4L2 video capture node (`/dev/video*`) using `UVCIOC_CTRL_QUERY` for proprietary Insta360 Extension Unit (`XU1`/`XU2`) commands and standard V4L2 `VIDIOC_G_CTRL`/`VIDIOC_S_CTRL` ioctls for Processing Unit / Camera Terminal controls, while reading USB descriptors from sysfs. Because `systemd-logind` automatically grants the active desktop user session access to `/dev/video*`, **no custom `udev` rules or `sudo` privileges are required**. +On Linux, `open-360-link-protocol` communicates directly with the primary V4L2 video capture node (`/dev/video*`) using `UVCIOC_CTRL_QUERY` for proprietary Insta360 Extension Unit (`XU1`/`XU2`) commands and standard V4L2 `VIDIOC_G_CTRL`/`VIDIOC_S_CTRL` ioctls for Processing Unit / Camera Terminal controls, while reading USB descriptors from sysfs. Because `systemd-logind` automatically grants the active desktop user session access to `/dev/video*`, **no custom `udev` rules or `sudo` privileges are required**. -#### Arch Linux (AUR) +#### Arch Linux & Arch Linux ARM (AUR) ```bash -yay -S insta360-link-controller-bin +yay -S open-360-link-bin ``` ### macOS @@ -87,74 +87,73 @@ On macOS, `nusb` uses `IOKit` to send UVC class-specific control requests direct ## Usage Examples -### 1. Command-Line Interface (`insta360-link-ctl`) +### 1. Command-Line Interface (`open-360-link-ctl`) -> **Tip**: Pass `--mock` (and optionally `--mock-model link2`, `link2c`, or `link`) to test any command against the simulated camera without physical hardware attached. +> **Tip**: Pass `--mock link2` (`link`, `link2`, or `link2c`) to test any command against the simulated camera without physical hardware attached. ```bash # List connected Insta360 Link cameras and discovered UVC unit IDs -cargo run -p insta360-link-cli -- list +cargo run -p open-360-link-cli -- list # Show complete camera status, firmware version, PTZ angles, and active features -cargo run -p insta360-link-cli -- status +cargo run -p open-360-link-cli -- info -# Move gimbal to absolute pan/tilt angles (in degrees) and set zoom (100 = 1.0x, 200 = 2.0x) -cargo run -p insta360-link-cli -- ptz --pan 15.0 --tilt -10.0 --zoom 150 +# Move gimbal to absolute pan/tilt angles and set zoom (100 = 1.0x, 200 = 2.0x) +cargo run -p open-360-link-cli -- ptz set-abs --pan 54000 --tilt -36000 +cargo run -p open-360-link-cli -- ptz zoom 150 # Center / home the gimbal -cargo run -p insta360-link-cli -- home +cargo run -p open-360-link-cli -- ptz reset -# Save and recall gimbal preset slots (0..5) -cargo run -p insta360-link-cli -- preset save 0 -cargo run -p insta360-link-cli -- preset recall 0 - -# Switch AI Video Mode (normal, tracking, whiteboard, overhead, deskview, auto-framing, smart-whiteboard) -cargo run -p insta360-link-cli -- mode tracking -cargo run -p insta360-link-cli -- tracking --speed fast --composition half-body +# Switch AI Video Mode (normal, auto-tracking, whiteboard, overhead, deskview, auto-framing, smart-whiteboard) +cargo run -p open-360-link-cli -- mode set auto-tracking +cargo run -p open-360-link-cli -- tracking speed fast # Configure image settings (brightness, contrast, saturation, sharpness, white balance, HDR) -cargo run -p insta360-link-cli -- image --brightness 60 --contrast 55 --hdr on --wb-kelvin 5600 +cargo run -p open-360-link-cli -- image brightness 60 +cargo run -p open-360-link-cli -- image hdr true -# Configure microphone beamforming mode (voice-focus, noise-suppression, music-balance, original, sound-wall) -cargo run -p insta360-link-cli -- audio --mode voice-focus --denoise on +# Configure microphone beamforming mode (voice-focus, noise-reduction, music-mode, original, sound-wall) +cargo run -p open-360-link-cli -- audio mode voice-focus +cargo run -p open-360-link-cli -- audio denoise true ``` -### 2. HTTP & WebSocket Server (`insta360-link-server`) +### 2. HTTP & WebSocket Server (`open-360-link-server`) -Start the daemon on `127.0.0.1:8360` (use `--mock` if no physical camera is connected): +Start the daemon on `127.0.0.1:9360` (use `--mock link2` if no physical camera is connected): ```bash -cargo run -p insta360-link-server -- --bind 127.0.0.1:8360 --mock +cargo run -p open-360-link-server -- --bind 127.0.0.1:9360 --mock link2 ``` Example REST API calls: ```bash # List discovered cameras -curl http://127.0.0.1:8360/api/cameras +curl http://127.0.0.1:9360/api/cameras # Fetch full camera state snapshot -curl http://127.0.0.1:8360/api/state +curl http://127.0.0.1:9360/api/state # Set PTZ pan, tilt, or zoom -curl -X POST http://127.0.0.1:8360/api/ptz \ +curl -X POST http://127.0.0.1:9360/api/ptz \ -H "Content-Type: application/json" \ -d '{"pan_deg": 20.0, "tilt_deg": -5.0, "zoom": 180}' # Switch video mode -curl -X POST http://127.0.0.1:8360/api/mode \ +curl -X POST http://127.0.0.1:9360/api/mode \ -H "Content-Type: application/json" \ -d '{"mode": "DeskView"}' ``` -Connect to `ws://127.0.0.1:8360/ws` over WebSockets to receive real-time JSON state broadcasts and send low-latency PTZ/control commands. +Connect to `ws://127.0.0.1:9360/ws` over WebSockets to receive real-time JSON state broadcasts and send low-latency PTZ/control commands. -### 3. Desktop GUI (`insta360-link-gui`) +### 3. Desktop GUI (`open-360-link-gui`) -Launch the native cross-platform GUI (automatically connects to a physical Insta360 Link camera if found, or falls back to the interactive simulator): +Launch the native cross-platform GUI (automatically connects to a physical Insta360 Link camera if found): ```bash -cargo run -p insta360-link-gui +cargo run -p open-360-link-gui ``` --- diff --git a/assets/insta360-link-controller.desktop b/assets/insta360-link-controller.desktop deleted file mode 100644 index 9a2a130..0000000 --- a/assets/insta360-link-controller.desktop +++ /dev/null @@ -1,14 +0,0 @@ -[Desktop Entry] -Version=1.0 -Type=Application -Name=Insta360 Link Controller -GenericName=Webcam Controller -Comment=Hardware PTZ, AI tracking, image settings, and live preview controller for Insta360 Link webcams -Exec=insta360-link-gui -TryExec=insta360-link-gui -Icon=insta360-link-controller -Terminal=false -Categories=AudioVideo;Video; -Keywords=insta360;link;webcam;camera;ptz;gimbal;uvc;video;tracking; -StartupNotify=true -StartupWMClass=insta360-link-controller diff --git a/assets/open-360-link.desktop b/assets/open-360-link.desktop new file mode 100644 index 0000000..2f52d6c --- /dev/null +++ b/assets/open-360-link.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Open 360 Link +GenericName=Webcam Controller +Comment=Open-source hardware PTZ, AI tracking, image settings, and live preview controller for Insta360 Link webcams +Exec=open-360-link-gui +TryExec=open-360-link-gui +Icon=open-360-link +Terminal=false +Categories=AudioVideo;Video; +Keywords=open-360-link;insta360;link;webcam;camera;ptz;gimbal;uvc;video;tracking; +StartupNotify=true +StartupWMClass=open-360-link diff --git a/assets/insta360-link-controller.svg b/assets/open-360-link.svg similarity index 100% rename from assets/insta360-link-controller.svg rename to assets/open-360-link.svg diff --git a/crates/insta360-link-cli/Cargo.toml b/crates/open-360-link-cli/Cargo.toml similarity index 53% rename from crates/insta360-link-cli/Cargo.toml rename to crates/open-360-link-cli/Cargo.toml index 955205e..1ece442 100644 --- a/crates/insta360-link-cli/Cargo.toml +++ b/crates/open-360-link-cli/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "insta360-link-cli" +name = "open-360-link-cli" version.workspace = true edition.workspace = true license.workspace = true -description = "Cross-platform CLI controller for Insta360 Link, Link 2, and Link 2C cameras" +description = "Open 360 Link: Cross-platform CLI controller for Insta360 Link, Link 2, and Link 2C cameras" [[bin]] -name = "insta360-link-ctl" +name = "open-360-link-ctl" path = "src/main.rs" [dependencies] -insta360-link-protocol = { path = "../insta360-link-protocol" } +open-360-link-protocol = { path = "../open-360-link-protocol" } clap.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/crates/insta360-link-cli/src/main.rs b/crates/open-360-link-cli/src/main.rs similarity index 99% rename from crates/insta360-link-cli/src/main.rs rename to crates/open-360-link-cli/src/main.rs index 4cebc21..3350e1c 100644 --- a/crates/insta360-link-cli/src/main.rs +++ b/crates/open-360-link-cli/src/main.rs @@ -1,5 +1,5 @@ use clap::{ArgAction, Parser, Subcommand, ValueEnum}; -use insta360_link_protocol::{ +use open_360_link_protocol::{ list_connected_cameras, AudioCaptureMode, CameraModel, CompositionStyle, DeviceInfo, DeviceStatus, DiscoveredCamera, ExtendFunction, FilterMode, ForbiddenAreaInfo, Gesture, GestureFunction, HostPtzInfo, ImageOperation, Insta360CameraController, MockCameraTransport, @@ -11,9 +11,9 @@ use std::process::ExitCode; #[derive(Parser, Debug)] #[command( - name = "insta360-link-ctl", + name = "open-360-link-ctl", version, - about = "Cross-platform CLI controller for Insta360 Link, Link 2, and Link 2C cameras" + about = "Open 360 Link: Cross-platform CLI controller for Insta360 Link, Link 2, and Link 2C cameras" )] pub struct Cli { /// Run against an in-memory simulated camera model instead of physical USB diff --git a/crates/insta360-link-gui/Cargo.toml b/crates/open-360-link-gui/Cargo.toml similarity index 60% rename from crates/insta360-link-gui/Cargo.toml rename to crates/open-360-link-gui/Cargo.toml index a080632..f567efe 100644 --- a/crates/insta360-link-gui/Cargo.toml +++ b/crates/open-360-link-gui/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "insta360-link-gui" +name = "open-360-link-gui" version.workspace = true edition.workspace = true license.workspace = true -description = "Cross-platform pure-Rust desktop GUI controller for Insta360 Link, Link 2, and Link 2C" +description = "Open 360 Link: Cross-platform pure-Rust desktop GUI controller for Insta360 Link, Link 2, and Link 2C" [[bin]] -name = "insta360-link-gui" +name = "open-360-link-gui" path = "src/main.rs" [dependencies] -insta360-link-protocol = { path = "../insta360-link-protocol" } +open-360-link-protocol = { path = "../open-360-link-protocol" } eframe.workspace = true egui.workspace = true image = { version = "0.25", default-features = false, features = ["png"] } diff --git a/crates/insta360-link-gui/src/main.rs b/crates/open-360-link-gui/src/main.rs similarity index 98% rename from crates/insta360-link-gui/src/main.rs rename to crates/open-360-link-gui/src/main.rs index 697bce9..4a8346c 100644 --- a/crates/insta360-link-gui/src/main.rs +++ b/crates/open-360-link-gui/src/main.rs @@ -7,7 +7,7 @@ mod video_engine; use std::time::{Duration, Instant}; use eframe::egui::{self, Align2, Color32, FontId, Margin, Pos2, Rect, RichText, Rounding, Stroke, Vec2}; -use insta360_link_protocol::{ +use open_360_link_protocol::{ list_connected_cameras, CameraModel, DiscoveredCamera, Insta360CameraController, MockCameraTransport, NusbTransport, PowerLineFrequency, VideoMode, VideoModeAuxData, }; @@ -181,41 +181,41 @@ impl Insta360App { if let Ok(ext) = self.controller.get_extend_function_status() { for (func, en) in ext { match func { - insta360_link_protocol::ExtendFunction::AiZoom => { + open_360_link_protocol::ExtendFunction::AiZoom => { self.state.ai_zoom_enabled = en } - insta360_link_protocol::ExtendFunction::AllGestures => { + open_360_link_protocol::ExtendFunction::AllGestures => { self.state.enable_all_gestures = en } - insta360_link_protocol::ExtendFunction::AutoTracking => { + open_360_link_protocol::ExtendFunction::AutoTracking => { self.state.enable_auto_tracking = en } - insta360_link_protocol::ExtendFunction::SingleTapTracking => { + open_360_link_protocol::ExtendFunction::SingleTapTracking => { self.state.enable_single_tap_tracking = en } - insta360_link_protocol::ExtendFunction::Hdr => self.state.hdr_enabled = en, - insta360_link_protocol::ExtendFunction::HorizontalMirror => { + open_360_link_protocol::ExtendFunction::Hdr => self.state.hdr_enabled = en, + open_360_link_protocol::ExtendFunction::HorizontalMirror => { self.state.horizontal_mirror = en } - insta360_link_protocol::ExtendFunction::VerticalMirror => { + open_360_link_protocol::ExtendFunction::VerticalMirror => { self.state.vertical_mirror = en } - insta360_link_protocol::ExtendFunction::VerticalMode => { + open_360_link_protocol::ExtendFunction::VerticalMode => { self.state.vertical_resolution_mode = en } - insta360_link_protocol::ExtendFunction::ForcedVertical => { + open_360_link_protocol::ExtendFunction::ForcedVertical => { self.state.forced_vertical_lock = en } - insta360_link_protocol::ExtendFunction::LowResolution => { + open_360_link_protocol::ExtendFunction::LowResolution => { self.state.low_resolution_mode = en } - insta360_link_protocol::ExtendFunction::SmartAdjustment => { + open_360_link_protocol::ExtendFunction::SmartAdjustment => { self.state.smart_adjustment = en } - insta360_link_protocol::ExtendFunction::StartupParams => { + open_360_link_protocol::ExtendFunction::StartupParams => { self.state.startup_params_enabled = en } - insta360_link_protocol::ExtendFunction::UltimatePrivacyMode => { + open_360_link_protocol::ExtendFunction::UltimatePrivacyMode => { self.state.ultimate_privacy = en } _ => {} @@ -231,11 +231,11 @@ impl Insta360App { if let Ok(gestures) = self.controller.get_gesture_status() { for (g, en) in gestures { match g { - insta360_link_protocol::Gesture::Ok => self.state.use_gesture_ok = en, - insta360_link_protocol::Gesture::Palm => self.state.use_gesture_palm = en, - insta360_link_protocol::Gesture::LShape => self.state.use_gesture_l = en, - insta360_link_protocol::Gesture::VShape => self.state.use_gesture_v = en, - insta360_link_protocol::Gesture::Rock => self.state.use_gesture_rock = en, + open_360_link_protocol::Gesture::Ok => self.state.use_gesture_ok = en, + open_360_link_protocol::Gesture::Palm => self.state.use_gesture_palm = en, + open_360_link_protocol::Gesture::LShape => self.state.use_gesture_l = en, + open_360_link_protocol::Gesture::VShape => self.state.use_gesture_v = en, + open_360_link_protocol::Gesture::Rock => self.state.use_gesture_rock = en, } } } @@ -325,15 +325,15 @@ impl Insta360App { .controller .set_all_gestures_enabled(self.state.enable_all_gestures); let _ = self.controller.set_gesture_enabled( - insta360_link_protocol::Gesture::Palm, + open_360_link_protocol::Gesture::Palm, self.state.use_gesture_palm, ); let _ = self.controller.set_gesture_enabled( - insta360_link_protocol::Gesture::VShape, + open_360_link_protocol::Gesture::VShape, self.state.use_gesture_v, ); let _ = self.controller.set_gesture_enabled( - insta360_link_protocol::Gesture::LShape, + open_360_link_protocol::Gesture::LShape, self.state.use_gesture_l, ); let _ = self @@ -412,14 +412,14 @@ impl Insta360App { ) .show(ctx, |ui| { ui.horizontal_centered(|ui| { - // Left: Green Insta360 camera icon + "Insta360 Link Controller" title + // Left: Green camera icon + "Open 360 Link" title ui.label( RichText::new("📷") .color(theme.brand_green) .size(17.0_f32), ); ui.label( - RichText::new("Insta360 Link Controller") + RichText::new("Open 360 Link") .strong() .size(14.5_f32) .color(theme.text01), @@ -1257,7 +1257,7 @@ impl Insta360App { .changed() { let _ = self.controller.set_gesture_enabled( - insta360_link_protocol::Gesture::Palm, + open_360_link_protocol::Gesture::Palm, self.state.use_gesture_palm, ); } @@ -1267,7 +1267,7 @@ impl Insta360App { .changed() { let _ = self.controller.set_gesture_enabled( - insta360_link_protocol::Gesture::VShape, + open_360_link_protocol::Gesture::VShape, self.state.use_gesture_v, ); } @@ -1277,7 +1277,7 @@ impl Insta360App { .changed() { let _ = self.controller.set_gesture_enabled( - insta360_link_protocol::Gesture::LShape, + open_360_link_protocol::Gesture::LShape, self.state.use_gesture_l, ); } @@ -1455,13 +1455,13 @@ impl Insta360App { ui.label("Tracking Speed:"); egui::ComboBox::from_id_salt("tb_track_speed") .selected_text(match self.state.track_speed { - insta360_link_protocol::TrackSpeed::Fast => { + open_360_link_protocol::TrackSpeed::Fast => { "Quick" } - insta360_link_protocol::TrackSpeed::Normal => { + open_360_link_protocol::TrackSpeed::Normal => { "Ordinary" } - insta360_link_protocol::TrackSpeed::Slow => { + open_360_link_protocol::TrackSpeed::Slow => { "Slow" } }) @@ -1469,37 +1469,37 @@ impl Insta360App { if ui .selectable_value( &mut self.state.track_speed, - insta360_link_protocol::TrackSpeed::Fast, + open_360_link_protocol::TrackSpeed::Fast, "Quick", ) .clicked() { let _ = self.controller.set_track_speed( - insta360_link_protocol::TrackSpeed::Fast, + open_360_link_protocol::TrackSpeed::Fast, ); } if ui .selectable_value( &mut self.state.track_speed, - insta360_link_protocol::TrackSpeed::Normal, + open_360_link_protocol::TrackSpeed::Normal, "Ordinary", ) .clicked() { let _ = self.controller.set_track_speed( - insta360_link_protocol::TrackSpeed::Normal, + open_360_link_protocol::TrackSpeed::Normal, ); } if ui .selectable_value( &mut self.state.track_speed, - insta360_link_protocol::TrackSpeed::Slow, + open_360_link_protocol::TrackSpeed::Slow, "Slow", ) .clicked() { let _ = self.controller.set_track_speed( - insta360_link_protocol::TrackSpeed::Slow, + open_360_link_protocol::TrackSpeed::Slow, ); } }); @@ -2307,9 +2307,9 @@ impl eframe::App for Insta360App { let z_read = self.controller.get_zoom().unwrap_or(0); let _ = self.controller.set_zoom(100); - let _ = self.controller.set_track_speed(insta360_link_protocol::TrackSpeed::Fast); + let _ = self.controller.set_track_speed(open_360_link_protocol::TrackSpeed::Fast); let ts_fast = self.controller.get_track_speed().ok(); - let _ = self.controller.set_track_speed(insta360_link_protocol::TrackSpeed::Normal); + let _ = self.controller.set_track_speed(open_360_link_protocol::TrackSpeed::Normal); let ts_norm = self.controller.get_track_speed().ok(); let _ = self.controller.set_auto_exposure(true); @@ -2370,7 +2370,7 @@ impl eframe::App for Insta360App { let _ = self.controller.set_all_gestures_enabled(true); let _ = self .controller - .set_gesture_enabled(insta360_link_protocol::Gesture::Palm, true); + .set_gesture_enabled(open_360_link_protocol::Gesture::Palm, true); let gest = self.controller.get_gesture_status().ok(); println!( @@ -2569,16 +2569,16 @@ fn main() -> eframe::Result<()> { // Matching webcam.qml: width: 1440, height: 714, minimumWidth: 1120, minimumHeight: 536 let native_options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default() - .with_app_id("insta360-link-controller") + .with_app_id("open-360-link") .with_icon(build_app_icon()) .with_inner_size([1440.0_f32, 714.0_f32]) .with_min_inner_size([1120.0_f32, 536.0_f32]) - .with_title("Insta360 Link Controller"), + .with_title("Open 360 Link"), ..Default::default() }; eframe::run_native( - "Insta360 Link Controller", + "Open 360 Link", native_options, Box::new(|cc| Ok(Box::new(Insta360App::new(cc)))), ) diff --git a/crates/insta360-link-gui/src/right_panel.rs b/crates/open-360-link-gui/src/right_panel.rs similarity index 99% rename from crates/insta360-link-gui/src/right_panel.rs rename to crates/open-360-link-gui/src/right_panel.rs index 1da973b..6d6a0dd 100644 --- a/crates/insta360-link-gui/src/right_panel.rs +++ b/crates/open-360-link-gui/src/right_panel.rs @@ -2,7 +2,7 @@ use egui::{ Align, Align2, Color32, Context, FontId, Grid, Layout, Pos2, Rect, RichText, Rounding, ScrollArea, Sense, Stroke, TextEdit, Ui, Vec2, }; -use insta360_link_protocol::{ +use open_360_link_protocol::{ AudioCaptureMode, CameraModel, CompositionStyle, ExposureCurve, FilterMode, HostPtzInfo, ImageOperation, Insta360CameraController, PowerLineFrequency, TrackSpeed, UvcControlTransport, Gesture, diff --git a/crates/insta360-link-gui/src/state.rs b/crates/open-360-link-gui/src/state.rs similarity index 96% rename from crates/insta360-link-gui/src/state.rs rename to crates/open-360-link-gui/src/state.rs index 110d75d..c4824bd 100644 --- a/crates/insta360-link-gui/src/state.rs +++ b/crates/open-360-link-gui/src/state.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use egui::{ColorImage, Pos2, TextureHandle}; -pub use insta360_link_protocol::*; +pub use open_360_link_protocol::*; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use std::time::Instant; @@ -285,7 +285,7 @@ pub struct CameraAppState { pub last_saved_settings: Option, } -/// User settings persisted across sessions in `~/.config/insta360-link-controller/settings.json`. +/// User settings persisted across sessions in `~/.config/open-360-link/settings.json`. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PersistedAppSettings { pub format_preset: VideoFormatPreset, @@ -325,12 +325,10 @@ pub struct PersistedAppSettings { } impl PersistedAppSettings { - pub fn config_path() -> PathBuf { + fn base_config_dir() -> PathBuf { if let Ok(xdg) = std::env::var("XDG_CONFIG_HOME") { if !xdg.trim().is_empty() { - return PathBuf::from(xdg) - .join("insta360-link-controller") - .join("settings.json"); + return PathBuf::from(xdg); } } let home = std::env::var("HOME") @@ -338,14 +336,29 @@ impl PersistedAppSettings { .map(PathBuf::from) .unwrap_or_else(|_| PathBuf::from(".")); home.join(".config") - .join("insta360-link-controller") + } + + pub fn config_path() -> PathBuf { + Self::base_config_dir() + .join("open-360-link") .join("settings.json") } pub fn load() -> Option { let path = Self::config_path(); - let data = std::fs::read_to_string(path).ok()?; - serde_json::from_str(&data).ok() + if let Ok(data) = std::fs::read_to_string(&path) { + if let Ok(parsed) = serde_json::from_str(&data) { + return Some(parsed); + } + } + // Automatic migration from legacy `~/.config/insta360-link-controller/settings.json` + let legacy = Self::base_config_dir() + .join("insta360-link-controller") + .join("settings.json"); + let data = std::fs::read_to_string(legacy).ok()?; + let parsed: Self = serde_json::from_str(&data).ok()?; + parsed.save(); + Some(parsed) } pub fn save(&self) { diff --git a/crates/insta360-link-gui/src/theme.rs b/crates/open-360-link-gui/src/theme.rs similarity index 100% rename from crates/insta360-link-gui/src/theme.rs rename to crates/open-360-link-gui/src/theme.rs diff --git a/crates/insta360-link-gui/src/transport.rs b/crates/open-360-link-gui/src/transport.rs similarity index 99% rename from crates/insta360-link-gui/src/transport.rs rename to crates/open-360-link-gui/src/transport.rs index a017509..52bc870 100644 --- a/crates/insta360-link-gui/src/transport.rs +++ b/crates/open-360-link-gui/src/transport.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] -use insta360_link_protocol::{ +use open_360_link_protocol::{ ct_selectors, pu_selectors, xu1_selectors, xu2_selectors, CameraModel, DiscoveredCamera, MockCameraTransport, NusbTransport, TransportError, UvcControlPacketHeader, UvcControlTransport, UvcRequest, UvcUnitIds, diff --git a/crates/insta360-link-gui/src/video_engine.rs b/crates/open-360-link-gui/src/video_engine.rs similarity index 99% rename from crates/insta360-link-gui/src/video_engine.rs rename to crates/open-360-link-gui/src/video_engine.rs index e943598..7ec8a1c 100644 --- a/crates/insta360-link-gui/src/video_engine.rs +++ b/crates/open-360-link-gui/src/video_engine.rs @@ -3,7 +3,7 @@ use crate::state::{ SHUTTER_DENOM_LIST, }; use egui::{Color32, ColorImage, Context, TextureHandle, TextureOptions}; -use insta360_link_protocol::{TrackSpeed, UvcRect, VideoMode}; +use open_360_link_protocol::{TrackSpeed, UvcRect, VideoMode}; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::process::{Child, ChildStdin, Command, Stdio}; diff --git a/crates/insta360-link-protocol/Cargo.toml b/crates/open-360-link-protocol/Cargo.toml similarity index 64% rename from crates/insta360-link-protocol/Cargo.toml rename to crates/open-360-link-protocol/Cargo.toml index 9260232..3f8e7b9 100644 --- a/crates/insta360-link-protocol/Cargo.toml +++ b/crates/open-360-link-protocol/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "insta360-link-protocol" +name = "open-360-link-protocol" version.workspace = true edition.workspace = true license.workspace = true -description = "Pure-Rust cross-platform UVC & Extension Unit protocol library for Insta360 Link, Link 2, and Link 2C webcams" +description = "Open 360 Link: Pure-Rust cross-platform UVC & Extension Unit protocol library for Insta360 Link, Link 2, and Link 2C webcams" [dependencies] thiserror.workspace = true diff --git a/crates/insta360-link-protocol/src/codec.rs b/crates/open-360-link-protocol/src/codec.rs similarity index 100% rename from crates/insta360-link-protocol/src/codec.rs rename to crates/open-360-link-protocol/src/codec.rs diff --git a/crates/insta360-link-protocol/src/controller.rs b/crates/open-360-link-protocol/src/controller.rs similarity index 100% rename from crates/insta360-link-protocol/src/controller.rs rename to crates/open-360-link-protocol/src/controller.rs diff --git a/crates/insta360-link-protocol/src/ids.rs b/crates/open-360-link-protocol/src/ids.rs similarity index 100% rename from crates/insta360-link-protocol/src/ids.rs rename to crates/open-360-link-protocol/src/ids.rs diff --git a/crates/insta360-link-protocol/src/lib.rs b/crates/open-360-link-protocol/src/lib.rs similarity index 100% rename from crates/insta360-link-protocol/src/lib.rs rename to crates/open-360-link-protocol/src/lib.rs diff --git a/crates/insta360-link-protocol/src/transport.rs b/crates/open-360-link-protocol/src/transport.rs similarity index 100% rename from crates/insta360-link-protocol/src/transport.rs rename to crates/open-360-link-protocol/src/transport.rs diff --git a/crates/insta360-link-protocol/src/types.rs b/crates/open-360-link-protocol/src/types.rs similarity index 100% rename from crates/insta360-link-protocol/src/types.rs rename to crates/open-360-link-protocol/src/types.rs diff --git a/crates/insta360-link-protocol/tests/protocol_integration.rs b/crates/open-360-link-protocol/tests/protocol_integration.rs similarity index 99% rename from crates/insta360-link-protocol/tests/protocol_integration.rs rename to crates/open-360-link-protocol/tests/protocol_integration.rs index e311ebe..ff73cbe 100644 --- a/crates/insta360-link-protocol/tests/protocol_integration.rs +++ b/crates/open-360-link-protocol/tests/protocol_integration.rs @@ -1,4 +1,4 @@ -use insta360_link_protocol::*; +use open_360_link_protocol::*; /// Builds a synthetic USB Configuration Descriptor containing: /// - Standard Interface Descriptor for UVC VideoControl (`bInterfaceNumber = 2`, `CC_VIDEO = 0x0e`, `SC_VIDEOCONTROL = 0x01`) diff --git a/crates/insta360-link-server/Cargo.toml b/crates/open-360-link-server/Cargo.toml similarity index 58% rename from crates/insta360-link-server/Cargo.toml rename to crates/open-360-link-server/Cargo.toml index 8caa719..400ad45 100644 --- a/crates/insta360-link-server/Cargo.toml +++ b/crates/open-360-link-server/Cargo.toml @@ -1,16 +1,16 @@ [package] -name = "insta360-link-server" +name = "open-360-link-server" version.workspace = true edition.workspace = true license.workspace = true -description = "Cross-platform HTTP REST & WebSocket remote control server for Insta360 Link cameras" +description = "Open 360 Link: Cross-platform HTTP REST & WebSocket remote control server for Insta360 Link cameras" [[bin]] -name = "insta360-link-server" +name = "open-360-link-server" path = "src/main.rs" [dependencies] -insta360-link-protocol = { path = "../insta360-link-protocol" } +open-360-link-protocol = { path = "../open-360-link-protocol" } clap.workspace = true tokio.workspace = true axum.workspace = true diff --git a/crates/insta360-link-server/src/lib.rs b/crates/open-360-link-server/src/lib.rs similarity index 99% rename from crates/insta360-link-server/src/lib.rs rename to crates/open-360-link-server/src/lib.rs index 70fe1ba..45639e7 100644 --- a/crates/insta360-link-server/src/lib.rs +++ b/crates/open-360-link-server/src/lib.rs @@ -7,7 +7,7 @@ use axum::{ routing::{get, post}, Json, Router, }; -use insta360_link_protocol::{ +use open_360_link_protocol::{ list_connected_cameras, AudioCaptureMode, CameraModel, CompositionStyle, DeviceCapabilities, DeviceInfo, DiscoveredCamera, ExtendFunction, FilterMode, ForbiddenAreaInfo, Gesture, GestureFunction, HostPtzInfo, Insta360CameraController, MockCameraTransport, NusbTransport, diff --git a/crates/insta360-link-server/src/main.rs b/crates/open-360-link-server/src/main.rs similarity index 91% rename from crates/insta360-link-server/src/main.rs rename to crates/open-360-link-server/src/main.rs index c59883e..de2b9f5 100644 --- a/crates/insta360-link-server/src/main.rs +++ b/crates/open-360-link-server/src/main.rs @@ -1,16 +1,16 @@ use clap::{Parser, ValueEnum}; -use insta360_link_protocol::{ +use open_360_link_protocol::{ CameraModel, Insta360CameraController, NusbTransport, UvcControlTransport, }; -use insta360_link_server::{build_router, new_mock_state, AppState, ServerTransport}; +use open_360_link_server::{build_router, new_mock_state, AppState, ServerTransport}; use std::net::SocketAddr; use std::sync::{Arc, Mutex}; #[derive(Parser, Debug)] #[command( - name = "insta360-link-server", + name = "open-360-link-server", version, - about = "Cross-platform HTTP REST & WebSocket remote control server for Insta360 Link cameras" + about = "Open 360 Link: Cross-platform HTTP REST & WebSocket remote control server for Insta360 Link cameras" )] struct Cli { /// Bind address for HTTP and WebSocket server @@ -47,7 +47,7 @@ async fn main() -> Result<(), Box> { tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() - .unwrap_or_else(|_| "insta360_link_server=info,tower_http=info".into()), + .unwrap_or_else(|_| "open_360_link_server=info,tower_http=info".into()), ) .init(); diff --git a/crates/insta360-link-server/tests/server_integration.rs b/crates/open-360-link-server/tests/server_integration.rs similarity index 99% rename from crates/insta360-link-server/tests/server_integration.rs rename to crates/open-360-link-server/tests/server_integration.rs index ff4a0da..bc6dce3 100644 --- a/crates/insta360-link-server/tests/server_integration.rs +++ b/crates/open-360-link-server/tests/server_integration.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; use std::net::SocketAddr; -use insta360_link_protocol::{ +use open_360_link_protocol::{ AudioCaptureMode, CameraModel, CompositionStyle, ExtendFunction, FilterMode, Gesture, GestureFunction, TrackSpeed, VideoMode, }; -use insta360_link_server::{ +use open_360_link_server::{ build_router, new_mock_state, AudioRequest, CameraStateSnapshot, ImageRequest, ModeRequest, PresetRequest, PtzRequest, SystemRequest, TrackingRequest, }; diff --git a/mise.toml b/mise.toml index ebf2f28..3ca9ade 100644 --- a/mise.toml +++ b/mise.toml @@ -2,20 +2,20 @@ rust = "stable" [tasks.default] -description = "Build and launch the Insta360 Link Controller desktop GUI" +description = "Build and launch the Open 360 Link desktop GUI" depends = ["gui"] [tasks.gui] -description = "Build and launch the Insta360 Link Controller desktop GUI" -run = "cargo run --release -p insta360-link-gui" +description = "Build and launch the Open 360 Link desktop GUI" +run = "cargo run --release -p open-360-link-gui" [tasks.cli] -description = "Run the insta360-link-ctl CLI (e.g. mise run cli -- info)" -run = "cargo run --release -p insta360-link-cli --" +description = "Run the open-360-link-ctl CLI (e.g. mise run cli -- info)" +run = "cargo run --release -p open-360-link-cli --" [tasks.server] description = "Start the HTTP & WebSocket remote control server on 127.0.0.1:8360" -run = "cargo run --release -p insta360-link-server --" +run = "cargo run --release -p open-360-link-server --" [tasks.build] description = "Build all workspace crates in release mode" @@ -27,7 +27,7 @@ run = "cargo test --workspace" [tasks.self-test] description = "Run the automated live hardware GUI verification sequence" -run = "INSTA360_GUI_SELF_TEST=1 cargo run --release -p insta360-link-gui" +run = "INSTA360_GUI_SELF_TEST=1 cargo run --release -p open-360-link-gui" [tasks.package-release] description = "Build and package release binaries for a target (usage: mise run package-release [target] [version])" @@ -67,15 +67,15 @@ case "$TARGET" in *) ASSET_SUFFIX="$TARGET"; EXT="tar.gz" ;; esac -STAGE_NAME="insta360-link-controller-v${VERSION}-${ASSET_SUFFIX}" +STAGE_NAME="open-360-link-v${VERSION}-${ASSET_SUFFIX}" STAGE_DIR="dist/${STAGE_NAME}" rm -rf "$STAGE_DIR" mkdir -p "$STAGE_DIR" if [ "$EXT" = "zip" ]; then - cp "target/${TARGET}/release/insta360-link-gui.exe" "$STAGE_DIR/" - cp "target/${TARGET}/release/insta360-link-ctl.exe" "$STAGE_DIR/" - cp "target/${TARGET}/release/insta360-link-server.exe" "$STAGE_DIR/" + cp "target/${TARGET}/release/open-360-link-gui.exe" "$STAGE_DIR/" + cp "target/${TARGET}/release/open-360-link-ctl.exe" "$STAGE_DIR/" + cp "target/${TARGET}/release/open-360-link-server.exe" "$STAGE_DIR/" cp README.md "$STAGE_DIR/" python3 -c ' import pathlib, sys, zipfile @@ -88,9 +88,9 @@ with zipfile.ZipFile(out_zip, "w", zipfile.ZIP_DEFLATED) as zf: ' "$STAGE_DIR" "dist/${STAGE_NAME}.zip" echo "Packaged dist/${STAGE_NAME}.zip" else - cp "target/${TARGET}/release/insta360-link-gui" "$STAGE_DIR/" - cp "target/${TARGET}/release/insta360-link-ctl" "$STAGE_DIR/" - cp "target/${TARGET}/release/insta360-link-server" "$STAGE_DIR/" + cp "target/${TARGET}/release/open-360-link-gui" "$STAGE_DIR/" + cp "target/${TARGET}/release/open-360-link-ctl" "$STAGE_DIR/" + cp "target/${TARGET}/release/open-360-link-server" "$STAGE_DIR/" cp README.md "$STAGE_DIR/" cp -r assets "$STAGE_DIR/" tar -C dist -czf "dist/${STAGE_NAME}.tar.gz" "$STAGE_NAME" @@ -107,7 +107,7 @@ set -euo pipefail VER="${1:-0.1.0}" VER="${VER#v}" env -u GITHUB_TOKEN -u GH_TOKEN -u GITHUB_AUTH_TOKEN gh workflow run release.yml \ - --repo lucasoskorep/insta360-link-controller \ + --repo lucasoskorep/open-360-link \ --ref master \ -f version="$VER" echo "Triggered release workflow on master for v${VER}"