- Prefer Print Clever (72) for canvas, Monster Digital (29) for apparel and mugs where available, fall back to default providers - Rename Art Print products to Canvas (new Satin Canvas blueprint) - Add Canvas Prints category in Printify tag extraction - Add --replace/-r flag to purge existing Printify products before generating (with interactive confirmation) - Add purge_all_products/1 to generator module - Remove old art print mockups, add new canvas + extra provider mockups Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
237 lines
6.9 KiB
Elixir
237 lines
6.9 KiB
Elixir
defmodule Mix.Tasks.GenerateMockups do
|
|
@moduledoc """
|
|
Generates product mockups using the Printify API.
|
|
|
|
This task automates the creation of product mockups for the SimpleshopTheme
|
|
sample content. It downloads artwork from Unsplash, uploads it to Printify,
|
|
creates products, and downloads the generated mockups.
|
|
|
|
## Requirements
|
|
|
|
- A Printify account with API access
|
|
- The PRINTIFY_API_TOKEN environment variable must be set
|
|
|
|
## Usage
|
|
|
|
# Generate mockups (keeps products in Printify)
|
|
mix generate_mockups
|
|
|
|
# Delete existing products first, then generate fresh ones
|
|
mix generate_mockups --replace
|
|
|
|
# Generate mockups and delete products afterwards
|
|
mix generate_mockups --cleanup
|
|
|
|
# Search for available blueprints
|
|
mix generate_mockups --search "poster"
|
|
|
|
# List all blueprints
|
|
mix generate_mockups --list-blueprints
|
|
|
|
## Output
|
|
|
|
Mockup images are saved to: priv/static/mockups/
|
|
"""
|
|
|
|
use Mix.Task
|
|
|
|
alias SimpleshopTheme.Mockups.Generator, as: MockupGenerator
|
|
|
|
@shortdoc "Generates product mockups using Printify API"
|
|
|
|
@impl Mix.Task
|
|
def run(args) do
|
|
# Start required applications
|
|
Mix.Task.run("app.start")
|
|
|
|
{opts, _, _} =
|
|
OptionParser.parse(args,
|
|
switches: [
|
|
cleanup: :boolean,
|
|
replace: :boolean,
|
|
search: :string,
|
|
list_blueprints: :boolean,
|
|
help: :boolean
|
|
],
|
|
aliases: [
|
|
c: :cleanup,
|
|
r: :replace,
|
|
s: :search,
|
|
l: :list_blueprints,
|
|
h: :help
|
|
]
|
|
)
|
|
|
|
cond do
|
|
opts[:help] ->
|
|
print_help()
|
|
|
|
opts[:list_blueprints] ->
|
|
list_blueprints()
|
|
|
|
opts[:search] ->
|
|
search_blueprints(opts[:search])
|
|
|
|
true ->
|
|
generate_mockups(opts)
|
|
end
|
|
end
|
|
|
|
defp print_help do
|
|
Mix.shell().info("""
|
|
|
|
Printify Mockup Generator
|
|
=========================
|
|
|
|
Generates product mockups using the Printify API.
|
|
|
|
Usage:
|
|
mix generate_mockups [options]
|
|
|
|
Options:
|
|
--replace, -r Delete ALL existing products from Printify before generating (with confirmation)
|
|
--cleanup, -c Delete created products from Printify after downloading mockups
|
|
--search, -s TERM Search for blueprints by name
|
|
--list-blueprints List all available blueprint IDs and names
|
|
--help, -h Show this help message
|
|
|
|
Environment:
|
|
PRINTIFY_API_TOKEN Required. Your Printify API token.
|
|
|
|
Examples:
|
|
# Generate all mockups
|
|
export PRINTIFY_API_TOKEN="your-token"
|
|
mix generate_mockups
|
|
|
|
# Generate and cleanup
|
|
mix generate_mockups --cleanup
|
|
|
|
# Find blueprint IDs
|
|
mix generate_mockups --search "poster"
|
|
""")
|
|
end
|
|
|
|
defp list_blueprints do
|
|
Mix.shell().info("Fetching blueprints from Printify...")
|
|
|
|
case MockupGenerator.list_blueprints() do
|
|
blueprints when is_list(blueprints) ->
|
|
Mix.shell().info("\nAvailable Blueprints:\n")
|
|
|
|
blueprints
|
|
|> Enum.each(fn {id, title} ->
|
|
Mix.shell().info(" #{id}: #{title}")
|
|
end)
|
|
|
|
Mix.shell().info("\nTotal: #{length(blueprints)} blueprints")
|
|
|
|
{:error, reason} ->
|
|
Mix.shell().error("Error fetching blueprints: #{inspect(reason)}")
|
|
end
|
|
end
|
|
|
|
defp search_blueprints(term) do
|
|
Mix.shell().info("Searching for blueprints matching '#{term}'...")
|
|
|
|
case MockupGenerator.search_blueprints(term) do
|
|
results when is_list(results) ->
|
|
if results == [] do
|
|
Mix.shell().info("No blueprints found matching '#{term}'")
|
|
else
|
|
Mix.shell().info("\nMatching Blueprints:\n")
|
|
|
|
results
|
|
|> Enum.each(fn {id, title} ->
|
|
Mix.shell().info(" #{id}: #{title}")
|
|
end)
|
|
|
|
Mix.shell().info("\nFound: #{length(results)} blueprints")
|
|
end
|
|
|
|
{:error, reason} ->
|
|
Mix.shell().error("Error searching blueprints: #{inspect(reason)}")
|
|
end
|
|
end
|
|
|
|
defp generate_mockups(opts) do
|
|
cleanup = Keyword.get(opts, :cleanup, false)
|
|
replace = Keyword.get(opts, :replace, false)
|
|
|
|
Mix.shell().info("""
|
|
|
|
╔═══════════════════════════════════════════╗
|
|
║ Printify Mockup Generator ║
|
|
╠═══════════════════════════════════════════╣
|
|
║ Replace mode: #{if replace, do: "ON ", else: "OFF"} ║
|
|
║ Cleanup mode: #{if cleanup, do: "ON ", else: "OFF"} ║
|
|
╚═══════════════════════════════════════════╝
|
|
|
|
""")
|
|
|
|
# Verify API token is set
|
|
case System.get_env("PRINTIFY_API_TOKEN") do
|
|
nil ->
|
|
Mix.shell().error("""
|
|
Error: PRINTIFY_API_TOKEN environment variable is not set.
|
|
|
|
To get your API token:
|
|
1. Log in to Printify
|
|
2. Go to Settings > API tokens
|
|
3. Create a new token with required permissions
|
|
|
|
Then run:
|
|
export PRINTIFY_API_TOKEN="your-token"
|
|
mix generate_mockups
|
|
""")
|
|
|
|
_token ->
|
|
if replace, do: purge_existing_products()
|
|
|
|
results = MockupGenerator.generate_all(cleanup: cleanup)
|
|
|
|
# Summary
|
|
successful = Enum.count(results, &match?({:ok, _, _, _}, &1))
|
|
failed = Enum.count(results, &match?({:error, _, _}, &1))
|
|
|
|
Mix.shell().info("""
|
|
|
|
═══════════════════════════════════════════
|
|
Summary
|
|
═══════════════════════════════════════════
|
|
Successful: #{successful}
|
|
Failed: #{failed}
|
|
═══════════════════════════════════════════
|
|
""")
|
|
|
|
if failed > 0 do
|
|
Mix.shell().error(
|
|
"Some products failed to generate. Check the output above for details."
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
defp purge_existing_products do
|
|
alias SimpleshopTheme.Clients.Printify, as: Client
|
|
|
|
Mix.shell().info("Fetching existing products...")
|
|
|
|
{:ok, shop_id} = Client.get_shop_id()
|
|
{:ok, %{"data" => products}} = Client.list_products(shop_id)
|
|
count = length(products)
|
|
|
|
if count == 0 do
|
|
Mix.shell().info("No existing products to delete.\n")
|
|
else
|
|
Mix.shell().info("Found #{count} existing products in Printify.")
|
|
|
|
if Mix.shell().yes?("Delete all #{count} products before generating new ones?") do
|
|
deleted = MockupGenerator.purge_all_products(shop_id)
|
|
Mix.shell().info("Deleted #{deleted} products.\n")
|
|
else
|
|
Mix.shell().info("Skipping purge.\n")
|
|
end
|
|
end
|
|
end
|
|
end
|