Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Stable Diffusion AI is an easy-to-use app that lets you quickly generate images

- Can use server environment powered by [AI Horde](https://stablehorde.net/) (a crowdsourced distributed cluster of Stable Diffusion workers)
- Can use server environment powered by [Stable-Diffusion-WebUI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) (AUTOMATIC1111)
- Can use server environment powered by [SwarmUI](https://github.com/mcmonkeyprojects/SwarmUI)
- Can use server envitonment powered by [Hugging Face Inference API](https://huggingface.co/docs/api-inference/quicktour).
- Can use server environment powered by [OpenAI](https://platform.openai.com/docs/api-reference/images) (DALL-E-2, DALL-E-3).
- Can use server environment powered by [Stability AI](https://platform.stability.ai/).
Expand Down Expand Up @@ -70,31 +71,39 @@ You can have it running either on your own hardware with modern GPU from Nvidia

If for some reason you have no ability to run your server instance, you can toggle the **Demo mode** switch on server setup page: it will allow you to test the app and get familiar with it, but it will return some mock images instead of AI-generated ones.

### Option 2: Use AI Horde
### Option 2: Use your own SwarmUI instance

This requires you to have the SwarmUI that is running in server mode.

You can have it running either on your own hardware with modern GPU from Nvidia or AMD, or running it using Google Colab.

Please refer to the [SwarmUI documentation](https://github.com/mcmonkeyprojects/SwarmUI?tab=readme-ov-file#swarmui) for installation instructions.

### Option 3: Use AI Horde

[AI Horde](https://stablehorde.net/) is a crowdsourced distributed cluster of Image generation workers and text generation workers.

AI Horde requires to use API KEY, this mobile app alows to use either default API KEY (which is "0000000000"), or type your own. You can sign up and get your own AI Horde API KEY [here](https://stablehorde.net/register).

### Option 3: Hugging Face Inference
### Option 4: Hugging Face Inference

[Hugging Face Inference API](https://huggingface.co/docs/api-inference/index) allows to test and evaluate, over 150,000 publicly accessible machine learning models, or your own private models, via simple HTTP requests, with fast inference hosted on Hugging Face shared infrastructure. This service is free, but is rate-limited.

Hugging Face Inference requires to use API KEY, which can be created in [Hugging Face account settings](https://huggingface.co/settings/tokens).

### Option 4: OpenAI
### Option 5: OpenAI

OpenAI provides a service for text to image generation using [DALLE-2](https://openai.com/dall-e-2) or [DALLE-3](https://openai.com/dall-e-3) models. This service is paid.

OpenAI requires to use API KEY, which can be created in [OpenAI API Key settings](https://platform.openai.com/api-keys).

### Option 5: StabilityAI
### Option 6: StabilityAI

[StabilityAI](https://platform.stability.ai/) is the image generation service provided by DreamStudio.

StabilityAI requires to use API KEY, which can be created in [API Keys page](https://platform.stability.ai/account/keys).

### Option 6: Local Diffusion (Beta)
### Option 7: Local Diffusion (Beta)

Only **txt2img** mode is supported.

Expand Down
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ android {
buildConfigField "String", "DONATE_URL", "\"https://www.buymeacoffee.com/shifthackz\""
buildConfigField "String", "GITHUB_SOURCE_URL", "\"https://github.com/ShiftHackZ/Stable-Diffusion-Android\""
buildConfigField "String", "SETUP_INSTRUCTIONS_URL", "\"https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki\""
buildConfigField "String", "SWARM_UI_INFO_URL", "\"https://github.com/mcmonkeyprojects/SwarmUI/tree/master/docs\""

resourceConfigurations = ["en", "ru", "uk", "tr", "zh"]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ val providersModule = module {
override val donateUrl: String = BuildConfig.DONATE_URL
override val gitHubSourceUrl: String = BuildConfig.GITHUB_SOURCE_URL
override val setupInstructionsUrl: String = BuildConfig.SETUP_INSTRUCTIONS_URL
override val swarmUiInfoUrl: String = BuildConfig.SWARM_UI_INFO_URL
override val demoModeUrl: String = BuildConfig.DEMO_MODE_API_URL
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ interface LinksProvider {
val donateUrl: String
val gitHubSourceUrl: String
val setupInstructionsUrl: String
val swarmUiInfoUrl: String
val demoModeUrl: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.shifthackz.aisdv1.core.common.model

import java.io.Serializable

data class Hexagonal<out A, out B, out C, out D, out E, out F>(
val first: A,
val second: B,
val third: C,
val fourth: D,
val fifth: E,
val sixth: F,
) : Serializable {

override fun toString(): String = "($first, $second, $third, $fourth, $fifth, $sixth)"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.shifthackz.aisdv1.core.common.model

import java.io.Serializable


data class Quadruple<out A, out B, out C, out D>(
val first: A,
val second: B,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.shifthackz.aisdv1.core.imageprocessing

import com.shifthackz.aisdv1.core.common.log.errorLog
import com.shifthackz.aisdv1.core.imageprocessing.Base64EncodingConverter.Input
import com.shifthackz.aisdv1.core.imageprocessing.Base64EncodingConverter.Output
import com.shifthackz.aisdv1.core.imageprocessing.contract.RxImageProcessor
import com.shifthackz.aisdv1.core.imageprocessing.utils.base64DefaultToNoWrap
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.core.Single

private typealias Base64EncodingProcessor = RxImageProcessor<Input, Output>

class Base64EncodingConverter(
private val processingScheduler: Scheduler,
) : Base64EncodingProcessor {

override fun invoke(input: Input): Single<Output> = Single
.create { emitter ->
convert(input).fold(
onSuccess = emitter::onSuccess,
onFailure = emitter::onError,
)
}
.onErrorReturn { t ->
errorLog(t)
Output(input.base64)
}
.subscribeOn(processingScheduler)

private fun convert(input: Input): Result<Output> = runCatching {
Output(base64DefaultToNoWrap(input.base64))
}

data class Input(val base64: String)
data class Output(val base64: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.shifthackz.aisdv1.core.imageprocessing.di

import android.graphics.BitmapFactory
import com.shifthackz.aisdv1.core.common.schedulers.SchedulersProvider
import com.shifthackz.aisdv1.core.imageprocessing.Base64EncodingConverter
import com.shifthackz.aisdv1.core.imageprocessing.Base64ToBitmapConverter
import com.shifthackz.aisdv1.core.imageprocessing.BitmapToBase64Converter
import com.shifthackz.aisdv1.core.imageprocessing.R
Expand All @@ -20,4 +21,8 @@ val imageProcessingModule = module {
factory {
BitmapToBase64Converter(get<SchedulersProvider>().computation)
}

factory {
Base64EncodingConverter(get<SchedulersProvider>().computation)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ fun bitmapToBase64(bitmap: Bitmap): String {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
return Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT)
}

fun base64DefaultToNoWrap(base64Default: String): String {
val byteArray = Base64.decode(base64Default, Base64.DEFAULT)
return Base64.encodeToString(byteArray, Base64.NO_WRAP)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,27 @@ package com.shifthackz.aisdv1.data.di
import com.shifthackz.aisdv1.data.gateway.DatabaseClearGatewayImpl
import com.shifthackz.aisdv1.data.gateway.mediastore.MediaStoreGatewayFactory
import com.shifthackz.aisdv1.data.local.DownloadableModelLocalDataSource
import com.shifthackz.aisdv1.data.local.EmbeddingsLocalDataSource
import com.shifthackz.aisdv1.data.local.GenerationResultLocalDataSource
import com.shifthackz.aisdv1.data.local.HuggingFaceModelsLocalDataSource
import com.shifthackz.aisdv1.data.local.LorasLocalDataSource
import com.shifthackz.aisdv1.data.local.ServerConfigurationLocalDataSource
import com.shifthackz.aisdv1.data.local.StabilityAiCreditsLocalDataSource
import com.shifthackz.aisdv1.data.local.StableDiffusionEmbeddingsLocalDataSource
import com.shifthackz.aisdv1.data.local.StableDiffusionHyperNetworksLocalDataSource
import com.shifthackz.aisdv1.data.local.StableDiffusionLorasLocalDataSource
import com.shifthackz.aisdv1.data.local.StableDiffusionModelsLocalDataSource
import com.shifthackz.aisdv1.data.local.StableDiffusionSamplersLocalDataSource
import com.shifthackz.aisdv1.data.local.SwarmUiModelsLocalDataSource
import com.shifthackz.aisdv1.domain.datasource.DownloadableModelDataSource
import com.shifthackz.aisdv1.domain.datasource.EmbeddingsDataSource
import com.shifthackz.aisdv1.domain.datasource.GenerationResultDataSource
import com.shifthackz.aisdv1.domain.datasource.HuggingFaceModelsDataSource
import com.shifthackz.aisdv1.domain.datasource.LorasDataSource
import com.shifthackz.aisdv1.domain.datasource.ServerConfigurationDataSource
import com.shifthackz.aisdv1.domain.datasource.StabilityAiCreditsDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionEmbeddingsDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionHyperNetworksDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionLorasDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionModelsDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionSamplersDataSource
import com.shifthackz.aisdv1.domain.datasource.SwarmUiModelsDataSource
import com.shifthackz.aisdv1.domain.gateway.DatabaseClearGateway
import org.koin.android.ext.koin.androidContext
import org.koin.core.module.dsl.factoryOf
Expand All @@ -35,9 +37,10 @@ val localDataSourceModule = module {
single<StabilityAiCreditsDataSource.Local> { StabilityAiCreditsLocalDataSource() }
factoryOf(::StableDiffusionModelsLocalDataSource) bind StableDiffusionModelsDataSource.Local::class
factoryOf(::StableDiffusionSamplersLocalDataSource) bind StableDiffusionSamplersDataSource.Local::class
factoryOf(::StableDiffusionLorasLocalDataSource) bind StableDiffusionLorasDataSource.Local::class
factoryOf(::LorasLocalDataSource) bind LorasDataSource.Local::class
factoryOf(::StableDiffusionHyperNetworksLocalDataSource) bind StableDiffusionHyperNetworksDataSource.Local::class
factoryOf(::StableDiffusionEmbeddingsLocalDataSource) bind StableDiffusionEmbeddingsDataSource.Local::class
factoryOf(::EmbeddingsLocalDataSource) bind EmbeddingsDataSource.Local::class
factoryOf(::SwarmUiModelsLocalDataSource) bind SwarmUiModelsDataSource.Local::class
factoryOf(::ServerConfigurationLocalDataSource) bind ServerConfigurationDataSource.Local::class
factoryOf(::GenerationResultLocalDataSource) bind GenerationResultDataSource.Local::class
factoryOf(::DownloadableModelLocalDataSource) bind DownloadableModelDataSource.Local::class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,30 @@ import com.shifthackz.aisdv1.data.remote.StableDiffusionHyperNetworksRemoteDataS
import com.shifthackz.aisdv1.data.remote.StableDiffusionLorasRemoteDataSource
import com.shifthackz.aisdv1.data.remote.StableDiffusionModelsRemoteDataSource
import com.shifthackz.aisdv1.data.remote.StableDiffusionSamplersRemoteDataSource
import com.shifthackz.aisdv1.data.remote.SwarmUiEmbeddingsRemoteDataSource
import com.shifthackz.aisdv1.data.remote.SwarmUiGenerationRemoteDataSource
import com.shifthackz.aisdv1.data.remote.SwarmUiLorasRemoteDataSource
import com.shifthackz.aisdv1.data.remote.SwarmUiModelsRemoteDataSource
import com.shifthackz.aisdv1.data.remote.SwarmUiSessionDataSourceImpl
import com.shifthackz.aisdv1.domain.datasource.DownloadableModelDataSource
import com.shifthackz.aisdv1.domain.datasource.EmbeddingsDataSource
import com.shifthackz.aisdv1.domain.datasource.HordeGenerationDataSource
import com.shifthackz.aisdv1.domain.datasource.HuggingFaceGenerationDataSource
import com.shifthackz.aisdv1.domain.datasource.HuggingFaceModelsDataSource
import com.shifthackz.aisdv1.domain.datasource.LorasDataSource
import com.shifthackz.aisdv1.domain.datasource.OpenAiGenerationDataSource
import com.shifthackz.aisdv1.domain.datasource.RandomImageDataSource
import com.shifthackz.aisdv1.domain.datasource.ServerConfigurationDataSource
import com.shifthackz.aisdv1.domain.datasource.StabilityAiCreditsDataSource
import com.shifthackz.aisdv1.domain.datasource.StabilityAiEnginesDataSource
import com.shifthackz.aisdv1.domain.datasource.StabilityAiGenerationDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionEmbeddingsDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionGenerationDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionHyperNetworksDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionLorasDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionModelsDataSource
import com.shifthackz.aisdv1.domain.datasource.StableDiffusionSamplersDataSource
import com.shifthackz.aisdv1.domain.datasource.SwarmUiGenerationDataSource
import com.shifthackz.aisdv1.domain.datasource.SwarmUiModelsDataSource
import com.shifthackz.aisdv1.domain.datasource.SwarmUiSessionDataSource
import com.shifthackz.aisdv1.domain.entity.ServerSource
import com.shifthackz.aisdv1.domain.gateway.ServerConnectivityGateway
import com.shifthackz.aisdv1.domain.preference.PreferenceManager
Expand All @@ -51,8 +59,12 @@ val remoteDataSourceModule = module {
single {
ServerUrlProvider { endpoint ->
val prefs = get<PreferenceManager>()
Single
.fromCallable(prefs::serverUrl)
val chain = if (prefs.source == ServerSource.SWARM_UI) {
Single.fromCallable(prefs::swarmUiServerUrl)
} else {
Single.fromCallable(prefs::automatic1111ServerUrl)
}
chain
.map(String::fixUrlSlashes)
.map { baseUrl -> "$baseUrl/$endpoint" }
}
Expand All @@ -61,12 +73,17 @@ val remoteDataSourceModule = module {
factoryOf(::HordeGenerationRemoteDataSource) bind HordeGenerationDataSource.Remote::class
factoryOf(::HuggingFaceGenerationRemoteDataSource) bind HuggingFaceGenerationDataSource.Remote::class
factoryOf(::OpenAiGenerationRemoteDataSource) bind OpenAiGenerationDataSource.Remote::class
factoryOf(::SwarmUiSessionDataSourceImpl) bind SwarmUiSessionDataSource::class
factoryOf(::SwarmUiGenerationRemoteDataSource) bind SwarmUiGenerationDataSource.Remote::class
factoryOf(::SwarmUiModelsRemoteDataSource) bind SwarmUiModelsDataSource.Remote::class
factoryOf(::SwarmUiLorasRemoteDataSource) bind LorasDataSource.Remote.SwarmUi::class
factoryOf(::SwarmUiEmbeddingsRemoteDataSource) bind EmbeddingsDataSource.Remote.SwarmUi::class
factoryOf(::StableDiffusionGenerationRemoteDataSource) bind StableDiffusionGenerationDataSource.Remote::class
factoryOf(::StableDiffusionSamplersRemoteDataSource) bind StableDiffusionSamplersDataSource.Remote::class
factoryOf(::StableDiffusionModelsRemoteDataSource) bind StableDiffusionModelsDataSource.Remote::class
factoryOf(::StableDiffusionLorasRemoteDataSource) bind StableDiffusionLorasDataSource.Remote::class
factoryOf(::StableDiffusionLorasRemoteDataSource) bind LorasDataSource.Remote.Automatic1111::class
factoryOf(::StableDiffusionHyperNetworksRemoteDataSource) bind StableDiffusionHyperNetworksDataSource.Remote::class
factoryOf(::StableDiffusionEmbeddingsRemoteDataSource) bind StableDiffusionEmbeddingsDataSource.Remote::class
factoryOf(::StableDiffusionEmbeddingsRemoteDataSource) bind EmbeddingsDataSource.Remote.Automatic1111::class
factoryOf(::ServerConfigurationRemoteDataSource) bind ServerConfigurationDataSource.Remote::class
factoryOf(::RandomImageRemoteDataSource) bind RandomImageDataSource.Remote::class
factoryOf(::DownloadableModelRemoteDataSource) bind DownloadableModelDataSource.Remote::class
Expand All @@ -78,7 +95,7 @@ val remoteDataSourceModule = module {
factory<ServerConnectivityGateway> {
val lambda: () -> Boolean = {
val prefs = get<PreferenceManager>()
prefs.source == ServerSource.AUTOMATIC1111
prefs.source == ServerSource.AUTOMATIC1111 || prefs.source == ServerSource.SWARM_UI
}
val monitor = get<ConnectivityMonitor> { parametersOf(lambda) }
ServerConnectivityGatewayImpl(monitor, get())
Expand Down
Loading