feat(immich): add Immich self-hosted import support
Adds an ImmichClient for Android to connect to a self-hosted Immich server, fetch albums and assets, and import selected images locally. API keys are encrypted with Android Keystore and stored securely. The plugin now exposes connect, disconnect and import actions and is documented for Immich import usage. - Encrypted Immich API keys with Android Keystore - Exposed connect, disconnect and import actions in the plugin - Updated docs and metadata to reflect Immich import flow
This commit is contained in:
@@ -69,3 +69,45 @@ pub(crate) async fn set_interval<R: Runtime>(app: AppHandle<R>, minutes: usize)
|
||||
pub(crate) async fn next_wallpaper<R: Runtime>(app: AppHandle<R>) -> Result<WallpaperState> {
|
||||
app.wallpaper().next_wallpaper()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn get_immich_connection<R: Runtime>(app: AppHandle<R>) -> Result<ImmichConnection> {
|
||||
app.wallpaper().get_immich_connection()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn connect_immich<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
server_url: String,
|
||||
api_key: String,
|
||||
) -> Result<ImmichConnection> {
|
||||
app.wallpaper().connect_immich(ImmichConnectRequest { server_url, api_key })
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn disconnect_immich<R: Runtime>(app: AppHandle<R>) -> Result<ImmichConnection> {
|
||||
app.wallpaper().disconnect_immich()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn get_immich_albums<R: Runtime>(app: AppHandle<R>) -> Result<ImmichAlbumsResponse> {
|
||||
app.wallpaper().get_immich_albums()
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn get_immich_assets<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
album_id: Option<String>,
|
||||
page: usize,
|
||||
size: usize,
|
||||
) -> Result<ImmichAssetsPage> {
|
||||
app.wallpaper().get_immich_assets(ImmichAssetsRequest { album_id, page, size })
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn import_immich_assets<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
ids: Vec<String>,
|
||||
) -> Result<WallpaperState> {
|
||||
app.wallpaper().import_immich_assets(ImmichImportRequest { ids })
|
||||
}
|
||||
|
||||
@@ -100,4 +100,47 @@ impl<R: Runtime> Wallpaper<R> {
|
||||
state.current_index = 1;
|
||||
Ok(state)
|
||||
}
|
||||
pub fn get_immich_connection(&self) -> crate::Result<ImmichConnection> {
|
||||
Ok(ImmichConnection::default())
|
||||
}
|
||||
pub fn connect_immich(&self, payload: ImmichConnectRequest) -> crate::Result<ImmichConnection> {
|
||||
Ok(ImmichConnection {
|
||||
configured: true,
|
||||
server_url: payload.server_url,
|
||||
user_name: "Demo User".into(),
|
||||
})
|
||||
}
|
||||
pub fn disconnect_immich(&self) -> crate::Result<ImmichConnection> {
|
||||
Ok(ImmichConnection::default())
|
||||
}
|
||||
pub fn get_immich_albums(&self) -> crate::Result<ImmichAlbumsResponse> {
|
||||
Ok(ImmichAlbumsResponse {
|
||||
albums: vec![ImmichAlbum {
|
||||
id: "demo-album".into(),
|
||||
name: "Nature".into(),
|
||||
asset_count: 3,
|
||||
thumbnail_url: "/wallpapers/alpine.png".into(),
|
||||
}],
|
||||
})
|
||||
}
|
||||
pub fn get_immich_assets(&self, payload: ImmichAssetsRequest) -> crate::Result<ImmichAssetsPage> {
|
||||
let urls = Self::demo().image_urls;
|
||||
Ok(ImmichAssetsPage {
|
||||
items: urls
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(index, thumbnail_url)| ImmichAsset {
|
||||
id: format!("immich-demo-{index}"),
|
||||
file_name: format!("wallpaper-{}.jpg", index + 1),
|
||||
thumbnail_url,
|
||||
taken_at: "2026-08-21T12:00:00Z".into(),
|
||||
})
|
||||
.collect(),
|
||||
page: payload.page,
|
||||
has_more: false,
|
||||
})
|
||||
}
|
||||
pub fn import_immich_assets(&self, _payload: ImmichImportRequest) -> crate::Result<WallpaperState> {
|
||||
Ok(Self::demo())
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -45,7 +45,13 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
commands::set_image_crop,
|
||||
commands::set_setting,
|
||||
commands::set_interval,
|
||||
commands::next_wallpaper
|
||||
commands::next_wallpaper,
|
||||
commands::get_immich_connection,
|
||||
commands::connect_immich,
|
||||
commands::disconnect_immich,
|
||||
commands::get_immich_albums,
|
||||
commands::get_immich_assets,
|
||||
commands::import_immich_assets
|
||||
])
|
||||
.setup(|app, api| {
|
||||
#[cfg(mobile)]
|
||||
|
||||
@@ -70,4 +70,22 @@ impl<R: Runtime> Wallpaper<R> {
|
||||
.run_mobile_plugin("nextWallpaper", ())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
pub fn get_immich_connection(&self) -> crate::Result<ImmichConnection> {
|
||||
self.0.run_mobile_plugin("getImmichConnection", ()).map_err(Into::into)
|
||||
}
|
||||
pub fn connect_immich(&self, payload: ImmichConnectRequest) -> crate::Result<ImmichConnection> {
|
||||
self.0.run_mobile_plugin("connectImmich", payload).map_err(Into::into)
|
||||
}
|
||||
pub fn disconnect_immich(&self) -> crate::Result<ImmichConnection> {
|
||||
self.0.run_mobile_plugin("disconnectImmich", ()).map_err(Into::into)
|
||||
}
|
||||
pub fn get_immich_albums(&self) -> crate::Result<ImmichAlbumsResponse> {
|
||||
self.0.run_mobile_plugin("getImmichAlbums", ()).map_err(Into::into)
|
||||
}
|
||||
pub fn get_immich_assets(&self, payload: ImmichAssetsRequest) -> crate::Result<ImmichAssetsPage> {
|
||||
self.0.run_mobile_plugin("getImmichAssets", payload).map_err(Into::into)
|
||||
}
|
||||
pub fn import_immich_assets(&self, payload: ImmichImportRequest) -> crate::Result<WallpaperState> {
|
||||
self.0.run_mobile_plugin("importImmichAssets", payload).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,3 +78,64 @@ pub struct GalleryPage {
|
||||
pub total: usize,
|
||||
pub items: Vec<GalleryImage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImmichConnectRequest {
|
||||
pub server_url: String,
|
||||
pub api_key: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImmichConnection {
|
||||
pub configured: bool,
|
||||
pub server_url: String,
|
||||
pub user_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImmichAlbum {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub asset_count: usize,
|
||||
pub thumbnail_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImmichAlbumsResponse {
|
||||
pub albums: Vec<ImmichAlbum>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImmichAssetsRequest {
|
||||
pub album_id: Option<String>,
|
||||
pub page: usize,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImmichAsset {
|
||||
pub id: String,
|
||||
pub file_name: String,
|
||||
pub thumbnail_url: String,
|
||||
pub taken_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImmichAssetsPage {
|
||||
pub items: Vec<ImmichAsset>,
|
||||
pub page: usize,
|
||||
pub has_more: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ImmichImportRequest {
|
||||
pub ids: Vec<String>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user