civitai-ruby 0.1.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/civitai/client.rb +203 -0
- data/lib/civitai/version.rb +5 -0
- data/lib/civitai.rb +16 -0
- metadata +64 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 9f71d8cde85129801aaa115cc63551ac8d1c1522358d061e5ab118bc6d46fbe3
|
|
4
|
+
data.tar.gz: 716f927d20dccf9e3414014d9b8fe0b46be747c5806d428c4bdd243b4f03ceec
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4fdeb6dae5c62c86c17799a585a86aff332e917a3cbb3c8376ae3a215390464db145a2b5cf5697f4f05dc9bf19601b24337a09c45ccc662914bf6c76269871de
|
|
7
|
+
data.tar.gz: 239dfe9cbff1d5253dd3b2dac8d10acdff0ea0026d06154076ffed8eec8e75430b17527a1f88fffa7a8a613bb57b149e28b63a053418381c788c84725ac7943e
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
|
|
7
|
+
module Civitai
|
|
8
|
+
# Ruby client for the CivitAI REST API.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# client = Civitai::Client.new(api_key: "your_key")
|
|
12
|
+
# client.model(12345)
|
|
13
|
+
# client.search("mecha", type: "LORA", base_model: "SDXL 1.0")
|
|
14
|
+
# client.download(version_id: 67890, output: "/path/to/models/")
|
|
15
|
+
#
|
|
16
|
+
class Client
|
|
17
|
+
attr_reader :api_key
|
|
18
|
+
|
|
19
|
+
# @param api_key [String, nil] CivitAI API key (optional, enables higher rate limits)
|
|
20
|
+
# @param timeout [Integer] HTTP timeout in seconds
|
|
21
|
+
def initialize(api_key: nil, timeout: 30)
|
|
22
|
+
@api_key = api_key || ENV["CIVITAI_API_KEY"]
|
|
23
|
+
@timeout = timeout
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# ----------------------------------------------------------------
|
|
27
|
+
# Model queries
|
|
28
|
+
# ----------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
# Fetch a model by ID.
|
|
31
|
+
# @param model_id [Integer]
|
|
32
|
+
# @return [Hash]
|
|
33
|
+
def model(model_id)
|
|
34
|
+
get("/models/#{model_id}")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Fetch a model version by ID.
|
|
38
|
+
# @param version_id [Integer]
|
|
39
|
+
# @return [Hash]
|
|
40
|
+
def model_version(version_id)
|
|
41
|
+
get("/model-versions/#{version_id}")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Fetch a model version by file SHA256 hash.
|
|
45
|
+
# @param hash [String] SHA256 hash
|
|
46
|
+
# @return [Hash]
|
|
47
|
+
def by_hash(hash)
|
|
48
|
+
get("/model-versions/by-hash/#{hash}")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# ----------------------------------------------------------------
|
|
52
|
+
# Search
|
|
53
|
+
# ----------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
# Search CivitAI models.
|
|
56
|
+
#
|
|
57
|
+
# @param query [String, nil] search query
|
|
58
|
+
# @param type [String, nil] model type: Checkpoint, LORA, TextualInversion, etc.
|
|
59
|
+
# @param base_model [String, nil] base model: "SD 1.5", "SDXL 1.0", "Pony", etc.
|
|
60
|
+
# @param sort [String] sort order: "Highest Rated", "Most Downloaded", "Newest"
|
|
61
|
+
# @param limit [Integer] max results (1-100)
|
|
62
|
+
# @param period [String, nil] time period: "Day", "Week", "Month", "Year", "AllTime"
|
|
63
|
+
# @param nsfw [Boolean, nil] nil = include all, true = only nsfw, false = exclude nsfw
|
|
64
|
+
# @param tag [String, nil] filter by tag
|
|
65
|
+
# @param username [String, nil] filter by creator
|
|
66
|
+
# @param page [Integer, nil] page number
|
|
67
|
+
# @return [Hash] with "items" array and "metadata" pagination
|
|
68
|
+
def search(
|
|
69
|
+
query = nil,
|
|
70
|
+
type: nil,
|
|
71
|
+
base_model: nil,
|
|
72
|
+
sort: "Most Downloaded",
|
|
73
|
+
limit: 20,
|
|
74
|
+
period: nil,
|
|
75
|
+
nsfw: nil,
|
|
76
|
+
tag: nil,
|
|
77
|
+
username: nil,
|
|
78
|
+
page: nil
|
|
79
|
+
)
|
|
80
|
+
params = build_search_params(
|
|
81
|
+
query: query, type: type, base_model: base_model,
|
|
82
|
+
sort: sort, limit: limit, period: period, nsfw: nsfw,
|
|
83
|
+
tag: tag, username: username, page: page
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
has_filters = !type.nil? || !base_model.nil? || !tag.nil?
|
|
87
|
+
result = get("/models", **params)
|
|
88
|
+
filter_results(result, query, has_filters, limit)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# ----------------------------------------------------------------
|
|
92
|
+
# Download
|
|
93
|
+
# ----------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
# Download a model file.
|
|
96
|
+
#
|
|
97
|
+
# @param version_id [Integer, nil] model version ID
|
|
98
|
+
# @param model_id [Integer, nil] model ID (downloads latest version)
|
|
99
|
+
# @param output [String] output directory or file path
|
|
100
|
+
# @param on_progress [Proc, nil] callback(downloaded_bytes, total_bytes)
|
|
101
|
+
# @return [String] path to downloaded file
|
|
102
|
+
def download(version_id: nil, model_id: nil, output: ".", on_progress: nil)
|
|
103
|
+
raise ArgumentError, "Provide version_id or model_id" unless version_id || model_id
|
|
104
|
+
|
|
105
|
+
unless version_id
|
|
106
|
+
m = model(model_id)
|
|
107
|
+
version_id = m.dig("modelVersions", 0, "id")
|
|
108
|
+
raise NotFoundError, "No versions found for model #{model_id}" unless version_id
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
url = "#{DOWNLOAD_BASE}/#{version_id}"
|
|
112
|
+
download_file(url, output, on_progress: on_progress)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def http
|
|
118
|
+
client = HTTP
|
|
119
|
+
.headers("Accept" => "application/json")
|
|
120
|
+
.headers("User-Agent" => "civitai-ruby/#{Civitai::VERSION}")
|
|
121
|
+
.timeout(@timeout)
|
|
122
|
+
.follow(max_hops: 5)
|
|
123
|
+
|
|
124
|
+
client = client.auth("Bearer #{@api_key}") if @api_key
|
|
125
|
+
client
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def get(path, **params)
|
|
129
|
+
url = "#{API_BASE}#{path}"
|
|
130
|
+
response = http.get(url, params: params)
|
|
131
|
+
|
|
132
|
+
case response.status.to_i
|
|
133
|
+
when 200
|
|
134
|
+
JSON.parse(response.body.to_s)
|
|
135
|
+
when 404
|
|
136
|
+
raise NotFoundError, "Not found: #{path}"
|
|
137
|
+
when 429
|
|
138
|
+
raise RateLimitError, "CivitAI rate limit exceeded"
|
|
139
|
+
else
|
|
140
|
+
raise APIError, "CivitAI API error: #{response.status}"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def build_search_params(query:, type:, base_model:, sort:, limit:, period:, nsfw:, tag:, username:, page:)
|
|
145
|
+
params = {limit: [limit, 100].min, sort: sort}
|
|
146
|
+
params[:nsfw] = nsfw.nil? ? "true" : nsfw.to_s
|
|
147
|
+
|
|
148
|
+
has_filters = !type.nil? || !base_model.nil? || !tag.nil?
|
|
149
|
+
params[:query] = query if query && !has_filters
|
|
150
|
+
params[:types] = type if type
|
|
151
|
+
params[:baseModels] = base_model if base_model
|
|
152
|
+
params[:period] = period if period
|
|
153
|
+
params[:tag] = tag if tag
|
|
154
|
+
params[:username] = username if username
|
|
155
|
+
params[:page] = page if page && page > 1
|
|
156
|
+
params[:limit] = 100 if query && has_filters
|
|
157
|
+
|
|
158
|
+
params
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def filter_results(result, query, has_filters, limit)
|
|
162
|
+
return result unless query && has_filters
|
|
163
|
+
|
|
164
|
+
q_lower = query.downcase
|
|
165
|
+
items = (result["items"] || []).select { |m| m["name"]&.downcase&.include?(q_lower) }
|
|
166
|
+
result.merge("items" => items.first(limit))
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def download_file(url, output, on_progress: nil)
|
|
170
|
+
output_path = File.directory?(output) ? output : File.dirname(output)
|
|
171
|
+
FileUtils.mkdir_p(output_path)
|
|
172
|
+
|
|
173
|
+
dl_client = HTTP
|
|
174
|
+
.headers("User-Agent" => "civitai-ruby/#{Civitai::VERSION}")
|
|
175
|
+
.follow(max_hops: 5)
|
|
176
|
+
dl_client = dl_client.auth("Bearer #{@api_key}") if @api_key
|
|
177
|
+
|
|
178
|
+
response = dl_client.get(url)
|
|
179
|
+
|
|
180
|
+
# Extract filename from Content-Disposition
|
|
181
|
+
cd = response.headers["Content-Disposition"]
|
|
182
|
+
filename = if cd && cd =~ /filename="?([^";\s]+)"?/
|
|
183
|
+
$1
|
|
184
|
+
else
|
|
185
|
+
"model_#{Time.now.to_i}.safetensors"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
dest = File.directory?(output) ? File.join(output, filename) : output
|
|
189
|
+
total = response.headers["Content-Length"]&.to_i || 0
|
|
190
|
+
downloaded = 0
|
|
191
|
+
|
|
192
|
+
File.open(dest, "wb") do |file|
|
|
193
|
+
response.body.each do |chunk|
|
|
194
|
+
file.write(chunk)
|
|
195
|
+
downloaded += chunk.bytesize
|
|
196
|
+
on_progress&.call(downloaded, total)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
dest
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
data/lib/civitai.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
require_relative "civitai/version"
|
|
6
|
+
require_relative "civitai/client"
|
|
7
|
+
|
|
8
|
+
module Civitai
|
|
9
|
+
API_BASE = "https://civitai.com/api/v1"
|
|
10
|
+
DOWNLOAD_BASE = "https://civitai.com/api/download/models"
|
|
11
|
+
|
|
12
|
+
class Error < StandardError; end
|
|
13
|
+
class NotFoundError < Error; end
|
|
14
|
+
class APIError < Error; end
|
|
15
|
+
class RateLimitError < Error; end
|
|
16
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: civitai-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0.pre1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- aladac
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-04-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: http
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '5.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '5.0'
|
|
27
|
+
description: Search, browse, and download AI models from CivitAI. Supports model lookup
|
|
28
|
+
by ID/hash, search with filters, and streaming downloads with resume.
|
|
29
|
+
email:
|
|
30
|
+
- aladac@saiden.dev
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- lib/civitai.rb
|
|
36
|
+
- lib/civitai/client.rb
|
|
37
|
+
- lib/civitai/version.rb
|
|
38
|
+
homepage: https://github.com/aladac/civitai-ruby
|
|
39
|
+
licenses:
|
|
40
|
+
- MIT
|
|
41
|
+
metadata:
|
|
42
|
+
homepage_uri: https://github.com/aladac/civitai-ruby
|
|
43
|
+
source_code_uri: https://github.com/aladac/civitai-ruby
|
|
44
|
+
changelog_uri: https://github.com/aladac/civitai-ruby/blob/main/CHANGELOG.md
|
|
45
|
+
post_install_message:
|
|
46
|
+
rdoc_options: []
|
|
47
|
+
require_paths:
|
|
48
|
+
- lib
|
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 3.2.0
|
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '0'
|
|
59
|
+
requirements: []
|
|
60
|
+
rubygems_version: 3.5.22
|
|
61
|
+
signing_key:
|
|
62
|
+
specification_version: 4
|
|
63
|
+
summary: Ruby client for the CivitAI API
|
|
64
|
+
test_files: []
|