aigifmaker 0.1.0
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/LICENSE +21 -0
- data/README.md +70 -0
- data/lib/aigifmaker/version.rb +3 -0
- data/lib/aigifmaker.rb +179 -0
- metadata +52 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c93c6ee3782f75742a19707c49bfe0988a265330640750624fa23603b7851ea5
|
|
4
|
+
data.tar.gz: cf2f3f1c6c24ce99d569f081183b37bbdfb095eb27ed8f049f372a117d63ec5b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 59d9728075bd774d504deb46ee3aca29c15f1be907d37404bbf4239bd6dc7dd8aeac7937ad47ba1b7099c3aeb7f99882d0e51da72f3f201a93e42d28d9b324ab
|
|
7
|
+
data.tar.gz: dea93c1b9b94aeb4017f8da901d704ea513478c4e35901babd7d1d9403dea9e281097e948473c3d80e0a0c42a0ac4eb66f6da7b903c65efdd4ae8538d4adb03e
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AI GIF Maker (https://aigifmaker.ai)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# aigifmaker
|
|
2
|
+
|
|
3
|
+
Turn text into animated GIFs with AI GIF Maker. Native GIF generation — no video conversion. Type what you want and download your GIF in about a minute.
|
|
4
|
+
|
|
5
|
+
This is the official Ruby client for [AI GIF Maker](https://aigifmaker.ai/?utm_source=rubygems&utm_medium=package). One call sends your prompt, waits for the GIF and returns its URL.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gem install aigifmaker
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
require "aigifmaker"
|
|
13
|
+
|
|
14
|
+
url = Aigifmaker.generate("a corgi in sunglasses dancing on a beach, looping")
|
|
15
|
+
puts url # the finished GIF
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Standard library only (`net/http`, `json`), no dependencies. Ruby 2.6+.
|
|
19
|
+
|
|
20
|
+
## API key
|
|
21
|
+
|
|
22
|
+
Create a key at https://aigifmaker.ai/settings/apikeys, then pass it as `api_key:` or export it:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
export AIGIFMAKER_API_KEY=sk-...
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Each GIF costs 1 credit on your AI GIF Maker account, the same as on the website. New accounts get one free GIF (it carries a small aigifmaker.ai badge). A GIF that fails to generate gives its credit back. Credit packs are on the [pricing](https://aigifmaker.ai/pricing?utm_source=rubygems&utm_medium=package) page.
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
puts Aigifmaker.credits # remaining credits, free to call
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Client
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
gif = Aigifmaker::Client.new("sk-...", timeout: 300) # timeout in seconds, 5 minutes by default
|
|
38
|
+
url = gif.generate("a tiny dragon sneezing a puff of glitter")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Submit now, collect later
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
task_id = gif.submit("happy birthday text bouncing in rainbow letters")
|
|
45
|
+
puts gif.status(task_id)["status"] # pending | processing | success | failed
|
|
46
|
+
url = gif.wait(task_id) # polls every 3 seconds
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A run from an account that has not bought credits yet may wait in a short queue first (`"queued" => true` while `status` stays `processing`). That is normal; `generate` and `wait` keep polling.
|
|
50
|
+
|
|
51
|
+
## Errors worth catching
|
|
52
|
+
|
|
53
|
+
Each failure is its own subclass of `Aigifmaker::Error`:
|
|
54
|
+
|
|
55
|
+
- `Aigifmaker::AuthError`: no key, or the key is wrong or deleted
|
|
56
|
+
- `Aigifmaker::QuotaError`: the account is out of credits
|
|
57
|
+
- `Aigifmaker::RejectedError`: the content filter refused the prompt, or a cooldown is active; reword it
|
|
58
|
+
- `Aigifmaker::GenerationFailed`: the run was accepted but failed; the message says why
|
|
59
|
+
- `Aigifmaker::TimeoutError`: still running when the timeout passed; the GIF may finish later in your history
|
|
60
|
+
|
|
61
|
+
Any other refusal raises `Aigifmaker::Error` with the server's message.
|
|
62
|
+
|
|
63
|
+
## Tests
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ruby test/test_aigifmaker.rb # mocked, offline
|
|
67
|
+
ruby test/smoke_live.rb # two free live checks, no generation
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
MIT licensed. Questions: support@aigifmaker.ai
|
data/lib/aigifmaker.rb
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# Official Ruby SDK for AI GIF Maker (https://aigifmaker.ai): turn text into
|
|
2
|
+
# animated GIFs.
|
|
3
|
+
#
|
|
4
|
+
# require "aigifmaker"
|
|
5
|
+
# url = Aigifmaker.generate("a corgi in sunglasses dancing on a beach")
|
|
6
|
+
#
|
|
7
|
+
# Create a key at https://aigifmaker.ai/settings/apikeys and pass it as
|
|
8
|
+
# api_key: or set AIGIFMAKER_API_KEY. Each GIF costs 1 credit on your
|
|
9
|
+
# AI GIF Maker account, the same as on the website.
|
|
10
|
+
require "json"
|
|
11
|
+
require "net/http"
|
|
12
|
+
require "uri"
|
|
13
|
+
require_relative "aigifmaker/version"
|
|
14
|
+
|
|
15
|
+
module Aigifmaker
|
|
16
|
+
BASE_URL = "https://aigifmaker.ai".freeze
|
|
17
|
+
KEYS_URL = "https://aigifmaker.ai/settings/apikeys".freeze
|
|
18
|
+
PRICING_URL = "https://aigifmaker.ai/pricing".freeze
|
|
19
|
+
DEFAULT_TIMEOUT = 300
|
|
20
|
+
POLL_EVERY = 3
|
|
21
|
+
|
|
22
|
+
# Base of every error this gem raises. #code is the JSON code when there was one.
|
|
23
|
+
class Error < StandardError
|
|
24
|
+
attr_reader :code
|
|
25
|
+
def initialize(msg = nil, code: nil)
|
|
26
|
+
super(msg)
|
|
27
|
+
@code = code
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
# No API key, or the key is invalid or deleted.
|
|
31
|
+
class AuthError < Error; end
|
|
32
|
+
# The account is out of credits.
|
|
33
|
+
class QuotaError < Error; end
|
|
34
|
+
# The content filter refused the prompt, or a cooldown is active. Reword it.
|
|
35
|
+
class RejectedError < Error; end
|
|
36
|
+
# The GIF was accepted but the run ended as failed or canceled.
|
|
37
|
+
class GenerationFailed < Error; end
|
|
38
|
+
# The deadline passed; the GIF may still finish in your AI GIF Maker history.
|
|
39
|
+
class TimeoutError < Error; end
|
|
40
|
+
|
|
41
|
+
# Return data for code 0; otherwise raise the error that says what to do next.
|
|
42
|
+
# Every response is HTTP 200; the JSON code tells success from refusal.
|
|
43
|
+
def self.unwrap(http_status, env)
|
|
44
|
+
env = {} unless env.is_a?(Hash)
|
|
45
|
+
code = env["code"]
|
|
46
|
+
data = env["data"].is_a?(Hash) ? env["data"] : {}
|
|
47
|
+
if code == 0
|
|
48
|
+
raise AuthError.new("an API key is required; create one at #{KEYS_URL} and set AIGIFMAKER_API_KEY", code: code) if data["wall"]
|
|
49
|
+
|
|
50
|
+
return data
|
|
51
|
+
end
|
|
52
|
+
msg = (env["message"] || env["error"] || "HTTP #{http_status}").to_s
|
|
53
|
+
low = msg.downcase
|
|
54
|
+
c = code.is_a?(Integer) ? code : nil
|
|
55
|
+
raise AuthError.new(msg, code: c) if low.include?("invalid api key") || low.include?("no auth") || http_status == 401
|
|
56
|
+
raise QuotaError.new("#{msg}. Top up at #{PRICING_URL}", code: c) if low.include?("insufficient credits")
|
|
57
|
+
raise RejectedError.new(msg, code: c) if c && c != -1
|
|
58
|
+
|
|
59
|
+
raise Error.new(msg, code: c)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A client bound to one API key. transport: is a callable (url, body, headers) -> [status, hash], for tests.
|
|
63
|
+
class Client
|
|
64
|
+
attr_accessor :poll_every, :timeout
|
|
65
|
+
|
|
66
|
+
def initialize(api_key = nil, base_url: BASE_URL, timeout: DEFAULT_TIMEOUT, poll_every: POLL_EVERY, transport: nil)
|
|
67
|
+
key = (api_key || ENV["AIGIFMAKER_API_KEY"]).to_s.strip
|
|
68
|
+
@key = key.empty? ? nil : key
|
|
69
|
+
@base = base_url.chomp("/")
|
|
70
|
+
@timeout = timeout
|
|
71
|
+
@poll_every = poll_every
|
|
72
|
+
@transport = transport || method(:http)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Start one GIF and return its task id without waiting.
|
|
76
|
+
def submit(prompt)
|
|
77
|
+
raise ArgumentError, "prompt is required" if prompt.to_s.strip.empty?
|
|
78
|
+
|
|
79
|
+
data = post("/api/ai/generate", {
|
|
80
|
+
"provider" => "openlux",
|
|
81
|
+
"mediaType" => "image",
|
|
82
|
+
"model" => "gif-maker-v1",
|
|
83
|
+
"scene" => "text-to-image",
|
|
84
|
+
"prompt" => prompt,
|
|
85
|
+
"options" => { "output_format" => "gif" }
|
|
86
|
+
})
|
|
87
|
+
raise Error, "the service returned no task id" unless data["id"]
|
|
88
|
+
|
|
89
|
+
data["id"].to_s
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Return the task record: "status" is pending, processing, success or failed;
|
|
93
|
+
# "images"[0] holds the GIF URL when it succeeds.
|
|
94
|
+
def status(task_id)
|
|
95
|
+
post("/api/ai/query", { "taskId" => task_id })
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Poll a task every 3 seconds until it finishes and return the GIF URL.
|
|
99
|
+
def wait(task_id, timeout: nil)
|
|
100
|
+
limit = timeout || @timeout
|
|
101
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + limit
|
|
102
|
+
loop do
|
|
103
|
+
d = begin
|
|
104
|
+
status(task_id)
|
|
105
|
+
rescue SystemCallError, SocketError, Net::OpenTimeout, Net::ReadTimeout
|
|
106
|
+
{} # a dropped poll is not a failed generation
|
|
107
|
+
end
|
|
108
|
+
state = d["status"].to_s.downcase
|
|
109
|
+
images = d["images"] || []
|
|
110
|
+
return images[0].to_s if state == "success" && !images.empty?
|
|
111
|
+
|
|
112
|
+
if %w[failed canceled].include?(state)
|
|
113
|
+
msg = (d["errorMessage"] || "the GIF #{state}").to_s
|
|
114
|
+
raise RejectedError, msg if d["failureKind"] == "moderation"
|
|
115
|
+
|
|
116
|
+
raise GenerationFailed, msg
|
|
117
|
+
end
|
|
118
|
+
if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
|
|
119
|
+
raise TimeoutError, "still running after #{limit}s; task #{task_id} may finish later in your AI GIF Maker history"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
sleep @poll_every
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Generate one animated GIF and return its URL.
|
|
127
|
+
def generate(prompt)
|
|
128
|
+
wait(submit(prompt))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Remaining credits on the account. Free to call.
|
|
132
|
+
def credits
|
|
133
|
+
post("/api/user/get-user-credits", nil)["remainingCredits"].to_i
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
def post(path, body)
|
|
139
|
+
headers = { "Content-Type" => "application/json", "User-Agent" => "aigifmaker-ruby/#{VERSION}" }
|
|
140
|
+
headers["Authorization"] = "Bearer #{@key}" if @key
|
|
141
|
+
st, env = @transport.call("#{@base}#{path}", body, headers)
|
|
142
|
+
Aigifmaker.unwrap(st, env)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def http(url, body, headers)
|
|
146
|
+
uri = URI(url)
|
|
147
|
+
req = Net::HTTP::Post.new(uri)
|
|
148
|
+
headers.each { |k, v| req[k] = v }
|
|
149
|
+
req.body = JSON.generate(body || {})
|
|
150
|
+
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", read_timeout: 60) { |h| h.request(req) }
|
|
151
|
+
parsed = begin
|
|
152
|
+
JSON.parse(res.body.to_s.empty? ? "{}" : res.body)
|
|
153
|
+
rescue JSON::ParserError
|
|
154
|
+
{ "code" => -1, "message" => res.body.to_s[0, 200] }
|
|
155
|
+
end
|
|
156
|
+
[res.code.to_i, parsed.is_a?(Hash) ? parsed : { "code" => -1, "message" => parsed.to_s }]
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Generate one animated GIF and return its URL.
|
|
161
|
+
def self.generate(prompt, api_key: nil, **opts)
|
|
162
|
+
Client.new(api_key, **opts).generate(prompt)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Start one GIF and return its task id.
|
|
166
|
+
def self.submit(prompt, api_key: nil, **opts)
|
|
167
|
+
Client.new(api_key, **opts).submit(prompt)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Return the task record for task_id.
|
|
171
|
+
def self.status(task_id, api_key: nil, **opts)
|
|
172
|
+
Client.new(api_key, **opts).status(task_id)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Remaining credits on the account.
|
|
176
|
+
def self.credits(api_key: nil, **opts)
|
|
177
|
+
Client.new(api_key, **opts).credits
|
|
178
|
+
end
|
|
179
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: aigifmaker
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- hanshs474
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-11-09 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Turn text into animated GIFs with AI GIF Maker. Native GIF generation
|
|
14
|
+
— no video conversion. Type what you want and download your GIF in about a minute.
|
|
15
|
+
email:
|
|
16
|
+
- support@aigifmaker.ai
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- LICENSE
|
|
22
|
+
- README.md
|
|
23
|
+
- lib/aigifmaker.rb
|
|
24
|
+
- lib/aigifmaker/version.rb
|
|
25
|
+
homepage: https://aigifmaker.ai/?utm_source=rubygems&utm_medium=package
|
|
26
|
+
licenses:
|
|
27
|
+
- MIT
|
|
28
|
+
metadata:
|
|
29
|
+
homepage_uri: https://aigifmaker.ai/?utm_source=rubygems&utm_medium=package
|
|
30
|
+
source_code_uri: https://github.com/hanshs474/aigifmaker-rb
|
|
31
|
+
bug_tracker_uri: https://github.com/hanshs474/aigifmaker-rb/issues
|
|
32
|
+
rubygems_mfa_required: 'false'
|
|
33
|
+
post_install_message:
|
|
34
|
+
rdoc_options: []
|
|
35
|
+
require_paths:
|
|
36
|
+
- lib
|
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - ">="
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '2.6'
|
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
requirements: []
|
|
48
|
+
rubygems_version: 3.0.3.1
|
|
49
|
+
signing_key:
|
|
50
|
+
specification_version: 4
|
|
51
|
+
summary: 'Official SDK for AI GIF Maker (aigifmaker.ai): turn text into animated GIFs.'
|
|
52
|
+
test_files: []
|