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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ Provider and query may be given as positionals after `s` / `-s`, or with `-P` /
| `freenaturestock` | [Free Nature Stock](https://freenaturestock.com) | Free CC0 nature & wildlife stock photography by Adrian Pelletier | Direct master full-resolution photo downloads (up to 20+ MB). |
| `goodfreephotos` | [Good Free Photos](https://goodfreephotos.com) | Free CC0 public domain photos, state parks, and landmarks by Yinan Chen | Direct master full-resolution photo downloads. |
| `wikimedia` | [Wikimedia Commons](https://commons.wikimedia.org) | Freely licensed and public domain educational & stock media repository | Direct master full-resolution image downloads. |
| `vecteezy` | [Vecteezy](https://vecteezy.com) | Free high-resolution stock photography and design assets | Direct master full-resolution photo downloads (up to 15+ MB). |

List providers at runtime via `ast help` (printed under **PROVIDERS**).

Expand Down Expand Up @@ -314,6 +315,7 @@ src/
freenaturestock.zig
goodfreephotos.zig
wikimedia.zig
vecteezy.zig
assets/ README banner and static assets
docs/ Provider implementation notes
install.sh curl | bash installer (Linux release binaries)
Expand Down
13 changes: 13 additions & 0 deletions docs/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,16 @@ This document summarizes the network endpoints, transport models, and image down
* Dimensions extracted from `imageinfo.width` and `imageinfo.height`.
* **Download**: Direct GET on full master resolution media via `upload.wikimedia.org`.
* **License**: Public Domain, CC0, and Creative Commons.

---

## 23. Vecteezy (`vecteezy.com`)

* **Search**: `GET https://www.vecteezy.com/search?qterm={query}&content_type=photo&license_class=free`
* **Format**: Structured search results containing embedded JSON-LD schema (`application/ld+json`).
* **Extraction**:
* Clean image IDs extracted from resource digits (`vecteezy-{resource_id}`).
* Descriptive prompt titles extracted and HTML unescaped from schema `name`.
* Master full-resolution URLs constructed by replacing `/non_2x/` previews with `/original/` master assets on Vecteezy CDN.
* **Download**: Direct GET on master original camera photo JPEG files (up to 15+ MB) with automated preview fallback.
* **License**: Vecteezy Free License.
11 changes: 6 additions & 5 deletions docs/stock_photo_sites.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ This document tracks all 24 stock photo websites listed in the [GrayGrids 21+ Be
## Summary Status

- **Total Sites Listed**: 24
- **Implemented & Working**: 13 (`unsplash`, `isorepublic`, `picjumbo`, `foodiesfeed`, `picography`, `gratisography`, `startupstockphotos`, `burst`, `jaymantri`, `publicdomainarchive`, `magdeleine`, `splitshire`, `deviantart`)
- **Implemented & Working**: 14 (`unsplash`, `isorepublic`, `picjumbo`, `foodiesfeed`, `picography`, `gratisography`, `startupstockphotos`, `burst`, `jaymantri`, `publicdomainarchive`, `magdeleine`, `splitshire`, `deviantart`, `vecteezy`)
- *(Additional Providers Implemented in `ast`)*: `aura` ([Aura.build](https://www.aura.build)), `negativespace` ([NegativeSpace.co](https://negativespace.co)), `skitterphoto` ([Skitterphoto.com](https://skitterphoto.com)), `libreshot` ([LibreShot.com](https://libreshot.com)), `moveast` ([Moveast.me](https://moveast.me)), `cupcake` ([Cupcake](https://cupcake.nilssonlee.se)), `freenaturestock` ([FreeNatureStock](https://freenaturestock.com)), `goodfreephotos` ([Good Free Photos](https://goodfreephotos.com)), `wikimedia` ([Wikimedia Commons](https://commons.wikimedia.org))
- **Unimplemented / Blocked / Pending**: 11
- **Unimplemented / Blocked / Pending**: 10

---

Expand Down Expand Up @@ -38,9 +38,10 @@ This document tracks all 24 stock photo websites listed in the [GrayGrids 21+ Be
- **Status**: Implemented & verified.
- **Notes**: Search and full master-resolution CC0 photo downloading supported.

- [ ] **Vecteezy** ([vecteezy.com](https://www.vecteezy.com/))
- **Status**: Unimplemented.
- **Notes**: Mixed free/pro licensing with attribution and auth requirements.
- [x] **Vecteezy** ([vecteezy.com](https://www.vecteezy.com/))
- **CLI Provider**: `vecteezy`
- **Status**: Implemented & verified.
- **Notes**: Free photo search and direct full-resolution master photo downloading (up to 15+ MB) via CloudFront CDN supported.

- [ ] **Freepik** ([freepik.com](https://www.freepik.com/))
- **Status**: Unimplemented.
Expand Down
9 changes: 9 additions & 0 deletions src/commands/search.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const cupcake = @import("../providers/cupcake.zig");
const freenaturestock = @import("../providers/freenaturestock.zig");
const goodfreephotos = @import("../providers/goodfreephotos.zig");
const wikimedia = @import("../providers/wikimedia.zig");
const vecteezy = @import("../providers/vecteezy.zig");
const stdio = @import("../stdio.zig");
const Allocator = std.mem.Allocator;
const Io = std.Io;
Expand Down Expand Up @@ -112,6 +113,9 @@ fn doSearch(
if (std.ascii.eqlIgnoreCase(provider_id, "wikimedia")) {
return wikimedia.search(client, allocator, query, limit);
}
if (std.ascii.eqlIgnoreCase(provider_id, "vecteezy")) {
return vecteezy.search(client, allocator, query, limit);
}
return error.UnknownProvider;
}

Expand Down Expand Up @@ -189,6 +193,9 @@ fn doDownload(
if (std.ascii.eqlIgnoreCase(provider_id, "wikimedia")) {
return wikimedia.download(client, allocator, io, a, output_dir);
}
if (std.ascii.eqlIgnoreCase(provider_id, "vecteezy")) {
return vecteezy.download(client, allocator, io, a, output_dir);
}
return error.UnknownProvider;
}

Expand All @@ -215,6 +222,7 @@ fn doPrompt(provider_id: []const u8, a: Asset) ?[]const u8 {
if (std.ascii.eqlIgnoreCase(provider_id, "freenaturestock")) return freenaturestock.getPrompt(a);
if (std.ascii.eqlIgnoreCase(provider_id, "goodfreephotos")) return goodfreephotos.getPrompt(a);
if (std.ascii.eqlIgnoreCase(provider_id, "wikimedia")) return wikimedia.getPrompt(a);
if (std.ascii.eqlIgnoreCase(provider_id, "vecteezy")) return vecteezy.getPrompt(a);
return a.prompt orelse a.description;
}

Expand All @@ -241,6 +249,7 @@ fn doUrl(provider_id: []const u8, a: Asset) ?[]const u8 {
if (std.ascii.eqlIgnoreCase(provider_id, "freenaturestock")) return freenaturestock.getUrl(a);
if (std.ascii.eqlIgnoreCase(provider_id, "goodfreephotos")) return goodfreephotos.getUrl(a);
if (std.ascii.eqlIgnoreCase(provider_id, "wikimedia")) return wikimedia.getUrl(a);
if (std.ascii.eqlIgnoreCase(provider_id, "vecteezy")) return vecteezy.getUrl(a);
return a.image_url;
}

Expand Down
9 changes: 9 additions & 0 deletions src/interactive.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const cupcake = @import("providers/cupcake.zig");
const freenaturestock = @import("providers/freenaturestock.zig");
const goodfreephotos = @import("providers/goodfreephotos.zig");
const wikimedia = @import("providers/wikimedia.zig");
const vecteezy = @import("providers/vecteezy.zig");
const registry = @import("providers/registry.zig");
const stdio = @import("stdio.zig");
const Allocator = std.mem.Allocator;
Expand Down Expand Up @@ -109,6 +110,9 @@ fn searchProvider(
if (std.ascii.eqlIgnoreCase(provider_id, "wikimedia")) {
return wikimedia.search(client, allocator, query, config.default_limit);
}
if (std.ascii.eqlIgnoreCase(provider_id, "vecteezy")) {
return vecteezy.search(client, allocator, query, config.default_limit);
}
return error.UnknownProvider;
}

Expand Down Expand Up @@ -185,6 +189,9 @@ fn downloadAsset(
if (std.ascii.eqlIgnoreCase(provider_id, "wikimedia")) {
return wikimedia.download(client, allocator, io, a, config.default_output_dir);
}
if (std.ascii.eqlIgnoreCase(provider_id, "vecteezy")) {
return vecteezy.download(client, allocator, io, a, config.default_output_dir);
}
return error.UnknownProvider;
}

Expand All @@ -211,6 +218,7 @@ fn promptOf(provider_id: []const u8, a: Asset) ?[]const u8 {
if (std.ascii.eqlIgnoreCase(provider_id, "freenaturestock")) return freenaturestock.getPrompt(a);
if (std.ascii.eqlIgnoreCase(provider_id, "goodfreephotos")) return goodfreephotos.getPrompt(a);
if (std.ascii.eqlIgnoreCase(provider_id, "wikimedia")) return wikimedia.getPrompt(a);
if (std.ascii.eqlIgnoreCase(provider_id, "vecteezy")) return vecteezy.getPrompt(a);
return a.prompt;
}

Expand All @@ -237,6 +245,7 @@ fn urlOf(provider_id: []const u8, a: Asset) ?[]const u8 {
if (std.ascii.eqlIgnoreCase(provider_id, "freenaturestock")) return freenaturestock.getUrl(a);
if (std.ascii.eqlIgnoreCase(provider_id, "goodfreephotos")) return goodfreephotos.getUrl(a);
if (std.ascii.eqlIgnoreCase(provider_id, "wikimedia")) return wikimedia.getUrl(a);
if (std.ascii.eqlIgnoreCase(provider_id, "vecteezy")) return vecteezy.getUrl(a);
return a.image_url;
}

Expand Down
4 changes: 3 additions & 1 deletion src/providers/registry.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const cupcake = @import("cupcake.zig");
const freenaturestock = @import("freenaturestock.zig");
const goodfreephotos = @import("goodfreephotos.zig");
const wikimedia = @import("wikimedia.zig");
const vecteezy = @import("vecteezy.zig");

pub const ProviderInfo = struct {
id: []const u8,
Expand Down Expand Up @@ -53,6 +54,7 @@ pub fn list() []const ProviderInfo {
.{ .id = freenaturestock.id, .name = freenaturestock.name },
.{ .id = goodfreephotos.id, .name = goodfreephotos.name },
.{ .id = wikimedia.id, .name = wikimedia.name },
.{ .id = vecteezy.id, .name = vecteezy.name },
};
}

Expand All @@ -71,5 +73,5 @@ pub fn displayName(id: []const u8) ?[]const u8 {
}

pub fn availableIds() []const u8 {
return "aura, unsplash, isorepublic, picjumbo, foodiesfeed, picography, gratisography, startupstockphotos, burst, jaymantri, publicdomainarchive, magdeleine, splitshire, deviantart, negativespace, skitterphoto, libreshot, moveast, cupcake, freenaturestock, goodfreephotos, wikimedia";
return "aura, unsplash, isorepublic, picjumbo, foodiesfeed, picography, gratisography, startupstockphotos, burst, jaymantri, publicdomainarchive, magdeleine, splitshire, deviantart, negativespace, skitterphoto, libreshot, moveast, cupcake, freenaturestock, goodfreephotos, wikimedia, vecteezy";
}
Loading
Loading