mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-21 01:12:40 -04:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de0776009b | ||
|
|
11f8885bf6 | ||
|
|
0e2926b179 | ||
|
|
671e02edcb | ||
|
|
10f8649089 | ||
|
|
3aeea6d2b2 | ||
|
|
abccceb5bd | ||
|
|
9add7ee1cd |
@@ -1,14 +1,13 @@
|
||||
SERVER_ENVIRONMENT=development
|
||||
|
||||
DATABASE_URL=mysql://storyteller:password@localhost/storyteller
|
||||
#DATABASE_URL=sqlite:/tmp/tasks.sqlite
|
||||
#DATABASE_URL=mysql://root:root@localhost/storyteller
|
||||
#DATABASE_URL=mysql://root@localhost/storyteller
|
||||
|
||||
# Diesel-cli reads this for the migration directory to use.
|
||||
# We've renamed it so the migrations are sorted near the top of the project directory tree
|
||||
# (since they kind of serve as documentation)
|
||||
MIGRATION_DIRECTORY=_database/sql/migrations/
|
||||
MIGRATION_DIRECTORY=_sql/migrations/
|
||||
|
||||
# SQLX normally requires a live database to reflect upon at build time.
|
||||
# This setting turns that on and requires a `sql-data.json` cache file instead.
|
||||
|
||||
@@ -200,10 +200,10 @@ jobs:
|
||||
|
||||
# Use npx to ensure we use the local nx version
|
||||
echo "Building with npx nx..."
|
||||
npx nx run artcraft:build
|
||||
npx nx run editor3d:build
|
||||
|
||||
echo "Checking build output..."
|
||||
ls -la apps/artcraft/dist/ | head -10
|
||||
ls -la apps/editor3d/dist/ | head -10
|
||||
|
||||
# Update Tauri config to skip the build (since we already built)
|
||||
cd ${{ github.workspace }}/crates/desktop/artcraft
|
||||
|
||||
@@ -75,10 +75,10 @@ jobs:
|
||||
|
||||
# Use npx to ensure we use the local nx version
|
||||
echo "Building with npx nx..."
|
||||
npx nx run artcraft:build
|
||||
npx nx run editor3d:build
|
||||
|
||||
echo "Checking build output..."
|
||||
dir apps/artcraft/dist/ | Select-Object -First 10
|
||||
dir apps/editor3d/dist/ | Select-Object -First 10
|
||||
|
||||
# Update Tauri config to skip the build (since we already built)
|
||||
cd ${{ github.workspace }}/crates/desktop/artcraft
|
||||
@@ -104,4 +104,4 @@ jobs:
|
||||
releaseBody: "Windows build - See the assets to download this version and install."
|
||||
releaseDraft: true
|
||||
prerelease: false
|
||||
args: ${{ matrix.args }}
|
||||
args: ${{ matrix.args }}
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
username: ${{ github.actor }} # username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
image_name: storytold/storyteller-rust
|
||||
dockerfile: ./build/service_cpu.Dockerfile
|
||||
dockerfile: service_cpu.Dockerfile
|
||||
# NB: The `git_push_tag: true` option does not work as expected, so we put the sha in `image_tag`:
|
||||
# https://github.com/whoan/docker-build-with-cache-action/issues/72
|
||||
image_tag: latest,${{ github.sha }},${{ steps.vars.outputs.sha_short }}
|
||||
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
username: ${{ github.actor }} # username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
image_name: storytold/storyteller-rust-gpu
|
||||
dockerfile: ./build/service_gpu.Dockerfile
|
||||
dockerfile: service_gpu.Dockerfile
|
||||
# NB: The `git_push_tag: true` option does not work as expected, so we put the sha in `image_tag`:
|
||||
# https://github.com/whoan/docker-build-with-cache-action/issues/72
|
||||
image_tag: latest,${{ github.sha }},${{ steps.vars.outputs.sha_short }}
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
username: ${{ github.actor }} # username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
image_name: storytold/storyteller-rust-speedup-exp
|
||||
dockerfile: ./build/service_cpu.Dockerfile
|
||||
dockerfile: service_cpu.Dockerfile
|
||||
# NB: The `git_push_tag: true` option does not work as expected, so we put the sha in `image_tag`:
|
||||
# https://github.com/whoan/docker-build-with-cache-action/issues/72
|
||||
image_tag: speedup-${{ steps.vars.outputs.sha_short }}
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ crates/lib/sqlite_queries/target/
|
||||
node_modules/
|
||||
|
||||
# Frontend build outputs
|
||||
frontend/apps/artcraft/bundle-report.html
|
||||
frontend/apps/editor3d/bundle-report.html
|
||||
|
||||
# Vim noise
|
||||
*.swo
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nSELECT\n token as `token: tokens::tokens::user_subscriptions::UserSubscriptionToken`,\n user_token as `user_token: tokens::tokens::users::UserToken`,\n subscription_namespace as `subscription_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n subscription_product_slug,\n maybe_stripe_customer_id,\n maybe_stripe_product_id,\n maybe_stripe_subscription_id,\n maybe_stripe_subscription_status as `maybe_stripe_subscription_status: enums::common::stripe_subscription_status::StripeSubscriptionStatus`,\n maybe_stripe_invoice_is_paid,\n subscription_start_at,\n subscription_expires_at,\n current_billing_period_end_at,\n maybe_cancel_at,\n maybe_canceled_at\n\nFROM user_subscriptions\n\nWHERE\n user_token = ?\n AND subscription_namespace = ?\n AND maybe_stripe_customer_id IS NOT NULL\n AND maybe_stripe_subscription_id IS NOT NULL\n AND maybe_stripe_subscription_status IS NOT NULL\n AND maybe_stripe_subscription_status NOT IN ('canceled', 'incomplete_expired')\n AND subscription_expires_at > CURRENT_TIMESTAMP\n ORDER BY id ASC\n LIMIT 1\n ",
|
||||
"query": "\nSELECT\n token as `token: tokens::tokens::user_subscriptions::UserSubscriptionToken`,\n user_token as `user_token: tokens::tokens::users::UserToken`,\n subscription_namespace as `subscription_namespace: enums::common::payments_namespace::PaymentsNamespace`,\n subscription_product_slug,\n maybe_stripe_customer_id,\n maybe_stripe_product_id,\n maybe_stripe_subscription_id,\n maybe_stripe_subscription_status,\n maybe_stripe_invoice_is_paid,\n subscription_start_at,\n subscription_expires_at,\n current_billing_period_end_at,\n maybe_cancel_at,\n maybe_canceled_at\n\nFROM user_subscriptions\n\nWHERE\n user_token = ?\n AND subscription_namespace = ?\n AND maybe_stripe_customer_id IS NOT NULL\n AND maybe_stripe_subscription_id IS NOT NULL\n AND maybe_stripe_subscription_status IS NOT NULL\n AND maybe_stripe_subscription_status NOT IN ('canceled', 'incomplete_expired')\n AND subscription_expires_at > CURRENT_TIMESTAMP\n ORDER BY id ASC\n LIMIT 1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -75,7 +75,7 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "maybe_stripe_subscription_status: enums::common::stripe_subscription_status::StripeSubscriptionStatus",
|
||||
"name": "maybe_stripe_subscription_status",
|
||||
"type_info": {
|
||||
"type": "VarString",
|
||||
"flags": "MULTIPLE_KEY | BINARY",
|
||||
@@ -164,5 +164,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "dbf85f29c614d243a50e4259218f21c446c6ab17de35c8a8da08fe02822e8f58"
|
||||
"hash": "1e08676857e999a58d3c85474c2a2acec38a413e53ca4207aade690e0bfffe25"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE tasks\n SET\n task_status = ?,\n on_complete_batch_token = ?,\n on_complete_primary_media_file_token = ?,\n on_complete_primary_media_file_class = ?,\n on_complete_primary_media_file_cdn_url = ?,\n on_complete_primary_media_file_thumbnail_url_template = ?\n WHERE\n provider = ?\n AND provider_job_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 8
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "1e3758851fc8451670f86a9432009b73bdb81de4a08dcaa96a86afb5f9be7a68"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE tasks\n SET task_status = \"cancelled_by_user\"\n WHERE task_status NOT IN (\n \"complete_success\",\n \"complete_failure\",\n \"dead\",\n \"cancelled_by_user\",\n \"cancelled_by_provider\",\n \"cancelled_by_us\"\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "30272128d83543d9fbfca930b7cdd4af30adfb4d90f2de044463a111c6340495"
|
||||
}
|
||||
-68
@@ -1,68 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n SELECT\n id,\n task_status,\n task_type,\n model_type,\n provider,\n provider_job_id,\n frontend_caller,\n frontend_subscriber_id,\n frontend_subscriber_payload\n FROM tasks\n WHERE\n provider = ?\n AND provider_job_id = ?\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "task_status",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "task_type",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "model_type",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "provider",
|
||||
"ordinal": 4,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "provider_job_id",
|
||||
"ordinal": 5,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "frontend_caller",
|
||||
"ordinal": 6,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "frontend_subscriber_id",
|
||||
"ordinal": 7,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "frontend_subscriber_payload",
|
||||
"ordinal": 8,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "38ff2df237b16f077865711dd81261de1b5fa05a9787679ce9ccd4ac576b1069"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO analytics_app_sessions\nSET\n session_token = ?,\n app_namespace = ?,\n user_token = ?,\n app_version = ?,\n os_platform = ?,\n os_version = ?,\n session_duration_seconds = ?,\n ip_address = ?,\n measurement_count = measurement_count + 1,\n total_generation_count = ?,\n image_generation_count = ?,\n video_generation_count = ?,\n object_generation_count = ?,\n text_to_image_count = ?,\n image_to_image_count = ?,\n text_to_video_count = ?,\n image_to_video_count = ?,\n text_to_object_count = ?,\n image_to_object_count = ?,\n image_page_prompt_count = ?,\n video_page_prompt_count = ?,\n edit_page_prompt_count = ?,\n stage_page_prompt_count = ?,\n object_page_prompt_count = ?,\n other_page_prompt_count = ?,\n first_active_at = NOW(),\n last_active_at = NOW()\nON DUPLICATE KEY UPDATE\n app_namespace = ?,\n user_token = ?,\n app_version = ?,\n os_platform = ?,\n os_version = ?,\n session_duration_seconds = ?,\n ip_address = ?,\n measurement_count = measurement_count + 1,\n total_generation_count = ?,\n image_generation_count = ?,\n video_generation_count = ?,\n object_generation_count = ?,\n text_to_image_count = ?,\n image_to_image_count = ?,\n text_to_video_count = ?,\n image_to_video_count = ?,\n text_to_object_count = ?,\n image_to_object_count = ?,\n image_page_prompt_count = ?,\n video_page_prompt_count = ?,\n edit_page_prompt_count = ?,\n stage_page_prompt_count = ?,\n object_page_prompt_count = ?,\n other_page_prompt_count = ?,\n last_active_at = NOW()\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 47
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4b3d363a4f5a088d2040cedd010e66964ea4d5e3b3a94aaad79bc122ea165715"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "SQLite",
|
||||
"query": "\n UPDATE tasks\n SET is_dismissed_by_user = 1\n WHERE is_dismissed_by_user != 1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 0
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "f83018ec5493982659a56576296914f937bb4e75db3a522eb4bda486d5caa402"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "MySQL",
|
||||
"query": "\nINSERT INTO analytics_app_sessions\nSET\n session_token = ?,\n app_namespace = ?,\n user_token = ?,\n app_version = ?,\n os_platform = ?,\n os_version = ?,\n session_duration_seconds = ?,\n ip_address = ?,\n measurement_count = measurement_count + 1,\n first_active_at = NOW(),\n last_active_at = NOW()\nON DUPLICATE KEY UPDATE\n app_namespace = ?,\n user_token = ?,\n app_version = ?,\n os_platform = ?,\n os_version = ?,\n session_duration_seconds = ?,\n ip_address = ?,\n measurement_count = measurement_count + 1,\n last_active_at = NOW()\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Right": 15
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "ff39576a5ec6a23c67f6f61e7eba8392e70b05c42b540bea260a9a4cdc478a5b"
|
||||
}
|
||||
Vendored
+2
-3
@@ -2,9 +2,8 @@
|
||||
"files.exclude": {
|
||||
"**/*~": true,
|
||||
".sqlx/**": true,
|
||||
"_database/**": true,
|
||||
"_database/elasticsearch/**": true,
|
||||
"_database/sql/**": true,
|
||||
"_elasticsearch/**": true,
|
||||
"_sql/**": true,
|
||||
"frontend_old/**": true,
|
||||
"includes/**": true,
|
||||
"target/**": true,
|
||||
|
||||
Generated
+703
-965
File diff suppressed because it is too large
Load Diff
+5
-21
@@ -12,13 +12,11 @@ members = [
|
||||
"crates/api_clients/fal_client",
|
||||
"crates/api_clients/google_drive_common",
|
||||
"crates/api_clients/google_sign_in",
|
||||
"crates/api_clients/grok_client",
|
||||
"crates/api_clients/midjourney_client",
|
||||
"crates/api_clients/openai_sora_client",
|
||||
"crates/api_clients/openart_client",
|
||||
"crates/api_clients/runwayml_client",
|
||||
"crates/api_clients/thumbnail_generator",
|
||||
"crates/api_clients/world_labs_client",
|
||||
|
||||
# CLI tools for operations, etc.
|
||||
"crates/cli/devenv/dev-database-seed",
|
||||
@@ -45,7 +43,6 @@ members = [
|
||||
"crates/lib/caching/memory_store",
|
||||
"crates/lib/caching/redis_caching",
|
||||
"crates/lib/cloudflare_errors",
|
||||
"crates/lib/cloudflare_mitigation",
|
||||
"crates/lib/concurrency",
|
||||
"crates/lib/cookie_store",
|
||||
"crates/lib/cookies",
|
||||
@@ -68,7 +65,6 @@ members = [
|
||||
"crates/lib/files/media",
|
||||
"crates/lib/files/mimetypes",
|
||||
"crates/lib/files/videos",
|
||||
"crates/lib/http_headers",
|
||||
"crates/lib/idempotency",
|
||||
"crates/lib/image_algorithms",
|
||||
"crates/lib/jobs_common",
|
||||
@@ -80,6 +76,7 @@ members = [
|
||||
"crates/lib/migration",
|
||||
"crates/lib/password",
|
||||
"crates/lib/server_environment",
|
||||
"crates/lib/shared_service_components",
|
||||
"crates/lib/sqlx_mysql_helpers",
|
||||
"crates/lib/storyteller_root",
|
||||
"crates/lib/subprocess_common",
|
||||
@@ -87,7 +84,6 @@ members = [
|
||||
"crates/lib/testing",
|
||||
"crates/lib/tts_common",
|
||||
"crates/lib/url_config",
|
||||
"crates/lib/url_utils",
|
||||
"crates/lib/user_input_common",
|
||||
"crates/lib/web_scrapers",
|
||||
#"crates/lib/api_clients/fakeyou_client",
|
||||
@@ -189,7 +185,6 @@ artcraft_api_defs = { path = "crates/api_clients/artcraft/artcraft_api_defs" }
|
||||
build_metadata = { path = "crates/lib/build_metadata" }
|
||||
cloud_storage = { path = "crates/api_clients/cloud_storage" }
|
||||
cloudflare_errors = { path = "crates/lib/cloudflare_errors" }
|
||||
cloudflare_mitigation = { path = "crates/lib/cloudflare_mitigation" }
|
||||
component_traits = { path = "crates/service/plugins/component_traits" }
|
||||
cookie_store = { path = "crates/lib/cookie_store" }
|
||||
crockford = { path = "crates/lib/data/crockford" }
|
||||
@@ -199,8 +194,6 @@ errors = { path = "crates/lib/errors" }
|
||||
fal_client = { path = "crates/api_clients/fal_client" }
|
||||
file_to_base64_url = { path = "crates/lib/files/file_to_base64_url" }
|
||||
filesys = { path = "crates/lib/files/filesys" }
|
||||
grok_client = { path = "crates/api_clients/grok_client" }
|
||||
http_headers = { path = "crates/lib/http_headers" }
|
||||
idempotency = { path = "crates/lib/idempotency" }
|
||||
images = { path = "crates/lib/files/images" }
|
||||
jwt_light = { path = "crates/lib/jwt_light" }
|
||||
@@ -217,9 +210,7 @@ subprocess_common = { path = "crates/lib/subprocess_common" }
|
||||
test_data = { path = "crates/lib/test_data" }
|
||||
testing = { path = "crates/lib/testing" }
|
||||
tokens = { path = "crates/schema/public/tokens" }
|
||||
url_utils = { path = "crates/lib/url_utils" }
|
||||
web_base64 = { path = "crates/lib/data/web_base64" }
|
||||
world_labs_client = { path = "crates/api_clients/world_labs_client" }
|
||||
|
||||
# External libs
|
||||
# Centrally pinned versions so that we can uniformly upgrade/downgrade versions across the workspace
|
||||
@@ -236,28 +227,22 @@ actix-web-actors = "4.3.1+deprecated"
|
||||
actix-web-lab = "0.20.2"
|
||||
anyhow = "1.0.98"
|
||||
base64 = "0.22.1"
|
||||
bstr = "1.12.1"
|
||||
bytes = "1.10.1"
|
||||
chrono = { version = "0.4.41", features = ["serde"] }
|
||||
cookie = "0.18.1"
|
||||
env = "1.0.1"
|
||||
env_logger = "0.11.8"
|
||||
futures = { version = "0.3.31" }
|
||||
hex-literal = "1.0.0"
|
||||
indexmap = "2.9.0"
|
||||
log = "0.4.27"
|
||||
once_cell = "1.21.3"
|
||||
r2d2 = "0.8.10"
|
||||
rand = "0.9.1"
|
||||
redis = { version = "1.0.2", features = ["r2d2", "tokio-rustls-comp", "tokio-comp", "cluster-async", "aio"] }
|
||||
regex = "1.12.2"
|
||||
reqwest = { version = "0.12.23", default-features = false, features = [ "charset", "http2", "system-proxy", "json", "cookies", "rustls-tls" ] }
|
||||
regex = "1.11.1"
|
||||
reqwest = { version = "0.12.23", features = ["json", "cookies"] }
|
||||
reqwest-websocket = { version = "0.5.0" }
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_derive = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
serde_yaml = "0.9.34+deprecated"
|
||||
sha2 = "0.11.0-rc.2"
|
||||
sha3 = "0.10.8"
|
||||
tauri = { version = "2.8.3", features = ["unstable", "devtools"] } # NB: "devtools" prevents App Store distribution; "ubstable" is needed for window access
|
||||
tauri-plugin-dialog = "2.3.3"
|
||||
@@ -270,7 +255,6 @@ thiserror = "2.0.12"
|
||||
tokio = { version = "1.45.1", features = ["macros"] }
|
||||
toml = "0.8.22"
|
||||
url = { version = "2.5.4", features = ["serde"] }
|
||||
uuid = "1.18.1"
|
||||
|
||||
# NB: wreq gets past fingerprinting, but we have to deal with platform-specific build issues.
|
||||
#
|
||||
@@ -282,5 +266,5 @@ uuid = "1.18.1"
|
||||
# choco install pkgconfiglite -y
|
||||
# choco install llvm -y
|
||||
# choco install nasm -y
|
||||
wreq = { version = "6.0.0-rc.20", features = ["json", "multipart", "cookies", "ws", "stream"] }
|
||||
wreq-util = "3.0.0-rc.6"
|
||||
wreq = { version = "6.0.0-rc.20", features = ["json", "multipart", "cookies"] }
|
||||
wreq-util = "3.0.0-rc.5"
|
||||
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
ArtCraft License (WIP)
|
||||
======================
|
||||
|
||||
(**Warning**: This isn't a real lawyery license full of legitimate legalese yet, but we'll take care of that ASAP.)
|
||||
|
||||
ArtCraft is a form of "fair source" software. It's like [free beer](https://en.wiktionary.org/wiki/free_as_in_beer)
|
||||
in that we give you the code, but there are a few small limitations on what you can do with it.
|
||||
|
||||
First, what you *can* do with ArtCraft:
|
||||
|
||||
> 1. You do not have to pay us to use ArtCraft. It's free.
|
||||
>
|
||||
> 2. You can back up, copy, modify, compile, and use the code for your own private personal purposes.
|
||||
>
|
||||
> 3. You can do anything you want with the media you create with ArtCraft. Generated assets are yours.
|
||||
|
||||
What you *can't do* with ArtCraft:
|
||||
|
||||
> 1. You cannot sell the ArtCraft software commercially (eg. as your own software).
|
||||
>
|
||||
> 2. You cannot use the ArtCraft code to develop a competing business or product (eg. selling AI image and video tools).
|
||||
>
|
||||
> 3. You cannot fork ArtCraft and remove ArtCraft community, donation links, or paid model services. We intend to
|
||||
> make money to improve ArtCraft for everyone.
|
||||
>
|
||||
> 4. You cannot use the ArtCraft name, logo, or mascot characters to promote your business without permission.
|
||||
|
||||
This license means you can see what we're doing and not be at risk of having the rug pulled out from under
|
||||
you (eg. if our founders die, if our business collapses, if zombies or space aliens invade). It also gives
|
||||
you a really great way to customize the software or lend a hand in reporting (or even fixing) bugs.
|
||||
|
||||
Our desire is to support artists, designers, and filmmakers. We want to help you build your own creative studios
|
||||
and businesses. We want to give you creative software you can use forever.
|
||||
|
||||
ArtCraft is free. We do offer models available through us where you can pay us and support our development, but we also
|
||||
offer the ability for you to use other providers - and we'll keep adding more options so you have maximum flexibility.
|
||||
|
||||
## Our Promise
|
||||
|
||||
- All development and business plans will be communicated openly with the community.
|
||||
|
||||
- Except for minor test scripts or a few small places where we might need to use licensed 3rd party code, all of
|
||||
our engineering efforts will live in a single publicly available monorepo. You can see everything we do and even
|
||||
benefit from using it for personal use. We plan on building a lot more than what you see today, and you'll get
|
||||
all of it.
|
||||
|
||||
- We will not "rug pull" our users. If our business shuts down, gets acquired, or otherwise changes, the tools
|
||||
we create will always continue to function in the same way.
|
||||
|
||||
- If the business ends and our founders quit working in this space, we'll make it so the software transitions
|
||||
to an OSI-approved open source license. (We don't intend to quit!)
|
||||
|
||||
## Why not OSI Open Source?
|
||||
|
||||
Philosophical answer: The OSI is an industry body led by hyperscalers. They poach from open source all the time, yet
|
||||
hyperscaler systems and code (AWS, GCP, YouTube, etc.) remain closed source. The tiniest nuggets are open, but the vast
|
||||
machinery surrounding it - systems, user behaviors, funnels, at-scale moneymakers - is totally closed.
|
||||
|
||||
Practical answer: OSI licenses make it exceptionally challenging to grow big things commercially. And many of
|
||||
the licenses leave room for hyperscalers to reach in and cherry-pick pieces for themselves that they can better
|
||||
commercialize than the original authors.
|
||||
|
||||
Then there's completely open source software that doesn't even attempt to be a business.
|
||||
|
||||
We love software like Blender, but (1) developing ArtCraft is our full-time job, (2) we want to build something at scale
|
||||
that is much bigger than what you presently see, and (3) this will eventually become much more capital intensive than
|
||||
just writing code - we want to train novel models that work directly in conjunction with the user interface.
|
||||
|
||||
If and when we do get to scale, we intend to keep every bit of our work in the open, including the models. Available
|
||||
for use, inspection, learning, fitting onto consumer GPU hardware, and more.
|
||||
|
||||
Not having a way to make money is unfortunately what killed Stability.ai. Stability.ai walked so that others could
|
||||
eventually run. (Black Forest Labs, et al.) We're not egotistical enough to count ourselves in that party yet, but we
|
||||
believe in the original Stability.ai ethos and that their model can work with a few tweaks.
|
||||
|
||||
We want to build big and incredible tools the world hasn't seen before. We want to keep them free and entirely source
|
||||
available. You don't have to pay us, but if you do, all of your investment goes right back into the engineering
|
||||
and the tools. We're writing code you can clone and keep.
|
||||
|
||||
## Inspiration
|
||||
|
||||
We took inspiration for this from [Fair Source](https://fair.io/), [Fair Code](https://faircode.io/),
|
||||
the [Defold License](https://defold.com/license/), and Hacker News discussions. The folks at Obsidian
|
||||
have some pretty good thoughts on this as well.
|
||||
|
||||
~ Brandon Thomas
|
||||
@@ -1,82 +1,50 @@
|
||||
<p align="center">
|
||||
<video src="https://github.com/user-attachments/assets/b4e24c27-d87d-4fd1-8599-dc0d0b8af48d" width="100%" autoplay="true" loop controls>
|
||||
</p>
|
||||
storyteller-rust
|
||||
================
|
||||
|
||||
<p align="center">The IDE for artists.</p>
|
||||
<p align="center">
|
||||
<a href="https://discord.gg/artcraft"><img alt="Discord" src="https://img.shields.io/discord/1359579021108842617?style=for-the-badge&label=discord&color=ffffff&logo=discord&logoColor=ffffff" /></a>
|
||||
<a href="https://www.youtube.com/@OfficialArtCraftStudios"><img alt="YouTube" src="https://img.shields.io/youtube/channel/subscribers/UCdjY4VG0ntoGwFsKZO4sVWA?style=for-the-badge&logo=YouTube" /></a>
|
||||
<a href="https://x.com/intent/follow?screen_name=get_artcraft"><img alt="X" src="https://img.shields.io/twitter/follow/get_artcraft?style=for-the-badge&label=follow&logo=x&logoColor=ffffff&color=ffffff" /></a>
|
||||
<a href="https://www.linkedin.com/company/artcraft-ai"><img alt="LinkedIn" src="https://img.shields.io/badge/linkedin--0A66C2?style=for-the-badge"></a>
|
||||
</p>
|
||||
Our Artcraft monorepo, which serves our desktop application, backend infrastructure, and more.
|
||||
|
||||
---
|
||||
|
||||
ArtCraft
|
||||
========
|
||||
ArtCraft is the IDE for interactive AI image and video creation.
|
||||
We turn prompting into *crafting*, so your ideas become a form of tangible expression and computing.
|
||||
This is Adobe Photoshop for everyone, and we're giving away the source code!
|
||||
|
||||
## Show, Don't Tell: Advanced Crafting Features
|
||||
|
||||
Text-to-image is great, but artists *need control*. It's important to know what your image will look like before you generate it, and it's vitally important to achieve consistency and repeatability.
|
||||
|
||||
| Feature | Demo + Explanation
|
||||
|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Image to Location** |  Placing virtual actors into physical environments establishes single-location consistency. You can film multiple shots within a room without having things disappear. |
|
||||
| **3D Image Compositing** |  Use images (backdrops, foreground elements, props, etc.) in scenes with depth and blend them naturally together. Just a couple of images usually leads to great compositions. |
|
||||
| **2D Image Compositing** |  Use images, background removal, layers, and simple drawing tools to precisely compose a scene. |
|
||||
| **Image to 3D Mesh** |  It's almost impossible to lay out complicated objects or block complicated scenes; turning images into 3D helps position elements exactingly and intentionally. |
|
||||
| **Character Posing** |  You can dynamically pose your characters to achieve the precise character, scene, and camera blocking before calling "action". |
|
||||
| **Scene Blocking w/ Kit Bashing** |  Use 3D asset kits to precisely block out your scene: get the correct angles, object positions, and rich depth layering you can't with text prompting. |
|
||||
| **Character Identity Transfer** |  Use mannequins as simple 3D ControlNets for posing any character. |
|
||||
| **Background Removal** |  Remove backgrounds from images to make them useful in 2D or 3D compositing. They can be props, layers, or backdrops. |
|
||||
| **Mixed Asset Crafting** |  You can use image cutouts, worlds, and simple 3D meshes all together to precisely and intentionally lay out your scenes. |
|
||||
| **Scene Blocking** | (preview coming soon) |
|
||||
| **Canvas Editing** | (preview coming soon) |
|
||||
| **Scene Relighting** | (preview coming soon) |
|
||||
|
||||
Note: all of the above videos were generated for free with Grok Video; the cost to build this README was negligible.
|
||||
|
||||
## Quick and Easy Prompting
|
||||
We haven't abandoned text-to-asset generation for quick prototyping and ideation. We support every popular workflow in a first class fashion.
|
||||
|
||||
| Feature | Demo + Explanation
|
||||
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **Text to Image** |  Text prompt over a dozen different image models. |
|
||||
| **Image Editing** |  Edit with Nano Banana Pro and GPT Image 1.5. |
|
||||
| **Image Editing** |  Use inpainting, drawing, masking, etc. to edit images. |
|
||||
| **Image to Video** |  Image to video with lots of different options and controls. |
|
||||
| **Image Inpainting** | (preview coming soon) |
|
||||
| **Image Ingredients** | (preview coming soon) |
|
||||
|
||||
## Models and Providers Supported within Artcraft
|
||||
|
||||
| Provider | Features |
|
||||
|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Artcraft | Nano Banana, Nano Banana Pro, GPT-Image-1 / 1.5, Seedream 4 / 4.5, Flux 1.1 / Kontext, Veo 2 / 3 / 3.1, Kling 1.6 / 2.1 / 2.5 / 2.6, Seedance, Sora 2 / Pro, Hunyuan 3d 2 / 3 |
|
||||
| Grok | Grok Imagine, Grok Video |
|
||||
| Midjourney | Image Gen (all versions) |
|
||||
| Sora | Sora 1, Sora 2, GPT-Image-1 |
|
||||
| WorldLabs | Marble (Gaussian Splat World Generation) |
|
||||
|
||||
We're going to be adding the following providers soon: Kling (via Kling website accounts), Google (via API keys),
|
||||
Runway (via website account), Luma (via website account).
|
||||
|
||||
We're potentially interested in adding other aggregators for those who already have subscriptions and credits at
|
||||
those providers, for example: OpenArt, FreePik, etc.
|
||||
|
||||
## Downloads
|
||||
|
||||
- [Visit our website for the stable Windows and MacOS releases](https://getartcraft.com/)
|
||||
- Or you can grab a [more recent Windows and MacOS build directly](https://github.com/storytold/artcraft/releases)
|
||||
- Linux requires building from source for now
|
||||
|
||||
## Documentation
|
||||
```
|
||||
."-,.__
|
||||
`. `. ,
|
||||
.--' .._,'"-' `.
|
||||
. .' `'
|
||||
`. / ,'
|
||||
` '--. ,-"'
|
||||
`"` | \
|
||||
-. \, |
|
||||
`--Y.' ___.
|
||||
\ L._, \
|
||||
_., `. < <\ _
|
||||
,' ' `, `. | \ ( `
|
||||
../, `. ` | .\`. \ \_
|
||||
,' ,.. . _.,' ||\l ) '".
|
||||
, ,' \ ,'.-.`-._,' | . _._`.
|
||||
,' / \ \ `' ' `--/ | \ / / ..\
|
||||
.' / \ . |\__ - _ ,'` ` / / `.`.
|
||||
| ' .. `-...-" | `-' / / . `.
|
||||
| / |L__ | | / / `. `.
|
||||
, / . . | | / / ` `
|
||||
/ / ,. ,`._ `-_ | | _ ,-' / ` \
|
||||
/ . \"`_/. `-_ \_,. ,' +-' `-' _, ..,-. \`.
|
||||
. ' .-f ,' ` '. \__.---' _ .' ' \ \
|
||||
' / `.' l .' / \.. ,_|/ `. ,'` L`
|
||||
|' _.-""` `. \ _,' ` \ `.___`.'"`-. , | | | \
|
||||
|| ,' `. `. ' _,...._ ` | `/ ' | ' .|
|
||||
|| ,' `. ;.,.---' ,' `. `.. `-' .-' /_ .' ;_ ||
|
||||
|| ' V / / ` | ` ,' ,' '. ! `. ||
|
||||
||/ _,-------7 ' . | `-' l / `||
|
||||
. | ,' .- ,' || | .-. `. .' ||
|
||||
`' ,' `".' | | `. '. -.' `'
|
||||
/ ,' | |,' \-.._,.'/'
|
||||
. / . . \ .''
|
||||
.`. | `. / :_,'.'
|
||||
\ `...\ _ ,'-. .' /_.-' S T O R Y T E L L E R
|
||||
`-.__ `, `' . _.>----''. _ __ / Turning memes into dreams
|
||||
.' /"' | "' '_
|
||||
/_|.-'\ ,". '.'`__'-( \
|
||||
/ ,"'"\,' `/ `-.|"
|
||||
```
|
||||
|
||||
- [developer documentation](./_docs)
|
||||
- [server API reference](https://storyteller-docs.netlify.app/)
|
||||
- [tools, scripts, misc](./script)
|
||||
- [license](./LICENSE.md)
|
||||
- [roadmap](./ROADMAP.md)
|
||||
|
||||
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
ArtCraft Roadmap
|
||||
================
|
||||
|
||||
## 2026 Goals (non-exhaustive)
|
||||
|
||||
Product
|
||||
|
||||
- Make ArtCraft better and more useful than model aggregation websites (our #1 goal).
|
||||
We don't dislike those websites; we just set a high standard for ourselves.
|
||||
|
||||
- Build the best "tangible" computing surfaces where you can mold scenes and videos like
|
||||
clay and direct them with precision and clarity.
|
||||
|
||||
- Add every model and provider into ArtCraft so you can build your own crafting IDE.
|
||||
Nothing will be left out.
|
||||
|
||||
- Build purpose-specific image models, eg. for relighting scenes and reposing characters
|
||||
and props.
|
||||
|
||||
Studio
|
||||
|
||||
- Build our content studio. Make lots of narrative shorts and at least one
|
||||
feature-length AI film by the year's end. Theatrical distribution, but also lots
|
||||
of social media content. (We are creators, and we want to employ more creators!)
|
||||
|
||||
- Host festivals and competitions, including in-person meetups and screenings.
|
||||
|
||||
Architecture
|
||||
|
||||
- Remove dependence on ArtCraft hosted services.
|
||||
|
||||
- More Rust, more speed.
|
||||
|
||||
- Code cleanup, more/better tests, CI, ...
|
||||
|
||||
Community
|
||||
|
||||
- Build a safe community free of negativity and anti-AI stigma. A community of filmmakers
|
||||
and artists.
|
||||
|
||||
## Loftier Goals
|
||||
|
||||
- Build an *Open* OpenAI / Google / Anthropic. Open models, open platforms, open hardware.
|
||||
Why? Because the future could be bleak without it. How, you might ask? Get in touch if
|
||||
you're curious.
|
||||
|
||||
- Don't Be Evil™
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN total_generation_count;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN total_generation_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN image_generation_count;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN image_generation_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN video_generation_count;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN video_generation_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN object_generation_count;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN object_generation_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN text_to_image_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN text_to_image_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN image_to_image_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN image_to_image_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN text_to_video_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN text_to_video_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN image_to_video_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN image_to_video_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN text_to_object_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN text_to_object_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN image_to_object_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN image_to_object_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN image_page_prompt_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN image_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN video_page_prompt_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN video_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN edit_page_prompt_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN edit_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN stage_page_prompt_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN stage_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN object_page_prompt_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN object_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
ALTER TABLE analytics_app_sessions DROP COLUMN other_page_prompt_count ;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
|
||||
ALTER TABLE analytics_app_sessions
|
||||
ADD COLUMN other_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0;
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
-- noinspection SqlDialectInspectionForFile
|
||||
-- noinspection SqlNoDataSourceInspectionForFile
|
||||
-- noinspection SqlResolveForFile
|
||||
|
||||
-- Track user session durations
|
||||
-- User clients ping the endpoint that writes this data.
|
||||
CREATE TABLE analytics_app_sessions (
|
||||
-- Not used for anything except replication.
|
||||
id BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
|
||||
-- Generated client-side
|
||||
-- Wide enough for a UUID, but typically one of our internal tokens.
|
||||
session_token VARCHAR(36) NOT NULL,
|
||||
|
||||
-- If we host multiple sites with distinct categories, this will enable us to
|
||||
-- segregate analytics.
|
||||
app_namespace VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Who sent the event.
|
||||
user_token VARCHAR(32) NOT NULL,
|
||||
|
||||
-- Updated on every write.
|
||||
-- The "user agent" or app version string of the client.
|
||||
app_version VARCHAR(255) DEFAULT NULL,
|
||||
|
||||
-- Updated on every write.
|
||||
-- The OS platform for the client.
|
||||
os_platform VARCHAR(16) DEFAULT NULL,
|
||||
|
||||
-- Updated on every write.
|
||||
-- The OS platform version for the client.
|
||||
os_version VARCHAR(64) DEFAULT NULL,
|
||||
|
||||
-- Updated on every write.
|
||||
-- How long the current user's session has been open
|
||||
session_duration_seconds INT(10) UNSIGNED DEFAULT NULL,
|
||||
|
||||
-- Updated on every write.
|
||||
-- The user's last known IP address.
|
||||
ip_address VARCHAR(40) NOT NULL,
|
||||
|
||||
-- Incrementing count of events.
|
||||
-- NB: This is kind of an "hours spent" measure, but will lose information if we ever
|
||||
-- change the ping cadence (eg. 1 minute pings --> 5 minute pings).
|
||||
measurement_count BIGINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== GENERATION COUNTS ==========
|
||||
|
||||
-- Number of items generated across all types.
|
||||
-- This counts attempts, not successes. This also includes multiples,
|
||||
-- eg. four requested images adds four to this total.
|
||||
-- Two bytes (65,535 max)
|
||||
total_generation_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of images generated
|
||||
-- This counts attempts, not successes. This also includes multiples,
|
||||
-- eg. four requested images adds four to this total.
|
||||
-- Two bytes (65,535 max)
|
||||
image_generation_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of images generated
|
||||
-- This counts attempts, not successes. This also includes multiples,
|
||||
-- eg. four requested images adds four to this total.
|
||||
-- Two bytes (65,535 max)
|
||||
video_generation_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of objects generated (meshes, etc.)
|
||||
-- This counts attempts, not successes. This also includes multiples,
|
||||
-- eg. four requested images adds four to this total.
|
||||
-- Two bytes (65,535 max)
|
||||
object_generation_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of images generated with text to image.
|
||||
text_to_image_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of images generated with text to image (image edits)
|
||||
image_to_image_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of videos generated with text to image.
|
||||
text_to_video_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of images generated with text to image.
|
||||
image_to_video_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of objects generated with text to image.
|
||||
text_to_object_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- Number of objects generated with text to image.
|
||||
image_to_object_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
image_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
video_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
edit_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
stage_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
object_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
other_page_prompt_count SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
|
||||
-- ========== RECORD TIMESTAMPS ==========
|
||||
|
||||
-- Written at create only.
|
||||
first_active_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Updated on every ping.
|
||||
last_active_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- ========== INDICES ==========
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY (session_token),
|
||||
INDEX idx_app_namespace (app_namespace),
|
||||
INDEX idx_user_token (user_token),
|
||||
INDEX idx_os_platform (os_platform),
|
||||
INDEX idx_os_version (os_version),
|
||||
INDEX idx_app_version (app_version),
|
||||
INDEX idx_last_active_at (last_active_at)
|
||||
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
@@ -0,0 +1,6 @@
|
||||
# Secrets
|
||||
db_secrets.toml
|
||||
|
||||
# Python Virtual Env
|
||||
python/
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# FIXME: Needs packages 'toml' and 'mysql-connector-python'. Put in requirements.txt.
|
||||
|
||||
import time
|
||||
import toml
|
||||
import mysql.connector
|
||||
|
||||
DB_SECRETS_FILE = 'db_secrets.toml'
|
||||
|
||||
# TODO: Share db-related setup when more scripts are added.
|
||||
|
||||
def read_database_secrets(secrets_file = DB_SECRETS_FILE):
|
||||
secrets = None
|
||||
with open(secrets_file, "r") as f:
|
||||
secrets = toml.load(f)
|
||||
|
||||
required_keys = ['database', 'hostname', 'port', 'password', 'username']
|
||||
for key in required_keys:
|
||||
if key not in secrets:
|
||||
raise Exception(f"Required key '#{key}' not present")
|
||||
|
||||
return secrets
|
||||
|
||||
def connect_to_mysql():
|
||||
secrets = read_database_secrets()
|
||||
connection = mysql.connector.connect(
|
||||
host = secrets['hostname'],
|
||||
port = secrets['port'],
|
||||
user = secrets['username'],
|
||||
password = secrets['password'],
|
||||
database = secrets['database'],
|
||||
)
|
||||
return connection
|
||||
|
||||
connection = connect_to_mysql()
|
||||
cursor = connection.cursor()
|
||||
|
||||
SAFE_ID = 76000000 # Reasonably completed on 2022-06-24
|
||||
LIMIT = 100000
|
||||
|
||||
#def get_record_count():
|
||||
# query = f"SELECT COUNT(*) FROM tts_inference_jobs"
|
||||
# cursor.execute(query)
|
||||
# result = cursor.fetchall()
|
||||
# print(result)
|
||||
# return result[0]
|
||||
#
|
||||
##total_count = get_record_count()
|
||||
|
||||
#total_count = 75082057
|
||||
total_count = 21925602
|
||||
|
||||
def get_first_record(cursor):
|
||||
query = f"select * from tts_inference_jobs order by id limit 1"
|
||||
cursor.execute(query)
|
||||
result = cursor.fetchall()
|
||||
print(result)
|
||||
return result
|
||||
|
||||
total_deleted = 0
|
||||
|
||||
keep_deleting = True
|
||||
|
||||
while keep_deleting:
|
||||
query = f"DELETE FROM tts_inference_jobs WHERE id < {SAFE_ID} ORDER BY ID ASC LIMIT {LIMIT}"
|
||||
query = f"DELETE FROM tts_inference_jobs WHERE status = 'complete_success' ORDER BY ID ASC LIMIT {LIMIT}"
|
||||
#print(query)
|
||||
|
||||
cursor.execute(query)
|
||||
connection.commit() # NB: Python does not autocommit changes!
|
||||
|
||||
#result = cursor.fetchall()
|
||||
count = cursor.rowcount
|
||||
total_deleted += count
|
||||
|
||||
percent_deleted = (total_deleted / total_count) * 100.0
|
||||
|
||||
print(f'Deleted {count}; total deleted = {total_deleted} ({percent_deleted:.4f})')
|
||||
|
||||
if count < LIMIT - 1:
|
||||
keep_deleting = False
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
#record = get_first_record(cursor)
|
||||
#time.sleep(2)
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# FIXME: Needs packages 'toml' and 'mysql-connector-python'. Put in requirements.txt.
|
||||
|
||||
import time
|
||||
import toml
|
||||
import mysql.connector
|
||||
|
||||
DB_SECRETS_FILE = 'db_secrets.toml'
|
||||
|
||||
# TODO: Share db-related setup when more scripts are added.
|
||||
|
||||
def read_database_secrets(secrets_file = DB_SECRETS_FILE):
|
||||
secrets = None
|
||||
with open(secrets_file, "r") as f:
|
||||
secrets = toml.load(f)
|
||||
|
||||
required_keys = ['database', 'hostname', 'port', 'password', 'username']
|
||||
for key in required_keys:
|
||||
if key not in secrets:
|
||||
raise Exception(f"Required key '#{key}' not present")
|
||||
|
||||
return secrets
|
||||
|
||||
def connect_to_mysql():
|
||||
secrets = read_database_secrets()
|
||||
connection = mysql.connector.connect(
|
||||
host = secrets['hostname'],
|
||||
port = secrets['port'],
|
||||
user = secrets['username'],
|
||||
password = secrets['password'],
|
||||
database = secrets['database'],
|
||||
)
|
||||
return connection
|
||||
|
||||
connection = connect_to_mysql()
|
||||
cursor = connection.cursor()
|
||||
|
||||
SAFE_ID = 76000000 # Reasonably completed on 2022-06-24
|
||||
LIMIT = 100000
|
||||
|
||||
#def get_record_count():
|
||||
# query = f"SELECT COUNT(*) FROM tts_inference_jobs"
|
||||
# cursor.execute(query)
|
||||
# result = cursor.fetchall()
|
||||
# print(result)
|
||||
# return result[0]
|
||||
#
|
||||
##total_count = get_record_count()
|
||||
|
||||
#total_count = 75082057
|
||||
total_count = 21925602
|
||||
|
||||
def get_user_tokens(cursor):
|
||||
query = f"""
|
||||
SELECT
|
||||
r.maybe_creator_user_token, r.creator_ip_address
|
||||
FROM
|
||||
tts_results as r
|
||||
LEFT OUTER JOIN
|
||||
users as u
|
||||
ON
|
||||
r.maybe_creator_user_token = u.token
|
||||
WHERE
|
||||
r.raw_inference_text LIKE '%spidermant%'
|
||||
AND r.created_at > ( CURDATE() - INTERVAL 1 HOUR )
|
||||
"""
|
||||
cursor.execute(query)
|
||||
results = cursor.fetchall()
|
||||
|
||||
user_tokens = [row[0] for row in results]
|
||||
user_tokens = [token for token in user_tokens if token is not None]
|
||||
user_tokens = set(user_tokens)
|
||||
|
||||
ip_addresses = [row[1] for row in results]
|
||||
ip_addresses = [ip for ip in ip_addresses if ip is not None]
|
||||
ip_addresses = set(ip_addresses)
|
||||
|
||||
return (user_tokens, ip_addresses)
|
||||
|
||||
(user_tokens, ip_addresses) = get_user_tokens(cursor)
|
||||
|
||||
print(user_tokens)
|
||||
print(len(user_tokens))
|
||||
print(len(ip_addresses))
|
||||
|
||||
with open('101_user_tokens.txt', 'w') as f:
|
||||
for user_token in user_tokens:
|
||||
f.write(user_token)
|
||||
f.write("\n")
|
||||
|
||||
with open('101_ip_addresses.txt', 'w') as f:
|
||||
for ip in ip_addresses:
|
||||
f.write(ip)
|
||||
f.write("\n")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
mysql-connector-python==8.0.32
|
||||
protobuf==3.20.3
|
||||
toml==0.10.2
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user