defmodule Mix.Tasks.GenerateMockups do @moduledoc """ Generates product mockups using Printify or Printful APIs. This task automates the creation of product mockups for the SimpleshopTheme sample content. It downloads artwork from Unsplash, uploads it to the print-on-demand provider, creates products, and downloads the generated mockups. ## Usage # Printify (default) mix generate_mockups mix generate_mockups --cleanup mix generate_mockups --replace # Printful mix generate_mockups --provider printful mix generate_mockups --provider printful --mockups-only mix generate_mockups --provider printful --products-only mix generate_mockups --provider printful --cleanup ## Output Mockup images are saved to: priv/static/mockups/ """ use Mix.Task alias SimpleshopTheme.Mockups.Generator, as: PrintifyGenerator alias SimpleshopTheme.Mockups.PrintfulGenerator @shortdoc "Generates product mockups using Printify or Printful API" @impl Mix.Task def run(args) do Mix.Task.run("app.start") {opts, _, _} = OptionParser.parse(args, switches: [ cleanup: :boolean, replace: :boolean, search: :string, list_blueprints: :boolean, help: :boolean, provider: :string, mockups_only: :boolean, products_only: :boolean ], aliases: [ c: :cleanup, r: :replace, s: :search, l: :list_blueprints, h: :help, p: :provider ] ) provider = Keyword.get(opts, :provider, "printify") cond do opts[:help] -> print_help() provider == "printful" -> run_printful(opts) opts[:list_blueprints] -> list_blueprints() opts[:search] -> search_blueprints(opts[:search]) true -> run_printify(opts) end end defp print_help do Mix.shell().info(""" Mockup Generator ================ Generates product mockups using Printify or Printful APIs. Usage: mix generate_mockups [options] Common options: --provider, -p NAME Provider to use: "printify" (default) or "printful" --cleanup, -c Delete created products after downloading mockups --help, -h Show this help message Printify options: --replace, -r Delete ALL existing products before generating (with confirmation) --search, -s TERM Search for blueprints by name --list-blueprints List all available blueprint IDs and names Printful options: --mockups-only Only generate mockup images (skip product creation) --products-only Only create sync products (skip mockup images) Environment: PRINTIFY_API_TOKEN Required for Printify provider PRINTFUL_API_TOKEN Required for Printful provider Examples: mix generate_mockups mix generate_mockups --provider printful mix generate_mockups --provider printful --mockups-only mix generate_mockups --provider printful --cleanup """) end defp list_blueprints do Mix.shell().info("Fetching blueprints from Printify...") case PrintifyGenerator.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 PrintifyGenerator.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 # =========================================================================== # Printify # =========================================================================== defp run_printify(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"} ║ ╚═══════════════════════════════════════════╝ """) case System.get_env("PRINTIFY_API_TOKEN") do nil -> Mix.shell().error(""" Error: PRINTIFY_API_TOKEN environment variable is not set. Set it and retry: export PRINTIFY_API_TOKEN="your-token" mix generate_mockups """) _token -> if replace, do: purge_existing_printify_products() results = PrintifyGenerator.generate_all(cleanup: cleanup) successful = Enum.count(results, &match?({:ok, _, _, _}, &1)) failed = Enum.count(results, &match?({:error, _, _}, &1)) Mix.shell().info(""" Summary: #{successful} succeeded, #{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_printify_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 = PrintifyGenerator.purge_all_products(shop_id) Mix.shell().info("Deleted #{deleted} products.\n") else Mix.shell().info("Skipping purge.\n") end end end # =========================================================================== # Printful # =========================================================================== defp run_printful(opts) do mockups_only = Keyword.get(opts, :mockups_only, false) products_only = Keyword.get(opts, :products_only, false) cleanup = Keyword.get(opts, :cleanup, false) do_mockups = !products_only do_products = !mockups_only Mix.shell().info(""" ╔═══════════════════════════════════════════╗ ║ Printful Mockup Generator ║ ╠═══════════════════════════════════════════╣ ║ Mockups: #{if do_mockups, do: "ON ", else: "OFF"} ║ ║ Products: #{if do_products, do: "ON ", else: "OFF"} ║ ║ Cleanup: #{if cleanup, do: "ON ", else: "OFF"} ║ ╚═══════════════════════════════════════════╝ """) case System.get_env("PRINTFUL_API_TOKEN") do nil -> Mix.shell().error(""" Error: PRINTFUL_API_TOKEN environment variable is not set. Set it and retry: export PRINTFUL_API_TOKEN="your-token" mix generate_mockups --provider printful """) _token -> results = PrintfulGenerator.generate_all( mockups: do_mockups, products: do_products, cleanup: cleanup ) mockup_ok = Enum.count(results.mockups, &match?({:ok, _, _}, &1)) mockup_fail = Enum.count(results.mockups, &match?({:error, _, _}, &1)) product_ok = Enum.count(results.products, &match?({:ok, _, _}, &1)) product_fail = Enum.count(results.products, &match?({:error, _, _}, &1)) Mix.shell().info(""" Summary: Mockups: #{mockup_ok} succeeded, #{mockup_fail} failed Products: #{product_ok} succeeded, #{product_fail} failed """) if mockup_fail + product_fail > 0 do Mix.shell().error("Some operations failed. Check the output above for details.") end end end end