Compare commits
3 Commits
4f2d311249
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e48eb8c91 | |||
| 72bf38a7ff | |||
| 97db96b105 |
199
AGENTS.md
199
AGENTS.md
@@ -1,199 +0,0 @@
|
|||||||
# Agent Guidelines for Jello
|
|
||||||
|
|
||||||
This document provides guidelines for AI coding agents working on the Jello codebase.
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
Jello is a WIP video client for Jellyfin written in Rust, focusing on HDR video playback using:
|
|
||||||
- **iced** - Primary GUI toolkit
|
|
||||||
- **gstreamer** - Video + audio decoding library
|
|
||||||
- **wgpu** - Rendering video from GStreamer in iced
|
|
||||||
|
|
||||||
## Build, Test, and Lint Commands
|
|
||||||
|
|
||||||
### Building
|
|
||||||
```bash
|
|
||||||
# Build in release mode
|
|
||||||
cargo build --release
|
|
||||||
cargo build -r
|
|
||||||
|
|
||||||
# Build specific workspace member
|
|
||||||
cargo build -p api
|
|
||||||
cargo build -p gst
|
|
||||||
cargo build -p ui-iced
|
|
||||||
|
|
||||||
# Run the application
|
|
||||||
cargo run --release -- -vv
|
|
||||||
just jello # Uses justfile
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
```bash
|
|
||||||
# Run all tests in workspace
|
|
||||||
cargo test --workspace
|
|
||||||
|
|
||||||
# Run tests for a specific package
|
|
||||||
cargo test -p gst
|
|
||||||
cargo test -p api
|
|
||||||
cargo test -p iced-video
|
|
||||||
|
|
||||||
# Run a single test by name
|
|
||||||
cargo test test_appsink
|
|
||||||
cargo test -p gst test_appsink
|
|
||||||
|
|
||||||
# Run a specific test in a specific file
|
|
||||||
cargo test -p gst --test <test_file_name> <test_function_name>
|
|
||||||
|
|
||||||
# Run tests with output
|
|
||||||
cargo test -- --nocapture
|
|
||||||
cargo test -- --show-output
|
|
||||||
```
|
|
||||||
|
|
||||||
### Linting and Formatting
|
|
||||||
```bash
|
|
||||||
# Check code without building
|
|
||||||
cargo check
|
|
||||||
cargo check --workspace
|
|
||||||
|
|
||||||
# Run clippy (linter)
|
|
||||||
cargo clippy
|
|
||||||
cargo clippy --workspace
|
|
||||||
cargo clippy --workspace -- -D warnings
|
|
||||||
|
|
||||||
# Format code
|
|
||||||
cargo fmt
|
|
||||||
cargo fmt --all
|
|
||||||
|
|
||||||
# Check formatting without modifying files
|
|
||||||
cargo fmt --all -- --check
|
|
||||||
```
|
|
||||||
|
|
||||||
### Other Tools
|
|
||||||
```bash
|
|
||||||
# Check for security vulnerabilities and license compliance
|
|
||||||
cargo deny check
|
|
||||||
|
|
||||||
# Generate Jellyfin type definitions
|
|
||||||
just typegen
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Style Guidelines
|
|
||||||
|
|
||||||
### Rust Edition
|
|
||||||
- Use **Rust 2024 edition** (as specified in Cargo.toml files)
|
|
||||||
|
|
||||||
### Imports
|
|
||||||
- Use `use` statements at the top of files
|
|
||||||
- Group imports: std library, external crates, then local modules
|
|
||||||
- Use `crate::` for absolute paths within the crate
|
|
||||||
- Common pattern: create a `priv_prelude` module for internal imports
|
|
||||||
- Use `pub use` to re-export commonly used items
|
|
||||||
- Use wildcard imports (`use crate::priv_prelude::*;`) within internal modules when a prelude exists
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```rust
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use reqwest::{Method, header::InvalidHeaderValue};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::errors::*;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Naming Conventions
|
|
||||||
- **Types/Structs/Enums**: PascalCase (e.g., `JellyfinClient`, `Error`, `AppSink`)
|
|
||||||
- **Functions/Methods**: snake_case (e.g., `request_builder`, `stream_url`)
|
|
||||||
- **Variables**: snake_case (e.g., `access_token`, `device_id`)
|
|
||||||
- **Constants**: SCREAMING_SNAKE_CASE (e.g., `NEXT_ID`, `GST`)
|
|
||||||
- **Modules**: snake_case (e.g., `priv_prelude`, `error_stack`)
|
|
||||||
|
|
||||||
### Error Handling
|
|
||||||
- Use **`error-stack`** for error handling with context propagation
|
|
||||||
- Use **`thiserror`** for defining error types
|
|
||||||
- Standard error type pattern:
|
|
||||||
```rust
|
|
||||||
pub use error_stack::{Report, ResultExt};
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[error("An error occurred")]
|
|
||||||
pub struct Error;
|
|
||||||
|
|
||||||
pub type Result<T, E = error_stack::Report<Error>> = core::result::Result<T, E>;
|
|
||||||
```
|
|
||||||
- Attach context to errors using `.change_context(Error)` and `.attach("description")`
|
|
||||||
- Use `#[track_caller]` on functions that may panic or error for better error messages
|
|
||||||
- Error handling example:
|
|
||||||
```rust
|
|
||||||
self.inner
|
|
||||||
.set_state(gstreamer::State::Playing)
|
|
||||||
.change_context(Error)
|
|
||||||
.attach("Failed to set pipeline to Playing state")?;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Types
|
|
||||||
- Prefer explicit types over type inference when it improves clarity
|
|
||||||
- Use `impl Trait` for function parameters when appropriate (e.g., `impl AsRef<str>`)
|
|
||||||
- Use `Option<T>` and `Result<T, E>` idiomatically
|
|
||||||
- Use `Arc<T>` for shared ownership
|
|
||||||
- Use newtype patterns for semantic clarity (e.g., `ApiKey` wrapping `secrecy::SecretBox<String>`)
|
|
||||||
|
|
||||||
### Formatting
|
|
||||||
- Use 4 spaces for indentation
|
|
||||||
- Line length: aim for 100 characters, but not strictly enforced
|
|
||||||
- Use trailing commas in multi-line collections
|
|
||||||
- Follow standard Rust formatting conventions (enforced by `cargo fmt`)
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
- Add doc comments (`///`) for public APIs
|
|
||||||
- Use inline comments (`//`) sparingly, prefer self-documenting code
|
|
||||||
- Include examples in doc comments when helpful
|
|
||||||
|
|
||||||
### Async/Await
|
|
||||||
- Use `tokio` as the async runtime
|
|
||||||
- Mark async functions with `async` keyword
|
|
||||||
- Use `.await` for async operations
|
|
||||||
- Common pattern: `tokio::fs` for file operations
|
|
||||||
|
|
||||||
### Module Structure
|
|
||||||
- Use `mod.rs` or inline modules as appropriate
|
|
||||||
- Keep related functionality together
|
|
||||||
- Use `pub(crate)` for internal APIs
|
|
||||||
- Re-export commonly used items at crate root
|
|
||||||
|
|
||||||
### Macros
|
|
||||||
- Custom macros used: `wrap_gst!`, `parent_child!`
|
|
||||||
- Use macros for reducing boilerplate, only in the `gst` crate
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
- Place tests in the same file with `#[test]` or `#[cfg(test)]`
|
|
||||||
- Use descriptive test function names (e.g., `test_appsink`, `unique_generates_different_ids`)
|
|
||||||
- Initialize tracing in tests when needed for debugging
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
- Prefer well-maintained crates from crates.io
|
|
||||||
- Use `workspace.dependencies` for shared dependencies across workspace members
|
|
||||||
- Pin versions when stability is important
|
|
||||||
|
|
||||||
### Workspace Structure
|
|
||||||
The project uses a Cargo workspace with multiple members:
|
|
||||||
- `.` - Main jello binary
|
|
||||||
- `api` - Jellyfin API client
|
|
||||||
- `gst` - GStreamer wrapper
|
|
||||||
- `ui-iced` - Iced UI implementation
|
|
||||||
- `ui-gpui` - GPUI UI implementation (optional)
|
|
||||||
- `store` - Secret/data/storage management
|
|
||||||
- `jello-types` - Shared type definitions
|
|
||||||
- `typegen` - Jellyfin type generator
|
|
||||||
- `crates/iced-video` - Custom iced video widget
|
|
||||||
- `examples/hdr-gstreamer-wgpu` - HDR example
|
|
||||||
|
|
||||||
### Project-Specific Patterns
|
|
||||||
- Use `LazyLock` for global initialization (e.g., GStreamer init)
|
|
||||||
- Use the builder pattern with method chaining (e.g., `request_builder()`)
|
|
||||||
- Use `tap` crate's `.pipe()` for functional transformations
|
|
||||||
- Prefer `BTreeMap`/`BTreeSet` over `HashMap`/`HashSet` when order matters
|
|
||||||
- Prefer a functional programming style instead of an imperative one.
|
|
||||||
- When building UIs keep the handler and view code in the same module (eg. settings view and settings handle in the same file)
|
|
||||||
|
|
||||||
## License
|
|
||||||
All code in this project is MIT licensed.
|
|
||||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -2348,12 +2348,6 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "funty"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futf"
|
name = "futf"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
|||||||
12
Cargo.toml
12
Cargo.toml
@@ -1,20 +1,20 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
".",
|
".",
|
||||||
"api",
|
|
||||||
"typegen",
|
"typegen",
|
||||||
"ui-gpui",
|
"ui-gpui",
|
||||||
"ui-iced",
|
"ui-iced",
|
||||||
"store",
|
"crates/api",
|
||||||
"gst",
|
"crates/gst",
|
||||||
"crates/iced-video",
|
"crates/iced-video",
|
||||||
|
"crates/store",
|
||||||
"examples/hdr-gstreamer-wgpu",
|
"examples/hdr-gstreamer-wgpu",
|
||||||
]
|
]
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
iced = { version = "0.14.0" }
|
iced = { version = "0.14.0" }
|
||||||
gst = { version = "0.1.0", path = "gst" }
|
gst = { version = "0.1.0", path = "crates/gst" }
|
||||||
iced_wgpu = { version = "0.14.0" }
|
|
||||||
iced-video = { version = "0.1.0", path = "crates/iced-video" }
|
iced-video = { version = "0.1.0", path = "crates/iced-video" }
|
||||||
|
api = { version = "0.1.0", path = "crates/api" }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
iced_wgpu = { git = "https://github.com/uttarayan21/iced", branch = "0.14" }
|
iced_wgpu = { git = "https://github.com/uttarayan21/iced", branch = "0.14" }
|
||||||
@@ -30,7 +30,7 @@ edition = "2024"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
api = { version = "0.1.0", path = "api" }
|
api = { version = "0.1.0", path = "crates/api" }
|
||||||
bytemuck = { version = "1.24.0", features = ["derive"] }
|
bytemuck = { version = "1.24.0", features = ["derive"] }
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
clap-verbosity-flag = { version = "3.0.4", features = ["tracing"] }
|
clap-verbosity-flag = { version = "3.0.4", features = ["tracing"] }
|
||||||
|
|||||||
0
api/.gitignore → crates/api/.gitignore
vendored
0
api/.gitignore → crates/api/.gitignore
vendored
0
gst/.gitignore → crates/gst/.gitignore
vendored
0
gst/.gitignore → crates/gst/.gitignore
vendored
0
gst/Cargo.lock → crates/gst/Cargo.lock
generated
0
gst/Cargo.lock → crates/gst/Cargo.lock
generated
@@ -19,11 +19,3 @@ wgpu = { version = "27.0.1", features = ["vulkan"] }
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
iced.workspace = true
|
iced.workspace = true
|
||||||
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
debug = true
|
|
||||||
[profile.release]
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
# [patch.crates-io]
|
|
||||||
# iced_wgpu = { git = "https://github.com/uttarayan21/iced", branch = "0.14" }
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use crate::{Error, Result, ResultExt};
|
use crate::{Error, Result, ResultExt};
|
||||||
use gst::{
|
use gst::{
|
||||||
Bus, Gst, MessageType, MessageView, Sink, Source,
|
Bus, Gst, Sink,
|
||||||
app::AppSink,
|
app::AppSink,
|
||||||
caps::{Caps, CapsType},
|
caps::{Caps, CapsType},
|
||||||
element::ElementExt,
|
element::ElementExt,
|
||||||
pipeline::PipelineExt,
|
pipeline::PipelineExt,
|
||||||
playback::{PlayFlags, Playbin3},
|
playback::{PlayFlags, Playbin3},
|
||||||
videoconvertscale::VideoConvert,
|
|
||||||
};
|
};
|
||||||
use std::sync::{Arc, Mutex, atomic::AtomicBool};
|
use std::sync::{Arc, Mutex, atomic::AtomicBool};
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,3 @@ anyhow = "*"
|
|||||||
pollster = "0.4.0"
|
pollster = "0.4.0"
|
||||||
tracing = { version = "0.1.43", features = ["log"] }
|
tracing = { version = "0.1.43", features = ["log"] }
|
||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
debug = true
|
|
||||||
|
|||||||
62
gst/.github/workflows/build.yaml
vendored
62
gst/.github/workflows/build.yaml
vendored
@@ -1,62 +0,0 @@
|
|||||||
name: build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
checks-matrix:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: DeterminateSystems/nix-installer-action@main
|
|
||||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
|
||||||
- id: set-matrix
|
|
||||||
name: Generate Nix Matrix
|
|
||||||
run: |
|
|
||||||
set -Eeu
|
|
||||||
matrix="$(nix eval --json '.#githubActions.matrix')"
|
|
||||||
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
checks-build:
|
|
||||||
needs: checks-matrix
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix: ${{fromJSON(needs.checks-matrix.outputs.matrix)}}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: DeterminateSystems/nix-installer-action@main
|
|
||||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
|
||||||
- run: nix build -L '.#${{ matrix.attr }}'
|
|
||||||
|
|
||||||
codecov:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
id-token: "write"
|
|
||||||
contents: "read"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: DeterminateSystems/nix-installer-action@main
|
|
||||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
|
||||||
|
|
||||||
- name: Run codecov
|
|
||||||
run: nix build .#checks.x86_64-linux.hello-llvm-cov
|
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
|
||||||
uses: codecov/codecov-action@v4.0.1
|
|
||||||
with:
|
|
||||||
flags: unittests
|
|
||||||
name: codecov-hello
|
|
||||||
fail_ci_if_error: true
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
files: ./result
|
|
||||||
verbose: true
|
|
||||||
|
|
||||||
38
gst/.github/workflows/docs.yaml
vendored
38
gst/.github/workflows/docs.yaml
vendored
@@ -1,38 +0,0 @@
|
|||||||
name: docs
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
docs:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
id-token: "write"
|
|
||||||
contents: "read"
|
|
||||||
pages: "write"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: DeterminateSystems/nix-installer-action@main
|
|
||||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
|
||||||
- uses: DeterminateSystems/flake-checker-action@main
|
|
||||||
|
|
||||||
- name: Generate docs
|
|
||||||
run: nix build .#checks.x86_64-linux.hello-docs
|
|
||||||
|
|
||||||
- name: Setup Pages
|
|
||||||
uses: actions/configure-pages@v5
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-pages-artifact@v3
|
|
||||||
with:
|
|
||||||
path: result/share/doc
|
|
||||||
|
|
||||||
- name: Deploy to gh-pages
|
|
||||||
id: deployment
|
|
||||||
uses: actions/deploy-pages@v4
|
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
pub use error_stack::{Report, ResultExt};
|
pub use error_stack::ResultExt;
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
#[error("An error occurred")]
|
#[error("An error occurred")]
|
||||||
pub struct Error;
|
pub struct Error;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
mod cli;
|
mod cli;
|
||||||
mod errors;
|
mod errors;
|
||||||
use api::JellyfinConfig;
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ edition = "2024"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
api = { version = "0.1.0", path = "../api" }
|
api = { workspace = true }
|
||||||
blurhash = "0.2.3"
|
blurhash = "0.2.3"
|
||||||
bytes = "1.11.0"
|
bytes = "1.11.0"
|
||||||
gpui_util = "0.2.2"
|
gpui_util = "0.2.2"
|
||||||
|
|||||||
@@ -270,7 +270,6 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Video(msg) => video::update(state, msg),
|
Message::Video(msg) => video::update(state, msg),
|
||||||
_ => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,51 +441,7 @@ fn card(item: &Item) -> Element<'_, Message> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn init() -> (State, Task<Message>) {
|
fn init() -> (State, Task<Message>) {
|
||||||
// Create a default config for initial state
|
(State::new(), Task::done(Message::Refresh))
|
||||||
|
|
||||||
// let default_config = api::JellyfinConfig {
|
|
||||||
// server_url: "http://localhost:8096".parse().expect("Valid URL"),
|
|
||||||
// device_id: "jello-iced".to_string(),
|
|
||||||
// device_name: "Jello Iced".to_string(),
|
|
||||||
// client_name: "Jello".to_string(),
|
|
||||||
// version: "0.1.0".to_string(),
|
|
||||||
// };
|
|
||||||
// let default_client = api::JellyfinClient::new_with_config(default_config);
|
|
||||||
|
|
||||||
(
|
|
||||||
State::new(),
|
|
||||||
Task::perform(
|
|
||||||
async move {
|
|
||||||
let config_str = std::fs::read_to_string("config.toml")
|
|
||||||
.map_err(|e| api::JellyfinApiError::IoError(e))?;
|
|
||||||
let config: api::JellyfinConfig = toml::from_str(&config_str).map_err(|e| {
|
|
||||||
api::JellyfinApiError::IoError(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::InvalidData,
|
|
||||||
e,
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Try to load cached token and authenticate
|
|
||||||
match std::fs::read_to_string(".session") {
|
|
||||||
Ok(token) => {
|
|
||||||
let client = api::JellyfinClient::pre_authenticated(token.trim(), config)?;
|
|
||||||
Ok((client, true))
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// No cached token, create unauthenticated client
|
|
||||||
let client = api::JellyfinClient::new_with_config(config);
|
|
||||||
Ok((client, false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|result: Result<_, api::JellyfinApiError>| match result {
|
|
||||||
// Ok((client, is_authenticated)) => Message::LoadedClient(client, is_authenticated),
|
|
||||||
Err(e) => Message::Error(format!("Initialization failed: {}", e)),
|
|
||||||
_ => Message::Error("Login Unimplemented".to_string()),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.chain(Task::done(Message::Refresh)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui() -> iced::Result {
|
pub fn ui() -> iced::Result {
|
||||||
|
|||||||
Reference in New Issue
Block a user