diff --git a/Dockerfile b/Dockerfile index 86c17615..deab9600 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # WebHarbor — slim, self-contained image. -# 24 Flask mirror sites + control plane on :8101. +# 25 Flask mirror sites + control plane on :8101. FROM python:3.12-slim-bookworm @@ -72,6 +72,6 @@ os.makedirs('instance_seed', exist_ok=True); \ shutil.copy2('instance/rotten_tomatoes.db', 'instance_seed/rotten_tomatoes.db'); \ print('Rotten Tomatoes seed DB generated at build time.')" && rm -rf /opt/WebSyn/rotten_tomatoes/instance -EXPOSE 8101 40000-40023 +EXPOSE 8101 40000-40024 CMD ["/opt/websyn_start.sh"] diff --git a/README.md b/README.md index a05d266a..4287b02f 100644 --- a/README.md +++ b/README.md @@ -36,17 +36,17 @@ WebHarbor takes a different approach. We leverage coding agent (e.g., Claude Cod - **Deep features unlocked** — carts, checkouts, accounts, all fully testable - **Evolving** — harder tasks drive richer mirrors; the environment grows with agents - **RL-ready** — sub-second database resets between rollouts -- **Community-driven** — 24 sites today, scaling to 100+ together +- **Community-driven** — 25 sites today, scaling to 100+ together ## 🚀 Quickstart One command to run all web environments: ```bash -docker run -p 8101:8101 -p 40000-40023:40000-40023 battalion7244/webharbor:latest +docker run -p 8101:8101 -p 40000-40024:40000-40024 battalion7244/webharbor:latest ``` -Then point your agent at `http://localhost:40000` through `http://localhost:40023` to explore 24 local mirrors of WebVoyager sites: `Allrecipes, Amazon, Apple, ArXiv, BBC News, Booking, GitHub, Google Flights, Google Maps, Google Search, Hugging Face, Wolfram Alpha, Cambridge Dictionary, Coursera, ESPN, Merriam-Webster, IKEA, Phys.org, Target, TED, Ohio State University, Rotten Tomatoes, Compass, and Walmart Careers`. +Then point your agent at `http://localhost:40000` through `http://localhost:40024` to explore 25 local mirrors of WebVoyager sites: `Allrecipes, Amazon, Apple, ArXiv, BBC News, Booking, GitHub, Google Flights, Google Maps, Google Search, Hugging Face, Wolfram Alpha, Cambridge Dictionary, Coursera, ESPN, Merriam-Webster, IKEA, Phys.org, Target, TED, Ohio State University, Rotten Tomatoes, Compass, Walmart Careers, and Bandcamp`. For sub-second reset between rollouts, expose the control plane and call `/reset/`: @@ -65,7 +65,7 @@ git clone https://github.com/aiming-lab/WebHarbor && cd WebHarbor ## 🤝 Contribute -We have built 23 high-quality mirrors covering the [WebVoyager](https://github.com/MinorJerry/WebVoyager) benchmark. The next goal is **100+ sites**, covering everything in [Online-Mind2Web](https://huggingface.co/datasets/osunlp/Online-Mind2Web). We are inviting the community to build this together. +We have built 25 high-quality mirrors covering the [WebVoyager](https://github.com/MinorJerry/WebVoyager) benchmark. The next goal is **100+ sites**, covering everything in [Online-Mind2Web](https://huggingface.co/datasets/osunlp/Online-Mind2Web). We are inviting the community to build this together. There are two ways to join the author list: diff --git a/agent_demo/README.md b/agent_demo/README.md index 44c25e60..c3d75c28 100644 --- a/agent_demo/README.md +++ b/agent_demo/README.md @@ -19,7 +19,7 @@ export OPENAI_BASE_URL=https://api.openai.com/v1 # or your Azure / vLLM endpoi ## Run a task -WebHarbor must already be running locally (`docker run -p 8101:8101 -p 40000-40023:40000-40023 battalion7244/webharbor:latest`). +WebHarbor must already be running locally (`docker run -p 8101:8101 -p 40000-40024:40000-40024 battalion7244/webharbor:latest`). Run a single task from a site's `tasks.jsonl`: diff --git a/control_server.py b/control_server.py index 7df0d9ee..6aeddef5 100644 --- a/control_server.py +++ b/control_server.py @@ -26,7 +26,8 @@ 'allrecipes', 'amazon', 'apple', 'arxiv', 'bbc_news', 'booking', 'github', 'google_flights', 'google_map', 'google_search', 'huggingface', 'wolfram_alpha', 'cambridge_dictionary', - 'coursera', 'espn', 'merriam_webster', 'ikea', 'phys_org', 'target', 'ted', 'osu', 'rotten_tomatoes', 'compass', 'walmart_careers', + 'coursera', 'espn', 'merriam_webster', 'ikea', 'phys_org', 'target', 'ted', 'osu', + 'rotten_tomatoes', 'compass', 'walmart_careers', 'bandcamp', ] BASE_PORT = 40000 WEBSYN_DIR = '/opt/WebSyn' diff --git a/sites/bandcamp/_health.py b/sites/bandcamp/_health.py new file mode 100644 index 00000000..dea78054 --- /dev/null +++ b/sites/bandcamp/_health.py @@ -0,0 +1,35 @@ +"""Bandcamp mirror health check.""" +from healthcheck import random_user + + +def run(p): + p.assert_get("home", "/", must_contain="bandcamp") + p.assert_get("discover", "/discover", must_contain="Featured release") + p.assert_get("search", "/search?q=tidal", must_contain="Tidal Memory") + + user = random_user() + p.assert_post( + "register", + "/register", + { + "display_name": user["name"], + "username": user["username"], + "email": user["email"], + "city": "Seattle", + "password": user["password"], + "confirm_password": user["password"], + "favorite_format": "digital", + }, + accept_status=(200, 302, 303), + ) + p.get("/logout") + p.assert_post( + "login", + "/login", + { + "email": user["email"], + "password": user["password"], + }, + accept_status=(200, 302, 303), + ) + p.assert_get("account", "/account", must_contain=user["first_name"]) diff --git a/sites/bandcamp/app.py b/sites/bandcamp/app.py new file mode 100644 index 00000000..9e301399 --- /dev/null +++ b/sites/bandcamp/app.py @@ -0,0 +1,1553 @@ +#!/usr/bin/env python3 +"""Bandcamp mirror - Flask app with music discovery, merch, and fan flows.""" +from __future__ import annotations + +import os +import re +from datetime import date, datetime, timedelta + +from flask import abort, flash, redirect, render_template, request, url_for, Flask +from flask_login import ( + LoginManager, + UserMixin, + current_user, + login_required, + login_user, + logout_user, +) +from flask_sqlalchemy import SQLAlchemy +from werkzeug.security import check_password_hash, generate_password_hash + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +INSTANCE_DIR = os.path.join(BASE_DIR, "instance") +DB_PATH = os.path.join(INSTANCE_DIR, "bandcamp.db") +MIRROR_REFERENCE_NOW = datetime(2026, 5, 1, 12, 0, 0) +STOP_WORDS = { + "the", + "a", + "an", + "and", + "or", + "of", + "for", + "in", + "on", + "with", + "to", + "by", + "at", + "from", + "my", + "your", + "their", + "is", + "it", + "this", + "that", + "all", +} + +os.makedirs(INSTANCE_DIR, exist_ok=True) + +app = Flask(__name__, instance_path=INSTANCE_DIR) +app.config["SECRET_KEY"] = "webharbor-bandcamp-dev-secret" +app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{DB_PATH}" +app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + +db = SQLAlchemy(app) +login_manager = LoginManager(app) +login_manager.login_view = "login" +login_manager.login_message = "Log in to access your Bandcamp fan account." +login_manager.login_message_category = "info" + +album_tags = db.Table( + "album_tags", + db.Column("album_id", db.Integer, db.ForeignKey("albums.id"), primary_key=True), + db.Column("tag_id", db.Integer, db.ForeignKey("tags.id"), primary_key=True), +) + + +class User(db.Model, UserMixin): + __tablename__ = "users" + + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(80), unique=True, nullable=False, index=True) + email = db.Column(db.String(120), unique=True, nullable=False, index=True) + password_hash = db.Column(db.String(255), nullable=False) + display_name = db.Column(db.String(120), nullable=False) + bio = db.Column(db.Text, default="") + city = db.Column(db.String(80), default="") + country = db.Column(db.String(80), default="United States") + address_line1 = db.Column(db.String(200), default="") + postal_code = db.Column(db.String(30), default="") + favorite_format = db.Column(db.String(40), default="digital") + favorite_scene_id = db.Column(db.Integer, db.ForeignKey("scenes.id")) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + + favorite_scene = db.relationship("Scene", foreign_keys=[favorite_scene_id]) + cart_items = db.relationship("CartItem", backref="user", cascade="all, delete-orphan", lazy=True) + wishlist_items = db.relationship("WishlistItem", backref="user", cascade="all, delete-orphan", lazy=True) + orders = db.relationship("Order", backref="user", cascade="all, delete-orphan", lazy=True) + collection_items = db.relationship( + "FanCollectionItem", backref="user", cascade="all, delete-orphan", lazy=True + ) + comments = db.relationship("FanComment", backref="user", cascade="all, delete-orphan", lazy=True) + + def set_password(self, password: str) -> None: + self.password_hash = generate_password_hash(password) + + def check_password(self, password: str) -> bool: + return check_password_hash(self.password_hash, password) + + @property + def first_name(self) -> str: + return self.display_name.split(" ")[0] + + +class Genre(db.Model): + __tablename__ = "genres" + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(60), unique=True, nullable=False) + slug = db.Column(db.String(80), unique=True, nullable=False, index=True) + description = db.Column(db.Text, default="") + accent_color = db.Column(db.String(20), default="#0f5ea8") + + artists = db.relationship("Artist", backref="primary_genre", lazy=True) + albums = db.relationship("Album", backref="primary_genre", lazy=True) + + +class Scene(db.Model): + __tablename__ = "scenes" + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(120), unique=True, nullable=False) + slug = db.Column(db.String(120), unique=True, nullable=False, index=True) + country = db.Column(db.String(80), default="") + description = db.Column(db.Text, default="") + + artists = db.relationship("Artist", backref="scene", lazy=True) + albums = db.relationship("Album", backref="scene", lazy=True) + + +class Label(db.Model): + __tablename__ = "labels" + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(120), unique=True, nullable=False) + slug = db.Column(db.String(120), unique=True, nullable=False, index=True) + location = db.Column(db.String(120), default="") + description = db.Column(db.Text, default="") + + artists = db.relationship("Artist", backref="label", lazy=True) + albums = db.relationship("Album", backref="label", lazy=True) + + +class Artist(db.Model): + __tablename__ = "artists" + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(140), nullable=False) + slug = db.Column(db.String(160), unique=True, nullable=False, index=True) + location = db.Column(db.String(120), default="") + bio = db.Column(db.Text, default="") + headline = db.Column(db.String(180), default="") + formed_year = db.Column(db.Integer, default=2018) + follow_count = db.Column(db.Integer, default=0) + avatar_image = db.Column(db.String(255), default="") + hero_image = db.Column(db.String(255), default="") + scene_id = db.Column(db.Integer, db.ForeignKey("scenes.id"), nullable=False) + label_id = db.Column(db.Integer, db.ForeignKey("labels.id"), nullable=False) + primary_genre_id = db.Column(db.Integer, db.ForeignKey("genres.id"), nullable=False) + + albums = db.relationship("Album", backref="artist", cascade="all, delete-orphan", lazy=True) + merch_items = db.relationship("MerchItem", backref="artist", cascade="all, delete-orphan", lazy=True) + + +class Tag(db.Model): + __tablename__ = "tags" + + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(80), unique=True, nullable=False) + slug = db.Column(db.String(80), unique=True, nullable=False, index=True) + + albums = db.relationship("Album", secondary=album_tags, back_populates="tags", lazy=True) + + +class Album(db.Model): + __tablename__ = "albums" + + id = db.Column(db.Integer, primary_key=True) + artist_id = db.Column(db.Integer, db.ForeignKey("artists.id"), nullable=False) + label_id = db.Column(db.Integer, db.ForeignKey("labels.id"), nullable=False) + primary_genre_id = db.Column(db.Integer, db.ForeignKey("genres.id"), nullable=False) + scene_id = db.Column(db.Integer, db.ForeignKey("scenes.id"), nullable=False) + title = db.Column(db.String(160), nullable=False) + slug = db.Column(db.String(180), unique=True, nullable=False, index=True) + description = db.Column(db.Text, default="") + story = db.Column(db.Text, default="") + cover_image = db.Column(db.String(255), default="") + header_image = db.Column(db.String(255), default="") + price = db.Column(db.Float, default=8.0) + release_date = db.Column(db.Date, nullable=False) + track_count = db.Column(db.Integer, default=0) + duration_seconds = db.Column(db.Integer, default=0) + fan_count = db.Column(db.Integer, default=0) + catalog_no = db.Column(db.String(40), default="") + is_featured = db.Column(db.Boolean, default=False) + is_new = db.Column(db.Boolean, default=False) + is_editorial = db.Column(db.Boolean, default=False) + + tracks = db.relationship("Track", backref="album", cascade="all, delete-orphan", lazy=True) + variants = db.relationship("FormatVariant", backref="album", cascade="all, delete-orphan", lazy=True) + comments = db.relationship("FanComment", backref="album", cascade="all, delete-orphan", lazy=True) + merch_items = db.relationship("MerchItem", backref="album", lazy=True) + tags = db.relationship("Tag", secondary=album_tags, back_populates="albums", lazy=True) + + def price_from(self) -> float: + prices = [variant.price for variant in self.variants] + return min(prices) if prices else self.price + + def format_kinds(self) -> list[str]: + return [variant.kind for variant in self.variants] + + def variant_for_kind(self, kind: str) -> "FormatVariant | None": + for variant in self.variants: + if variant.kind == kind: + return variant + return None + + def primary_tag_names(self) -> list[str]: + return [tag.name for tag in self.tags[:4]] + + def focus_track(self) -> "Track | None": + for track in self.tracks: + if track.is_focus_track: + return track + return self.tracks[0] if self.tracks else None + + +class Track(db.Model): + __tablename__ = "tracks" + + id = db.Column(db.Integer, primary_key=True) + album_id = db.Column(db.Integer, db.ForeignKey("albums.id"), nullable=False) + title = db.Column(db.String(160), nullable=False) + slug = db.Column(db.String(180), unique=True, nullable=False, index=True) + track_number = db.Column(db.Integer, nullable=False) + duration_seconds = db.Column(db.Integer, default=180) + preview_hook = db.Column(db.String(255), default="") + lyrics_excerpt = db.Column(db.Text, default="") + is_focus_track = db.Column(db.Boolean, default=False) + + comments = db.relationship("FanComment", backref="track", lazy=True) + + +class MerchItem(db.Model): + __tablename__ = "merch_items" + + id = db.Column(db.Integer, primary_key=True) + artist_id = db.Column(db.Integer, db.ForeignKey("artists.id"), nullable=False) + album_id = db.Column(db.Integer, db.ForeignKey("albums.id")) + title = db.Column(db.String(160), nullable=False) + slug = db.Column(db.String(180), unique=True, nullable=False, index=True) + item_type = db.Column(db.String(50), default="shirt") + description = db.Column(db.Text, default="") + short_blurb = db.Column(db.String(200), default="") + image = db.Column(db.String(255), default="") + price = db.Column(db.Float, default=20.0) + inventory = db.Column(db.Integer, default=100) + release_date = db.Column(db.Date, nullable=False) + is_featured = db.Column(db.Boolean, default=False) + + variants = db.relationship("FormatVariant", backref="merch_item", cascade="all, delete-orphan", lazy=True) + + +class FormatVariant(db.Model): + __tablename__ = "format_variants" + + id = db.Column(db.Integer, primary_key=True) + album_id = db.Column(db.Integer, db.ForeignKey("albums.id")) + merch_item_id = db.Column(db.Integer, db.ForeignKey("merch_items.id")) + kind = db.Column(db.String(50), nullable=False) + name = db.Column(db.String(120), nullable=False) + option_a = db.Column(db.String(80), default="") + option_b = db.Column(db.String(80), default="") + price = db.Column(db.Float, default=0.0) + inventory = db.Column(db.Integer, default=9999) + sku = db.Column(db.String(80), unique=True, nullable=False) + shipping_note = db.Column(db.String(160), default="") + edition_note = db.Column(db.String(200), default="") + is_default = db.Column(db.Boolean, default=False) + + def label_text(self) -> str: + extras = [piece for piece in [self.option_a, self.option_b] if piece] + return f"{self.name} / {' / '.join(extras)}" if extras else self.name + + +class FanComment(db.Model): + __tablename__ = "fan_comments" + + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) + album_id = db.Column(db.Integer, db.ForeignKey("albums.id")) + track_id = db.Column(db.Integer, db.ForeignKey("tracks.id")) + headline = db.Column(db.String(140), default="") + body = db.Column(db.Text, default="") + rating = db.Column(db.Integer, default=5) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + + +class WishlistItem(db.Model): + __tablename__ = "wishlist_items" + + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) + album_id = db.Column(db.Integer, db.ForeignKey("albums.id")) + merch_item_id = db.Column(db.Integer, db.ForeignKey("merch_items.id")) + added_at = db.Column(db.DateTime, default=datetime.utcnow) + + album = db.relationship("Album") + merch_item = db.relationship("MerchItem") + + def title(self) -> str: + return self.album.title if self.album else self.merch_item.title + + def image_path(self) -> str: + return self.album.cover_image if self.album else self.merch_item.image + + def artist_name(self) -> str: + return self.album.artist.name if self.album else self.merch_item.artist.name + + +class CartItem(db.Model): + __tablename__ = "cart_items" + + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) + album_id = db.Column(db.Integer, db.ForeignKey("albums.id")) + merch_item_id = db.Column(db.Integer, db.ForeignKey("merch_items.id")) + format_variant_id = db.Column(db.Integer, db.ForeignKey("format_variants.id")) + quantity = db.Column(db.Integer, default=1) + added_at = db.Column(db.DateTime, default=datetime.utcnow) + + album = db.relationship("Album") + merch_item = db.relationship("MerchItem") + format_variant = db.relationship("FormatVariant") + + def subject(self) -> Album | MerchItem: + return self.album if self.album else self.merch_item + + def title(self) -> str: + return self.subject().title + + def artist_name(self) -> str: + return self.subject().artist.name + + def image_path(self) -> str: + target = self.subject() + return target.cover_image if isinstance(target, Album) else target.image + + def variant_label(self) -> str: + return self.format_variant.label_text() if self.format_variant else "Standard" + + def unit_price(self) -> float: + return self.format_variant.price if self.format_variant else self.subject().price + + def line_total(self) -> float: + return self.unit_price() * self.quantity + + +class Order(db.Model): + __tablename__ = "orders" + + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) + order_number = db.Column(db.String(60), unique=True, nullable=False) + status = db.Column(db.String(40), default="completed") + subtotal = db.Column(db.Float, default=0.0) + shipping = db.Column(db.Float, default=0.0) + tax = db.Column(db.Float, default=0.0) + total = db.Column(db.Float, default=0.0) + shipping_name = db.Column(db.String(140), default="") + shipping_line1 = db.Column(db.String(200), default="") + shipping_city = db.Column(db.String(120), default="") + shipping_country = db.Column(db.String(120), default="") + payment_label = db.Column(db.String(120), default="") + note = db.Column(db.String(240), default="") + placed_at = db.Column(db.DateTime, default=datetime.utcnow) + + items = db.relationship("OrderItem", backref="order", cascade="all, delete-orphan", lazy=True) + + def item_count(self) -> int: + return sum(item.quantity for item in self.items) + + +class OrderItem(db.Model): + __tablename__ = "order_items" + + id = db.Column(db.Integer, primary_key=True) + order_id = db.Column(db.Integer, db.ForeignKey("orders.id"), nullable=False) + album_id = db.Column(db.Integer, db.ForeignKey("albums.id")) + merch_item_id = db.Column(db.Integer, db.ForeignKey("merch_items.id")) + format_variant_id = db.Column(db.Integer, db.ForeignKey("format_variants.id")) + title = db.Column(db.String(200), nullable=False) + artist_name = db.Column(db.String(140), default="") + image_path = db.Column(db.String(255), default="") + variant_label = db.Column(db.String(160), default="") + quantity = db.Column(db.Integer, default=1) + unit_price = db.Column(db.Float, default=0.0) + + album = db.relationship("Album") + merch_item = db.relationship("MerchItem") + format_variant = db.relationship("FormatVariant") + + def line_total(self) -> float: + return self.quantity * self.unit_price + + +class FanCollectionItem(db.Model): + __tablename__ = "fan_collection_items" + + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) + album_id = db.Column(db.Integer, db.ForeignKey("albums.id"), nullable=False) + format_variant_id = db.Column(db.Integer, db.ForeignKey("format_variants.id")) + favorite_track_id = db.Column(db.Integer, db.ForeignKey("tracks.id")) + acquired_via = db.Column(db.String(80), default="purchase") + notes = db.Column(db.String(240), default="") + added_at = db.Column(db.DateTime, default=datetime.utcnow) + + album = db.relationship("Album") + format_variant = db.relationship("FormatVariant") + favorite_track = db.relationship("Track") + + +@login_manager.user_loader +def load_user(user_id: str) -> User | None: + return db.session.get(User, int(user_id)) + + +def slugify(value: str) -> str: + value = value.lower().strip() + value = re.sub(r"[^a-z0-9]+", "-", value) + return re.sub(r"-{2,}", "-", value).strip("-") + + +def safe_next_url(raw: str | None) -> str | None: + if not raw or raw.startswith("//") or not raw.startswith("/"): + return None + return raw + + +def money(amount: float) -> str: + return f"${amount:,.2f}" + + +def compact_date(value: date | datetime) -> str: + if isinstance(value, datetime): + return value.strftime("%b %d, %Y") + return value.strftime("%b %d, %Y") + + +def long_date(value: date | datetime) -> str: + if isinstance(value, datetime): + return value.strftime("%B %d, %Y") + return value.strftime("%B %d, %Y") + + +def duration_label(seconds: int) -> str: + minutes, secs = divmod(seconds, 60) + return f"{minutes}:{secs:02d}" + + +def tokenize(text: str) -> list[str]: + return [ + token + for token in re.split(r"[^a-z0-9]+", (text or "").lower()) + if token and token not in STOP_WORDS and len(token) > 1 + ] + + +def scored_search(query: str, items: list, text_builder) -> list: + terms = tokenize(query) + if not terms: + return items + lowered_query = query.lower().strip() + scored: list[tuple[int, object]] = [] + for item in items: + haystack = text_builder(item).lower() + score = 10 if lowered_query and lowered_query in haystack else 0 + for term in terms: + if re.search(rf"\b{re.escape(term)}\b", haystack): + score += 4 + elif term in haystack: + score += 2 + if score > 0: + scored.append((score, item)) + scored.sort(key=lambda pair: pair[0], reverse=True) + return [item for _, item in scored] + + +def album_blob(album: Album) -> str: + return " ".join( + [ + album.title, + album.artist.name, + album.primary_genre.name, + album.scene.name, + album.label.name, + " ".join(tag.name for tag in album.tags), + " ".join(track.title for track in album.tracks[:3]), + album.description, + album.story, + ] + ) + + +def artist_blob(artist: Artist) -> str: + return " ".join([artist.name, artist.primary_genre.name, artist.location, artist.bio, artist.label.name]) + + +def track_blob(track: Track) -> str: + return " ".join( + [track.title, track.album.title, track.album.artist.name, track.album.primary_genre.name, track.preview_hook] + ) + + +def merch_blob(merch: MerchItem) -> str: + return " ".join( + [ + merch.title, + merch.artist.name, + merch.item_type, + merch.short_blurb, + merch.description, + " ".join(variant.label_text() for variant in merch.variants), + ] + ) + + +def get_cart_items() -> list[CartItem]: + if not current_user.is_authenticated: + return [] + return ( + CartItem.query.filter_by(user_id=current_user.id) + .order_by(CartItem.added_at.desc(), CartItem.id.desc()) + .all() + ) + + +def cart_summary(items: list[CartItem]) -> dict[str, float]: + subtotal = round(sum(item.line_total() for item in items), 2) + shipping = 0.0 if subtotal >= 45 or subtotal == 0 else 6.5 + tax = round(subtotal * 0.0825, 2) + total = round(subtotal + shipping + tax, 2) + return {"subtotal": subtotal, "shipping": shipping, "tax": tax, "total": total} + + +def latest_support_items(limit: int = 8) -> list[OrderItem]: + return ( + OrderItem.query.join(Order) + .order_by(Order.placed_at.desc(), OrderItem.id.desc()) + .limit(limit) + .all() + ) + + +def generate_order_number() -> str: + next_id = (db.session.query(db.func.count(Order.id)).scalar() or 0) + 1 + return f"BC-20260501-{next_id:04d}" + + +def add_collection_item(user: User, album: Album, variant: FormatVariant | None, note: str = "") -> None: + existing = FanCollectionItem.query.filter_by(user_id=user.id, album_id=album.id).first() + if existing: + return + focus_track = album.focus_track() + db.session.add( + FanCollectionItem( + user_id=user.id, + album_id=album.id, + format_variant_id=variant.id if variant else None, + favorite_track_id=focus_track.id if focus_track else None, + acquired_via="purchase", + notes=note, + added_at=MIRROR_REFERENCE_NOW, + ) + ) + + +@app.template_filter("money") +def money_filter(value: float) -> str: + return money(value) + + +@app.template_filter("duration") +def duration_filter(value: int) -> str: + return duration_label(value) + + +@app.template_filter("longdate") +def longdate_filter(value: date | datetime) -> str: + return long_date(value) + + +@app.template_filter("compactdate") +def compactdate_filter(value: date | datetime) -> str: + return compact_date(value) + + +@app.context_processor +def inject_globals(): + genres = Genre.query.order_by(Genre.name.asc()).limit(12).all() + format_nav = [ + ("all", "All"), + ("digital", "Digital"), + ("vinyl", "Vinyl"), + ("cassette", "Cassette"), + ("cd", "CD"), + ("shirt", "Merch"), + ] + if current_user.is_authenticated: + cart_count = sum(item.quantity for item in get_cart_items()) + wishlist_count = WishlistItem.query.filter_by(user_id=current_user.id).count() + else: + cart_count = 0 + wishlist_count = 0 + return { + "genre_nav": genres, + "format_nav": format_nav, + "cart_count": cart_count, + "wishlist_count": wishlist_count, + } + + +@app.route("/") +def index(): + return render_template( + "index.html", + featured_albums=Album.query.filter_by(is_featured=True).order_by(Album.release_date.desc()).limit(6).all(), + new_releases=Album.query.order_by(Album.release_date.desc()).limit(10).all(), + editorial=Album.query.filter_by(is_editorial=True).order_by(Album.release_date.desc()).limit(4).all(), + featured_merch=MerchItem.query.filter_by(is_featured=True).order_by(MerchItem.release_date.desc()).limit(8).all(), + scenes=Scene.query.order_by(Scene.name.asc()).limit(9).all(), + support_items=latest_support_items(9), + support_total=round(sum(order.total for order in Order.query.all()), 2), + ) + + +@app.route("/editorial") +def editorial_page(): + editorial = Album.query.filter_by(is_editorial=True).order_by(Album.release_date.desc()).all() + recent = Album.query.order_by(Album.release_date.desc()).limit(16).all() + seen: set[int] = set() + merged: list[Album] = [] + for album in editorial + recent: + if album.id not in seen: + seen.add(album.id) + merged.append(album) + features = merged[:8] + daily = [ + {"kicker": ["Album of the Day", "Scene Report", "Label Focus", "Artist Profile"][index % 4], "album": album} + for index, album in enumerate(features) + ] + return render_template("editorial.html", daily=daily, + scenes=Scene.query.order_by(Scene.name.asc()).limit(6).all()) + + +@app.route("/radio") +def radio_page(): + return render_template( + "radio.html", + shows=Genre.query.order_by(Genre.name.asc()).limit(6).all(), + episodes=Album.query.order_by(Album.release_date.desc()).limit(10).all(), + ) + + +@app.route("/gift-cards", methods=["GET", "POST"]) +def gift_cards(): + if request.method == "POST": + code = (request.form.get("code") or "").strip().upper() + if re.fullmatch(r"BC-GC-[A-Z0-9]{4}-[A-Z0-9]{4}", code): + flash(f"Gift card {code} is valid. Balance added to this demo account.", "success") + else: + flash("That gift card code is not recognized.", "error") + return redirect(url_for("gift_cards")) + return render_template("gift_cards.html", denominations=[10, 25, 50, 100]) + + +@app.route("/discover") +def discover(): + selected_genre = request.args.get("genre", "all") + selected_scene = request.args.get("scene", "all") + selected_format = request.args.get("format", "all") + selected_tag = request.args.get("tag", "all") + sort = request.args.get("sort", "new") + query = request.args.get("q", "").strip() + view = request.args.get("view", "albums") + + genres = Genre.query.order_by(Genre.name.asc()).all() + scenes = Scene.query.order_by(Scene.name.asc()).all() + tags = Tag.query.order_by(Tag.name.asc()).all() + albums = Album.query.order_by(Album.release_date.desc()).all() + merch = MerchItem.query.order_by(MerchItem.release_date.desc()).all() + + if selected_genre != "all": + albums = [album for album in albums if album.primary_genre.slug == selected_genre] + merch = [item for item in merch if item.artist.primary_genre.slug == selected_genre] + if selected_scene != "all": + albums = [album for album in albums if album.scene.slug == selected_scene] + merch = [item for item in merch if item.artist.scene.slug == selected_scene] + if selected_tag != "all": + albums = [album for album in albums if any(tag.slug == selected_tag for tag in album.tags)] + if selected_format != "all": + albums = [album for album in albums if any(variant.kind == selected_format for variant in album.variants)] + if selected_format in {"shirt", "hoodie", "poster", "tote", "slipmat", "pin", "zine"}: + merch = [item for item in merch if item.item_type == selected_format] + + if query: + albums = scored_search(query, albums, album_blob) + merch = scored_search(query, merch, merch_blob) + else: + if sort == "price-low": + albums.sort(key=lambda album: (album.price_from(), album.release_date)) + merch.sort(key=lambda item: item.price) + elif sort == "price-high": + albums.sort(key=lambda album: (album.price_from(), album.release_date), reverse=True) + merch.sort(key=lambda item: item.price, reverse=True) + elif sort == "popular": + albums.sort(key=lambda album: (album.fan_count, album.release_date), reverse=True) + merch.sort(key=lambda item: item.inventory) + else: + albums.sort(key=lambda album: (album.release_date, album.fan_count), reverse=True) + merch.sort(key=lambda item: item.release_date, reverse=True) + + return render_template( + "discover.html", + albums=albums[:30], + merch_items=merch[:30], + featured_album=albums[0] if albums else None, + genres=genres, + scenes=scenes, + tags=tags, + selected_genre=selected_genre, + selected_scene=selected_scene, + selected_format=selected_format, + selected_tag=selected_tag, + sort=sort, + query=query, + view=view, + ) + + +@app.route("/search") +def search(): + query = request.args.get("q", "").strip() + artists = scored_search(query, Artist.query.all(), artist_blob)[:8] if query else [] + albums = scored_search(query, Album.query.all(), album_blob)[:12] if query else [] + tracks = scored_search(query, Track.query.all(), track_blob)[:10] if query else [] + merch_items = scored_search(query, MerchItem.query.all(), merch_blob)[:8] if query else [] + return render_template( + "search.html", + query=query, + artists=artists, + albums=albums, + tracks=tracks, + merch_items=merch_items, + ) + + +@app.route("/artist/") +def artist_page(slug: str): + artist = Artist.query.filter_by(slug=slug).first_or_404() + albums = Album.query.filter_by(artist_id=artist.id).order_by(Album.release_date.desc()).all() + merch_items = MerchItem.query.filter_by(artist_id=artist.id).order_by(MerchItem.release_date.desc()).all() + collection_count = FanCollectionItem.query.join(Album).filter(Album.artist_id == artist.id).count() + return render_template( + "artist.html", + artist=artist, + albums=albums, + merch_items=merch_items, + collection_count=collection_count, + ) + + +@app.route("/label/") +def label_page(slug: str): + label = Label.query.filter_by(slug=slug).first_or_404() + return render_template( + "label.html", + label=label, + artists=Artist.query.filter_by(label_id=label.id).order_by(Artist.name.asc()).all(), + albums=Album.query.filter_by(label_id=label.id).order_by(Album.release_date.desc()).all(), + ) + + +@app.route("/scene/") +def scene_page(slug: str): + scene = Scene.query.filter_by(slug=slug).first_or_404() + return render_template( + "scene.html", + scene=scene, + artists=Artist.query.filter_by(scene_id=scene.id).order_by(Artist.name.asc()).all(), + albums=Album.query.filter_by(scene_id=scene.id).order_by(Album.release_date.desc()).all(), + ) + + +@app.route("/album/") +def album_page(slug: str): + album = Album.query.filter_by(slug=slug).first_or_404() + related_merch = MerchItem.query.filter_by(artist_id=album.artist_id).order_by(MerchItem.release_date.desc()).limit(4).all() + recommended = ( + Album.query.filter( + Album.primary_genre_id == album.primary_genre_id, + Album.artist_id != album.artist_id, + Album.id != album.id, + ) + .order_by(Album.release_date.desc()) + .limit(4) + .all() + ) + in_wishlist = current_user.is_authenticated and ( + WishlistItem.query.filter_by(user_id=current_user.id, album_id=album.id).first() is not None + ) + return render_template( + "album.html", + album=album, + related_merch=related_merch, + recommended=recommended, + in_wishlist=in_wishlist, + ) + + +@app.route("/track/") +def track_page(slug: str): + track = Track.query.filter_by(slug=slug).first_or_404() + return render_template("track.html", track=track, album=track.album) + + +@app.route("/merch/") +def merch_page(slug: str): + merch = MerchItem.query.filter_by(slug=slug).first_or_404() + related = ( + MerchItem.query.filter(MerchItem.artist_id == merch.artist_id, MerchItem.id != merch.id) + .order_by(MerchItem.release_date.desc()) + .limit(4) + .all() + ) + in_wishlist = current_user.is_authenticated and ( + WishlistItem.query.filter_by(user_id=current_user.id, merch_item_id=merch.id).first() is not None + ) + return render_template("merch.html", merch=merch, related=related, in_wishlist=in_wishlist) + + +@app.route("/compare/releases") +def compare_releases(): + featured = Album.query.filter_by(is_featured=True).order_by(Album.release_date.desc()).limit(10).all() + left_slug = request.args.get("left") + right_slug = request.args.get("right") + left = Album.query.filter_by(slug=left_slug).first() if left_slug else (featured[0] if featured else None) + right = Album.query.filter_by(slug=right_slug).first() if right_slug else (featured[1] if len(featured) > 1 else None) + return render_template("compare.html", left=left, right=right, featured=featured) + + +@app.route("/login", methods=["GET", "POST"]) +def login(): + if current_user.is_authenticated: + return redirect(url_for("account")) + if request.method == "POST": + email = request.form.get("email", "").strip().lower() + password = request.form.get("password", "") + user = User.query.filter_by(email=email).first() + if not user or not user.check_password(password): + flash("Invalid email or password.", "error") + else: + login_user(user) + flash(f"Welcome back, {user.display_name}.", "success") + return redirect(safe_next_url(request.form.get("next") or request.args.get("next")) or url_for("account")) + return render_template("login.html", next_url=safe_next_url(request.args.get("next"))) + + +@app.route("/register", methods=["GET", "POST"]) +def register(): + if current_user.is_authenticated: + return redirect(url_for("account")) + if request.method == "POST": + display_name = request.form.get("display_name", "").strip() + username = slugify(request.form.get("username", "").strip()) + email = request.form.get("email", "").strip().lower() + password = request.form.get("password", "") + confirm = request.form.get("confirm_password", "") + city = request.form.get("city", "").strip() + favorite_format = request.form.get("favorite_format", "digital") + if not display_name or not username or not email or not password: + flash("Name, username, email, and password are required.", "error") + elif password != confirm: + flash("Passwords do not match.", "error") + elif User.query.filter_by(email=email).first(): + flash("That email is already registered.", "error") + elif User.query.filter_by(username=username).first(): + flash("That username is already taken.", "error") + else: + user = User( + display_name=display_name, + username=username, + email=email, + city=city, + favorite_format=favorite_format, + ) + user.set_password(password) + db.session.add(user) + db.session.commit() + login_user(user) + flash("Your fan account is ready.", "success") + return redirect(url_for("account")) + return render_template("register.html") + + +@app.route("/logout") +@login_required +def logout(): + logout_user() + flash("You have been signed out.", "info") + return redirect(url_for("index")) + + +@app.route("/account") +@login_required +def account(): + return render_template( + "account.html", + orders=Order.query.filter_by(user_id=current_user.id).order_by(Order.placed_at.desc()).limit(5).all(), + collection_count=FanCollectionItem.query.filter_by(user_id=current_user.id).count(), + wishlist_count=WishlistItem.query.filter_by(user_id=current_user.id).count(), + cart_count=CartItem.query.filter_by(user_id=current_user.id).count(), + scenes=Scene.query.order_by(Scene.name.asc()).all(), + ) + + +@app.route("/account/edit", methods=["GET", "POST"]) +@login_required +def account_edit(): + scenes = Scene.query.order_by(Scene.name.asc()).all() + if request.method == "POST": + current_user.display_name = request.form.get("display_name", "").strip() or current_user.display_name + current_user.bio = request.form.get("bio", "").strip() + current_user.city = request.form.get("city", "").strip() + current_user.country = request.form.get("country", "").strip() or "United States" + current_user.address_line1 = request.form.get("address_line1", "").strip() + current_user.postal_code = request.form.get("postal_code", "").strip() + current_user.favorite_format = request.form.get("favorite_format", "digital") + scene = Scene.query.filter_by(slug=request.form.get("favorite_scene", "")).first() + current_user.favorite_scene_id = scene.id if scene else None + db.session.commit() + flash("Profile updated.", "success") + return redirect(url_for("account")) + return render_template("account_edit.html", scenes=scenes) + + +@app.route("/wishlist") +@login_required +def wishlist(): + return render_template( + "wishlist.html", + items=WishlistItem.query.filter_by(user_id=current_user.id).order_by(WishlistItem.added_at.desc()).all(), + ) + + +@app.route("/wishlist/add", methods=["POST"]) +@login_required +def wishlist_add(): + album_id = request.form.get("album_id", type=int) + merch_id = request.form.get("merch_id", type=int) + if not album_id and not merch_id: + flash("Nothing was selected to save.", "error") + return redirect(request.referrer or url_for("discover")) + existing = WishlistItem.query.filter_by(user_id=current_user.id, album_id=album_id, merch_item_id=merch_id).first() + if existing: + flash("That item is already in your wishlist.", "info") + else: + db.session.add(WishlistItem(user_id=current_user.id, album_id=album_id, merch_item_id=merch_id)) + db.session.commit() + flash("Saved to your wishlist.", "success") + return redirect(request.referrer or url_for("wishlist")) + + +@app.route("/wishlist/remove/", methods=["POST"]) +@login_required +def wishlist_remove(item_id: int): + item = WishlistItem.query.filter_by(id=item_id, user_id=current_user.id).first_or_404() + db.session.delete(item) + db.session.commit() + flash("Removed from your wishlist.", "info") + return redirect(url_for("wishlist")) + + +@app.route("/cart") +@login_required +def cart(): + items = get_cart_items() + return render_template("cart.html", items=items, summary=cart_summary(items)) + + +@app.route("/cart/add", methods=["POST"]) +@login_required +def cart_add(): + album_id = request.form.get("album_id", type=int) + merch_id = request.form.get("merch_id", type=int) + variant_id = request.form.get("variant_id", type=int) + quantity = max(1, request.form.get("quantity", type=int) or 1) + variant = db.session.get(FormatVariant, variant_id) if variant_id else None + + if album_id: + album = db.session.get(Album, album_id) + if not album: + abort(404) + if variant and variant.album_id != album.id: + flash("Selected format is not available for that album.", "error") + return redirect(request.referrer or url_for("album_page", slug=album.slug)) + subject_album_id = album.id + subject_merch_id = None + subject_title = album.title + elif merch_id: + merch = db.session.get(MerchItem, merch_id) + if not merch: + abort(404) + if variant and variant.merch_item_id != merch.id: + flash("Selected variant is not available for that merch item.", "error") + return redirect(request.referrer or url_for("merch_page", slug=merch.slug)) + subject_album_id = None + subject_merch_id = merch.id + subject_title = merch.title + else: + flash("No product was selected.", "error") + return redirect(url_for("cart")) + + if variant and variant.inventory < quantity: + flash("That variant does not have enough stock left.", "error") + return redirect(request.referrer or url_for("cart")) + + existing = CartItem.query.filter_by( + user_id=current_user.id, + album_id=subject_album_id, + merch_item_id=subject_merch_id, + format_variant_id=variant.id if variant else None, + ).first() + if existing: + existing.quantity += quantity + else: + db.session.add( + CartItem( + user_id=current_user.id, + album_id=subject_album_id, + merch_item_id=subject_merch_id, + format_variant_id=variant.id if variant else None, + quantity=quantity, + ) + ) + db.session.commit() + flash(f"Added {subject_title} to your cart.", "success") + if request.form.get("next") == "checkout": + return redirect(url_for("checkout")) + return redirect(request.referrer or url_for("cart")) + + +@app.route("/cart/update/", methods=["POST"]) +@login_required +def cart_update(item_id: int): + item = CartItem.query.filter_by(id=item_id, user_id=current_user.id).first_or_404() + quantity = max(1, request.form.get("quantity", type=int) or 1) + if item.format_variant and item.format_variant.inventory < quantity: + flash("Not enough stock for that quantity.", "error") + else: + item.quantity = quantity + db.session.commit() + flash("Cart updated.", "success") + return redirect(url_for("cart")) + + +@app.route("/cart/remove/", methods=["POST"]) +@login_required +def cart_remove(item_id: int): + item = CartItem.query.filter_by(id=item_id, user_id=current_user.id).first_or_404() + db.session.delete(item) + db.session.commit() + flash("Removed from your cart.", "info") + return redirect(url_for("cart")) + + +@app.route("/checkout", methods=["GET", "POST"]) +@login_required +def checkout(): + items = get_cart_items() + if not items: + flash("Your cart is empty.", "info") + return redirect(url_for("cart")) + summary = cart_summary(items) + if request.method == "POST": + shipping_name = request.form.get("shipping_name", "").strip() + shipping_line1 = request.form.get("shipping_line1", "").strip() + shipping_city = request.form.get("shipping_city", "").strip() + shipping_country = request.form.get("shipping_country", "").strip() + payment_label = request.form.get("payment_label", "").strip() + note = request.form.get("note", "").strip() + if not all([shipping_name, shipping_line1, shipping_city, shipping_country, payment_label]): + flash("Shipping and payment fields are required.", "error") + else: + order = Order( + user_id=current_user.id, + order_number=generate_order_number(), + status="paid", + subtotal=summary["subtotal"], + shipping=summary["shipping"], + tax=summary["tax"], + total=summary["total"], + shipping_name=shipping_name, + shipping_line1=shipping_line1, + shipping_city=shipping_city, + shipping_country=shipping_country, + payment_label=payment_label, + note=note, + placed_at=MIRROR_REFERENCE_NOW + timedelta(minutes=(Order.query.count() + 1)), + ) + db.session.add(order) + db.session.flush() + for item in items: + db.session.add( + OrderItem( + order_id=order.id, + album_id=item.album_id, + merch_item_id=item.merch_item_id, + format_variant_id=item.format_variant_id, + title=item.title(), + artist_name=item.artist_name(), + image_path=item.image_path(), + variant_label=item.variant_label(), + quantity=item.quantity, + unit_price=item.unit_price(), + ) + ) + if item.album: + add_collection_item(current_user, item.album, item.format_variant, note="Added from checkout") + item.album.fan_count += item.quantity + if item.format_variant and item.format_variant.inventory < 9999: + item.format_variant.inventory = max(0, item.format_variant.inventory - item.quantity) + elif item.merch_item: + item.merch_item.inventory = max(0, item.merch_item.inventory - item.quantity) + db.session.delete(item) + current_user.address_line1 = shipping_line1 + current_user.city = shipping_city + current_user.country = shipping_country + db.session.commit() + flash("Checkout complete. No payment was processed.", "success") + return redirect(url_for("order_detail", order_number=order.order_number)) + return render_template("checkout.html", items=items, summary=summary) + + +@app.route("/collection") +@login_required +def collection(): + return render_template( + "collection.html", + items=FanCollectionItem.query.filter_by(user_id=current_user.id).order_by(FanCollectionItem.added_at.desc()).all(), + ) + + +@app.route("/orders") +@login_required +def orders(): + return render_template( + "orders.html", + orders=Order.query.filter_by(user_id=current_user.id).order_by(Order.placed_at.desc()).all(), + ) + + +@app.route("/orders/") +@login_required +def order_detail(order_number: str): + return render_template( + "order_detail.html", + order=Order.query.filter_by(order_number=order_number, user_id=current_user.id).first_or_404(), + ) + + +@app.route("/album//comment", methods=["POST"]) +@login_required +def album_comment(slug: str): + album = Album.query.filter_by(slug=slug).first_or_404() + body = request.form.get("body", "").strip() + if not body: + flash("Write a comment before posting.", "error") + else: + db.session.add( + FanComment( + user_id=current_user.id, + album_id=album.id, + headline=request.form.get("headline", "").strip(), + body=body, + rating=min(5, max(1, request.form.get("rating", type=int) or 5)), + ) + ) + db.session.commit() + flash("Comment added to the release page.", "success") + return redirect(url_for("album_page", slug=slug)) + + +@app.route("/_health") +def health(): + return {"ok": True, "site": "bandcamp"} + + +@app.errorhandler(404) +def not_found(_error): + return render_template("404.html"), 404 + + +@app.errorhandler(500) +def server_error(_error): + return render_template("500.html"), 500 + + +def seed_database() -> None: + if Artist.query.count() > 0: + return + from seed_data import run_seed + + run_seed( + db=db, + base_dir=BASE_DIR, + mirror_reference_now=MIRROR_REFERENCE_NOW, + models={ + "Genre": Genre, + "Scene": Scene, + "Label": Label, + "Artist": Artist, + "Album": Album, + "Track": Track, + "Tag": Tag, + "MerchItem": MerchItem, + "FormatVariant": FormatVariant, + "FanComment": FanComment, + }, + ) + + +def seed_benchmark_users() -> None: + if User.query.filter_by(email="alice.j@test.com").first(): + return + + users_data = [ + { + "username": "alice_j", + "email": "alice.j@test.com", + "display_name": "Alice Johnson", + "city": "Seattle", + "country": "United States", + "favorite_format": "vinyl", + "favorite_scene_slug": "berlin-germany", + "bio": "Collects ambient and dub techno 12-inches.", + "address_line1": "128 Lake Union Ave", + "postal_code": "98109", + }, + { + "username": "bob_c", + "email": "bob.c@test.com", + "display_name": "Bob Chen", + "city": "Chicago", + "country": "United States", + "favorite_format": "digital", + "favorite_scene_slug": "detroit-united-states", + "bio": "Hip-hop fan chasing sharp lyric sheets and live bootlegs.", + "address_line1": "77 Fulton Market", + "postal_code": "60607", + }, + { + "username": "carol_d", + "email": "carol.d@test.com", + "display_name": "Carol Davis", + "city": "Brooklyn", + "country": "United States", + "favorite_format": "cassette", + "favorite_scene_slug": "tokyo-japan", + "bio": "Tags every purchase with the scene where she found it.", + "address_line1": "55 Bergen St", + "postal_code": "11201", + }, + { + "username": "david_k", + "email": "david.k@test.com", + "display_name": "David Kim", + "city": "Portland", + "country": "United States", + "favorite_format": "shirt", + "favorite_scene_slug": "melbourne-australia", + "bio": "Merch-heavy collector who buys a tote with almost every record.", + "address_line1": "204 Burnside St", + "postal_code": "97209", + }, + ] + + created_users: dict[str, User] = {} + for data in users_data: + scene = Scene.query.filter_by(slug=data["favorite_scene_slug"]).first() + user = User( + username=data["username"], + email=data["email"], + display_name=data["display_name"], + city=data["city"], + country=data["country"], + favorite_format=data["favorite_format"], + favorite_scene_id=scene.id if scene else None, + bio=data["bio"], + address_line1=data["address_line1"], + postal_code=data["postal_code"], + ) + user.set_password("TestPass123!") + db.session.add(user) + db.session.flush() + created_users[data["username"]] = user + + def album_by_slug(slug: str) -> Album: + album = Album.query.filter_by(slug=slug).first() + if not album: + raise LookupError(f"Missing album seed: {slug}") + return album + + def merch_by_slug(slug: str) -> MerchItem: + merch = MerchItem.query.filter_by(slug=slug).first() + if not merch: + raise LookupError(f"Missing merch seed: {slug}") + return merch + + def variant_for_album(slug: str, kind: str) -> FormatVariant: + variant = FormatVariant.query.join(Album).filter(Album.slug == slug, FormatVariant.kind == kind).first() + if not variant: + raise LookupError(f"Missing album variant: {slug} / {kind}") + return variant + + def variant_for_merch(slug: str, needle: str) -> FormatVariant: + variant = ( + FormatVariant.query.join(MerchItem) + .filter(MerchItem.slug == slug, FormatVariant.name.ilike(f"%{needle}%")) + .first() + ) + if not variant: + variant = ( + FormatVariant.query.join(MerchItem) + .filter(MerchItem.slug == slug, FormatVariant.option_a.ilike(f"%{needle}%")) + .first() + ) + if not variant: + raise LookupError(f"Missing merch variant: {slug} / {needle}") + return variant + + def make_order( + user: User, + order_number: str, + status: str, + placed_at: datetime, + shipping_name: str, + shipping_line1: str, + shipping_city: str, + shipping_country: str, + payment_label: str, + items_spec: list[tuple[str, str, str, int]], + ) -> None: + order = Order( + user_id=user.id, + order_number=order_number, + status=status, + shipping_name=shipping_name, + shipping_line1=shipping_line1, + shipping_city=shipping_city, + shipping_country=shipping_country, + payment_label=payment_label, + placed_at=placed_at, + note="Deterministic benchmark seed order.", + ) + db.session.add(order) + db.session.flush() + + subtotal = 0.0 + for kind, slug, variant_key, qty in items_spec: + if kind == "album": + album = album_by_slug(slug) + variant = variant_for_album(slug, variant_key) + db.session.add( + OrderItem( + order_id=order.id, + album_id=album.id, + format_variant_id=variant.id, + title=album.title, + artist_name=album.artist.name, + image_path=album.cover_image, + variant_label=variant.label_text(), + quantity=qty, + unit_price=variant.price, + ) + ) + add_collection_item(user, album, variant, note="Seeded library item") + album.fan_count += qty + subtotal += variant.price * qty + else: + merch = merch_by_slug(slug) + variant = variant_for_merch(slug, variant_key) + db.session.add( + OrderItem( + order_id=order.id, + merch_item_id=merch.id, + format_variant_id=variant.id, + title=merch.title, + artist_name=merch.artist.name, + image_path=merch.image, + variant_label=variant.label_text(), + quantity=qty, + unit_price=variant.price, + ) + ) + subtotal += variant.price * qty + + order.subtotal = round(subtotal, 2) + order.shipping = 0.0 if subtotal >= 45 else 6.5 + order.tax = round(order.subtotal * 0.0825, 2) + order.total = round(order.subtotal + order.shipping + order.tax, 2) + + alice = created_users["alice_j"] + bob = created_users["bob_c"] + carol = created_users["carol_d"] + david = created_users["david_k"] + + def add_cart_seed(user: User, specs: list[tuple[str, str, str, int]]) -> None: + for kind, slug, variant_key, qty in specs: + if kind == "album": + album = album_by_slug(slug) + variant = variant_for_album(slug, variant_key) + db.session.add( + CartItem(user_id=user.id, album_id=album.id, format_variant_id=variant.id, quantity=qty) + ) + else: + merch = merch_by_slug(slug) + variant = variant_for_merch(slug, variant_key) + db.session.add( + CartItem(user_id=user.id, merch_item_id=merch.id, format_variant_id=variant.id, quantity=qty) + ) + + def add_wishlist_seed(user: User, album_slugs: list[str], merch_slugs: list[str]) -> None: + for slug in album_slugs: + db.session.add(WishlistItem(user_id=user.id, album_id=album_by_slug(slug).id)) + for slug in merch_slugs: + db.session.add(WishlistItem(user_id=user.id, merch_item_id=merch_by_slug(slug).id)) + + add_cart_seed( + alice, + [("album", "tidal-memory", "vinyl", 1), ("album", "between-stations", "cassette", 1), ("merch", "neon-harbor-studio-tee", "M", 1)], + ) + add_cart_seed( + bob, + [("album", "signal-debt", "digital", 1), ("merch", "cinder-plaza-blueprint-fever-poster", "Signed", 1)], + ) + add_cart_seed( + carol, + [("album", "riverlights", "vinyl", 1), ("merch", "soft-locale-drift-hoodie", "L", 1)], + ) + add_cart_seed( + david, + [("merch", "salt-meadow-field-notes-tote", "Natural", 1), ("album", "elastic-hearts", "cd", 1)], + ) + + add_wishlist_seed(alice, ["static-bloom", "blue-hour-broadcast", "resin-language"], ["velvet-avenue-night-shift-poster"]) + add_wishlist_seed(bob, ["iron-sleep", "machine-prayer"], ["ashen-circuit-grid-slipmat", "mono-shrine-fault-choir-zine"]) + add_wishlist_seed(carol, ["riverlights", "paper-signal", "harbor-burn"], ["glass-choir-stairwell-tee"]) + add_wishlist_seed(david, ["elastic-hearts", "sleep-maps"], ["soft-locale-drift-hoodie", "salt-meadow-field-notes-tote"]) + + make_order( + alice, + "BC-20260414-0001", + "delivered", + MIRROR_REFERENCE_NOW - timedelta(days=17), + "Alice Johnson", + "128 Lake Union Ave", + "Seattle", + "United States", + "Visa ending in 4242", + [("album", "blue-hour-broadcast", "vinyl", 1), ("merch", "velvet-avenue-night-shift-poster", "Signed", 1)], + ) + make_order( + alice, + "BC-20260426-0002", + "processing", + MIRROR_REFERENCE_NOW - timedelta(days=5), + "Alice Johnson", + "128 Lake Union Ave", + "Seattle", + "United States", + "Visa ending in 1177", + [("album", "tidal-memory", "digital", 1)], + ) + make_order( + bob, + "BC-20260409-0003", + "delivered", + MIRROR_REFERENCE_NOW - timedelta(days=22), + "Bob Chen", + "77 Fulton Market", + "Chicago", + "United States", + "Mastercard ending in 7788", + [("album", "signal-debt", "digital", 1), ("album", "resin-language", "cassette", 1)], + ) + make_order( + carol, + "BC-20260421-0004", + "delivered", + MIRROR_REFERENCE_NOW - timedelta(days=10), + "Carol Davis", + "55 Bergen St", + "Brooklyn", + "United States", + "Visa ending in 9901", + [("album", "between-stations", "cassette", 1), ("merch", "soft-locale-drift-hoodie", "M", 1)], + ) + make_order( + david, + "BC-20260428-0005", + "shipped", + MIRROR_REFERENCE_NOW - timedelta(days=3), + "David Kim", + "204 Burnside St", + "Portland", + "United States", + "Amex ending in 3401", + [("album", "elastic-hearts", "cd", 1), ("merch", "salt-meadow-field-notes-tote", "Natural", 1)], + ) + + for username, album_slug, headline, body in [ + ("alice_j", "tidal-memory", "Most replayed this month", "The dub bassline on side B makes the whole release feel tidal."), + ("bob_c", "signal-debt", "Sharp sequencing", "Track order is ruthless in the best possible way."), + ("carol_d", "between-stations", "Commuter ambient", "Feels like catching the last Yamanote line with the windows fogged over."), + ("david_k", "riverlights", "Warm and tactile", "The lyric sheet tucked into the LP package is a lovely touch."), + ]: + db.session.add( + FanComment( + user_id=created_users[username].id, + album_id=album_by_slug(album_slug).id, + headline=headline, + body=body, + rating=5, + created_at=MIRROR_REFERENCE_NOW - timedelta(days=2), + ) + ) + + db.session.commit() + + +with app.app_context(): + db.create_all() + seed_database() + seed_benchmark_users() + + +if __name__ == "__main__": + port = int(os.environ.get("PORT", 40024)) + app.run(host="0.0.0.0", port=port, debug=False) diff --git a/sites/bandcamp/requirements.txt b/sites/bandcamp/requirements.txt new file mode 100644 index 00000000..d93f1ec0 --- /dev/null +++ b/sites/bandcamp/requirements.txt @@ -0,0 +1,3 @@ +Flask==3.1.0 +Flask-Login==0.6.3 +Flask-SQLAlchemy==3.1.1 diff --git a/sites/bandcamp/seed_data.py b/sites/bandcamp/seed_data.py new file mode 100644 index 00000000..202776a6 --- /dev/null +++ b/sites/bandcamp/seed_data.py @@ -0,0 +1,1057 @@ +"""Deterministic seed data for the Bandcamp mirror (real image assets ship via HF bundle).""" +from __future__ import annotations + +import hashlib +import os +from datetime import date, timedelta +from pathlib import Path + +PALETTES = [ + ("#0f5ea8", "#86d7ef", "#f8fcff", "#08192b"), + ("#6b2fb3", "#ff8b5c", "#fff8f0", "#1e1229"), + ("#145c4b", "#a7f3d0", "#f3fff9", "#0e1f1a"), + ("#a63b34", "#ffd39f", "#fff6ee", "#2a1512"), + ("#274ab8", "#f7c948", "#f9fbff", "#101829"), + ("#9333ea", "#22d3ee", "#fbf6ff", "#171126"), + ("#0f766e", "#f97316", "#f8fffd", "#142321"), + ("#be123c", "#7dd3fc", "#fff7fb", "#2a0d18"), + ("#1d4ed8", "#34d399", "#f3f8ff", "#111827"), + ("#7c2d12", "#facc15", "#fffbea", "#2a150d"), +] + +GENRES = [ + ("electronic", "Clubs, synth architecture, and digital fog.", "#0f5ea8"), + ("experimental", "Boundary-pushing releases with noise, tape, and collage impulses.", "#6b2fb3"), + ("alternative", "Hook-heavy independent records, dream pop, and guitar shimmer.", "#274ab8"), + ("rock", "Fuzz, motorik rhythm sections, and widescreen choruses.", "#a63b34"), + ("ambient", "Slow-moving drift, field recordings, and restorative detail.", "#145c4b"), + ("hip-hop/rap", "Sharp lyric sheets, beat experiments, and street-level memoir.", "#9333ea"), + ("metal", "Dense distortion, ritual percussion, and high-pressure dynamics.", "#7c2d12"), + ("punk", "Fast, bright, political, and built for tiny rooms.", "#be123c"), + ("jazz", "Late-night improvisation, spiritual harmony, and room sound.", "#0f766e"), + ("folk", "Acoustic storytelling, communal choruses, and road-worn detail.", "#7c2d12"), + ("pop", "Polished melodies, neon hooks, and emotional lift.", "#1d4ed8"), + ("techno", "Machine pulse, analog grit, and hypnotic low-end.", "#0f5ea8"), +] + +SCENES = [ + ("Los Angeles", "United States", "Sun-bleached studios, late-night FM nostalgia, and movie-score sheen."), + ("Berlin", "Germany", "Dub chambers, warehouse drums, and experimental club cross-pollination."), + ("Tokyo", "Japan", "Compact detail, commuter ambience, and hyper-precise sound design."), + ("London", "United Kingdom", "Independent label culture with left turns into post-punk, jazz, and pop."), + ("New York", "United States", "Small-room virtuosity, art-school hooks, and downtown improvisation."), + ("Melbourne", "Australia", "Open-hearted songwriting, DIY scenes, and tactile physical editions."), + ("Sao Paulo", "Brazil", "Percussive movement, raw punk energy, and bright visual identity."), + ("Detroit", "United States", "Machine rhythm, soul memory, and durable underground infrastructure."), + ("Paris", "France", "Elegant arrangements, metallic tension, and art-book presentation."), +] + +LABELS = [ + ("Aperture Tapes", "Berlin, Germany", "Pressings that lean toward dub techno, electro, and humid afterhours ambience."), + ("Midnight Service", "London, United Kingdom", "Independent label focused on guitar records and night-bus pop."), + ("Motor Relay", "Detroit, United States", "Hardware-forward dance music and disciplined machine funk."), + ("Inland Weather", "Tokyo, Japan", "Ambient and electro-acoustic labels with carefully built physical editions."), + ("South District", "Sao Paulo, Brazil", "Razor-wire punk, no-wave experiments, and scene-documentation merch."), + ("Sun Trace", "Los Angeles, United States", "Beat music, jazz crossover, and cinematic low-end."), + ("Night School Annex", "New York, United States", "Jazz-leaning independents with tactile design systems."), + ("Lantern Union", "Melbourne, Australia", "Songwriter records and small-batch merch with printshop charm."), + ("Obsidian Bloom", "Paris, France", "Heavy records with monochrome art direction and deluxe inserts."), + ("Harbor Circuit", "Global", "Cross-scene collaborations curated for the mirror benchmark."), +] + +GENRE_TAGS = { + "electronic": ["dub techno", "afterhours", "submerged", "drum machine", "deep groove", "modular", "slow burn"], + "experimental": ["collage", "microtone", "field recordings", "tape hiss", "glitch", "avant pop", "noise drift"], + "alternative": ["indie rock", "dream pop", "shoegaze", "jangle", "bedroom", "overcast hooks", "reverb"], + "rock": ["psych rock", "motorik", "garage", "widescreen", "burnt amp", "festival ready", "riff driven"], + "ambient": ["drone", "sleep tape", "meditation", "commuter", "soundscape", "quiet bloom", "late train"], + "hip-hop/rap": ["lyric sheet", "boom bap", "left field", "basement tape", "jazz rap", "city pressure", "loop heavy"], + "metal": ["doom", "blackened", "ritual", "blast beat", "cathedral reverb", "ash cloud", "ferrous"], + "punk": ["d-beat", "basement", "agitprop", "sprint", "DIY", "stick and poke", "street flyer"], + "jazz": ["spiritual", "modal", "trio", "late set", "horn blend", "improv", "blue room"], + "folk": ["acoustic", "story song", "americana", "river road", "soft harmonies", "field note", "slow weather"], + "pop": ["hook", "synth pop", "gloss", "heartbreak", "dancefloor", "night drive", "bright chorus"], + "techno": ["warehouse", "acid", "analog", "tool track", "four on the floor", "strobe", "detuned kick"], +} + +CURATED_ARTISTS = [ + { + "name": "Neon Harbor", + "scene": "Berlin", + "genre": "electronic", + "label": "Aperture Tapes", + "headline": "Dub silhouettes and rail-line bass pressure.", + "bio": "Neon Harbor build patient club music from modular haze, train-window reflections, and low-end pressure designed for the final hour of the night.", + "albums": [ + { + "title": "Tidal Memory", + "release_date": date(2025, 10, 14), + "price": 8.5, + "tags": ["dub techno", "afterhours", "submerged", "Berlin"], + "catalog_no": "AT-042", + "featured": True, + "editorial": True, + "track_titles": ["Incoming Tide", "Mooring Light", "Breakwater", "Low Pier", "Morning Channel"], + }, + { + "title": "Night Ferry", + "release_date": date(2024, 6, 7), + "price": 7.0, + "tags": ["deep groove", "modular", "late deck", "Berlin"], + "catalog_no": "AT-031", + "track_titles": ["Platform Sleep", "Signal Two", "Dock Exchange", "Wake Window", "Westbound Static"], + }, + ], + "merch": [ + { + "title": "Neon Harbor Studio Tee", + "item_type": "shirt", + "album": "tidal-memory", + "price": 32.0, + "short_blurb": "Garment-dyed heavyweight tee with the Tidal Memory mark.", + "variants": [("shirt", "Studio Tee", size, "Washed Navy", 32.0) for size in ["S", "M", "L", "XL"]], + }, + { + "title": "Neon Harbor Breakwater Slipmat", + "item_type": "slipmat", + "album": "night-ferry", + "price": 24.0, + "short_blurb": "Pair of felt slipmats printed with breakwater geometry.", + "variants": [("slipmat", "Pair", "12-inch", "", 24.0), ("slipmat", "Deluxe Pair", "Glow Edge", "", 29.0)], + }, + ], + }, + { + "name": "Glass Choir", + "scene": "London", + "genre": "alternative", + "label": "Midnight Service", + "headline": "Guitar shimmer with stairwell echo and commuter melancholy.", + "bio": "Glass Choir turn small-room guitar songs into widescreen nighttime records, leaving enough static in the mix to keep every chorus grounded.", + "albums": [ + { + "title": "Static Bloom", + "release_date": date(2026, 2, 20), + "price": 9.0, + "tags": ["dream pop", "reverb", "overcast hooks", "London"], + "catalog_no": "MS-118", + "featured": True, + "track_titles": ["Glasshouse Lobby", "Northbound Blue", "Tin Roof Weather", "Run the Balcony", "Static Bloom"], + }, + { + "title": "Paper Signal", + "release_date": date(2024, 11, 8), + "price": 8.0, + "tags": ["shoegaze", "jangle", "bedroom", "London"], + "catalog_no": "MS-094", + "track_titles": ["Sunday Turnstile", "Fluorescent Map", "Paper Signal", "Taxi Dust", "Window Figures"], + }, + ], + "merch": [ + { + "title": "Glass Choir Stairwell Tee", + "item_type": "shirt", + "album": "static-bloom", + "price": 30.0, + "short_blurb": "Soft grey shirt with stairwell photo treatment.", + "variants": [("shirt", "Stairwell Tee", size, "Heather Grey", 30.0) for size in ["S", "M", "L", "XL"]], + }, + { + "title": "Glass Choir Balcony Poster", + "item_type": "poster", + "album": "paper-signal", + "price": 18.0, + "short_blurb": "Risograph poster pulled from the Paper Signal cover session.", + "variants": [("poster", "Standard", "18x24", "", 18.0), ("poster", "Signed", "18x24", "", 24.0)], + }, + ], + }, + { + "name": "Ashen Circuit", + "scene": "Detroit", + "genre": "techno", + "label": "Motor Relay", + "headline": "Machine discipline with rust-belt force and analog grit.", + "bio": "Ashen Circuit work out of a one-room studio stacked with drum machines, borrowed test equipment, and lovingly repaired mixers.", + "albums": [ + { + "title": "Redline Ritual", + "release_date": date(2023, 9, 15), + "price": 8.0, + "tags": ["warehouse", "analog", "strobe", "Detroit"], + "catalog_no": "MR-207", + "track_titles": ["Factory Dawn", "Heater Coil", "Redline Ritual", "Locked Loop", "Night Shift Press"], + }, + { + "title": "Machine Prayer", + "release_date": date(2025, 5, 30), + "price": 9.5, + "tags": ["acid", "tool track", "ferrous", "Detroit"], + "catalog_no": "MR-223", + "featured": True, + "track_titles": ["Servo Chant", "Welded Saints", "Machine Prayer", "Bulkhead Glow", "After Conveyor"], + }, + ], + "merch": [ + { + "title": "Ashen Circuit Grid Slipmat", + "item_type": "slipmat", + "album": "machine-prayer", + "price": 22.0, + "short_blurb": "Dense white-on-black slipmat pair with the Machine Prayer grid.", + "variants": [("slipmat", "Pair", "12-inch", "", 22.0), ("slipmat", "Glow Pair", "12-inch", "", 27.0)], + }, + { + "title": "Ashen Circuit Weld Patch", + "item_type": "patch", + "album": "redline-ritual", + "price": 10.0, + "short_blurb": "Embroidered patch cut from the Redline Ritual symbol language.", + "variants": [("patch", "Standard", "Black", "", 10.0), ("patch", "Reflective", "Silver", "", 13.0)], + }, + ], + }, + { + "name": "Soft Locale", + "scene": "Tokyo", + "genre": "ambient", + "label": "Inland Weather", + "headline": "Commuter ambient for reflective trains and dim apartment corners.", + "bio": "Soft Locale map movement through stations, elevators, and rain channels into gently detailed records full of piano dust and field recording glow.", + "albums": [ + { + "title": "Between Stations", + "release_date": date(2025, 8, 1), + "price": 8.0, + "tags": ["commuter", "late train", "sleep tape", "Tokyo"], + "catalog_no": "IW-052", + "featured": True, + "editorial": True, + "track_titles": ["Transfer Bell", "Quiet Platform", "Between Stations", "River Line", "Window Heat"], + }, + { + "title": "Sleep Maps", + "release_date": date(2024, 2, 23), + "price": 7.5, + "tags": ["drone", "meditation", "field recordings", "Tokyo"], + "catalog_no": "IW-041", + "track_titles": ["Pocket Lantern", "Hallway Air", "Sleep Maps", "Paper Screen", "End of Service"], + }, + ], + "merch": [ + { + "title": "Soft Locale Drift Hoodie", + "item_type": "hoodie", + "album": "between-stations", + "price": 48.0, + "short_blurb": "Midweight hoodie with a reflective commuter-grid chest print.", + "variants": [("hoodie", "Drift Hoodie", size, "Stone", 48.0) for size in ["S", "M", "L", "XL"]], + }, + { + "title": "Soft Locale Rain Map Cassette Box", + "item_type": "cassette", + "album": "sleep-maps", + "price": 26.0, + "short_blurb": "Cassette shell and booklet edition with a fold-out station map.", + "variants": [("cassette", "Blue Shell", "Numbered", "", 26.0), ("cassette", "Smoke Shell", "Numbered", "", 29.0)], + }, + ], + }, + { + "name": "South Exit", + "scene": "Sao Paulo", + "genre": "punk", + "label": "South District", + "headline": "Fast songs, street posters, and DIY urgency.", + "bio": "South Exit play sprint-length songs built from hand-painted flyers, blown amps, and city pressure released at full velocity.", + "albums": [ + { + "title": "Concrete Carnival", + "release_date": date(2025, 4, 11), + "price": 7.5, + "tags": ["d-beat", "DIY", "street flyer", "Sao Paulo"], + "catalog_no": "SD-014", + "track_titles": ["Bus Lane", "Concrete Carnival", "Sticker Wall", "No Permit", "Two Minute Exit"], + }, + { + "title": "Siren Economy", + "release_date": date(2023, 12, 1), + "price": 7.0, + "tags": ["basement", "agitprop", "sprint", "Sao Paulo"], + "catalog_no": "SD-008", + "track_titles": ["Median Strip", "Siren Economy", "Paper Badge", "Turn the Bolts", "Close the Gate"], + }, + ], + "merch": [ + { + "title": "South Exit Flyer Tee", + "item_type": "shirt", + "album": "concrete-carnival", + "price": 28.0, + "short_blurb": "Cracked-print tee based on a xeroxed show flyer.", + "variants": [("shirt", "Flyer Tee", size, "White", 28.0) for size in ["S", "M", "L", "XL"]], + }, + { + "title": "South Exit Poster Pack", + "item_type": "poster", + "album": "siren-economy", + "price": 16.0, + "short_blurb": "Three-poster bundle featuring concrete stencil variants.", + "variants": [("poster", "3-Pack", "18x24", "", 16.0), ("poster", "Signed 3-Pack", "18x24", "", 22.0)], + }, + ], + }, + { + "name": "Cinder Plaza", + "scene": "Los Angeles", + "genre": "hip-hop/rap", + "label": "Sun Trace", + "headline": "Sharp verses, bruised synths, and freeway-night hooks.", + "bio": "Cinder Plaza thread jazz-adjacent loops, voice notes, and concrete percussion into detail-rich rap records with cinematic pacing.", + "albums": [ + { + "title": "Signal Debt", + "release_date": date(2025, 9, 19), + "price": 8.5, + "tags": ["lyric sheet", "left field", "city pressure", "Los Angeles"], + "catalog_no": "ST-072", + "featured": True, + "track_titles": ["Signal Debt", "Median Palm", "Overpass Dialtone", "Small Claims", "Interchange Prayer"], + }, + { + "title": "Blueprint Fever", + "release_date": date(2024, 3, 22), + "price": 8.0, + "tags": ["jazz rap", "loop heavy", "basement tape", "Los Angeles"], + "catalog_no": "ST-059", + "track_titles": ["Rollout Plan", "Blueprint Fever", "Room Tone", "Silver Marker", "Exit Column"], + }, + ], + "merch": [ + { + "title": "Cinder Plaza Blueprint Fever Poster", + "item_type": "poster", + "album": "blueprint-fever", + "price": 20.0, + "short_blurb": "Blueprint grid poster with a signed edition for collectors.", + "variants": [("poster", "Standard", "18x24", "", 20.0), ("poster", "Signed", "18x24", "", 28.0)], + }, + { + "title": "Cinder Plaza Signal Debt Cap", + "item_type": "cap", + "album": "signal-debt", + "price": 26.0, + "short_blurb": "Low-profile cap embroidered with the Signal Debt skyline.", + "variants": [("cap", "Adjustable", "Black", "", 26.0), ("cap", "Adjustable", "Sand", "", 26.0)], + }, + ], + }, + { + "name": "Velvet Avenue", + "scene": "New York", + "genre": "jazz", + "label": "Night School Annex", + "headline": "Blue-room improvisation with downtown velvet and brass glow.", + "bio": "Velvet Avenue record live takes with the room left intact, building jazz records that feel like late-set discoveries with a patient sense of space.", + "albums": [ + { + "title": "Blue Hour Broadcast", + "release_date": date(2025, 1, 31), + "price": 9.5, + "tags": ["late set", "trio", "blue room", "New York"], + "catalog_no": "NSA-211", + "featured": True, + "track_titles": ["Blue Hour Broadcast", "Canal Echo", "Fifth Table", "Smoke Ladder", "After Set Receipt"], + }, + { + "title": "Lobby Mirage", + "release_date": date(2023, 8, 18), + "price": 8.0, + "tags": ["modal", "horn blend", "improv", "New York"], + "catalog_no": "NSA-196", + "track_titles": ["Lobby Mirage", "Quarter Note Rain", "Overnight Guest", "Stairwell Vibraphone", "Quiet Receipt"], + }, + ], + "merch": [ + { + "title": "Velvet Avenue Night Shift Poster", + "item_type": "poster", + "album": "blue-hour-broadcast", + "price": 19.0, + "short_blurb": "Matte poster with the Blue Hour Broadcast room diagram.", + "variants": [("poster", "Standard", "18x24", "", 19.0), ("poster", "Signed", "18x24", "", 27.0)], + }, + { + "title": "Velvet Avenue Setlist Tote", + "item_type": "tote", + "album": "lobby-mirage", + "price": 24.0, + "short_blurb": "Natural cotton tote printed with a handwritten setlist.", + "variants": [("tote", "Setlist Tote", "Natural", "", 24.0), ("tote", "Setlist Tote", "Black", "", 24.0)], + }, + ], + }, + { + "name": "Salt Meadow", + "scene": "Melbourne", + "genre": "folk", + "label": "Lantern Union", + "headline": "Field-note songwriting with dusk harmonies and weathered detail.", + "bio": "Salt Meadow turn notebooks, back-porch harmonies, and road-case acoustics into richly specific folk records.", + "albums": [ + { + "title": "Riverlights", + "release_date": date(2025, 7, 11), + "price": 8.0, + "tags": ["story song", "field note", "river road", "Melbourne"], + "catalog_no": "LU-063", + "featured": True, + "track_titles": ["Riverlights", "Fence Post August", "Borrowed Kettle", "Downwind Choir", "Common Thread"], + }, + { + "title": "Common Thread", + "release_date": date(2024, 5, 3), + "price": 7.5, + "tags": ["americana", "soft harmonies", "slow weather", "Melbourne"], + "catalog_no": "LU-051", + "track_titles": ["Handrail", "Common Thread", "Old Union Hall", "Weather Note", "West Creek"], + }, + ], + "merch": [ + { + "title": "Salt Meadow Field Notes Tote", + "item_type": "tote", + "album": "riverlights", + "price": 22.0, + "short_blurb": "Natural tote printed with Riverlights notebook fragments.", + "variants": [("tote", "Field Notes Tote", "Natural", "", 22.0), ("tote", "Field Notes Tote", "Forest", "", 22.0)], + }, + { + "title": "Salt Meadow Riverlights Lyric Zine", + "item_type": "zine", + "album": "riverlights", + "price": 14.0, + "short_blurb": "Staple-bound lyric zine with recording notes and Polaroids.", + "variants": [("zine", "Issue One", "Stapled", "", 14.0), ("zine", "Signed Issue", "Stapled", "", 18.0)], + }, + ], + }, + { + "name": "Iron Veil", + "scene": "Paris", + "genre": "metal", + "label": "Obsidian Bloom", + "headline": "Cathedral reverb, iron filings, and ritual pressure.", + "bio": "Iron Veil stretch doom, black metal atmosphere, and choir samples into heavy records that feel ceremonial rather than theatrical.", + "albums": [ + { + "title": "Iron Sleep", + "release_date": date(2025, 11, 21), + "price": 9.0, + "tags": ["doom", "ritual", "cathedral reverb", "Paris"], + "catalog_no": "OB-119", + "featured": True, + "track_titles": ["Bell Ash", "Iron Sleep", "Procession Stair", "Shutter Psalm", "Ember Chapel"], + }, + { + "title": "Saint of Noise", + "release_date": date(2024, 1, 26), + "price": 8.5, + "tags": ["blackened", "ash cloud", "blast beat", "Paris"], + "catalog_no": "OB-101", + "track_titles": ["Noisework", "Saint of Noise", "Window Soot", "Tower Rain", "Rust Halo"], + }, + ], + "merch": [ + { + "title": "Iron Veil Chapel Longsleeve", + "item_type": "shirt", + "album": "iron-sleep", + "price": 36.0, + "short_blurb": "Longsleeve with metallic ink front and sleeve glyphs.", + "variants": [("shirt", "Longsleeve", size, "Black", 36.0) for size in ["S", "M", "L", "XL"]], + }, + { + "title": "Iron Veil Noise Patch Set", + "item_type": "patch", + "album": "saint-of-noise", + "price": 12.0, + "short_blurb": "Three embroidered patches with silver overlock.", + "variants": [("patch", "3-Pack", "Silver Edge", "", 12.0), ("patch", "3-Pack Deluxe", "Glow Edge", "", 16.0)], + }, + ], + }, + { + "name": "Fever Arcade", + "scene": "London", + "genre": "pop", + "label": "Midnight Service", + "headline": "Neon chorus writing with club bruises and soft-focus hooks.", + "bio": "Fever Arcade make sharp pop records whose production leaves just enough room for hallway echo and emotional static.", + "albums": [ + { + "title": "Elastic Hearts", + "release_date": date(2025, 12, 5), + "price": 9.0, + "tags": ["hook", "night drive", "bright chorus", "London"], + "catalog_no": "MS-132", + "featured": True, + "track_titles": ["Elastic Hearts", "Flicker Map", "Checkout Lights", "Half Fare", "Aftercare FM"], + }, + { + "title": "Mirror Mosaic", + "release_date": date(2024, 9, 13), + "price": 8.0, + "tags": ["synth pop", "gloss", "dancefloor", "London"], + "catalog_no": "MS-107", + "track_titles": ["Mirror Mosaic", "South Loop", "Cab Floor Glitter", "Rain Delay", "Window Waltz"], + }, + ], + "merch": [ + { + "title": "Fever Arcade Gloss Tee", + "item_type": "shirt", + "album": "elastic-hearts", + "price": 29.0, + "short_blurb": "Bright print tee with split-tone Elastic Hearts graphic.", + "variants": [("shirt", "Gloss Tee", size, "White", 29.0) for size in ["S", "M", "L", "XL"]], + }, + { + "title": "Fever Arcade Mirror Pin Set", + "item_type": "pin", + "album": "mirror-mosaic", + "price": 15.0, + "short_blurb": "Two hard-enamel pins shaped like mirror fragments.", + "variants": [("pin", "2-Pin Set", "Chrome", "", 15.0), ("pin", "2-Pin Set", "Rose", "", 15.0)], + }, + ], + }, + { + "name": "Mono Shrine", + "scene": "Berlin", + "genre": "experimental", + "label": "Harbor Circuit", + "headline": "Tape haze, microtonal hooks, and collage architecture.", + "bio": "Mono Shrine fold chant fragments, room-tone edits, and damaged synth timbres into experimental pop that still lands emotionally.", + "albums": [ + { + "title": "Resin Language", + "release_date": date(2025, 3, 28), + "price": 8.5, + "tags": ["collage", "tape hiss", "microtone", "Berlin"], + "catalog_no": "HC-014", + "featured": True, + "track_titles": ["Resin Language", "Circuit Teeth", "Broken Caption", "Choir Exit", "Warm Static"], + }, + { + "title": "Fault Choir", + "release_date": date(2024, 7, 26), + "price": 8.0, + "tags": ["glitch", "avant pop", "noise drift", "Berlin"], + "catalog_no": "HC-006", + "track_titles": ["Fault Choir", "Signal Prayer", "Paper Mouth", "Room Dust", "Small Collapse"], + }, + ], + "merch": [ + { + "title": "Mono Shrine Fault Choir Zine", + "item_type": "zine", + "album": "fault-choir", + "price": 13.0, + "short_blurb": "Eight-page foldout zine of lyric fragments and cassette labels.", + "variants": [("zine", "Issue One", "Stapled", "", 13.0), ("zine", "Bundle", "With Sticker Sheet", "", 17.0)], + }, + { + "title": "Mono Shrine Resin Poster", + "item_type": "poster", + "album": "resin-language", + "price": 17.0, + "short_blurb": "A2 poster using the album's resin-grid art.", + "variants": [("poster", "Standard", "A2", "", 17.0), ("poster", "Signed", "A2", "", 23.0)], + }, + ], + }, + { + "name": "Tide Static", + "scene": "Los Angeles", + "genre": "rock", + "label": "Sun Trace", + "headline": "Motorik travel songs and blown-speaker optimism.", + "bio": "Tide Static write road-length rock songs that feel as interested in momentum and tone color as they are in choruses.", + "albums": [ + { + "title": "Harbor Burn", + "release_date": date(2025, 6, 27), + "price": 8.0, + "tags": ["psych rock", "motorik", "widescreen", "Los Angeles"], + "catalog_no": "ST-064", + "featured": True, + "track_titles": ["Harbor Burn", "Median Gold", "Glass Toll", "Breaker Motel", "Wide Exit"], + }, + { + "title": "Quiet Engine", + "release_date": date(2024, 4, 5), + "price": 7.5, + "tags": ["garage", "riff driven", "festival ready", "Los Angeles"], + "catalog_no": "ST-051", + "track_titles": ["Quiet Engine", "Chrome Valley", "Heat Map", "August Freight", "Left Signal"], + }, + ], + "merch": [ + { + "title": "Tide Static Burn Tee", + "item_type": "shirt", + "album": "harbor-burn", + "price": 30.0, + "short_blurb": "Vintage white tee with a cracked orange harbor-burn print.", + "variants": [("shirt", "Burn Tee", size, "Vintage White", 30.0) for size in ["S", "M", "L", "XL"]], + }, + { + "title": "Tide Static Quiet Engine Tote", + "item_type": "tote", + "album": "quiet-engine", + "price": 21.0, + "short_blurb": "Canvas tote featuring the Quiet Engine highway diagram.", + "variants": [("tote", "Engine Tote", "Natural", "", 21.0), ("tote", "Engine Tote", "Black", "", 21.0)], + }, + ], + }, +] + +EXTRA_ARTISTS = [ + "Amber Relay", + "Paper Current", + "Silver Weather", + "North Routine", + "Copper Bloom", + "Ladder Choir", + "Hour Motel", + "Signal Lake", + "Delta Hall", + "Mirror Union", + "Quiet Metric", + "Blue Archive", + "Stair Pattern", + "Velvet Current", + "Harbor Study", + "Street Lantern", + "Noon Frame", + "Low Atlas", + "Chrome Willow", + "Radio Meadow", + "Fault Parade", + "Static Ledger", + "Stone Balcony", + "West Transit", + "Echo Dividend", + "Cloud Bureau", + "Ridge Cinema", + "Hollow Method", +] + +ALBUM_WORDS_A = [ + "Static", + "Harbor", + "River", + "Signal", + "Blue", + "Late", + "Golden", + "Quiet", + "Paper", + "Chrome", + "South", + "Midnight", + "Glass", + "Motel", + "Stone", + "Delta", + "Velvet", + "Open", + "After", + "Broken", +] + +ALBUM_WORDS_B = [ + "Weather", + "Ledger", + "Cinema", + "Method", + "Current", + "Boulevard", + "Garden", + "Transit", + "Archive", + "Pattern", + "Dusk", + "Vector", + "Thread", + "Parade", + "Engine", + "Signal", + "Mosaic", + "Harbor", + "Circuit", + "Minutes", +] + +TRACK_WORDS_A = [ + "Window", + "Breaker", + "Hallway", + "Transfer", + "Rain", + "Invoice", + "Current", + "Median", + "Receipt", + "Basin", + "Anchor", + "Static", + "Platform", + "August", + "Pattern", +] + +TRACK_WORDS_B = [ + "Light", + "Note", + "Line", + "Receipt", + "Dust", + "Dialtone", + "Index", + "Signal", + "Weather", + "Map", + "Ledger", + "Glow", + "Transit", + "Machine", + "Murmur", +] + + +def slugify(value: str) -> str: + return "-".join(part for part in "".join(ch.lower() if ch.isalnum() else "-" for ch in value).split("-") if part) + + +def make_unique_slug(base_slug: str, used: set[str], suffix: str = "") -> str: + candidate = base_slug + if candidate not in used: + used.add(candidate) + return candidate + if suffix: + candidate = f"{base_slug}-{suffix}" + if candidate not in used: + used.add(candidate) + return candidate + counter = 2 + while f"{candidate}-{counter}" in used: + counter += 1 + final_slug = f"{candidate}-{counter}" + used.add(final_slug) + return final_slug + + +def ensure_asset_scaffold(base_dir: str) -> None: + images = Path(base_dir) / "static" / "images" + for folder in ["covers", "banners", "artists", "merch"]: + (images / folder).mkdir(parents=True, exist_ok=True) + + +def build_generated_artists() -> list[dict]: + generated = [] + for idx, name in enumerate(EXTRA_ARTISTS): + genre_name = GENRES[idx % len(GENRES)][0] + scene_name = SCENES[idx % len(SCENES)][0] + label_name = LABELS[idx % len(LABELS)][0] + first_title = f"{ALBUM_WORDS_A[idx % len(ALBUM_WORDS_A)]} {ALBUM_WORDS_B[idx % len(ALBUM_WORDS_B)]}" + second_title = f"{ALBUM_WORDS_A[(idx + 7) % len(ALBUM_WORDS_A)]} {ALBUM_WORDS_B[(idx + 11) % len(ALBUM_WORDS_B)]}" + generated.append( + { + "name": name, + "scene": scene_name, + "genre": genre_name, + "label": label_name, + "headline": f"{genre_name.title()} releases tuned to {scene_name} after-dark energy.", + "bio": f"{name} operate inside the {scene_name} scene, shaping {genre_name} records that favor texture, place, and carefully staged physical editions.", + "albums": [ + { + "title": first_title, + "release_date": date(2023 + (idx % 4), (idx % 11) + 1, ((idx * 3) % 24) + 1), + "price": 7.0 + (idx % 5) * 0.5, + "tags": GENRE_TAGS[genre_name][:3] + [scene_name], + "catalog_no": f"HC-{200 + idx:03d}", + "featured": idx < 4, + }, + { + "title": second_title, + "release_date": date(2024 + (idx % 3), ((idx + 4) % 11) + 1, ((idx * 5) % 24) + 1), + "price": 7.5 + (idx % 4) * 0.5, + "tags": GENRE_TAGS[genre_name][2:5] + [scene_name], + "catalog_no": f"HC-{240 + idx:03d}", + }, + ], + } + ) + return generated + + +def generated_track_titles(album_title: str, offset: int) -> list[str]: + titles = [] + base = slugify(album_title) + seed = int(hashlib.sha256(base.encode("utf-8")).hexdigest()[:6], 16) + for idx in range(5): + first = TRACK_WORDS_A[(seed + offset + idx) % len(TRACK_WORDS_A)] + second = TRACK_WORDS_B[(seed + offset * 2 + idx) % len(TRACK_WORDS_B)] + titles.append(f"{first} {second}") + return titles + + +def album_description(artist_name: str, title: str, genre_name: str, scene_name: str, tags: list[str]) -> tuple[str, str]: + desc = ( + f"{title} by {artist_name} folds {genre_name} textures from {scene_name} into a detailed release built around " + f"{', '.join(tags[:3])}, tactile arrangements, and patient pacing." + ) + story = ( + f"Recorded for the WebHarbor Bandcamp mirror as a fully local release package, {title} leans on {tags[0]} " + f"energy while keeping the visual and written presentation tight enough for benchmark tasks." + ) + return desc, story + + +def merch_description(title: str, album_title: str | None, artist_name: str, item_type: str) -> str: + if album_title: + return f"{title} is a {item_type} release tied to {album_title}, printed locally for {artist_name} with clean benchmark-ready variant information." + return f"{title} is a {item_type} item from {artist_name}, created for the local mirror with deterministic colors and edition notes." + + +def create_assets_for_artist(base_dir: str, artist_slug: str, artist_name: str, scene_name: str, albums: list[dict], merch_items: list[dict]) -> None: + """Real image assets ship inside static/images via the HF bundle; seeding only + references them by path, so there is nothing to generate here.""" + return None + + +def run_seed(db, base_dir: str, mirror_reference_now, models: dict) -> None: + Genre = models["Genre"] + Scene = models["Scene"] + Label = models["Label"] + Artist = models["Artist"] + Album = models["Album"] + Track = models["Track"] + Tag = models["Tag"] + MerchItem = models["MerchItem"] + FormatVariant = models["FormatVariant"] + + ensure_asset_scaffold(base_dir) + used_album_slugs: set[str] = set() + used_track_slugs: set[str] = set() + used_merch_slugs: set[str] = set() + + scene_records = {} + for name, country, description in SCENES: + record = Scene(name=f"{name}, {country}", slug=slugify(f"{name}-{country}"), country=country, description=description) + db.session.add(record) + db.session.flush() + scene_records[name] = record + + genre_records = {} + for name, description, accent in GENRES: + record = Genre(name=name, slug=slugify(name), description=description, accent_color=accent) + db.session.add(record) + db.session.flush() + genre_records[name] = record + + label_records = {} + for name, location, description in LABELS: + record = Label(name=name, slug=slugify(name), location=location, description=description) + db.session.add(record) + db.session.flush() + label_records[name] = record + + tag_records = {} + + def tag_for(name: str): + slug = slugify(name) + if slug in tag_records: + return tag_records[slug] + record = Tag(name=name, slug=slug) + db.session.add(record) + db.session.flush() + tag_records[slug] = record + return record + + all_artist_specs = CURATED_ARTISTS + build_generated_artists() + + for artist_index, artist_spec in enumerate(all_artist_specs): + artist_slug = slugify(artist_spec["name"]) + scene = scene_records[artist_spec["scene"]] + genre = genre_records[artist_spec["genre"]] + label = label_records[artist_spec["label"]] + artist = Artist( + name=artist_spec["name"], + slug=artist_slug, + location=scene.name, + bio=artist_spec["bio"], + headline=artist_spec["headline"], + formed_year=2015 + (artist_index % 9), + follow_count=1200 + artist_index * 143, + avatar_image=f"images/artists/{artist_slug}-avatar.jpg", + hero_image=f"images/artists/{artist_slug}-hero.jpg", + scene_id=scene.id, + label_id=label.id, + primary_genre_id=genre.id, + ) + db.session.add(artist) + db.session.flush() + + local_album_specs = [] + for album_index, album_spec in enumerate(artist_spec["albums"]): + album_slug = make_unique_slug(slugify(album_spec["title"]), used_album_slugs, artist_slug) + tags = album_spec.get("tags") or GENRE_TAGS[artist_spec["genre"]][:3] + [artist_spec["scene"]] + description, story = album_description( + artist_spec["name"], album_spec["title"], artist_spec["genre"], artist_spec["scene"], tags + ) + album = Album( + artist_id=artist.id, + label_id=label.id, + primary_genre_id=genre.id, + scene_id=scene.id, + title=album_spec["title"], + slug=album_slug, + description=description, + story=story, + cover_image=f"images/covers/{album_slug}.jpg", + header_image=f"images/banners/{album_slug}.jpg", + price=album_spec["price"], + release_date=album_spec["release_date"], + fan_count=140 + artist_index * 18 + album_index * 37, + catalog_no=album_spec.get("catalog_no", f"WH-{artist_index:02d}{album_index:02d}"), + is_featured=bool(album_spec.get("featured", False)), + is_new=album_spec["release_date"] >= date(2025, 1, 1), + is_editorial=bool(album_spec.get("editorial", False)), + ) + db.session.add(album) + db.session.flush() + + for tag_name in tags: + album.tags.append(tag_for(tag_name)) + + track_titles = album_spec.get("track_titles") or generated_track_titles(album_spec["title"], artist_index + album_index) + total_duration = 0 + for track_number, track_title in enumerate(track_titles, start=1): + duration = 155 + ((artist_index * 13 + album_index * 21 + track_number * 29) % 170) + total_duration += duration + track_slug = make_unique_slug(slugify(f"{album_slug}-{track_title}"), used_track_slugs) + db.session.add( + Track( + album_id=album.id, + title=track_title, + slug=track_slug, + track_number=track_number, + duration_seconds=duration, + preview_hook=f"{track_title} is the preview focus from {album.title}.", + lyrics_excerpt=f"{track_title} traces the emotional contour of {album.title.lower()} in one sharp phrase.", + is_focus_track=track_number == 3, + ) + ) + + album.track_count = len(track_titles) + album.duration_seconds = total_duration + + album_variants = [ + ("digital", "Digital Album", "MP3 + FLAC", "", album.price, 9999, True, "Instant download", "Unlimited streams in the mirror."), + ] + if artist_spec["genre"] in {"ambient", "electronic", "techno", "metal", "folk", "jazz", "alternative", "rock", "pop"}: + album_variants.append(("vinyl", "Colored Vinyl", "12-inch", "Ocean Blue", round(album.price + 15.5, 2), 42 + artist_index % 35, False, "Ships in 3-5 days", "Limited mirror pressing.")) + if artist_spec["genre"] in {"ambient", "electronic", "hip-hop/rap", "punk", "experimental", "folk"}: + album_variants.append(("cassette", "Cassette", "Transparent Shell", "", round(album.price + 6.5, 2), 28 + artist_index % 21, False, "Ships in 2-4 days", "Numbered shell edition.")) + if artist_spec["genre"] in {"pop", "rock", "alternative", "jazz", "metal"}: + album_variants.append(("cd", "Compact Disc", "Gatefold", "", round(album.price + 8.0, 2), 34 + artist_index % 17, False, "Ships in 2-4 days", "Includes lyric foldout.")) + + for variant_index, (kind, name, option_a, option_b, price, inventory, is_default, shipping_note, edition_note) in enumerate(album_variants, start=1): + db.session.add( + FormatVariant( + album_id=album.id, + kind=kind, + name=name, + option_a=option_a, + option_b=option_b, + price=price, + inventory=inventory, + sku=f"{album_slug}-{kind}-{variant_index}", + shipping_note=shipping_note, + edition_note=edition_note, + is_default=is_default, + ) + ) + + local_album_specs.append({"title": album.title, "slug": album.slug}) + + merch_specs = artist_spec.get("merch", []) + if not merch_specs: + first_album_slug = slugify(artist_spec["albums"][0]["title"]) + second_album_slug = slugify(artist_spec["albums"][1]["title"]) + merch_specs = [ + { + "title": f"{artist_spec['name']} Tour Tee", + "item_type": "shirt", + "album": first_album_slug, + "price": 29.0 + (artist_index % 4), + "short_blurb": "Standard artist tee with benchmark-ready sizing.", + "variants": [("shirt", "Tour Tee", size, "Black", 29.0 + (artist_index % 4)) for size in ["S", "M", "L", "XL"]], + }, + { + "title": f"{artist_spec['name']} Tour Cassette", + "item_type": "cassette", + "album": second_album_slug, + "price": 14.0 + (artist_index % 3), + "short_blurb": "Limited tour cassette with scene-specific artwork.", + "variants": [("cassette", "Tour Cassette", "Smoke", "", 14.0 + (artist_index % 3))], + }, + ] + + local_merch_specs = [] + for merch_index, merch_spec in enumerate(merch_specs): + merch_slug = make_unique_slug(slugify(merch_spec["title"]), used_merch_slugs, artist_slug) + album_link = next((alb for alb in artist.albums if alb.slug == merch_spec.get("album")), None) + merch = MerchItem( + artist_id=artist.id, + album_id=album_link.id if album_link else None, + title=merch_spec["title"], + slug=merch_slug, + item_type=merch_spec["item_type"], + description=merch_description(merch_spec["title"], album_link.title if album_link else None, artist.name, merch_spec["item_type"]), + short_blurb=merch_spec["short_blurb"], + image=f"images/merch/{merch_slug}.jpg", + price=merch_spec["price"], + inventory=55 + artist_index * 2 + merch_index * 6, + release_date=max(album.release_date for album in artist.albums) - timedelta(days=14 - merch_index * 5), + is_featured=artist_index < 10 or merch_index == 0, + ) + db.session.add(merch) + db.session.flush() + + for variant_index, variant_spec in enumerate(merch_spec["variants"], start=1): + kind, name, option_a, option_b, price = variant_spec + db.session.add( + FormatVariant( + merch_item_id=merch.id, + kind=kind, + name=name, + option_a=option_a, + option_b=option_b, + price=price, + inventory=18 + ((artist_index + merch_index + variant_index) % 24), + sku=f"{merch_slug}-{kind}-{variant_index}", + shipping_note="Ships in 2-5 days", + edition_note="Locally generated merch photo and deterministic variant data.", + is_default=variant_index == 1, + ) + ) + local_merch_specs.append({"title": merch.title, "slug": merch.slug, "item_type": merch.item_type}) + + create_assets_for_artist(base_dir, artist_slug, artist.name, artist.location, local_album_specs, local_merch_specs) + + db.session.commit() diff --git a/sites/bandcamp/static/css/main.css b/sites/bandcamp/static/css/main.css new file mode 100644 index 00000000..8a0bc9de --- /dev/null +++ b/sites/bandcamp/static/css/main.css @@ -0,0 +1,636 @@ +/* Bandcamp mirror stylesheet — aligned with bandcamp.com (light pages + dark release pages). */ + +:root { + --bg: #ecf3f4; + --bg-flat: #f6f6f6; + --panel: #ffffff; + --ink: #222222; + --muted: #6f6f6f; + --line: #dcdcdc; + --chip: #f7f7f7; + --brand: #0cacd7; + --dark: #111111; + --dark-panel: #1b1b1b; + --dark-line: #3a3a3a; +} + +* { box-sizing: border-box; } + +html, body { margin: 0; padding: 0; } + +body { + background: var(--bg); + color: var(--ink); + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +a { color: #1d6fb8; text-decoration: none; } +a:hover { text-decoration: underline; } + +.brand, .brand:hover, .catnav a, .header-actions a, .release-meta h3 a, .merch-meta h3 a, +.genre-chip, .scene-card, .sell-card, .button-primary, .button-secondary { color: var(--ink); } + +img { max-width: 100%; display: block; } + +.container { + width: min(1160px, calc(100% - 48px)); + margin: 0 auto; +} + +.eyebrow { + margin: 0 0 6px; + font-size: 12px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--muted); +} + +/* ---------- Header ---------- */ + +.site-header { + background: var(--bg); + border-bottom: 1px solid rgba(34, 34, 34, 0.08); +} + +.header-main { + display: flex; + align-items: center; + gap: 24px; + padding: 18px 0 14px; +} + +.brand { + display: flex; + align-items: baseline; + gap: 8px; + font-weight: 700; + letter-spacing: 0.01em; +} + +.brand-mark { + width: 14px; height: 14px; + background: var(--brand); + display: inline-block; + transform: translateY(1px); + clip-path: polygon(0 30%, 100% 0, 100% 100%, 0 100%); +} + +.brand-text { font-size: 22px; color: var(--ink); } +.brand-sub { display: none; } + +.header-search { + display: flex; + align-items: center; + flex: 1; + max-width: 420px; + background: #dce3e4; + border: 1px solid #d2d9da; + border-radius: 3px; + overflow: hidden; +} + +.header-search input { + flex: 1; + border: 0; + background: transparent; + padding: 9px 12px 9px 14px; + font-size: 14px; + color: var(--ink); + outline: none; +} + +.header-search button { + border: 0; + background: transparent; + padding: 9px 12px; + font-size: 13px; + color: var(--muted); + cursor: pointer; +} + +.header-actions { + margin-left: auto; + display: flex; + align-items: center; + gap: 18px; + font-size: 14px; + white-space: nowrap; +} + +.header-actions .count { + display: inline-block; + min-width: 18px; + padding: 0 4px; + border-radius: 9px; + background: var(--ink); + color: #fff; + font-size: 11px; + text-align: center; +} + +.header-subnav { padding: 0 0 10px; } + +.catnav { + display: flex; + align-items: center; + gap: 26px; + padding: 2px 0 4px; + overflow-x: auto; +} + +.catnav a { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: 14px; + color: var(--ink); + white-space: nowrap; +} + +.catnav a svg { width: 18px; height: 18px; } + +/* ---------- Buttons ---------- */ + +.button-primary, +button.button-primary { + display: inline-block; + background: var(--ink); + color: #fff; + border: 1px solid var(--ink); + border-radius: 3px; + padding: 9px 16px; + font-size: 14px; + cursor: pointer; + text-decoration: none; +} + +.button-primary:hover { background: #000; text-decoration: none; } + +.button-secondary { + display: inline-block; + background: transparent; + color: var(--ink); + border: 1px solid var(--ink); + border-radius: 3px; + padding: 9px 16px; + font-size: 14px; + cursor: pointer; + text-decoration: none; +} + +.button-secondary:hover { background: rgba(34, 34, 34, 0.06); text-decoration: none; } + +.button-secondary.full { display: block; text-align: center; } + +/* ---------- Flash + layout ---------- */ + +.flash-wrap { margin: 14px auto 0; } +.flash { + background: #fff3cd; + border: 1px solid #ecd9a0; + padding: 10px 14px; + border-radius: 3px; + margin-bottom: 8px; + font-size: 14px; +} +.flash-error { background: #fde8e8; border-color: #f3bcbc; } + +.page-main { padding: 22px 0 60px; } + +.section-block { margin-top: 34px; } + +.section-head { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 20px; + margin: 26px 0 14px; +} + +.section-head h2 { font-size: 24px; font-weight: 500; margin: 0; } +.section-head p { margin: 4px 0 0; color: var(--muted); font-size: 14px; } +.section-head.compact { margin: 18px 0 10px; } + +/* ---------- Home ---------- */ + +.home-hero { padding: 34px 0 10px; } + +.home-hero h1 { + font-size: 44px; + line-height: 1.2; + font-weight: 500; + margin: 0 0 18px; + max-width: 900px; +} + +.home-hero .fact { + display: inline-block; + background: var(--ink); + color: #fff; + padding: 0 10px; + transform: skewX(-6deg); +} + +.home-hero .fact > span { display: inline-block; transform: skewX(6deg); } + +.home-hero .sub { color: var(--muted); font-size: 16px; margin: 0 0 18px; } + +.hero-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; } + +.selling-now { + display: flex; + gap: 18px; + overflow-x: auto; + padding: 6px 0 10px; +} + +.sell-card { width: 168px; flex: 0 0 auto; } + +.sell-card img { + width: 168px; height: 168px; + object-fit: cover; + background: #ddd; +} + +.sell-card .sold { font-size: 13px; margin-top: 8px; } +.sell-card .who { font-size: 13px; color: var(--muted); } +.sell-card .when { font-size: 12px; color: var(--muted); margin-top: 2px; } + +/* ---------- Cards ---------- */ + +.card-grid { display: grid; gap: 26px 22px; } +.release-grid { grid-template-columns: repeat(auto-fill, minmax(238px, 1fr)); } +.merch-grid { grid-template-columns: repeat(auto-fill, minmax(238px, 1fr)); } + +.release-card { position: relative; } + +.release-cover { position: relative; display: block; } + +.release-cover img { + width: 100%; + aspect-ratio: 1 / 1; + object-fit: cover; + background: #d9d9d9; +} + +.format-rail { + position: absolute; + top: 8px; + right: 8px; + background: rgba(34, 34, 34, 0.85); + color: #fff; + font-size: 11px; + padding: 3px 8px; + border-radius: 10px; +} + +.release-meta h3 { font-size: 16px; font-weight: 700; margin: 10px 0 2px; } +.release-meta h3 a { color: var(--ink); } +.release-byline { margin: 0; font-size: 14px; color: var(--muted); } +.release-copy, .release-tags, .release-bottom { display: none; } + +.merch-card .merch-cover img { + width: 100%; + aspect-ratio: 1 / 1; + object-fit: contain; + background: #fff; + border: 1px solid var(--line); +} + +.merch-meta h3 { font-size: 15px; margin: 10px 0 2px; } +.merch-meta p { margin: 0; font-size: 13px; color: var(--muted); } +.merch-meta .release-bottom { display: block; margin-top: 4px; } +.merch-meta .release-bottom strong { font-size: 13px; } + +.artist-card { display: flex; gap: 12px; align-items: flex-start; } +.artist-avatar img { width: 56px; height: 56px; border-radius: 50%; object-fit: cover; } +.artist-card h3 { margin: 0 0 2px; font-size: 15px; } +.artist-card p { margin: 0; font-size: 13px; color: var(--muted); } +.artist-copy { display: none; } + +.artist-stack { display: grid; gap: 14px; } + +/* ---------- Panels ---------- */ + +.editorial-panel { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 3px; + padding: 20px; +} + +.editorial-panel.pale { background: #f2f7f8; } +.editorial-grid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 22px; } + +.support-feed { display: grid; gap: 12px; } +.support-item { display: flex; gap: 12px; align-items: center; } +.support-item img { width: 52px; height: 52px; object-fit: cover; } +.support-item strong { display: block; font-size: 14px; } +.support-item span { display: block; font-size: 13px; color: var(--muted); } + +.empty-panel { + border: 1px dashed var(--line); + padding: 18px; + color: var(--muted); + background: rgba(255, 255, 255, 0.5); +} + +.scene-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; } +.scene-card { + background: var(--panel); + border: 1px solid var(--line); + padding: 14px; + display: block; +} +.scene-card strong { font-size: 15px; } +.scene-card span { display: block; color: var(--muted); font-size: 13px; } +.scene-card p { font-size: 13px; margin: 8px 0 0; color: var(--muted); } + +/* ---------- Discover ---------- */ + +.discover-layout { display: grid; grid-template-columns: 290px 1fr; gap: 30px; align-items: start; } + +.filters-panel h1 { font-size: 28px; font-weight: 500; margin: 0 0 4px; } +.filters-panel > p { color: var(--muted); font-size: 14px; margin: 0 0 16px; } + +.filters-form { + background: var(--panel); + border: 1px solid var(--line); + padding: 16px; + display: grid; + gap: 12px; + position: sticky; + top: 16px; +} + +.filters-form label { font-size: 13px; color: var(--muted); display: grid; gap: 4px; } + +.filters-form input, +.filters-form select, +.filters-form textarea, +.auth-card input, +.auth-card select, +.purchase-panel input, +.purchase-panel select, +.editorial-panel input, +.editorial-panel select, +.editorial-panel textarea, +form input[type="text"], +form input[type="search"], +form input[type="email"], +form input[type="password"], +form input[type="number"], +form select, +form textarea { + width: 100%; + border: 1px solid #c9c9c9; + border-radius: 3px; + padding: 8px 10px; + font-size: 14px; + background: #fff; + color: var(--ink); +} + +.filters-form .filter-buttons { display: flex; gap: 8px; } + +.discover-main { min-width: 0; } + +.focus-release { + display: grid; + grid-template-columns: 300px 1fr; + gap: 22px; + background: var(--panel); + border: 1px solid var(--line); + padding: 20px; + margin-bottom: 26px; +} + +.focus-release img { width: 100%; aspect-ratio: 1/1; object-fit: cover; } +.focus-release h2 { margin: 0 0 4px; font-size: 24px; } +.focus-byline { color: var(--muted); font-size: 14px; } +.focus-meta { display: flex; gap: 14px; font-size: 13px; color: var(--muted); flex-wrap: wrap; } + +.genre-row { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 20px; } +.genre-chip { + border: 1px solid var(--line); + background: var(--chip); + border-radius: 3px; + padding: 6px 11px; + font-size: 13px; +} +.genre-chip.active { background: var(--ink); border-color: var(--ink); color: #fff; } + +/* ---------- Release pages (light, bandcamp layout) ---------- */ + +.album-hero { + display: grid; + grid-template-columns: 1.15fr 1fr 0.9fr; + gap: 34px; + padding-top: 26px; + align-items: start; +} + +.album-info h1 { font-size: 34px; font-weight: 700; margin: 4px 0 6px; } +.album-info .release-byline { color: var(--muted); } +.album-info .release-byline a { color: #1d6fb8; } + +.album-art img { + width: 100%; + aspect-ratio: 1 / 1; + object-fit: cover; + background: #e4e4e4; +} + +.album-copy-text { color: #333; font-size: 14px; margin-top: 14px; } +.album-copy-text p { margin: 0 0 10px; } + +.album-stats { display: flex; flex-wrap: wrap; gap: 8px 14px; margin: 12px 0 4px; font-size: 13px; color: var(--muted); } +.album-stats span { border-bottom: 1px dotted #c9c9c9; padding-bottom: 2px; } + +.release-tags { display: flex; gap: 6px; flex-wrap: wrap; margin: 10px 0 4px; } +.release-tags a, .release-tags span { + border: 1px solid var(--line); + color: #1d6fb8; + font-size: 12px; + padding: 3px 8px; + border-radius: 10px; +} + +.tracklist { margin-top: 16px; border-top: 1px solid var(--line); } +.track-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 9px 0; + border-bottom: 1px solid #e7e7e7; + font-size: 14px; +} +.track-row strong { font-weight: 400; } +.track-row strong a { color: #1d6fb8; } +.track-row > div { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; } +.track-row .preview-toggle { border: 0; background: transparent; color: #1d6fb8; font-size: 12px; cursor: pointer; padding: 0; } +.preview-panel { display: none; width: 100%; } +.preview-panel.open, .preview-panel.static-open { display: block; } +.preview-panel p { margin: 4px 0 0; font-size: 12px; color: var(--muted); } +.wave { display: inline-flex; gap: 3px; align-items: flex-end; height: 16px; } +.wave span { width: 3px; background: #1d6fb8; height: 8px; display: inline-block; } +.wave span:nth-child(2) { height: 14px; } +.wave span:nth-child(3) { height: 6px; } +.wave span:nth-child(4) { height: 12px; } +.wave span:nth-child(5) { height: 9px; } + +.purchase-panel { + background: #fff; + border: 1px solid var(--line); + padding: 18px; + display: grid; + gap: 10px; +} +.purchase-panel h2 { font-size: 18px; margin: 0; } +.purchase-panel label { color: var(--muted); font-size: 13px; display: grid; gap: 4px; } +.price-big { font-size: 22px; margin: 0; color: #1d6fb8; } + +.variant-stack { display: grid; gap: 8px; margin-top: 8px; } +.variant-card { border: 1px solid var(--line); padding: 10px; font-size: 13px; color: #444; } +.variant-card strong { display: block; } +.variant-card p, .variant-card small { color: var(--muted); margin: 4px 0 0; display: block; } + +/* ---------- Auth / forms pages ---------- */ + +.auth-shell { display: grid; place-items: start center; padding-top: 30px; } +.auth-card { + width: min(420px, 100%); + background: var(--panel); + border: 1px solid var(--line); + padding: 22px; + display: grid; + gap: 12px; +} +.auth-card h1 { margin: 0; font-size: 24px; font-weight: 500; } +.auth-card label { font-size: 13px; color: var(--muted); display: grid; gap: 4px; } + +.form-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); } + +/* ---------- Cart / checkout / orders ---------- */ + +.cart-layout { display: grid; grid-template-columns: 1.4fr 1fr; gap: 26px; align-items: start; } +.cart-items { display: grid; gap: 12px; } +.cart-item { + display: flex; + gap: 14px; + align-items: center; + background: var(--panel); + border: 1px solid var(--line); + padding: 12px; +} +.cart-item img { width: 64px; height: 64px; object-fit: cover; } +.cart-copy { flex: 1; } +.cart-copy strong { display: block; font-size: 14px; } +.cart-copy p { margin: 2px 0 0; color: var(--muted); font-size: 13px; } + +.summary-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid var(--line); font-size: 14px; } +.summary-row.total { font-weight: 700; border-bottom: 0; } +.summary-mini { color: var(--muted); font-size: 13px; } + +.order-list { display: grid; gap: 10px; } +.order-row { + display: flex; + justify-content: space-between; + gap: 12px; + background: var(--panel); + border: 1px solid var(--line); + padding: 12px; + font-size: 14px; +} + +.compare-metric { border-bottom: 1px solid var(--line); padding: 10px 0; } +.compare-metric strong { display: block; font-size: 13px; color: var(--muted); } + +.stat-card { + background: var(--panel); + border: 1px solid var(--line); + padding: 14px; +} +.stat-card strong { display: block; font-size: 18px; } +.stat-card span { color: var(--muted); font-size: 13px; } +.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; } + +.wishlist-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(238px, 1fr)); gap: 22px; } +.wish-card { background: var(--panel); border: 1px solid var(--line); padding: 12px; } +.wish-card img { width: 100%; aspect-ratio: 1/1; object-fit: cover; } + +/* ---------- Artist / label / scene / merch ---------- */ + +.artist-hero { + position: relative; + min-height: 260px; + background-size: cover; + background-position: center; + display: flex; + align-items: flex-end; + color: #fff; +} +.artist-hero::before { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,0.25), rgba(0,0,0,0.82)); } +.artist-hero .container { position: relative; padding: 26px 0; display: flex; align-items: flex-end; gap: 18px; } +.artist-hero img.avatar { width: 96px; height: 96px; border-radius: 4px; object-fit: cover; } +.artist-hero h1 { margin: 0 0 4px; font-size: 30px; } + +.merch-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 30px; align-items: start; } +.merch-layout img { width: 100%; object-fit: contain; background: #fff; border: 1px solid var(--line); } + +/* ---------- Search ---------- */ + +.track-search-row { display: flex; gap: 8px; } +.track-search-list { display: grid; gap: 8px; margin-top: 12px; } + +/* ---------- Footer ---------- */ + +.site-footer { border-top: 1px solid var(--line); background: var(--bg-flat); margin-top: 40px; } +.footer-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; padding: 26px 0; } +.site-footer h3 { font-size: 14px; margin: 0 0 6px; } +.site-footer p { margin: 0 0 4px; font-size: 13px; color: var(--muted); } + +/* ---------- Responsive ---------- */ + +@media (max-width: 980px) { + .header-main { flex-wrap: wrap; } + .header-search { order: 3; max-width: none; width: 100%; } + .discover-layout { grid-template-columns: 1fr; } + .focus-release { grid-template-columns: 1fr; } + .album-hero { grid-template-columns: 1fr; } + .editorial-grid, .cart-layout, .merch-layout { grid-template-columns: 1fr; } + .home-hero h1 { font-size: 32px; } +} + +/* ---------- Editorial / Radio / Gift cards ---------- */ + +.daily-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 22px; } +.daily-card { display: grid; grid-template-columns: 120px 1fr; gap: 14px; background: #fff; border: 1px solid var(--line); padding: 12px; } +.daily-card img { width: 120px; height: 120px; object-fit: cover; } +.daily-card h3 { margin: 2px 0 4px; font-size: 16px; } +.daily-byline { margin: 0 0 6px; color: var(--muted); font-size: 13px; } +.daily-blurb { margin: 0; font-size: 13px; color: #444; } + +.radio-shows { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; } +.radio-show { background: #fff; border: 1px solid var(--line); padding: 14px; } +.radio-show h3 { margin: 0 0 6px; font-size: 16px; } +.radio-show p { margin: 0; color: var(--muted); font-size: 13px; } +.radio-episodes { display: grid; gap: 12px; } +.radio-episode { display: flex; gap: 14px; align-items: center; background: #fff; border: 1px solid var(--line); padding: 12px; } +.radio-episode img { width: 64px; height: 64px; object-fit: cover; } +.radio-episode > div { display: flex; flex-direction: column; gap: 2px; font-size: 14px; } +.radio-episode span { color: var(--muted); font-size: 13px; } + +.gift-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; } +.gift-card { height: 130px; display: flex; flex-direction: column; justify-content: flex-end; padding: 16px; color: #fff; border-radius: 4px; background: linear-gradient(135deg, #0cacd7, #1d6fb8); } +.gift-card.gift-25 { background: linear-gradient(135deg, #1d6fb8, #5b3aa8); } +.gift-card.gift-50 { background: linear-gradient(135deg, #5b3aa8, #c2417a); } +.gift-card.gift-100 { background: linear-gradient(135deg, #c2417a, #e0873a); } +.gift-card strong { font-size: 26px; } +.gift-card span { font-size: 12px; opacity: 0.85; } +.gift-form { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; } +.gift-form label { flex: 1; min-width: 220px; } diff --git a/sites/bandcamp/static/icons/.gitkeep b/sites/bandcamp/static/icons/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/sites/bandcamp/static/js/main.js b/sites/bandcamp/static/js/main.js new file mode 100644 index 00000000..0f365ae6 --- /dev/null +++ b/sites/bandcamp/static/js/main.js @@ -0,0 +1,31 @@ +document.querySelectorAll("form select").forEach((select) => { + select.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + event.preventDefault(); + } + }); +}); + +document.querySelectorAll("form input, form select, form textarea").forEach((field) => { + field.addEventListener("invalid", () => { + if (field.validity.valueMissing) { + field.setCustomValidity("Please fill out this field."); + } else if (field.validity.typeMismatch) { + field.setCustomValidity("Please enter a valid value."); + } else { + field.setCustomValidity(""); + } + }); + field.addEventListener("input", () => field.setCustomValidity("")); + field.addEventListener("change", () => field.setCustomValidity("")); +}); + +document.querySelectorAll(".preview-toggle").forEach((button) => { + button.addEventListener("click", () => { + const targetId = button.getAttribute("data-preview-target"); + const panel = document.getElementById(targetId); + if (!panel) return; + panel.classList.toggle("open"); + button.textContent = panel.classList.contains("open") ? "Hide preview" : "Preview"; + }); +}); diff --git a/sites/bandcamp/tasks.jsonl b/sites/bandcamp/tasks.jsonl new file mode 100644 index 00000000..f00b0855 --- /dev/null +++ b/sites/bandcamp/tasks.jsonl @@ -0,0 +1,18 @@ +{"web_name":"Bandcamp","id":"Bandcamp--0","ques":"Search for Neon Harbor's 2025 Berlin electronic release and tell me the price of its cassette edition.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_0.py","judge_rubric":"FACT CHECKPOINTS: Report the cassette price for the release identified by the requested artist, year and scene. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--1","ques":"Use Discover filters to find the Tokyo ambient release issued on August 1, 2025, then report the title of its third track.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/discover","verifier_path":"sites/bandcamp/verify/verify_1.py","judge_rubric":"FACT CHECKPOINTS: Identify the third track, not just the same-named release, using the requested scene, genre and date. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--2","ques":"Find Ashen Circuit's merch and identify the cheaper slipmat option.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/discover","verifier_path":"sites/bandcamp/verify/verify_2.py","judge_rubric":"FACT CHECKPOINTS: Identify the lower-priced slipmat option; reject reversed comparisons or incorrect prices if supplied. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--3","ques":"Sign in as Alice Johnson using alice.j@test.com / TestPass123! and save Machine Prayer to the wishlist if it is not already there.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_3.py","judge_rubric":"FACT CHECKPOINTS: The named account gains exactly the requested wishlist item, preserving all existing rows. Require a nonempty final response, related same-origin page evidence, and the exact authoritative before/after transition. Do not grade response language or require a fixed login route or final URL. No-op, wrong account, wrong quantity, wrong target, or unrelated mutations fail."} +{"web_name":"Bandcamp","id":"Bandcamp--4","ques":"Sign in as Bob Chen using bob.c@test.com / TestPass123! and add the Medium Soft Locale Drift Hoodie to the cart.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_4.py","judge_rubric":"FACT CHECKPOINTS: The named account gains exactly one unit of the requested size and product, preserving the existing cart. Require a nonempty final response, related same-origin page evidence, and the exact authoritative before/after transition. Do not grade response language or require a fixed login route or final URL. No-op, wrong account, wrong quantity, wrong target, or unrelated mutations fail."} +{"web_name":"Bandcamp","id":"Bandcamp--5","ques":"Use the compare view to determine which release is longer: Tidal Memory or Harbor Burn.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/discover","verifier_path":"sites/bandcamp/verify/verify_5.py","judge_rubric":"FACT CHECKPOINTS: Use a compare page containing both requested releases and identify the longer one; reject reversed runtimes if supplied. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--6","ques":"On the Tidal Memory release page, what is the duration of track 4?","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_6.py","judge_rubric":"FACT CHECKPOINTS: Report the duration of the requested track, with correct units. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--7","ques":"Sign in as Bob Chen using bob.c@test.com / TestPass123! and complete a mock checkout using the items already in his cart and his Chicago shipping address (77 Fulton Market, Chicago, United States).","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_7.py","judge_rubric":"FACT CHECKPOINTS: The named account completes one paid mock order from the entire initial cart, with exact variants, quantities, amounts and requested shipping address. Preserve previous orders and other accounts; allow only checkout side effects. No final-answer order number is required. Require a nonempty final response, related same-origin page evidence, and the exact authoritative before/after transition. Do not grade response language or require a fixed login route or final URL. No-op, wrong account, wrong quantity, wrong target, or unrelated mutations fail."} +{"web_name":"Bandcamp","id":"Bandcamp--8","ques":"Sign in as David Kim using david.k@test.com / TestPass123!, change the preferred format to Vinyl, and update the city to Eugene.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_8.py","judge_rubric":"FACT CHECKPOINTS: Only the requested profile fields change for the named account; preserve all other fields and accounts. Require a nonempty final response, related same-origin page evidence, and the exact authoritative before/after transition. Do not grade response language or require a fixed login route or final URL. No-op, wrong account, wrong quantity, wrong target, or unrelated mutations fail."} +{"web_name":"Bandcamp","id":"Bandcamp--9","ques":"Sign in as Alice Johnson using alice.j@test.com / TestPass123! and find the order number of her most recent seeded order.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_9.py","judge_rubric":"FACT CHECKPOINTS: Report the most recent initial order number for the named account from the protected order pages. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--10","ques":"Find the Berlin experimental release tagged with microtone and report its digital price.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/discover","verifier_path":"sites/bandcamp/verify/verify_10.py","judge_rubric":"FACT CHECKPOINTS: Report the digital price of the release identified by the requested scene, genre and tag. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--11","ques":"Use Discover to locate the London pop release from December 2025 and report which physical formats it offers.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/discover","verifier_path":"sites/bandcamp/verify/verify_11.py","judge_rubric":"FACT CHECKPOINTS: Report the complete physical-format set for the identified release; reject additional formats. An explicit explanation excluding nonphysical formats is valid. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--12","ques":"From Velvet Avenue's pages, find the poster linked to Blue Hour Broadcast and give the price of the signed edition.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_12.py","judge_rubric":"FACT CHECKPOINTS: Report the signed-edition price of the poster linked to the requested release. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--13","ques":"On Blue Hour Broadcast, identify the cheapest available format and its price.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_13.py","judge_rubric":"FACT CHECKPOINTS: Identify the minimum-priced format and its price for the requested release. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--14","ques":"Sign in as Carol Davis using carol.d@test.com / TestPass123!, open the collection, and identify the favorite track attached to Between Stations.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_14.py","judge_rubric":"FACT CHECKPOINTS: Identify the favorite track on the requested collection entry, not merely its album title. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--15","ques":"Search for the artist whose bio mentions small-room guitar songs and tell me the title of that artist's newest album.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/search","verifier_path":"sites/bandcamp/verify/verify_15.py","judge_rubric":"FACT CHECKPOINTS: Identify the newest release of the artist matching the bio. Any relevant search wording is valid. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--16","ques":"Find the Los Angeles rock album released on June 27, 2025 and report its closing track.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/discover","verifier_path":"sites/bandcamp/verify/verify_16.py","judge_rubric":"FACT CHECKPOINTS: Identify the closing track of the release matching the requested scene, genre and date. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} +{"web_name":"Bandcamp","id":"Bandcamp--17","ques":"Open Salt Meadow's tote item and list the two available color variants.","web":"http://localhost:40024/","upstream_url":"https://bandcamp.com/","verifier_path":"sites/bandcamp/verify/verify_17.py","judge_rubric":"FACT CHECKPOINTS: Report exactly the complete color-variant set of the requested tote; reject extra colors. Require correct affirmative facts, related same-origin page evidence and unchanged authoritative database contents. No-op, missing evidence, unsupported or contradicted facts, and external-page shortcuts fail. Do not require a fixed search phrase, login route or final URL."} diff --git a/sites/bandcamp/templates/404.html b/sites/bandcamp/templates/404.html new file mode 100644 index 00000000..cd9452bc --- /dev/null +++ b/sites/bandcamp/templates/404.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block title %}Not Found - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+

That page could not be found.

+

Try returning to discover or searching for the release directly.

+
+
+{% endblock %} diff --git a/sites/bandcamp/templates/500.html b/sites/bandcamp/templates/500.html new file mode 100644 index 00000000..5218e005 --- /dev/null +++ b/sites/bandcamp/templates/500.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block title %}Server Error - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+

Something went wrong.

+

An unexpected server error occurred. Please try again.

+
+
+{% endblock %} diff --git a/sites/bandcamp/templates/_cards.html b/sites/bandcamp/templates/_cards.html new file mode 100644 index 00000000..5237e0d1 --- /dev/null +++ b/sites/bandcamp/templates/_cards.html @@ -0,0 +1,39 @@ +{% macro album_card(album, show_scene=True) %} + +{% endmacro %} + +{% macro merch_card(merch) %} + +{% endmacro %} + +{% macro artist_card(artist) %} + +{% endmacro %} diff --git a/sites/bandcamp/templates/account.html b/sites/bandcamp/templates/account.html new file mode 100644 index 00000000..3ecd2fd6 --- /dev/null +++ b/sites/bandcamp/templates/account.html @@ -0,0 +1,43 @@ +{% extends "base.html" %} +{% block title %}Account - Bandcamp Mirror{% endblock %} +{% block content %} +
+ + + + +
+

Recent order history

+
+ +
+{% endblock %} diff --git a/sites/bandcamp/templates/account_edit.html b/sites/bandcamp/templates/account_edit.html new file mode 100644 index 00000000..776b944b --- /dev/null +++ b/sites/bandcamp/templates/account_edit.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} +{% block title %}Edit Account - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+

Edit profile

+
+ + + + + + + + +
+ +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/album.html b/sites/bandcamp/templates/album.html new file mode 100644 index 00000000..c487a83b --- /dev/null +++ b/sites/bandcamp/templates/album.html @@ -0,0 +1,146 @@ +{% extends "base.html" %} +{% from "_cards.html" import album_card, merch_card %} +{% block title %}{{ album.title }} - Bandcamp{% endblock %} +{% block content %} +
+
+

{{ album.primary_genre.name }} / {{ album.scene.name }}

+

{{ album.title }}

+ +
+ {{ album.track_count }} tracks + {{ album.duration_seconds|duration }} + released {{ album.release_date|longdate }} + {{ album.catalog_no }} +
+
+ {% for tag in album.tags %} + {{ tag.name }} + {% endfor %} +
+ +
+ {% for track in album.tracks %} +
+
+ {{ track.track_number }}. {{ track.title }} + +
+
+ +
+

{{ track.preview_hook }}

+
+
+ {{ track.duration_seconds|duration }} +
+ {% endfor %} +
+
+ +
+ {{ album.title }} cover +
+ +
+ +
+
+ +
+
+
+

{{ album.description }}

+

{{ album.story }}

+
+
+
+ +
+
+

Fan comments

+ {% if album.comments %} + {% for comment in album.comments %} +
+ {{ comment.headline or 'Fan note' }} + {{ comment.user.display_name }} / {{ comment.rating }}/5 +

{{ comment.body }}

+
+ {% endfor %} + {% else %} +

No comments yet. Be the first to add one.

+ {% endif %} +
+
+

Add a comment

+ + + + {% if current_user.is_authenticated %} + + {% else %} + Log in to comment + {% endif %} +
+
+ +
+

Related merch

+
+ {% for merch in related_merch %} + {{ merch_card(merch) }} + {% endfor %} +
+
+ +
+

More in this lane

+
+ {% for release in recommended %} + {{ album_card(release) }} + {% endfor %} +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/artist.html b/sites/bandcamp/templates/artist.html new file mode 100644 index 00000000..1bd500ee --- /dev/null +++ b/sites/bandcamp/templates/artist.html @@ -0,0 +1,49 @@ +{% extends "base.html" %} +{% from "_cards.html" import album_card, merch_card %} +{% block title %}{{ artist.name }} - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+ {{ artist.name }} avatar +
+

{{ artist.primary_genre.name }} / {{ artist.scene.name }}

+

{{ artist.name }}

+

{{ artist.headline }}

+

{{ artist.bio }}

+
+ {{ artist.label.name }} + {{ artist.follow_count }} followers + {{ collection_count }} collection adds +
+
+
+
+ +
+
+
+

Discography

+

Albums and EPs

+
+
+
+ {% for album in albums %} + {{ album_card(album, show_scene=False) }} + {% endfor %} +
+
+ +
+
+
+

Merch

+

Artist-linked physical items

+
+
+
+ {% for merch in merch_items %} + {{ merch_card(merch) }} + {% endfor %} +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/base.html b/sites/bandcamp/templates/base.html new file mode 100644 index 00000000..b604475d --- /dev/null +++ b/sites/bandcamp/templates/base.html @@ -0,0 +1,111 @@ + + + + + + {% block title %}Bandcamp Mirror{% endblock %} + + + + + + {% with messages = get_flashed_messages(with_categories=True) %} + {% if messages %} +
+ {% for category, message in messages %} +
{{ message }}
+ {% endfor %} +
+ {% endif %} + {% endwith %} + +
+ {% block content %}{% endblock %} +
+ + + + + + diff --git a/sites/bandcamp/templates/cart.html b/sites/bandcamp/templates/cart.html new file mode 100644 index 00000000..7ec317f8 --- /dev/null +++ b/sites/bandcamp/templates/cart.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} +{% block title %}Cart - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+

Cart

+ {% for item in items %} +
+ {{ item.title() }} +
+

{{ item.title() }}

+

{{ item.artist_name() }}

+

{{ item.variant_label() }}

+
+
+ + +
+ {{ item.line_total()|money }} +
+ +
+
+ {% else %} +

Your cart is empty.

+ {% endfor %} +
+ +
+{% endblock %} diff --git a/sites/bandcamp/templates/checkout.html b/sites/bandcamp/templates/checkout.html new file mode 100644 index 00000000..2dc43227 --- /dev/null +++ b/sites/bandcamp/templates/checkout.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% block title %}Checkout - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+

Checkout

+

No payment is processed. Completing checkout creates an order and adds albums to your collection.

+
+ + + + + + +
+ +
+ +
+{% endblock %} diff --git a/sites/bandcamp/templates/collection.html b/sites/bandcamp/templates/collection.html new file mode 100644 index 00000000..ba303ea3 --- /dev/null +++ b/sites/bandcamp/templates/collection.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} +{% block title %}Collection - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+

Collection

+

Your purchased library, with formats and favorite tracks.

+
+
+ {% if items %} +
+ {% for item in items %} +
+ + {{ item.album.title }} + +
+

{{ item.album.title }}

+

{{ item.album.artist.name }}

+

{{ item.format_variant.label_text() if item.format_variant else 'Library add' }}

+

Favorite track: {{ item.favorite_track.title if item.favorite_track else 'Not set' }}

+
+
+ {% endfor %} +
+ {% else %} +

Your collection is empty.

+ {% endif %} +
+{% endblock %} diff --git a/sites/bandcamp/templates/compare.html b/sites/bandcamp/templates/compare.html new file mode 100644 index 00000000..dff605ce --- /dev/null +++ b/sites/bandcamp/templates/compare.html @@ -0,0 +1,48 @@ +{% extends "base.html" %} +{% block title %}Compare Releases - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+

Compare releases

+

Useful for format and decision tasks that need a side-by-side view.

+
+
+
+ + + +
+ + {% if left and right %} +
+ {% for album in [left, right] %} +
+ {{ album.title }} +

{{ album.title }}

+

{{ album.artist.name }}

+
Genre{{ album.primary_genre.name }}
+
Scene{{ album.scene.name.split(',')[0] }}
+
Tracks{{ album.track_count }}
+
Runtime{{ album.duration_seconds|duration }}
+
Starting price{{ album.price_from()|money }}
+
Formats{{ album.format_kinds()|join(' / ') }}
+
Release date{{ album.release_date|compactdate }}
+
+ {% endfor %} +
+ {% endif %} +
+{% endblock %} diff --git a/sites/bandcamp/templates/discover.html b/sites/bandcamp/templates/discover.html new file mode 100644 index 00000000..dccc99c4 --- /dev/null +++ b/sites/bandcamp/templates/discover.html @@ -0,0 +1,114 @@ +{% extends "base.html" %} +{% from "_cards.html" import album_card, merch_card %} +{% block title %}Discover - Bandcamp Mirror{% endblock %} +{% block content %} +
+ + +
+
+ all genres + {% for genre in genres %} + {{ genre.name }} + {% endfor %} +
+ + {% if featured_album %} +
+ {{ featured_album.title }} cover +
+

Focused release

+

{{ featured_album.title }}

+ +

{{ featured_album.story }}

+
+ {{ featured_album.track_count }} tracks / {{ featured_album.duration_seconds|duration }} + released {{ featured_album.release_date|longdate }} + from {{ featured_album.price_from()|money }} +
+ +
+
+ {% endif %} + +
+
+

Albums

+

{{ albums|length }} filtered releases in the current view

+
+
+
+ {% for album in albums %} + {{ album_card(album) }} + {% endfor %} +
+ +
+
+

Merch

+

{{ merch_items|length }} merch items linked to the same scenes and genres

+
+
+
+ {% for merch in merch_items %} + {{ merch_card(merch) }} + {% endfor %} +
+
+
+{% endblock %} diff --git a/sites/bandcamp/templates/editorial.html b/sites/bandcamp/templates/editorial.html new file mode 100644 index 00000000..3e453219 --- /dev/null +++ b/sites/bandcamp/templates/editorial.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% from "_cards.html" import album_card %} +{% block title %}Editorial - Bandcamp{% endblock %} +{% block content %} +
+
+
+

Bandcamp Daily

+

Editorial

+

Stories, scene reports, and album picks from the mirror catalog.

+
+
+
+ {% for item in daily %} +
+ + {{ item.album.title }} + +
+

{{ item.kicker }}

+

{{ item.album.title }}

+ +

{{ item.album.story[:150] }}{% if item.album.story|length > 150 %}...{% endif %}

+
+
+ {% endfor %} +
+
+ +
+

Scene reports

+ +
+{% endblock %} diff --git a/sites/bandcamp/templates/gift_cards.html b/sites/bandcamp/templates/gift_cards.html new file mode 100644 index 00000000..efc14f19 --- /dev/null +++ b/sites/bandcamp/templates/gift_cards.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} +{% block title %}Gift Cards - Bandcamp{% endblock %} +{% block content %} +
+
+
+

Bandcamp

+

Gift cards

+

Give the gift of independent music. Denominations are shown in USD.

+
+
+
+ {% for amount in denominations %} +
+ ${{ amount }} + Bandcamp gift card +
+ {% endfor %} +
+
+ +
+
+

Redeem a gift card

+
+ + +
+

Card purchases are simulated in this offline mirror; redeem codes use the BC-GC-XXXX-XXXX format.

+
+
+{% endblock %} diff --git a/sites/bandcamp/templates/index.html b/sites/bandcamp/templates/index.html new file mode 100644 index 00000000..13630e41 --- /dev/null +++ b/sites/bandcamp/templates/index.html @@ -0,0 +1,95 @@ +{% extends "base.html" %} +{% from "_cards.html" import album_card, merch_card, artist_card %} +{% block title %}Bandcamp{% endblock %} +{% block content %} +
+

Fans have paid artists {{ support_total|money }} using Bandcamp, and keep independent music alive.

+

Explore independent releases, artist merchandise, and scenes from around the world.

+ +
+ +
+
+

Selling right now

+ Explore more +
+ +
+ +
+
+

New and notable

+ Newest first +
+
+ {% for album in new_releases[:8] %} + {{ album_card(album) }} + {% endfor %} +
+
+ +
+
+

Featured releases

+ Open discover +
+
+ {% for album in featured_albums %} + {{ album_card(album) }} + {% endfor %} +
+
+ +
+
+

Merch table

+ Explore merch +
+
+ {% for merch in featured_merch %} + {{ merch_card(merch) }} + {% endfor %} +
+
+ +
+
+

Artists to follow

+ Most supported +
+
+
+ {% for album in editorial %} + {{ artist_card(album.artist) }} + {% endfor %} +
+
+
+ +
+
+

Scenes

+
+ +
+{% endblock %} diff --git a/sites/bandcamp/templates/label.html b/sites/bandcamp/templates/label.html new file mode 100644 index 00000000..99ac3da4 --- /dev/null +++ b/sites/bandcamp/templates/label.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% from "_cards.html" import album_card, artist_card %} +{% block title %}{{ label.name }} - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+

Label page

+

{{ label.name }}

+

{{ label.description }}

+

{{ label.location }}

+
+
+
+ {% for artist in artists %} + {{ artist_card(artist) }} + {% endfor %} +
+
+ {% for album in albums %} + {{ album_card(album) }} + {% endfor %} +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/login.html b/sites/bandcamp/templates/login.html new file mode 100644 index 00000000..15d71b11 --- /dev/null +++ b/sites/bandcamp/templates/login.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% block title %}Log In - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+

Log in

+

Use your fan account to continue.

+ {% if next_url %}{% endif %} + + + +

Need an account? Create one.

+
+
+{% endblock %} diff --git a/sites/bandcamp/templates/merch.html b/sites/bandcamp/templates/merch.html new file mode 100644 index 00000000..ef688234 --- /dev/null +++ b/sites/bandcamp/templates/merch.html @@ -0,0 +1,64 @@ +{% extends "base.html" %} +{% from "_cards.html" import merch_card %} +{% block title %}{{ merch.title }} - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+ {{ merch.title }} merch image +
+
+

{{ merch.item_type }} / {{ merch.artist.primary_genre.name }}

+

{{ merch.title }}

+ +

{{ merch.description }}

+
+ {{ merch.inventory }} units available + {{ merch.release_date|longdate }} + {{ merch.price|money }} +
+
+
+ + +
+ +
+

More merch from {{ merch.artist.name }}

+
+ {% for item in related %} + {{ merch_card(item) }} + {% endfor %} +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/order_detail.html b/sites/bandcamp/templates/order_detail.html new file mode 100644 index 00000000..fa546291 --- /dev/null +++ b/sites/bandcamp/templates/order_detail.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} +{% block title %}{{ order.order_number }} - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+

Receipt {{ order.order_number }}

+

{{ order.status }} / placed {{ order.placed_at|longdate }}

+
+
+
+
+ {% for item in order.items %} +
+ {{ item.title }} +
+

{{ item.title }}

+

{{ item.artist_name }}

+

{{ item.variant_label }}

+
+ {{ item.line_total()|money }} +
+ {% endfor %} +
+ +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/orders.html b/sites/bandcamp/templates/orders.html new file mode 100644 index 00000000..6be4eef6 --- /dev/null +++ b/sites/bandcamp/templates/orders.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} +{% block title %}Orders - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+

Order history

+

Your past purchases and receipts.

+
+
+ +
+{% endblock %} diff --git a/sites/bandcamp/templates/radio.html b/sites/bandcamp/templates/radio.html new file mode 100644 index 00000000..5300aab5 --- /dev/null +++ b/sites/bandcamp/templates/radio.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% block title %}Radio - Bandcamp{% endblock %} +{% block content %} +
+
+
+

Bandcamp Radio

+

Shows

+

Weekly sets and genre spotlights from the mirror catalog.

+
+
+
+ {% for genre in shows %} + + {% endfor %} +
+
+ +
+

Latest episodes

+
+ {% for album in episodes %} +
+ {{ album.title }} +
+ {{ album.title }} + {{ album.artist.name }} / {{ album.primary_genre.name }} + +
+
+

{{ album.title }} — full set in the mirror player.

+
+
+
+ {% endfor %} +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/register.html b/sites/bandcamp/templates/register.html new file mode 100644 index 00000000..97026639 --- /dev/null +++ b/sites/bandcamp/templates/register.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} +{% block title %}Register - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+

Create a fan account

+
+ + + + + + + +
+ +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/scene.html b/sites/bandcamp/templates/scene.html new file mode 100644 index 00000000..42262524 --- /dev/null +++ b/sites/bandcamp/templates/scene.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} +{% from "_cards.html" import album_card, artist_card %} +{% block title %}{{ scene.name }} - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+

Scene page

+

{{ scene.name }}

+

{{ scene.description }}

+
+
+
+ {% for artist in artists %} + {{ artist_card(artist) }} + {% endfor %} +
+
+ {% for album in albums %} + {{ album_card(album, show_scene=False) }} + {% endfor %} +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/search.html b/sites/bandcamp/templates/search.html new file mode 100644 index 00000000..efc46532 --- /dev/null +++ b/sites/bandcamp/templates/search.html @@ -0,0 +1,64 @@ +{% extends "base.html" %} +{% from "_cards.html" import album_card, merch_card, artist_card %} +{% block title %}Search - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+

Search

+

Scored token-overlap search across artists, albums, tracks, and merch.

+
+
+ {% if not query %} +
+

Try a query like berlin ambient, slipmat, or blue hour.

+
+ {% else %} +
+

Results for "{{ query }}"

+

{{ artists|length + albums|length + tracks|length + merch_items|length }} ranked matches

+
+ + {% if artists %} +

Artists

+
+ {% for artist in artists %} + {{ artist_card(artist) }} + {% endfor %} +
+ {% endif %} + + {% if albums %} +

Albums

+
+ {% for album in albums %} + {{ album_card(album) }} + {% endfor %} +
+ {% endif %} + + {% if tracks %} +

Tracks

+ + {% endif %} + + {% if merch_items %} +

Merch

+
+ {% for merch in merch_items %} + {{ merch_card(merch) }} + {% endfor %} +
+ {% endif %} + {% endif %} +
+{% endblock %} diff --git a/sites/bandcamp/templates/track.html b/sites/bandcamp/templates/track.html new file mode 100644 index 00000000..6a153c05 --- /dev/null +++ b/sites/bandcamp/templates/track.html @@ -0,0 +1,29 @@ +{% extends "base.html" %} +{% block title %}{{ track.title }} - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+

Track detail

+

{{ track.title }}

+

Track {{ track.track_number }} on {{ album.title }} by {{ album.artist.name }}.

+
+ {{ track.duration_seconds|duration }} + {{ album.primary_genre.name }} + {{ album.release_date|longdate }} +
+
+
+ +
+

{{ track.preview_hook }}

+
+
{{ track.lyrics_excerpt }}
+
+
+ {{ album.title }} cover +

Album context

+

{{ album.description }}

+ Back to album +
+
+{% endblock %} diff --git a/sites/bandcamp/templates/wishlist.html b/sites/bandcamp/templates/wishlist.html new file mode 100644 index 00000000..a8de2adf --- /dev/null +++ b/sites/bandcamp/templates/wishlist.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} +{% block title %}Wishlist - Bandcamp Mirror{% endblock %} +{% block content %} +
+
+
+

Wishlist

+

Albums and merch you want to revisit later.

+
+
+ {% if items %} +
+ {% for item in items %} +
+ {{ item.title() }} +
+

{{ item.title() }}

+

{{ item.artist_name() }}

+
+
+ +
+
+ {% endfor %} +
+ {% else %} +

Your wishlist is empty right now.

+ {% endif %} +
+{% endblock %} diff --git a/sites/bandcamp/verify/README.md b/sites/bandcamp/verify/README.md new file mode 100644 index 00000000..a0e2c8bb --- /dev/null +++ b/sites/bandcamp/verify/README.md @@ -0,0 +1,25 @@ +# Bandcamp Task Verifiers + +18 个 wrapper 对应 `Bandcamp--0..17`,只消费本题冻结快照,不连接 Docker 或在线模型。 + +```bash +python3 -B -m unittest discover -s sites/bandcamp/verify -p 'test_*.py' -v +python3 -B sites/bandcamp/verify/verify_0.py --run_dir runs/bandcamp-0 +``` + +`run_dir` 必须包含 `trajectory.json`,其中 `task_id`、`start_url`、`steps[].url` 和字符串 `final_answer` 必须有效。 +自动发现 `initial.db`(否则 `before.db`)及 `after.db`;也支持显式 `--initial_db` / `--before_db` 和 `--after_db`。 +若有 `task.json`,其 `id` 必须匹配 wrapper。旧消费者的 `--container` / `--no_llm` 参数仅接受解析,不触发 live-state fallback。 + +- Exit 0:有效 PASS。 +- Exit 1:有效普通 FAIL,包括 no-op、错误结果、缺少站内页面、非请求的最终业务变更。 +- Exit 2:缺快照、解析/身份/schema/完整性错误,不能算合格负例。 + +信息题要求正确肯定事实、相关同源页面和全表不变。简短回答由任务与页面确定主体;显式错误比较、同名 album/track 歧义、已覆盖的否定和多余集合值会被拒绝。 +这些是确定性语言规则,不是任意自然语言的完整语义证明,真实 actor 答案仍须独立主审。不要为了迎合某一条生成文本放宽谓词。 + +状态题要求非空 final,但不要求英文关键词、固定 login/final URL 或结账后的订单号复述。 +所有原有行与字段保留;允许的变化仅为目标 wishlist/cart 新行、指定 profile 字段,或完整 cart 转单及同步 checkout 的预期副作用。 + +`test_seed.sql` 从已审计的合成 seed 导出完整真实 schema/数据,正例由独立 SQL 和手写回答建立,不从 verifier PASS 常量生成。fixture、HTTP 诊断和 no-op 都不是纯视觉 E2E 成绩。 +根共享 `agent_demo/eval_judge.py` 的 `success/pass` 缺陷不在本目录修复;调用方必须把 exit 0/1 作为健康判分、exit 2 作为基础设施错误。 diff --git a/sites/bandcamp/verify/test_seed.sql b/sites/bandcamp/verify/test_seed.sql new file mode 100644 index 00000000..5b3303b5 --- /dev/null +++ b/sites/bandcamp/verify/test_seed.sql @@ -0,0 +1,1797 @@ +-- Audited synthetic HF seed; full real schema and rows. Not E2E evidence. +BEGIN TRANSACTION; +CREATE TABLE album_tags ( + album_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + PRIMARY KEY (album_id, tag_id), + FOREIGN KEY(album_id) REFERENCES albums (id), + FOREIGN KEY(tag_id) REFERENCES tags (id) +); +INSERT INTO "album_tags" VALUES(1,1); +INSERT INTO "album_tags" VALUES(1,2); +INSERT INTO "album_tags" VALUES(1,3); +INSERT INTO "album_tags" VALUES(1,4); +INSERT INTO "album_tags" VALUES(2,5); +INSERT INTO "album_tags" VALUES(2,6); +INSERT INTO "album_tags" VALUES(2,7); +INSERT INTO "album_tags" VALUES(2,4); +INSERT INTO "album_tags" VALUES(3,8); +INSERT INTO "album_tags" VALUES(3,9); +INSERT INTO "album_tags" VALUES(3,10); +INSERT INTO "album_tags" VALUES(3,11); +INSERT INTO "album_tags" VALUES(4,12); +INSERT INTO "album_tags" VALUES(4,13); +INSERT INTO "album_tags" VALUES(4,14); +INSERT INTO "album_tags" VALUES(4,11); +INSERT INTO "album_tags" VALUES(5,15); +INSERT INTO "album_tags" VALUES(5,16); +INSERT INTO "album_tags" VALUES(5,17); +INSERT INTO "album_tags" VALUES(5,18); +INSERT INTO "album_tags" VALUES(6,19); +INSERT INTO "album_tags" VALUES(6,20); +INSERT INTO "album_tags" VALUES(6,21); +INSERT INTO "album_tags" VALUES(6,18); +INSERT INTO "album_tags" VALUES(7,22); +INSERT INTO "album_tags" VALUES(7,23); +INSERT INTO "album_tags" VALUES(7,24); +INSERT INTO "album_tags" VALUES(7,25); +INSERT INTO "album_tags" VALUES(8,26); +INSERT INTO "album_tags" VALUES(8,27); +INSERT INTO "album_tags" VALUES(8,28); +INSERT INTO "album_tags" VALUES(8,25); +INSERT INTO "album_tags" VALUES(9,29); +INSERT INTO "album_tags" VALUES(9,30); +INSERT INTO "album_tags" VALUES(9,31); +INSERT INTO "album_tags" VALUES(9,32); +INSERT INTO "album_tags" VALUES(10,33); +INSERT INTO "album_tags" VALUES(10,34); +INSERT INTO "album_tags" VALUES(10,35); +INSERT INTO "album_tags" VALUES(10,32); +INSERT INTO "album_tags" VALUES(11,36); +INSERT INTO "album_tags" VALUES(11,37); +INSERT INTO "album_tags" VALUES(11,38); +INSERT INTO "album_tags" VALUES(11,39); +INSERT INTO "album_tags" VALUES(12,40); +INSERT INTO "album_tags" VALUES(12,41); +INSERT INTO "album_tags" VALUES(12,42); +INSERT INTO "album_tags" VALUES(12,39); +INSERT INTO "album_tags" VALUES(13,43); +INSERT INTO "album_tags" VALUES(13,44); +INSERT INTO "album_tags" VALUES(13,45); +INSERT INTO "album_tags" VALUES(13,46); +INSERT INTO "album_tags" VALUES(14,47); +INSERT INTO "album_tags" VALUES(14,48); +INSERT INTO "album_tags" VALUES(14,49); +INSERT INTO "album_tags" VALUES(14,46); +INSERT INTO "album_tags" VALUES(15,50); +INSERT INTO "album_tags" VALUES(15,51); +INSERT INTO "album_tags" VALUES(15,52); +INSERT INTO "album_tags" VALUES(15,53); +INSERT INTO "album_tags" VALUES(16,54); +INSERT INTO "album_tags" VALUES(16,55); +INSERT INTO "album_tags" VALUES(16,56); +INSERT INTO "album_tags" VALUES(16,53); +INSERT INTO "album_tags" VALUES(17,57); +INSERT INTO "album_tags" VALUES(17,58); +INSERT INTO "album_tags" VALUES(17,59); +INSERT INTO "album_tags" VALUES(17,60); +INSERT INTO "album_tags" VALUES(18,61); +INSERT INTO "album_tags" VALUES(18,62); +INSERT INTO "album_tags" VALUES(18,63); +INSERT INTO "album_tags" VALUES(18,60); +INSERT INTO "album_tags" VALUES(19,64); +INSERT INTO "album_tags" VALUES(19,65); +INSERT INTO "album_tags" VALUES(19,66); +INSERT INTO "album_tags" VALUES(19,11); +INSERT INTO "album_tags" VALUES(20,67); +INSERT INTO "album_tags" VALUES(20,68); +INSERT INTO "album_tags" VALUES(20,69); +INSERT INTO "album_tags" VALUES(20,11); +INSERT INTO "album_tags" VALUES(21,70); +INSERT INTO "album_tags" VALUES(21,71); +INSERT INTO "album_tags" VALUES(21,72); +INSERT INTO "album_tags" VALUES(21,4); +INSERT INTO "album_tags" VALUES(22,73); +INSERT INTO "album_tags" VALUES(22,74); +INSERT INTO "album_tags" VALUES(22,75); +INSERT INTO "album_tags" VALUES(22,4); +INSERT INTO "album_tags" VALUES(23,76); +INSERT INTO "album_tags" VALUES(23,77); +INSERT INTO "album_tags" VALUES(23,78); +INSERT INTO "album_tags" VALUES(23,39); +INSERT INTO "album_tags" VALUES(24,79); +INSERT INTO "album_tags" VALUES(24,80); +INSERT INTO "album_tags" VALUES(24,81); +INSERT INTO "album_tags" VALUES(24,39); +INSERT INTO "album_tags" VALUES(25,1); +INSERT INTO "album_tags" VALUES(25,2); +INSERT INTO "album_tags" VALUES(25,3); +INSERT INTO "album_tags" VALUES(25,39); +INSERT INTO "album_tags" VALUES(26,3); +INSERT INTO "album_tags" VALUES(26,82); +INSERT INTO "album_tags" VALUES(26,5); +INSERT INTO "album_tags" VALUES(26,39); +INSERT INTO "album_tags" VALUES(27,70); +INSERT INTO "album_tags" VALUES(27,72); +INSERT INTO "album_tags" VALUES(27,28); +INSERT INTO "album_tags" VALUES(27,4); +INSERT INTO "album_tags" VALUES(28,28); +INSERT INTO "album_tags" VALUES(28,71); +INSERT INTO "album_tags" VALUES(28,73); +INSERT INTO "album_tags" VALUES(28,4); +INSERT INTO "album_tags" VALUES(29,83); +INSERT INTO "album_tags" VALUES(29,8); +INSERT INTO "album_tags" VALUES(29,12); +INSERT INTO "album_tags" VALUES(29,25); +INSERT INTO "album_tags" VALUES(30,12); +INSERT INTO "album_tags" VALUES(30,13); +INSERT INTO "album_tags" VALUES(30,14); +INSERT INTO "album_tags" VALUES(30,25); +INSERT INTO "album_tags" VALUES(31,76); +INSERT INTO "album_tags" VALUES(31,77); +INSERT INTO "album_tags" VALUES(31,79); +INSERT INTO "album_tags" VALUES(31,11); +INSERT INTO "album_tags" VALUES(32,79); +INSERT INTO "album_tags" VALUES(32,78); +INSERT INTO "album_tags" VALUES(32,84); +INSERT INTO "album_tags" VALUES(32,11); +INSERT INTO "album_tags" VALUES(33,26); +INSERT INTO "album_tags" VALUES(33,24); +INSERT INTO "album_tags" VALUES(33,27); +INSERT INTO "album_tags" VALUES(33,46); +INSERT INTO "album_tags" VALUES(34,27); +INSERT INTO "album_tags" VALUES(34,22); +INSERT INTO "album_tags" VALUES(34,85); +INSERT INTO "album_tags" VALUES(34,46); +INSERT INTO "album_tags" VALUES(35,36); +INSERT INTO "album_tags" VALUES(35,86); +INSERT INTO "album_tags" VALUES(35,37); +INSERT INTO "album_tags" VALUES(35,53); +INSERT INTO "album_tags" VALUES(36,37); +INSERT INTO "album_tags" VALUES(36,42); +INSERT INTO "album_tags" VALUES(36,40); +INSERT INTO "album_tags" VALUES(36,53); +INSERT INTO "album_tags" VALUES(37,57); +INSERT INTO "album_tags" VALUES(37,61); +INSERT INTO "album_tags" VALUES(37,58); +INSERT INTO "album_tags" VALUES(37,32); +INSERT INTO "album_tags" VALUES(38,58); +INSERT INTO "album_tags" VALUES(38,63); +INSERT INTO "album_tags" VALUES(38,59); +INSERT INTO "album_tags" VALUES(38,32); +INSERT INTO "album_tags" VALUES(39,29); +INSERT INTO "album_tags" VALUES(39,33); +INSERT INTO "album_tags" VALUES(39,34); +INSERT INTO "album_tags" VALUES(39,18); +INSERT INTO "album_tags" VALUES(40,34); +INSERT INTO "album_tags" VALUES(40,35); +INSERT INTO "album_tags" VALUES(40,30); +INSERT INTO "album_tags" VALUES(40,18); +INSERT INTO "album_tags" VALUES(41,87); +INSERT INTO "album_tags" VALUES(41,47); +INSERT INTO "album_tags" VALUES(41,44); +INSERT INTO "album_tags" VALUES(41,60); +INSERT INTO "album_tags" VALUES(42,44); +INSERT INTO "album_tags" VALUES(42,43); +INSERT INTO "album_tags" VALUES(42,48); +INSERT INTO "album_tags" VALUES(42,60); +INSERT INTO "album_tags" VALUES(43,88); +INSERT INTO "album_tags" VALUES(43,50); +INSERT INTO "album_tags" VALUES(43,54); +INSERT INTO "album_tags" VALUES(43,39); +INSERT INTO "album_tags" VALUES(44,54); +INSERT INTO "album_tags" VALUES(44,52); +INSERT INTO "album_tags" VALUES(44,55); +INSERT INTO "album_tags" VALUES(44,39); +INSERT INTO "album_tags" VALUES(45,64); +INSERT INTO "album_tags" VALUES(45,67); +INSERT INTO "album_tags" VALUES(45,68); +INSERT INTO "album_tags" VALUES(45,4); +INSERT INTO "album_tags" VALUES(46,68); +INSERT INTO "album_tags" VALUES(46,89); +INSERT INTO "album_tags" VALUES(46,69); +INSERT INTO "album_tags" VALUES(46,4); +INSERT INTO "album_tags" VALUES(47,15); +INSERT INTO "album_tags" VALUES(47,19); +INSERT INTO "album_tags" VALUES(47,16); +INSERT INTO "album_tags" VALUES(47,25); +INSERT INTO "album_tags" VALUES(48,16); +INSERT INTO "album_tags" VALUES(48,20); +INSERT INTO "album_tags" VALUES(48,90); +INSERT INTO "album_tags" VALUES(48,25); +INSERT INTO "album_tags" VALUES(49,1); +INSERT INTO "album_tags" VALUES(49,2); +INSERT INTO "album_tags" VALUES(49,3); +INSERT INTO "album_tags" VALUES(49,11); +INSERT INTO "album_tags" VALUES(50,3); +INSERT INTO "album_tags" VALUES(50,82); +INSERT INTO "album_tags" VALUES(50,5); +INSERT INTO "album_tags" VALUES(50,11); +INSERT INTO "album_tags" VALUES(51,70); +INSERT INTO "album_tags" VALUES(51,72); +INSERT INTO "album_tags" VALUES(51,28); +INSERT INTO "album_tags" VALUES(51,46); +INSERT INTO "album_tags" VALUES(52,28); +INSERT INTO "album_tags" VALUES(52,71); +INSERT INTO "album_tags" VALUES(52,73); +INSERT INTO "album_tags" VALUES(52,46); +INSERT INTO "album_tags" VALUES(53,83); +INSERT INTO "album_tags" VALUES(53,8); +INSERT INTO "album_tags" VALUES(53,12); +INSERT INTO "album_tags" VALUES(53,53); +INSERT INTO "album_tags" VALUES(54,12); +INSERT INTO "album_tags" VALUES(54,13); +INSERT INTO "album_tags" VALUES(54,14); +INSERT INTO "album_tags" VALUES(54,53); +INSERT INTO "album_tags" VALUES(55,76); +INSERT INTO "album_tags" VALUES(55,77); +INSERT INTO "album_tags" VALUES(55,79); +INSERT INTO "album_tags" VALUES(55,32); +INSERT INTO "album_tags" VALUES(56,79); +INSERT INTO "album_tags" VALUES(56,78); +INSERT INTO "album_tags" VALUES(56,84); +INSERT INTO "album_tags" VALUES(56,32); +INSERT INTO "album_tags" VALUES(57,26); +INSERT INTO "album_tags" VALUES(57,24); +INSERT INTO "album_tags" VALUES(57,27); +INSERT INTO "album_tags" VALUES(57,18); +INSERT INTO "album_tags" VALUES(58,27); +INSERT INTO "album_tags" VALUES(58,22); +INSERT INTO "album_tags" VALUES(58,85); +INSERT INTO "album_tags" VALUES(58,18); +INSERT INTO "album_tags" VALUES(59,36); +INSERT INTO "album_tags" VALUES(59,86); +INSERT INTO "album_tags" VALUES(59,37); +INSERT INTO "album_tags" VALUES(59,60); +INSERT INTO "album_tags" VALUES(60,37); +INSERT INTO "album_tags" VALUES(60,42); +INSERT INTO "album_tags" VALUES(60,40); +INSERT INTO "album_tags" VALUES(60,60); +INSERT INTO "album_tags" VALUES(61,57); +INSERT INTO "album_tags" VALUES(61,61); +INSERT INTO "album_tags" VALUES(61,58); +INSERT INTO "album_tags" VALUES(61,39); +INSERT INTO "album_tags" VALUES(62,58); +INSERT INTO "album_tags" VALUES(62,63); +INSERT INTO "album_tags" VALUES(62,59); +INSERT INTO "album_tags" VALUES(62,39); +INSERT INTO "album_tags" VALUES(63,29); +INSERT INTO "album_tags" VALUES(63,33); +INSERT INTO "album_tags" VALUES(63,34); +INSERT INTO "album_tags" VALUES(63,4); +INSERT INTO "album_tags" VALUES(64,34); +INSERT INTO "album_tags" VALUES(64,35); +INSERT INTO "album_tags" VALUES(64,30); +INSERT INTO "album_tags" VALUES(64,4); +INSERT INTO "album_tags" VALUES(65,87); +INSERT INTO "album_tags" VALUES(65,47); +INSERT INTO "album_tags" VALUES(65,44); +INSERT INTO "album_tags" VALUES(65,25); +INSERT INTO "album_tags" VALUES(66,44); +INSERT INTO "album_tags" VALUES(66,43); +INSERT INTO "album_tags" VALUES(66,48); +INSERT INTO "album_tags" VALUES(66,25); +INSERT INTO "album_tags" VALUES(67,88); +INSERT INTO "album_tags" VALUES(67,50); +INSERT INTO "album_tags" VALUES(67,54); +INSERT INTO "album_tags" VALUES(67,11); +INSERT INTO "album_tags" VALUES(68,54); +INSERT INTO "album_tags" VALUES(68,52); +INSERT INTO "album_tags" VALUES(68,55); +INSERT INTO "album_tags" VALUES(68,11); +INSERT INTO "album_tags" VALUES(69,64); +INSERT INTO "album_tags" VALUES(69,67); +INSERT INTO "album_tags" VALUES(69,68); +INSERT INTO "album_tags" VALUES(69,46); +INSERT INTO "album_tags" VALUES(70,68); +INSERT INTO "album_tags" VALUES(70,89); +INSERT INTO "album_tags" VALUES(70,69); +INSERT INTO "album_tags" VALUES(70,46); +INSERT INTO "album_tags" VALUES(71,15); +INSERT INTO "album_tags" VALUES(71,19); +INSERT INTO "album_tags" VALUES(71,16); +INSERT INTO "album_tags" VALUES(71,53); +INSERT INTO "album_tags" VALUES(72,16); +INSERT INTO "album_tags" VALUES(72,20); +INSERT INTO "album_tags" VALUES(72,90); +INSERT INTO "album_tags" VALUES(72,53); +INSERT INTO "album_tags" VALUES(73,1); +INSERT INTO "album_tags" VALUES(73,2); +INSERT INTO "album_tags" VALUES(73,3); +INSERT INTO "album_tags" VALUES(73,32); +INSERT INTO "album_tags" VALUES(74,3); +INSERT INTO "album_tags" VALUES(74,82); +INSERT INTO "album_tags" VALUES(74,5); +INSERT INTO "album_tags" VALUES(74,32); +INSERT INTO "album_tags" VALUES(75,70); +INSERT INTO "album_tags" VALUES(75,72); +INSERT INTO "album_tags" VALUES(75,28); +INSERT INTO "album_tags" VALUES(75,18); +INSERT INTO "album_tags" VALUES(76,28); +INSERT INTO "album_tags" VALUES(76,71); +INSERT INTO "album_tags" VALUES(76,73); +INSERT INTO "album_tags" VALUES(76,18); +INSERT INTO "album_tags" VALUES(77,83); +INSERT INTO "album_tags" VALUES(77,8); +INSERT INTO "album_tags" VALUES(77,12); +INSERT INTO "album_tags" VALUES(77,60); +INSERT INTO "album_tags" VALUES(78,12); +INSERT INTO "album_tags" VALUES(78,13); +INSERT INTO "album_tags" VALUES(78,14); +INSERT INTO "album_tags" VALUES(78,60); +INSERT INTO "album_tags" VALUES(79,76); +INSERT INTO "album_tags" VALUES(79,77); +INSERT INTO "album_tags" VALUES(79,79); +INSERT INTO "album_tags" VALUES(79,39); +INSERT INTO "album_tags" VALUES(80,79); +INSERT INTO "album_tags" VALUES(80,78); +INSERT INTO "album_tags" VALUES(80,84); +INSERT INTO "album_tags" VALUES(80,39); +CREATE TABLE albums ( + id INTEGER NOT NULL, + artist_id INTEGER NOT NULL, + label_id INTEGER NOT NULL, + primary_genre_id INTEGER NOT NULL, + scene_id INTEGER NOT NULL, + title VARCHAR(160) NOT NULL, + slug VARCHAR(180) NOT NULL, + description TEXT, + story TEXT, + cover_image VARCHAR(255), + header_image VARCHAR(255), + price FLOAT, + release_date DATE NOT NULL, + track_count INTEGER, + duration_seconds INTEGER, + fan_count INTEGER, + catalog_no VARCHAR(40), + is_featured BOOLEAN, + is_new BOOLEAN, + is_editorial BOOLEAN, + PRIMARY KEY (id), + FOREIGN KEY(artist_id) REFERENCES artists (id), + FOREIGN KEY(label_id) REFERENCES labels (id), + FOREIGN KEY(primary_genre_id) REFERENCES genres (id), + FOREIGN KEY(scene_id) REFERENCES scenes (id) +); +INSERT INTO "albums" VALUES(1,1,1,1,2,'Tidal Memory','tidal-memory','Tidal Memory by Neon Harbor folds electronic textures from Berlin into a detailed release built around dub techno, afterhours, submerged, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Tidal Memory leans on dub techno energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/tidal-memory.svg','images/banners/tidal-memory.svg',8.5,'2025-10-14',5,1210,141,'AT-042',1,1,1); +INSERT INTO "albums" VALUES(2,1,1,1,2,'Night Ferry','night-ferry','Night Ferry by Neon Harbor folds electronic textures from Berlin into a detailed release built around deep groove, modular, late deck, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Night Ferry leans on deep groove energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/night-ferry.svg','images/banners/night-ferry.svg',7.0,'2024-06-07',5,1315,177,'AT-031',0,0,0); +INSERT INTO "albums" VALUES(3,2,2,3,4,'Static Bloom','static-bloom','Static Bloom by Glass Choir folds alternative textures from London into a detailed release built around dream pop, reverb, overcast hooks, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Static Bloom leans on dream pop energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/static-bloom.svg','images/banners/static-bloom.svg',9.0,'2026-02-20',5,1275,158,'MS-118',1,1,0); +INSERT INTO "albums" VALUES(4,2,2,3,4,'Paper Signal','paper-signal','Paper Signal by Glass Choir folds alternative textures from London into a detailed release built around shoegaze, jangle, bedroom, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Paper Signal leans on shoegaze energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/paper-signal.svg','images/banners/paper-signal.svg',8.0,'2024-11-08',5,1210,195,'MS-094',0,0,0); +INSERT INTO "albums" VALUES(5,3,3,12,8,'Redline Ritual','redline-ritual','Redline Ritual by Ashen Circuit folds techno textures from Detroit into a detailed release built around warehouse, analog, strobe, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Redline Ritual leans on warehouse energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/redline-ritual.svg','images/banners/redline-ritual.svg',8.0,'2023-09-15',5,1170,176,'MR-207',0,0,0); +INSERT INTO "albums" VALUES(6,3,3,12,8,'Machine Prayer','machine-prayer','Machine Prayer by Ashen Circuit folds techno textures from Detroit into a detailed release built around acid, tool track, ferrous, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Machine Prayer leans on acid energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/machine-prayer.svg','images/banners/machine-prayer.svg',9.5,'2025-05-30',5,1275,213,'MR-223',1,1,0); +INSERT INTO "albums" VALUES(7,4,4,5,3,'Between Stations','between-stations','Between Stations by Soft Locale folds ambient textures from Tokyo into a detailed release built around commuter, late train, sleep tape, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Between Stations leans on commuter energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/between-stations.svg','images/banners/between-stations.svg',8.0,'2025-08-01',5,1235,195,'IW-052',1,1,1); +INSERT INTO "albums" VALUES(8,4,4,5,3,'Sleep Maps','sleep-maps','Sleep Maps by Soft Locale folds ambient textures from Tokyo into a detailed release built around drone, meditation, field recordings, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Sleep Maps leans on drone energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/sleep-maps.svg','images/banners/sleep-maps.svg',7.5,'2024-02-23',5,1170,231,'IW-041',0,0,0); +INSERT INTO "albums" VALUES(9,5,5,8,7,'Concrete Carnival','concrete-carnival','Concrete Carnival by South Exit folds punk textures from Sao Paulo into a detailed release built around d-beat, DIY, street flyer, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Concrete Carnival leans on d-beat energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/concrete-carnival.svg','images/banners/concrete-carnival.svg',7.5,'2025-04-11',5,1300,212,'SD-014',0,1,0); +INSERT INTO "albums" VALUES(10,5,5,8,7,'Siren Economy','siren-economy','Siren Economy by South Exit folds punk textures from Sao Paulo into a detailed release built around basement, agitprop, sprint, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Siren Economy leans on basement energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/siren-economy.svg','images/banners/siren-economy.svg',7.0,'2023-12-01',5,1235,249,'SD-008',0,0,0); +INSERT INTO "albums" VALUES(11,6,6,6,1,'Signal Debt','signal-debt','Signal Debt by Cinder Plaza folds hip-hop/rap textures from Los Angeles into a detailed release built around lyric sheet, left field, city pressure, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Signal Debt leans on lyric sheet energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/signal-debt.svg','images/banners/signal-debt.svg',8.5,'2025-09-19',5,1195,231,'ST-072',1,1,0); +INSERT INTO "albums" VALUES(12,6,6,6,1,'Blueprint Fever','blueprint-fever','Blueprint Fever by Cinder Plaza folds hip-hop/rap textures from Los Angeles into a detailed release built around jazz rap, loop heavy, basement tape, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Blueprint Fever leans on jazz rap energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/blueprint-fever.svg','images/banners/blueprint-fever.svg',8.0,'2024-03-22',5,1130,267,'ST-059',0,0,0); +INSERT INTO "albums" VALUES(13,7,7,9,5,'Blue Hour Broadcast','blue-hour-broadcast','Blue Hour Broadcast by Velvet Avenue folds jazz textures from New York into a detailed release built around late set, trio, blue room, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Blue Hour Broadcast leans on late set energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/blue-hour-broadcast.svg','images/banners/blue-hour-broadcast.svg',9.5,'2025-01-31',5,1260,249,'NSA-211',1,1,0); +INSERT INTO "albums" VALUES(14,7,7,9,5,'Lobby Mirage','lobby-mirage','Lobby Mirage by Velvet Avenue folds jazz textures from New York into a detailed release built around modal, horn blend, improv, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Lobby Mirage leans on modal energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/lobby-mirage.svg','images/banners/lobby-mirage.svg',8.0,'2023-08-18',5,1195,285,'NSA-196',0,0,0); +INSERT INTO "albums" VALUES(15,8,8,10,6,'Riverlights','riverlights','Riverlights by Salt Meadow folds folk textures from Melbourne into a detailed release built around story song, field note, river road, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Riverlights leans on story song energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/riverlights.svg','images/banners/riverlights.svg',8.0,'2025-07-11',5,1155,266,'LU-063',1,1,0); +INSERT INTO "albums" VALUES(16,8,8,10,6,'Common Thread','common-thread','Common Thread by Salt Meadow folds folk textures from Melbourne into a detailed release built around americana, soft harmonies, slow weather, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Common Thread leans on americana energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/common-thread.svg','images/banners/common-thread.svg',7.5,'2024-05-03',5,1090,303,'LU-051',0,0,0); +INSERT INTO "albums" VALUES(17,9,9,7,9,'Iron Sleep','iron-sleep','Iron Sleep by Iron Veil folds metal textures from Paris into a detailed release built around doom, ritual, cathedral reverb, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Iron Sleep leans on doom energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/iron-sleep.svg','images/banners/iron-sleep.svg',9.0,'2025-11-21',5,1220,284,'OB-119',1,1,0); +INSERT INTO "albums" VALUES(18,9,9,7,9,'Saint of Noise','saint-of-noise','Saint of Noise by Iron Veil folds metal textures from Paris into a detailed release built around blackened, ash cloud, blast beat, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Saint of Noise leans on blackened energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/saint-of-noise.svg','images/banners/saint-of-noise.svg',8.5,'2024-01-26',5,1155,321,'OB-101',0,0,0); +INSERT INTO "albums" VALUES(19,10,2,11,4,'Elastic Hearts','elastic-hearts','Elastic Hearts by Fever Arcade folds pop textures from London into a detailed release built around hook, night drive, bright chorus, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Elastic Hearts leans on hook energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/elastic-hearts.svg','images/banners/elastic-hearts.svg',9.0,'2025-12-05',5,1115,303,'MS-132',1,1,0); +INSERT INTO "albums" VALUES(20,10,2,11,4,'Mirror Mosaic','mirror-mosaic','Mirror Mosaic by Fever Arcade folds pop textures from London into a detailed release built around synth pop, gloss, dancefloor, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Mirror Mosaic leans on synth pop energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/mirror-mosaic.svg','images/banners/mirror-mosaic.svg',8.0,'2024-09-13',5,1220,339,'MS-107',0,0,0); +INSERT INTO "albums" VALUES(21,11,10,2,2,'Resin Language','resin-language','Resin Language by Mono Shrine folds experimental textures from Berlin into a detailed release built around collage, tape hiss, microtone, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Resin Language leans on collage energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/resin-language.svg','images/banners/resin-language.svg',8.5,'2025-03-28',5,1180,321,'HC-014',1,1,0); +INSERT INTO "albums" VALUES(22,11,10,2,2,'Fault Choir','fault-choir','Fault Choir by Mono Shrine folds experimental textures from Berlin into a detailed release built around glitch, avant pop, noise drift, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Fault Choir leans on glitch energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/fault-choir.svg','images/banners/fault-choir.svg',8.0,'2024-07-26',5,1115,357,'HC-006',0,0,0); +INSERT INTO "albums" VALUES(23,12,6,4,1,'Harbor Burn','harbor-burn','Harbor Burn by Tide Static folds rock textures from Los Angeles into a detailed release built around psych rock, motorik, widescreen, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Harbor Burn leans on psych rock energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/harbor-burn.svg','images/banners/harbor-burn.svg',8.0,'2025-06-27',5,1075,338,'ST-064',1,1,0); +INSERT INTO "albums" VALUES(24,12,6,4,1,'Quiet Engine','quiet-engine','Quiet Engine by Tide Static folds rock textures from Los Angeles into a detailed release built around garage, riff driven, festival ready, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Quiet Engine leans on garage energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/quiet-engine.svg','images/banners/quiet-engine.svg',7.5,'2024-04-05',5,1180,375,'ST-051',0,0,0); +INSERT INTO "albums" VALUES(25,13,1,1,1,'Static Weather','static-weather','Static Weather by Amber Relay folds electronic textures from Los Angeles into a detailed release built around dub techno, afterhours, submerged, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Static Weather leans on dub techno energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/static-weather.svg','images/banners/static-weather.svg',7.0,'2023-01-01',5,1140,356,'HC-200',1,0,0); +INSERT INTO "albums" VALUES(26,13,1,1,1,'Quiet Vector','quiet-vector','Quiet Vector by Amber Relay folds electronic textures from Los Angeles into a detailed release built around submerged, drum machine, deep groove, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Quiet Vector leans on submerged energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/quiet-vector.svg','images/banners/quiet-vector.svg',7.5,'2024-05-01',5,1245,393,'HC-240',0,0,0); +INSERT INTO "albums" VALUES(27,14,2,2,2,'Harbor Ledger','harbor-ledger','Harbor Ledger by Paper Current folds experimental textures from Berlin into a detailed release built around collage, microtone, field recordings, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Harbor Ledger leans on collage energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/harbor-ledger.svg','images/banners/harbor-ledger.svg',7.5,'2024-02-04',5,1205,374,'HC-201',1,0,0); +INSERT INTO "albums" VALUES(28,14,2,2,2,'Paper Thread','paper-thread','Paper Thread by Paper Current folds experimental textures from Berlin into a detailed release built around field recordings, tape hiss, glitch, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Paper Thread leans on field recordings energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/paper-thread.svg','images/banners/paper-thread.svg',8.0,'2025-06-06',5,1310,411,'HC-241',0,1,0); +INSERT INTO "albums" VALUES(29,15,3,3,3,'River Cinema','river-cinema','River Cinema by Silver Weather folds alternative textures from Tokyo into a detailed release built around indie rock, dream pop, shoegaze, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, River Cinema leans on indie rock energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/river-cinema.svg','images/banners/river-cinema.svg',8.0,'2025-03-07',5,1270,392,'HC-202',1,1,0); +INSERT INTO "albums" VALUES(30,15,3,3,3,'Chrome Parade','chrome-parade','Chrome Parade by Silver Weather folds alternative textures from Tokyo into a detailed release built around shoegaze, jangle, bedroom, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Chrome Parade leans on shoegaze energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/chrome-parade.svg','images/banners/chrome-parade.svg',8.5,'2026-07-11',5,1205,429,'HC-242',0,1,0); +INSERT INTO "albums" VALUES(31,16,4,4,4,'Signal Method','signal-method','Signal Method by North Routine folds rock textures from London into a detailed release built around psych rock, motorik, garage, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Signal Method leans on psych rock energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/signal-method.svg','images/banners/signal-method.svg',8.5,'2026-04-10',5,1165,410,'HC-203',1,1,0); +INSERT INTO "albums" VALUES(32,16,4,4,4,'South Engine','south-engine','South Engine by North Routine folds rock textures from London into a detailed release built around garage, widescreen, burnt amp, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, South Engine leans on garage energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/south-engine.svg','images/banners/south-engine.svg',9.0,'2024-08-16',5,1270,447,'HC-243',0,0,0); +INSERT INTO "albums" VALUES(33,17,5,5,5,'Blue Current','blue-current','Blue Current by Copper Bloom folds ambient textures from New York into a detailed release built around drone, sleep tape, meditation, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Blue Current leans on drone energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/blue-current.svg','images/banners/blue-current.svg',9.0,'2023-05-13',5,1230,428,'HC-204',0,0,0); +INSERT INTO "albums" VALUES(34,17,5,5,5,'Midnight Signal','midnight-signal','Midnight Signal by Copper Bloom folds ambient textures from New York into a detailed release built around meditation, commuter, soundscape, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Midnight Signal leans on meditation energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/midnight-signal.svg','images/banners/midnight-signal.svg',7.5,'2025-09-21',5,1165,465,'HC-244',0,1,0); +INSERT INTO "albums" VALUES(35,18,6,6,6,'Late Boulevard','late-boulevard','Late Boulevard by Ladder Choir folds hip-hop/rap textures from Melbourne into a detailed release built around lyric sheet, boom bap, left field, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Late Boulevard leans on lyric sheet energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/late-boulevard.svg','images/banners/late-boulevard.svg',7.0,'2024-06-16',5,1295,446,'HC-205',0,0,0); +INSERT INTO "albums" VALUES(36,18,6,6,6,'Glass Mosaic','glass-mosaic','Glass Mosaic by Ladder Choir folds hip-hop/rap textures from Melbourne into a detailed release built around left field, basement tape, jazz rap, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Glass Mosaic leans on left field energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/glass-mosaic.svg','images/banners/glass-mosaic.svg',8.0,'2026-10-02',5,1230,483,'HC-245',0,1,0); +INSERT INTO "albums" VALUES(37,19,7,7,7,'Golden Garden','golden-garden','Golden Garden by Hour Motel folds metal textures from Sao Paulo into a detailed release built around doom, blackened, ritual, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Golden Garden leans on doom energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/golden-garden.svg','images/banners/golden-garden.svg',7.5,'2025-07-19',5,1190,464,'HC-206',0,1,0); +INSERT INTO "albums" VALUES(38,19,7,7,7,'Motel Harbor','motel-harbor','Motel Harbor by Hour Motel folds metal textures from Sao Paulo into a detailed release built around ritual, blast beat, cathedral reverb, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Motel Harbor leans on ritual energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/motel-harbor.svg','images/banners/motel-harbor.svg',8.5,'2024-11-07',5,1125,501,'HC-246',0,0,0); +INSERT INTO "albums" VALUES(39,20,8,8,8,'Quiet Transit','quiet-transit','Quiet Transit by Signal Lake folds punk textures from Detroit into a detailed release built around d-beat, basement, agitprop, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Quiet Transit leans on d-beat energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/quiet-transit.svg','images/banners/quiet-transit.svg',8.0,'2026-08-22',5,1255,482,'HC-207',0,1,0); +INSERT INTO "albums" VALUES(40,20,8,8,8,'Stone Circuit','stone-circuit','Stone Circuit by Signal Lake folds punk textures from Detroit into a detailed release built around agitprop, sprint, DIY, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Stone Circuit leans on agitprop energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/stone-circuit.svg','images/banners/stone-circuit.svg',9.0,'2025-01-12',5,1190,519,'HC-247',0,1,0); +INSERT INTO "albums" VALUES(41,21,9,9,9,'Paper Archive','paper-archive','Paper Archive by Delta Hall folds jazz textures from Paris into a detailed release built around spiritual, modal, trio, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Paper Archive leans on spiritual energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/paper-archive.svg','images/banners/paper-archive.svg',8.5,'2023-09-01',5,1150,500,'HC-208',0,0,0); +INSERT INTO "albums" VALUES(42,21,9,9,9,'Delta Minutes','delta-minutes','Delta Minutes by Delta Hall folds jazz textures from Paris into a detailed release built around trio, late set, horn blend, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Delta Minutes leans on trio energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/delta-minutes.svg','images/banners/delta-minutes.svg',7.5,'2026-02-17',5,1255,537,'HC-248',0,1,0); +INSERT INTO "albums" VALUES(43,22,10,10,1,'Chrome Pattern','chrome-pattern','Chrome Pattern by Mirror Union folds folk textures from Los Angeles into a detailed release built around acoustic, story song, americana, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Chrome Pattern leans on acoustic energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/chrome-pattern.svg','images/banners/chrome-pattern.svg',9.0,'2024-10-04',5,1215,518,'HC-209',0,0,0); +INSERT INTO "albums" VALUES(44,22,10,10,1,'Velvet Weather','velvet-weather','Velvet Weather by Mirror Union folds folk textures from Los Angeles into a detailed release built around americana, river road, soft harmonies, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Velvet Weather leans on americana energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/velvet-weather.svg','images/banners/velvet-weather.svg',8.0,'2024-03-22',5,1150,555,'HC-249',0,0,0); +INSERT INTO "albums" VALUES(45,23,1,11,2,'South Dusk','south-dusk','South Dusk by Quiet Metric folds pop textures from Berlin into a detailed release built around hook, synth pop, gloss, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, South Dusk leans on hook energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/south-dusk.svg','images/banners/south-dusk.svg',7.0,'2025-11-07',5,1110,536,'HC-210',0,1,0); +INSERT INTO "albums" VALUES(46,23,1,11,2,'Open Ledger','open-ledger','Open Ledger by Quiet Metric folds pop textures from Berlin into a detailed release built around gloss, heartbreak, dancefloor, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Open Ledger leans on gloss energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/open-ledger.svg','images/banners/open-ledger.svg',8.5,'2025-04-03',5,1215,573,'HC-250',0,1,0); +INSERT INTO "albums" VALUES(47,24,2,12,3,'Midnight Vector','midnight-vector','Midnight Vector by Blue Archive folds techno textures from Tokyo into a detailed release built around warehouse, acid, analog, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Midnight Vector leans on warehouse energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/midnight-vector.svg','images/banners/midnight-vector.svg',7.5,'2026-01-10',5,1175,554,'HC-211',0,1,0); +INSERT INTO "albums" VALUES(48,24,2,12,3,'After Cinema','after-cinema','After Cinema by Blue Archive folds techno textures from Tokyo into a detailed release built around analog, tool track, four on the floor, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, After Cinema leans on analog energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/after-cinema.svg','images/banners/after-cinema.svg',9.0,'2026-05-08',5,1110,591,'HC-251',0,1,0); +INSERT INTO "albums" VALUES(49,25,3,1,4,'Glass Thread','glass-thread','Glass Thread by Stair Pattern folds electronic textures from London into a detailed release built around dub techno, afterhours, submerged, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Glass Thread leans on dub techno energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/glass-thread.svg','images/banners/glass-thread.svg',8.0,'2023-02-13',5,1070,572,'HC-212',0,0,0); +INSERT INTO "albums" VALUES(50,25,3,1,4,'Broken Method','broken-method','Broken Method by Stair Pattern folds electronic textures from London into a detailed release built around submerged, drum machine, deep groove, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Broken Method leans on submerged energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/broken-method.svg','images/banners/broken-method.svg',7.5,'2024-06-13',5,1175,609,'HC-252',0,0,0); +INSERT INTO "albums" VALUES(51,26,4,2,5,'Motel Parade','motel-parade','Motel Parade by Velvet Current folds experimental textures from New York into a detailed release built around collage, microtone, field recordings, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Motel Parade leans on collage energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/motel-parade.svg','images/banners/motel-parade.svg',8.5,'2024-03-16',5,1135,590,'HC-213',0,0,0); +INSERT INTO "albums" VALUES(52,26,4,2,5,'Static Current','static-current','Static Current by Velvet Current folds experimental textures from New York into a detailed release built around field recordings, tape hiss, glitch, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Static Current leans on field recordings energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/static-current.svg','images/banners/static-current.svg',8.0,'2025-07-18',5,1240,627,'HC-253',0,1,0); +INSERT INTO "albums" VALUES(53,27,5,3,6,'Stone Engine','stone-engine','Stone Engine by Harbor Study folds alternative textures from Melbourne into a detailed release built around indie rock, dream pop, shoegaze, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Stone Engine leans on indie rock energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/stone-engine.svg','images/banners/stone-engine.svg',9.0,'2025-04-19',5,1200,608,'HC-214',0,1,0); +INSERT INTO "albums" VALUES(54,27,5,3,6,'Harbor Boulevard','harbor-boulevard','Harbor Boulevard by Harbor Study folds alternative textures from Melbourne into a detailed release built around shoegaze, jangle, bedroom, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Harbor Boulevard leans on shoegaze energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/harbor-boulevard.svg','images/banners/harbor-boulevard.svg',8.5,'2026-08-23',5,1305,645,'HC-254',0,1,0); +INSERT INTO "albums" VALUES(55,28,6,4,7,'Delta Signal','delta-signal','Delta Signal by Street Lantern folds rock textures from Sao Paulo into a detailed release built around psych rock, motorik, garage, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Delta Signal leans on psych rock energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/delta-signal.svg','images/banners/delta-signal.svg',7.0,'2026-05-22',5,1265,626,'HC-215',0,1,0); +INSERT INTO "albums" VALUES(56,28,6,4,7,'River Garden','river-garden','River Garden by Street Lantern folds rock textures from Sao Paulo into a detailed release built around garage, widescreen, burnt amp, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, River Garden leans on garage energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/river-garden.svg','images/banners/river-garden.svg',9.0,'2024-09-04',5,1200,663,'HC-255',0,0,0); +INSERT INTO "albums" VALUES(57,29,7,5,8,'Velvet Mosaic','velvet-mosaic','Velvet Mosaic by Noon Frame folds ambient textures from Detroit into a detailed release built around drone, sleep tape, meditation, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Velvet Mosaic leans on drone energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/velvet-mosaic.svg','images/banners/velvet-mosaic.svg',7.5,'2023-06-01',5,1330,644,'HC-216',0,0,0); +INSERT INTO "albums" VALUES(58,29,7,5,8,'Signal Transit','signal-transit','Signal Transit by Noon Frame folds ambient textures from Detroit into a detailed release built around meditation, commuter, soundscape, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Signal Transit leans on meditation energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/signal-transit.svg','images/banners/signal-transit.svg',7.5,'2025-10-09',5,1265,681,'HC-256',0,1,0); +INSERT INTO "albums" VALUES(59,30,8,6,9,'Open Harbor','open-harbor','Open Harbor by Low Atlas folds hip-hop/rap textures from Paris into a detailed release built around lyric sheet, boom bap, left field, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Open Harbor leans on lyric sheet energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/open-harbor.svg','images/banners/open-harbor.svg',8.0,'2024-07-04',5,1225,662,'HC-217',0,0,0); +INSERT INTO "albums" VALUES(60,30,8,6,9,'Blue Archive','blue-archive','Blue Archive by Low Atlas folds hip-hop/rap textures from Paris into a detailed release built around left field, basement tape, jazz rap, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Blue Archive leans on left field energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/blue-archive.svg','images/banners/blue-archive.svg',8.0,'2026-11-14',5,1160,699,'HC-257',0,1,0); +INSERT INTO "albums" VALUES(61,31,9,7,1,'After Circuit','after-circuit','After Circuit by Chrome Willow folds metal textures from Los Angeles into a detailed release built around doom, blackened, ritual, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, After Circuit leans on doom energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/after-circuit.svg','images/banners/after-circuit.svg',8.5,'2025-08-07',5,1290,680,'HC-218',0,1,0); +INSERT INTO "albums" VALUES(62,31,9,7,1,'Late Pattern','late-pattern','Late Pattern by Chrome Willow folds metal textures from Los Angeles into a detailed release built around ritual, blast beat, cathedral reverb, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Late Pattern leans on ritual energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/late-pattern.svg','images/banners/late-pattern.svg',8.5,'2024-01-19',5,1225,717,'HC-258',0,0,0); +INSERT INTO "albums" VALUES(63,32,10,8,2,'Broken Minutes','broken-minutes','Broken Minutes by Radio Meadow folds punk textures from Berlin into a detailed release built around d-beat, basement, agitprop, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Broken Minutes leans on d-beat energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/broken-minutes.svg','images/banners/broken-minutes.svg',9.0,'2026-09-10',5,1185,698,'HC-219',0,1,0); +INSERT INTO "albums" VALUES(64,32,10,8,2,'Golden Dusk','golden-dusk','Golden Dusk by Radio Meadow folds punk textures from Berlin into a detailed release built around agitprop, sprint, DIY, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Golden Dusk leans on agitprop energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/golden-dusk.svg','images/banners/golden-dusk.svg',9.0,'2025-02-24',5,1120,735,'HC-259',0,1,0); +INSERT INTO "albums" VALUES(65,33,1,9,3,'Static Weather','static-weather-fault-parade','Static Weather by Fault Parade folds jazz textures from Tokyo into a detailed release built around spiritual, modal, trio, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Static Weather leans on spiritual energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/static-weather-fault-parade.svg','images/banners/static-weather-fault-parade.svg',7.0,'2023-10-13',5,1250,716,'HC-220',0,0,0); +INSERT INTO "albums" VALUES(66,33,1,9,3,'Quiet Vector','quiet-vector-fault-parade','Quiet Vector by Fault Parade folds jazz textures from Tokyo into a detailed release built around trio, late set, horn blend, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Quiet Vector leans on trio energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/quiet-vector-fault-parade.svg','images/banners/quiet-vector-fault-parade.svg',7.5,'2026-03-05',5,1185,753,'HC-260',0,1,0); +INSERT INTO "albums" VALUES(67,34,2,10,4,'Harbor Ledger','harbor-ledger-static-ledger','Harbor Ledger by Static Ledger folds folk textures from London into a detailed release built around acoustic, story song, americana, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Harbor Ledger leans on acoustic energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/harbor-ledger-static-ledger.svg','images/banners/harbor-ledger-static-ledger.svg',7.5,'2024-11-16',5,1145,734,'HC-221',0,0,0); +INSERT INTO "albums" VALUES(68,34,2,10,4,'Paper Thread','paper-thread-static-ledger','Paper Thread by Static Ledger folds folk textures from London into a detailed release built around americana, river road, soft harmonies, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Paper Thread leans on americana energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/paper-thread-static-ledger.svg','images/banners/paper-thread-static-ledger.svg',8.0,'2024-04-10',5,1250,771,'HC-261',0,0,0); +INSERT INTO "albums" VALUES(69,35,3,11,5,'River Cinema','river-cinema-stone-balcony','River Cinema by Stone Balcony folds pop textures from New York into a detailed release built around hook, synth pop, gloss, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, River Cinema leans on hook energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/river-cinema-stone-balcony.svg','images/banners/river-cinema-stone-balcony.svg',8.0,'2025-01-19',5,1210,752,'HC-222',0,1,0); +INSERT INTO "albums" VALUES(70,35,3,11,5,'Chrome Parade','chrome-parade-stone-balcony','Chrome Parade by Stone Balcony folds pop textures from New York into a detailed release built around gloss, heartbreak, dancefloor, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Chrome Parade leans on gloss energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/chrome-parade-stone-balcony.svg','images/banners/chrome-parade-stone-balcony.svg',8.5,'2025-05-15',5,1145,789,'HC-262',0,1,0); +INSERT INTO "albums" VALUES(71,36,4,12,6,'Signal Method','signal-method-west-transit','Signal Method by West Transit folds techno textures from Melbourne into a detailed release built around warehouse, acid, analog, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Signal Method leans on warehouse energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/signal-method-west-transit.svg','images/banners/signal-method-west-transit.svg',8.5,'2026-02-22',5,1105,770,'HC-223',0,1,0); +INSERT INTO "albums" VALUES(72,36,4,12,6,'South Engine','south-engine-west-transit','South Engine by West Transit folds techno textures from Melbourne into a detailed release built around analog, tool track, four on the floor, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, South Engine leans on analog energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/south-engine-west-transit.svg','images/banners/south-engine-west-transit.svg',9.0,'2026-06-20',5,1210,807,'HC-263',0,1,0); +INSERT INTO "albums" VALUES(73,37,5,1,7,'Blue Current','blue-current-echo-dividend','Blue Current by Echo Dividend folds electronic textures from Sao Paulo into a detailed release built around dub techno, afterhours, submerged, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Blue Current leans on dub techno energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/blue-current-echo-dividend.svg','images/banners/blue-current-echo-dividend.svg',9.0,'2023-03-01',5,1170,788,'HC-224',0,0,0); +INSERT INTO "albums" VALUES(74,37,5,1,7,'Midnight Signal','midnight-signal-echo-dividend','Midnight Signal by Echo Dividend folds electronic textures from Sao Paulo into a detailed release built around submerged, drum machine, deep groove, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Midnight Signal leans on submerged energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/midnight-signal-echo-dividend.svg','images/banners/midnight-signal-echo-dividend.svg',7.5,'2024-07-01',5,1105,825,'HC-264',0,0,0); +INSERT INTO "albums" VALUES(75,38,6,2,8,'Late Boulevard','late-boulevard-cloud-bureau','Late Boulevard by Cloud Bureau folds experimental textures from Detroit into a detailed release built around collage, microtone, field recordings, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Late Boulevard leans on collage energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/late-boulevard-cloud-bureau.svg','images/banners/late-boulevard-cloud-bureau.svg',7.0,'2024-04-04',5,1065,806,'HC-225',0,0,0); +INSERT INTO "albums" VALUES(76,38,6,2,8,'Glass Mosaic','glass-mosaic-cloud-bureau','Glass Mosaic by Cloud Bureau folds experimental textures from Detroit into a detailed release built around field recordings, tape hiss, glitch, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Glass Mosaic leans on field recordings energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/glass-mosaic-cloud-bureau.svg','images/banners/glass-mosaic-cloud-bureau.svg',8.0,'2025-08-06',5,1170,843,'HC-265',0,1,0); +INSERT INTO "albums" VALUES(77,39,7,3,9,'Golden Garden','golden-garden-ridge-cinema','Golden Garden by Ridge Cinema folds alternative textures from Paris into a detailed release built around indie rock, dream pop, shoegaze, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Golden Garden leans on indie rock energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/golden-garden-ridge-cinema.svg','images/banners/golden-garden-ridge-cinema.svg',7.5,'2025-05-07',5,1130,824,'HC-226',0,1,0); +INSERT INTO "albums" VALUES(78,39,7,3,9,'Motel Harbor','motel-harbor-ridge-cinema','Motel Harbor by Ridge Cinema folds alternative textures from Paris into a detailed release built around shoegaze, jangle, bedroom, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Motel Harbor leans on shoegaze energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/motel-harbor-ridge-cinema.svg','images/banners/motel-harbor-ridge-cinema.svg',8.5,'2026-09-11',5,1235,861,'HC-266',0,1,0); +INSERT INTO "albums" VALUES(79,40,8,4,1,'Quiet Transit','quiet-transit-hollow-method','Quiet Transit by Hollow Method folds rock textures from Los Angeles into a detailed release built around psych rock, motorik, garage, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Quiet Transit leans on psych rock energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/quiet-transit-hollow-method.svg','images/banners/quiet-transit-hollow-method.svg',8.0,'2026-06-10',5,1195,842,'HC-227',0,1,0); +INSERT INTO "albums" VALUES(80,40,8,4,1,'Stone Circuit','stone-circuit-hollow-method','Stone Circuit by Hollow Method folds rock textures from Los Angeles into a detailed release built around garage, widescreen, burnt amp, tactile arrangements, and patient pacing.','Recorded for the WebHarbor Bandcamp mirror as a fully local release package, Stone Circuit leans on garage energy while keeping the visual and written presentation tight enough for benchmark tasks.','images/covers/stone-circuit-hollow-method.svg','images/banners/stone-circuit-hollow-method.svg',9.0,'2024-10-16',5,1300,879,'HC-267',0,0,0); +CREATE TABLE artists ( + id INTEGER NOT NULL, + name VARCHAR(140) NOT NULL, + slug VARCHAR(160) NOT NULL, + location VARCHAR(120), + bio TEXT, + headline VARCHAR(180), + formed_year INTEGER, + follow_count INTEGER, + avatar_image VARCHAR(255), + hero_image VARCHAR(255), + scene_id INTEGER NOT NULL, + label_id INTEGER NOT NULL, + primary_genre_id INTEGER NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY(scene_id) REFERENCES scenes (id), + FOREIGN KEY(label_id) REFERENCES labels (id), + FOREIGN KEY(primary_genre_id) REFERENCES genres (id) +); +INSERT INTO "artists" VALUES(1,'Neon Harbor','neon-harbor','Berlin, Germany','Neon Harbor build patient club music from modular haze, train-window reflections, and low-end pressure designed for the final hour of the night.','Dub silhouettes and rail-line bass pressure.',2015,1200,'images/artists/neon-harbor-avatar.svg','images/artists/neon-harbor-hero.svg',2,1,1); +INSERT INTO "artists" VALUES(2,'Glass Choir','glass-choir','London, United Kingdom','Glass Choir turn small-room guitar songs into widescreen nighttime records, leaving enough static in the mix to keep every chorus grounded.','Guitar shimmer with stairwell echo and commuter melancholy.',2016,1343,'images/artists/glass-choir-avatar.svg','images/artists/glass-choir-hero.svg',4,2,3); +INSERT INTO "artists" VALUES(3,'Ashen Circuit','ashen-circuit','Detroit, United States','Ashen Circuit work out of a one-room studio stacked with drum machines, borrowed test equipment, and lovingly repaired mixers.','Machine discipline with rust-belt force and analog grit.',2017,1486,'images/artists/ashen-circuit-avatar.svg','images/artists/ashen-circuit-hero.svg',8,3,12); +INSERT INTO "artists" VALUES(4,'Soft Locale','soft-locale','Tokyo, Japan','Soft Locale map movement through stations, elevators, and rain channels into gently detailed records full of piano dust and field recording glow.','Commuter ambient for reflective trains and dim apartment corners.',2018,1629,'images/artists/soft-locale-avatar.svg','images/artists/soft-locale-hero.svg',3,4,5); +INSERT INTO "artists" VALUES(5,'South Exit','south-exit','Sao Paulo, Brazil','South Exit play sprint-length songs built from hand-painted flyers, blown amps, and city pressure released at full velocity.','Fast songs, street posters, and DIY urgency.',2019,1772,'images/artists/south-exit-avatar.svg','images/artists/south-exit-hero.svg',7,5,8); +INSERT INTO "artists" VALUES(6,'Cinder Plaza','cinder-plaza','Los Angeles, United States','Cinder Plaza thread jazz-adjacent loops, voice notes, and concrete percussion into detail-rich rap records with cinematic pacing.','Sharp verses, bruised synths, and freeway-night hooks.',2020,1915,'images/artists/cinder-plaza-avatar.svg','images/artists/cinder-plaza-hero.svg',1,6,6); +INSERT INTO "artists" VALUES(7,'Velvet Avenue','velvet-avenue','New York, United States','Velvet Avenue record live takes with the room left intact, building jazz records that feel like late-set discoveries with a patient sense of space.','Blue-room improvisation with downtown velvet and brass glow.',2021,2058,'images/artists/velvet-avenue-avatar.svg','images/artists/velvet-avenue-hero.svg',5,7,9); +INSERT INTO "artists" VALUES(8,'Salt Meadow','salt-meadow','Melbourne, Australia','Salt Meadow turn notebooks, back-porch harmonies, and road-case acoustics into richly specific folk records.','Field-note songwriting with dusk harmonies and weathered detail.',2022,2201,'images/artists/salt-meadow-avatar.svg','images/artists/salt-meadow-hero.svg',6,8,10); +INSERT INTO "artists" VALUES(9,'Iron Veil','iron-veil','Paris, France','Iron Veil stretch doom, black metal atmosphere, and choir samples into heavy records that feel ceremonial rather than theatrical.','Cathedral reverb, iron filings, and ritual pressure.',2023,2344,'images/artists/iron-veil-avatar.svg','images/artists/iron-veil-hero.svg',9,9,7); +INSERT INTO "artists" VALUES(10,'Fever Arcade','fever-arcade','London, United Kingdom','Fever Arcade make sharp pop records whose production leaves just enough room for hallway echo and emotional static.','Neon chorus writing with club bruises and soft-focus hooks.',2015,2487,'images/artists/fever-arcade-avatar.svg','images/artists/fever-arcade-hero.svg',4,2,11); +INSERT INTO "artists" VALUES(11,'Mono Shrine','mono-shrine','Berlin, Germany','Mono Shrine fold chant fragments, room-tone edits, and damaged synth timbres into experimental pop that still lands emotionally.','Tape haze, microtonal hooks, and collage architecture.',2016,2630,'images/artists/mono-shrine-avatar.svg','images/artists/mono-shrine-hero.svg',2,10,2); +INSERT INTO "artists" VALUES(12,'Tide Static','tide-static','Los Angeles, United States','Tide Static write road-length rock songs that feel as interested in momentum and tone color as they are in choruses.','Motorik travel songs and blown-speaker optimism.',2017,2773,'images/artists/tide-static-avatar.svg','images/artists/tide-static-hero.svg',1,6,4); +INSERT INTO "artists" VALUES(13,'Amber Relay','amber-relay','Los Angeles, United States','Amber Relay operate inside the Los Angeles scene, shaping electronic records that favor texture, place, and carefully staged physical editions.','Electronic releases tuned to Los Angeles after-dark energy.',2018,2916,'images/artists/amber-relay-avatar.svg','images/artists/amber-relay-hero.svg',1,1,1); +INSERT INTO "artists" VALUES(14,'Paper Current','paper-current','Berlin, Germany','Paper Current operate inside the Berlin scene, shaping experimental records that favor texture, place, and carefully staged physical editions.','Experimental releases tuned to Berlin after-dark energy.',2019,3059,'images/artists/paper-current-avatar.svg','images/artists/paper-current-hero.svg',2,2,2); +INSERT INTO "artists" VALUES(15,'Silver Weather','silver-weather','Tokyo, Japan','Silver Weather operate inside the Tokyo scene, shaping alternative records that favor texture, place, and carefully staged physical editions.','Alternative releases tuned to Tokyo after-dark energy.',2020,3202,'images/artists/silver-weather-avatar.svg','images/artists/silver-weather-hero.svg',3,3,3); +INSERT INTO "artists" VALUES(16,'North Routine','north-routine','London, United Kingdom','North Routine operate inside the London scene, shaping rock records that favor texture, place, and carefully staged physical editions.','Rock releases tuned to London after-dark energy.',2021,3345,'images/artists/north-routine-avatar.svg','images/artists/north-routine-hero.svg',4,4,4); +INSERT INTO "artists" VALUES(17,'Copper Bloom','copper-bloom','New York, United States','Copper Bloom operate inside the New York scene, shaping ambient records that favor texture, place, and carefully staged physical editions.','Ambient releases tuned to New York after-dark energy.',2022,3488,'images/artists/copper-bloom-avatar.svg','images/artists/copper-bloom-hero.svg',5,5,5); +INSERT INTO "artists" VALUES(18,'Ladder Choir','ladder-choir','Melbourne, Australia','Ladder Choir operate inside the Melbourne scene, shaping hip-hop/rap records that favor texture, place, and carefully staged physical editions.','Hip-Hop/Rap releases tuned to Melbourne after-dark energy.',2023,3631,'images/artists/ladder-choir-avatar.svg','images/artists/ladder-choir-hero.svg',6,6,6); +INSERT INTO "artists" VALUES(19,'Hour Motel','hour-motel','Sao Paulo, Brazil','Hour Motel operate inside the Sao Paulo scene, shaping metal records that favor texture, place, and carefully staged physical editions.','Metal releases tuned to Sao Paulo after-dark energy.',2015,3774,'images/artists/hour-motel-avatar.svg','images/artists/hour-motel-hero.svg',7,7,7); +INSERT INTO "artists" VALUES(20,'Signal Lake','signal-lake','Detroit, United States','Signal Lake operate inside the Detroit scene, shaping punk records that favor texture, place, and carefully staged physical editions.','Punk releases tuned to Detroit after-dark energy.',2016,3917,'images/artists/signal-lake-avatar.svg','images/artists/signal-lake-hero.svg',8,8,8); +INSERT INTO "artists" VALUES(21,'Delta Hall','delta-hall','Paris, France','Delta Hall operate inside the Paris scene, shaping jazz records that favor texture, place, and carefully staged physical editions.','Jazz releases tuned to Paris after-dark energy.',2017,4060,'images/artists/delta-hall-avatar.svg','images/artists/delta-hall-hero.svg',9,9,9); +INSERT INTO "artists" VALUES(22,'Mirror Union','mirror-union','Los Angeles, United States','Mirror Union operate inside the Los Angeles scene, shaping folk records that favor texture, place, and carefully staged physical editions.','Folk releases tuned to Los Angeles after-dark energy.',2018,4203,'images/artists/mirror-union-avatar.svg','images/artists/mirror-union-hero.svg',1,10,10); +INSERT INTO "artists" VALUES(23,'Quiet Metric','quiet-metric','Berlin, Germany','Quiet Metric operate inside the Berlin scene, shaping pop records that favor texture, place, and carefully staged physical editions.','Pop releases tuned to Berlin after-dark energy.',2019,4346,'images/artists/quiet-metric-avatar.svg','images/artists/quiet-metric-hero.svg',2,1,11); +INSERT INTO "artists" VALUES(24,'Blue Archive','blue-archive','Tokyo, Japan','Blue Archive operate inside the Tokyo scene, shaping techno records that favor texture, place, and carefully staged physical editions.','Techno releases tuned to Tokyo after-dark energy.',2020,4489,'images/artists/blue-archive-avatar.svg','images/artists/blue-archive-hero.svg',3,2,12); +INSERT INTO "artists" VALUES(25,'Stair Pattern','stair-pattern','London, United Kingdom','Stair Pattern operate inside the London scene, shaping electronic records that favor texture, place, and carefully staged physical editions.','Electronic releases tuned to London after-dark energy.',2021,4632,'images/artists/stair-pattern-avatar.svg','images/artists/stair-pattern-hero.svg',4,3,1); +INSERT INTO "artists" VALUES(26,'Velvet Current','velvet-current','New York, United States','Velvet Current operate inside the New York scene, shaping experimental records that favor texture, place, and carefully staged physical editions.','Experimental releases tuned to New York after-dark energy.',2022,4775,'images/artists/velvet-current-avatar.svg','images/artists/velvet-current-hero.svg',5,4,2); +INSERT INTO "artists" VALUES(27,'Harbor Study','harbor-study','Melbourne, Australia','Harbor Study operate inside the Melbourne scene, shaping alternative records that favor texture, place, and carefully staged physical editions.','Alternative releases tuned to Melbourne after-dark energy.',2023,4918,'images/artists/harbor-study-avatar.svg','images/artists/harbor-study-hero.svg',6,5,3); +INSERT INTO "artists" VALUES(28,'Street Lantern','street-lantern','Sao Paulo, Brazil','Street Lantern operate inside the Sao Paulo scene, shaping rock records that favor texture, place, and carefully staged physical editions.','Rock releases tuned to Sao Paulo after-dark energy.',2015,5061,'images/artists/street-lantern-avatar.svg','images/artists/street-lantern-hero.svg',7,6,4); +INSERT INTO "artists" VALUES(29,'Noon Frame','noon-frame','Detroit, United States','Noon Frame operate inside the Detroit scene, shaping ambient records that favor texture, place, and carefully staged physical editions.','Ambient releases tuned to Detroit after-dark energy.',2016,5204,'images/artists/noon-frame-avatar.svg','images/artists/noon-frame-hero.svg',8,7,5); +INSERT INTO "artists" VALUES(30,'Low Atlas','low-atlas','Paris, France','Low Atlas operate inside the Paris scene, shaping hip-hop/rap records that favor texture, place, and carefully staged physical editions.','Hip-Hop/Rap releases tuned to Paris after-dark energy.',2017,5347,'images/artists/low-atlas-avatar.svg','images/artists/low-atlas-hero.svg',9,8,6); +INSERT INTO "artists" VALUES(31,'Chrome Willow','chrome-willow','Los Angeles, United States','Chrome Willow operate inside the Los Angeles scene, shaping metal records that favor texture, place, and carefully staged physical editions.','Metal releases tuned to Los Angeles after-dark energy.',2018,5490,'images/artists/chrome-willow-avatar.svg','images/artists/chrome-willow-hero.svg',1,9,7); +INSERT INTO "artists" VALUES(32,'Radio Meadow','radio-meadow','Berlin, Germany','Radio Meadow operate inside the Berlin scene, shaping punk records that favor texture, place, and carefully staged physical editions.','Punk releases tuned to Berlin after-dark energy.',2019,5633,'images/artists/radio-meadow-avatar.svg','images/artists/radio-meadow-hero.svg',2,10,8); +INSERT INTO "artists" VALUES(33,'Fault Parade','fault-parade','Tokyo, Japan','Fault Parade operate inside the Tokyo scene, shaping jazz records that favor texture, place, and carefully staged physical editions.','Jazz releases tuned to Tokyo after-dark energy.',2020,5776,'images/artists/fault-parade-avatar.svg','images/artists/fault-parade-hero.svg',3,1,9); +INSERT INTO "artists" VALUES(34,'Static Ledger','static-ledger','London, United Kingdom','Static Ledger operate inside the London scene, shaping folk records that favor texture, place, and carefully staged physical editions.','Folk releases tuned to London after-dark energy.',2021,5919,'images/artists/static-ledger-avatar.svg','images/artists/static-ledger-hero.svg',4,2,10); +INSERT INTO "artists" VALUES(35,'Stone Balcony','stone-balcony','New York, United States','Stone Balcony operate inside the New York scene, shaping pop records that favor texture, place, and carefully staged physical editions.','Pop releases tuned to New York after-dark energy.',2022,6062,'images/artists/stone-balcony-avatar.svg','images/artists/stone-balcony-hero.svg',5,3,11); +INSERT INTO "artists" VALUES(36,'West Transit','west-transit','Melbourne, Australia','West Transit operate inside the Melbourne scene, shaping techno records that favor texture, place, and carefully staged physical editions.','Techno releases tuned to Melbourne after-dark energy.',2023,6205,'images/artists/west-transit-avatar.svg','images/artists/west-transit-hero.svg',6,4,12); +INSERT INTO "artists" VALUES(37,'Echo Dividend','echo-dividend','Sao Paulo, Brazil','Echo Dividend operate inside the Sao Paulo scene, shaping electronic records that favor texture, place, and carefully staged physical editions.','Electronic releases tuned to Sao Paulo after-dark energy.',2015,6348,'images/artists/echo-dividend-avatar.svg','images/artists/echo-dividend-hero.svg',7,5,1); +INSERT INTO "artists" VALUES(38,'Cloud Bureau','cloud-bureau','Detroit, United States','Cloud Bureau operate inside the Detroit scene, shaping experimental records that favor texture, place, and carefully staged physical editions.','Experimental releases tuned to Detroit after-dark energy.',2016,6491,'images/artists/cloud-bureau-avatar.svg','images/artists/cloud-bureau-hero.svg',8,6,2); +INSERT INTO "artists" VALUES(39,'Ridge Cinema','ridge-cinema','Paris, France','Ridge Cinema operate inside the Paris scene, shaping alternative records that favor texture, place, and carefully staged physical editions.','Alternative releases tuned to Paris after-dark energy.',2017,6634,'images/artists/ridge-cinema-avatar.svg','images/artists/ridge-cinema-hero.svg',9,7,3); +INSERT INTO "artists" VALUES(40,'Hollow Method','hollow-method','Los Angeles, United States','Hollow Method operate inside the Los Angeles scene, shaping rock records that favor texture, place, and carefully staged physical editions.','Rock releases tuned to Los Angeles after-dark energy.',2018,6777,'images/artists/hollow-method-avatar.svg','images/artists/hollow-method-hero.svg',1,8,4); +CREATE TABLE cart_items ( + id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + album_id INTEGER, + merch_item_id INTEGER, + format_variant_id INTEGER, + quantity INTEGER, + added_at DATETIME, + PRIMARY KEY (id), + FOREIGN KEY(user_id) REFERENCES users (id), + FOREIGN KEY(album_id) REFERENCES albums (id), + FOREIGN KEY(merch_item_id) REFERENCES merch_items (id), + FOREIGN KEY(format_variant_id) REFERENCES format_variants (id) +); +INSERT INTO "cart_items" VALUES(1,1,1,NULL,2,1,'2026-05-27 10:33:29.724432'); +INSERT INTO "cart_items" VALUES(2,1,7,NULL,35,1,'2026-05-27 10:33:29.725265'); +INSERT INTO "cart_items" VALUES(3,1,NULL,1,8,1,'2026-05-27 10:33:29.727873'); +INSERT INTO "cart_items" VALUES(4,2,11,NULL,55,1,'2026-05-27 10:33:29.728486'); +INSERT INTO "cart_items" VALUES(5,2,NULL,11,60,1,'2026-05-27 10:33:29.729074'); +INSERT INTO "cart_items" VALUES(6,3,15,NULL,74,1,'2026-05-27 10:33:29.729657'); +INSERT INTO "cart_items" VALUES(7,3,NULL,7,41,1,'2026-05-27 10:33:29.730503'); +INSERT INTO "cart_items" VALUES(8,4,NULL,15,79,1,'2026-05-27 10:33:29.731346'); +INSERT INTO "cart_items" VALUES(9,4,19,NULL,97,1,'2026-05-27 10:33:29.731901'); +CREATE TABLE fan_collection_items ( + id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + album_id INTEGER NOT NULL, + format_variant_id INTEGER, + favorite_track_id INTEGER, + acquired_via VARCHAR(80), + notes VARCHAR(240), + added_at DATETIME, + PRIMARY KEY (id), + FOREIGN KEY(user_id) REFERENCES users (id), + FOREIGN KEY(album_id) REFERENCES albums (id), + FOREIGN KEY(format_variant_id) REFERENCES format_variants (id), + FOREIGN KEY(favorite_track_id) REFERENCES tracks (id) +); +INSERT INTO "fan_collection_items" VALUES(1,1,13,64,63,'purchase','Seeded library item','2026-05-01 12:00:00.000000'); +INSERT INTO "fan_collection_items" VALUES(2,1,1,1,3,'purchase','Seeded library item','2026-05-01 12:00:00.000000'); +INSERT INTO "fan_collection_items" VALUES(3,2,11,55,53,'purchase','Seeded library item','2026-05-01 12:00:00.000000'); +INSERT INTO "fan_collection_items" VALUES(4,2,21,108,103,'purchase','Seeded library item','2026-05-01 12:00:00.000000'); +INSERT INTO "fan_collection_items" VALUES(5,3,7,35,33,'purchase','Seeded library item','2026-05-01 12:00:00.000000'); +INSERT INTO "fan_collection_items" VALUES(6,4,19,97,93,'purchase','Seeded library item','2026-05-01 12:00:00.000000'); +CREATE TABLE fan_comments ( + id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + album_id INTEGER, + track_id INTEGER, + headline VARCHAR(140), + body TEXT, + rating INTEGER, + created_at DATETIME, + PRIMARY KEY (id), + FOREIGN KEY(user_id) REFERENCES users (id), + FOREIGN KEY(album_id) REFERENCES albums (id), + FOREIGN KEY(track_id) REFERENCES tracks (id) +); +INSERT INTO "fan_comments" VALUES(1,1,1,NULL,'Most replayed this month','The dub bassline on side B makes the whole release feel tidal.',5,'2026-04-29 12:00:00.000000'); +INSERT INTO "fan_comments" VALUES(2,2,11,NULL,'Sharp sequencing','Track order is ruthless in the best possible way.',5,'2026-04-29 12:00:00.000000'); +INSERT INTO "fan_comments" VALUES(3,3,7,NULL,'Commuter ambient','Feels like catching the last Yamanote line with the windows fogged over.',5,'2026-04-29 12:00:00.000000'); +INSERT INTO "fan_comments" VALUES(4,4,15,NULL,'Warm and tactile','The lyric sheet tucked into the LP package is a lovely touch.',5,'2026-04-29 12:00:00.000000'); +CREATE TABLE format_variants ( + id INTEGER NOT NULL, + album_id INTEGER, + merch_item_id INTEGER, + kind VARCHAR(50) NOT NULL, + name VARCHAR(120) NOT NULL, + option_a VARCHAR(80), + option_b VARCHAR(80), + price FLOAT, + inventory INTEGER, + sku VARCHAR(80) NOT NULL, + shipping_note VARCHAR(160), + edition_note VARCHAR(200), + is_default BOOLEAN, + PRIMARY KEY (id), + FOREIGN KEY(album_id) REFERENCES albums (id), + FOREIGN KEY(merch_item_id) REFERENCES merch_items (id), + UNIQUE (sku) +); +INSERT INTO "format_variants" VALUES(1,1,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'tidal-memory-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(2,1,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,42,'tidal-memory-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(3,1,NULL,'cassette','Cassette','Transparent Shell','',15.0,28,'tidal-memory-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(4,2,NULL,'digital','Digital Album','MP3 + FLAC','',7.0,9999,'night-ferry-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(5,2,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',22.5,42,'night-ferry-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(6,2,NULL,'cassette','Cassette','Transparent Shell','',13.5,28,'night-ferry-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(7,NULL,1,'shirt','Studio Tee','S','Washed Navy',32.0,19,'neon-harbor-studio-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(8,NULL,1,'shirt','Studio Tee','M','Washed Navy',32.0,20,'neon-harbor-studio-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(9,NULL,1,'shirt','Studio Tee','L','Washed Navy',32.0,21,'neon-harbor-studio-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(10,NULL,1,'shirt','Studio Tee','XL','Washed Navy',32.0,22,'neon-harbor-studio-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(11,NULL,2,'slipmat','Pair','12-inch','',24.0,20,'neon-harbor-breakwater-slipmat-slipmat-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(12,NULL,2,'slipmat','Deluxe Pair','Glow Edge','',29.0,21,'neon-harbor-breakwater-slipmat-slipmat-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(13,3,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'static-bloom-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(14,3,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,43,'static-bloom-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(15,3,NULL,'cd','Compact Disc','Gatefold','',17.0,35,'static-bloom-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(16,4,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'paper-signal-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(17,4,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,43,'paper-signal-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(18,4,NULL,'cd','Compact Disc','Gatefold','',16.0,35,'paper-signal-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(19,NULL,3,'shirt','Stairwell Tee','S','Heather Grey',30.0,20,'glass-choir-stairwell-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(20,NULL,3,'shirt','Stairwell Tee','M','Heather Grey',30.0,21,'glass-choir-stairwell-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(21,NULL,3,'shirt','Stairwell Tee','L','Heather Grey',30.0,22,'glass-choir-stairwell-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(22,NULL,3,'shirt','Stairwell Tee','XL','Heather Grey',30.0,23,'glass-choir-stairwell-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(23,NULL,4,'poster','Standard','18x24','',18.0,21,'glass-choir-balcony-poster-poster-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(24,NULL,4,'poster','Signed','18x24','',24.0,22,'glass-choir-balcony-poster-poster-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(25,5,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'redline-ritual-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(26,5,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,44,'redline-ritual-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(27,6,NULL,'digital','Digital Album','MP3 + FLAC','',9.5,9999,'machine-prayer-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(28,6,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',25.0,44,'machine-prayer-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(29,NULL,5,'slipmat','Pair','12-inch','',22.0,21,'ashen-circuit-grid-slipmat-slipmat-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(30,NULL,5,'slipmat','Glow Pair','12-inch','',27.0,22,'ashen-circuit-grid-slipmat-slipmat-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(31,NULL,6,'patch','Standard','Black','',10.0,22,'ashen-circuit-weld-patch-patch-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(32,NULL,6,'patch','Reflective','Silver','',13.0,23,'ashen-circuit-weld-patch-patch-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(33,7,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'between-stations-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(34,7,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,45,'between-stations-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(35,7,NULL,'cassette','Cassette','Transparent Shell','',14.5,31,'between-stations-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(36,8,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'sleep-maps-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(37,8,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,45,'sleep-maps-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(38,8,NULL,'cassette','Cassette','Transparent Shell','',14.0,31,'sleep-maps-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(39,NULL,7,'hoodie','Drift Hoodie','S','Stone',48.0,22,'soft-locale-drift-hoodie-hoodie-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(40,NULL,7,'hoodie','Drift Hoodie','M','Stone',48.0,23,'soft-locale-drift-hoodie-hoodie-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(41,NULL,7,'hoodie','Drift Hoodie','L','Stone',48.0,24,'soft-locale-drift-hoodie-hoodie-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(42,NULL,7,'hoodie','Drift Hoodie','XL','Stone',48.0,25,'soft-locale-drift-hoodie-hoodie-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(43,NULL,8,'cassette','Blue Shell','Numbered','',26.0,23,'soft-locale-rain-map-cassette-box-cassette-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(44,NULL,8,'cassette','Smoke Shell','Numbered','',29.0,24,'soft-locale-rain-map-cassette-box-cassette-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(45,9,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'concrete-carnival-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(46,9,NULL,'cassette','Cassette','Transparent Shell','',14.0,32,'concrete-carnival-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(47,10,NULL,'digital','Digital Album','MP3 + FLAC','',7.0,9999,'siren-economy-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(48,10,NULL,'cassette','Cassette','Transparent Shell','',13.5,32,'siren-economy-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(49,NULL,9,'shirt','Flyer Tee','S','White',28.0,23,'south-exit-flyer-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(50,NULL,9,'shirt','Flyer Tee','M','White',28.0,24,'south-exit-flyer-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(51,NULL,9,'shirt','Flyer Tee','L','White',28.0,25,'south-exit-flyer-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(52,NULL,9,'shirt','Flyer Tee','XL','White',28.0,26,'south-exit-flyer-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(53,NULL,10,'poster','3-Pack','18x24','',16.0,24,'south-exit-poster-pack-poster-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(54,NULL,10,'poster','Signed 3-Pack','18x24','',22.0,25,'south-exit-poster-pack-poster-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(55,11,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'signal-debt-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(56,11,NULL,'cassette','Cassette','Transparent Shell','',15.0,33,'signal-debt-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(57,12,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'blueprint-fever-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(58,12,NULL,'cassette','Cassette','Transparent Shell','',14.5,33,'blueprint-fever-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(59,NULL,11,'poster','Standard','18x24','',20.0,24,'cinder-plaza-blueprint-fever-poster-poster-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(60,NULL,11,'poster','Signed','18x24','',28.0,25,'cinder-plaza-blueprint-fever-poster-poster-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(61,NULL,12,'cap','Adjustable','Black','',26.0,25,'cinder-plaza-signal-debt-cap-cap-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(62,NULL,12,'cap','Adjustable','Sand','',26.0,26,'cinder-plaza-signal-debt-cap-cap-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(63,13,NULL,'digital','Digital Album','MP3 + FLAC','',9.5,9999,'blue-hour-broadcast-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(64,13,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',25.0,48,'blue-hour-broadcast-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(65,13,NULL,'cd','Compact Disc','Gatefold','',17.5,40,'blue-hour-broadcast-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(66,14,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'lobby-mirage-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(67,14,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,48,'lobby-mirage-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(68,14,NULL,'cd','Compact Disc','Gatefold','',16.0,40,'lobby-mirage-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(69,NULL,13,'poster','Standard','18x24','',19.0,25,'velvet-avenue-night-shift-poster-poster-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(70,NULL,13,'poster','Signed','18x24','',27.0,26,'velvet-avenue-night-shift-poster-poster-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(71,NULL,14,'tote','Setlist Tote','Natural','',24.0,26,'velvet-avenue-setlist-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(72,NULL,14,'tote','Setlist Tote','Black','',24.0,27,'velvet-avenue-setlist-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(73,15,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'riverlights-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(74,15,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,49,'riverlights-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(75,15,NULL,'cassette','Cassette','Transparent Shell','',14.5,35,'riverlights-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(76,16,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'common-thread-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(77,16,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,49,'common-thread-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(78,16,NULL,'cassette','Cassette','Transparent Shell','',14.0,35,'common-thread-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(79,NULL,15,'tote','Field Notes Tote','Natural','',22.0,26,'salt-meadow-field-notes-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(80,NULL,15,'tote','Field Notes Tote','Forest','',22.0,27,'salt-meadow-field-notes-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(81,NULL,16,'zine','Issue One','Stapled','',14.0,27,'salt-meadow-riverlights-lyric-zine-zine-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(82,NULL,16,'zine','Signed Issue','Stapled','',18.0,28,'salt-meadow-riverlights-lyric-zine-zine-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(83,17,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'iron-sleep-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(84,17,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,50,'iron-sleep-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(85,17,NULL,'cd','Compact Disc','Gatefold','',17.0,42,'iron-sleep-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(86,18,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'saint-of-noise-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(87,18,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,50,'saint-of-noise-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(88,18,NULL,'cd','Compact Disc','Gatefold','',16.5,42,'saint-of-noise-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(89,NULL,17,'shirt','Longsleeve','S','Black',36.0,27,'iron-veil-chapel-longsleeve-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(90,NULL,17,'shirt','Longsleeve','M','Black',36.0,28,'iron-veil-chapel-longsleeve-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(91,NULL,17,'shirt','Longsleeve','L','Black',36.0,29,'iron-veil-chapel-longsleeve-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(92,NULL,17,'shirt','Longsleeve','XL','Black',36.0,30,'iron-veil-chapel-longsleeve-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(93,NULL,18,'patch','3-Pack','Silver Edge','',12.0,28,'iron-veil-noise-patch-set-patch-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(94,NULL,18,'patch','3-Pack Deluxe','Glow Edge','',16.0,29,'iron-veil-noise-patch-set-patch-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(95,19,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'elastic-hearts-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(96,19,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,51,'elastic-hearts-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(97,19,NULL,'cd','Compact Disc','Gatefold','',17.0,43,'elastic-hearts-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(98,20,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'mirror-mosaic-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(99,20,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,51,'mirror-mosaic-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(100,20,NULL,'cd','Compact Disc','Gatefold','',16.0,43,'mirror-mosaic-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(101,NULL,19,'shirt','Gloss Tee','S','White',29.0,28,'fever-arcade-gloss-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(102,NULL,19,'shirt','Gloss Tee','M','White',29.0,29,'fever-arcade-gloss-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(103,NULL,19,'shirt','Gloss Tee','L','White',29.0,30,'fever-arcade-gloss-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(104,NULL,19,'shirt','Gloss Tee','XL','White',29.0,31,'fever-arcade-gloss-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(105,NULL,20,'pin','2-Pin Set','Chrome','',15.0,29,'fever-arcade-mirror-pin-set-pin-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(106,NULL,20,'pin','2-Pin Set','Rose','',15.0,30,'fever-arcade-mirror-pin-set-pin-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(107,21,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'resin-language-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(108,21,NULL,'cassette','Cassette','Transparent Shell','',15.0,38,'resin-language-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(109,22,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'fault-choir-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(110,22,NULL,'cassette','Cassette','Transparent Shell','',14.5,38,'fault-choir-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(111,NULL,21,'zine','Issue One','Stapled','',13.0,29,'mono-shrine-fault-choir-zine-zine-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(112,NULL,21,'zine','Bundle','With Sticker Sheet','',17.0,30,'mono-shrine-fault-choir-zine-zine-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(113,NULL,22,'poster','Standard','A2','',17.0,30,'mono-shrine-resin-poster-poster-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(114,NULL,22,'poster','Signed','A2','',23.0,31,'mono-shrine-resin-poster-poster-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(115,23,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'harbor-burn-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(116,23,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,53,'harbor-burn-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(117,23,NULL,'cd','Compact Disc','Gatefold','',16.0,45,'harbor-burn-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(118,24,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'quiet-engine-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(119,24,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,53,'quiet-engine-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(120,24,NULL,'cd','Compact Disc','Gatefold','',15.5,45,'quiet-engine-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(121,NULL,23,'shirt','Burn Tee','S','Vintage White',30.0,30,'tide-static-burn-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(122,NULL,23,'shirt','Burn Tee','M','Vintage White',30.0,31,'tide-static-burn-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(123,NULL,23,'shirt','Burn Tee','L','Vintage White',30.0,32,'tide-static-burn-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(124,NULL,23,'shirt','Burn Tee','XL','Vintage White',30.0,33,'tide-static-burn-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(125,NULL,24,'tote','Engine Tote','Natural','',21.0,31,'tide-static-quiet-engine-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(126,NULL,24,'tote','Engine Tote','Black','',21.0,32,'tide-static-quiet-engine-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(127,25,NULL,'digital','Digital Album','MP3 + FLAC','',7.0,9999,'static-weather-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(128,25,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',22.5,54,'static-weather-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(129,25,NULL,'cassette','Cassette','Transparent Shell','',13.5,40,'static-weather-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(130,26,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'quiet-vector-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(131,26,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,54,'quiet-vector-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(132,26,NULL,'cassette','Cassette','Transparent Shell','',14.0,40,'quiet-vector-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(133,NULL,25,'shirt','Tour Tee','S','Black',29.0,31,'amber-relay-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(134,NULL,25,'shirt','Tour Tee','M','Black',29.0,32,'amber-relay-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(135,NULL,25,'shirt','Tour Tee','L','Black',29.0,33,'amber-relay-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(136,NULL,25,'shirt','Tour Tee','XL','Black',29.0,34,'amber-relay-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(137,NULL,26,'tote','Carry Tote','Natural','',21.0,32,'amber-relay-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(138,NULL,26,'tote','Carry Tote','Black','',21.0,33,'amber-relay-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(139,27,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'harbor-ledger-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(140,27,NULL,'cassette','Cassette','Transparent Shell','',14.0,41,'harbor-ledger-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(141,28,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'paper-thread-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(142,28,NULL,'cassette','Cassette','Transparent Shell','',14.5,41,'paper-thread-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(143,NULL,27,'shirt','Tour Tee','S','Black',30.0,32,'paper-current-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(144,NULL,27,'shirt','Tour Tee','M','Black',30.0,33,'paper-current-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(145,NULL,27,'shirt','Tour Tee','L','Black',30.0,34,'paper-current-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(146,NULL,27,'shirt','Tour Tee','XL','Black',30.0,35,'paper-current-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(147,NULL,28,'tote','Carry Tote','Natural','',22.0,33,'paper-current-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(148,NULL,28,'tote','Carry Tote','Black','',22.0,34,'paper-current-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(149,29,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'river-cinema-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(150,29,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,56,'river-cinema-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(151,29,NULL,'cd','Compact Disc','Gatefold','',16.0,48,'river-cinema-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(152,30,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'chrome-parade-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(153,30,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,56,'chrome-parade-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(154,30,NULL,'cd','Compact Disc','Gatefold','',16.5,48,'chrome-parade-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(155,NULL,29,'shirt','Tour Tee','S','Black',31.0,33,'silver-weather-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(156,NULL,29,'shirt','Tour Tee','M','Black',31.0,34,'silver-weather-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(157,NULL,29,'shirt','Tour Tee','L','Black',31.0,35,'silver-weather-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(158,NULL,29,'shirt','Tour Tee','XL','Black',31.0,36,'silver-weather-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(159,NULL,30,'tote','Carry Tote','Natural','',23.0,34,'silver-weather-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(160,NULL,30,'tote','Carry Tote','Black','',23.0,35,'silver-weather-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(161,31,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'signal-method-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(162,31,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,57,'signal-method-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(163,31,NULL,'cd','Compact Disc','Gatefold','',16.5,49,'signal-method-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(164,32,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'south-engine-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(165,32,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,57,'south-engine-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(166,32,NULL,'cd','Compact Disc','Gatefold','',17.0,49,'south-engine-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(167,NULL,31,'shirt','Tour Tee','S','Black',32.0,34,'north-routine-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(168,NULL,31,'shirt','Tour Tee','M','Black',32.0,35,'north-routine-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(169,NULL,31,'shirt','Tour Tee','L','Black',32.0,36,'north-routine-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(170,NULL,31,'shirt','Tour Tee','XL','Black',32.0,37,'north-routine-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(171,NULL,32,'tote','Carry Tote','Natural','',21.0,35,'north-routine-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(172,NULL,32,'tote','Carry Tote','Black','',21.0,36,'north-routine-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(173,33,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'blue-current-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(174,33,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,58,'blue-current-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(175,33,NULL,'cassette','Cassette','Transparent Shell','',15.5,44,'blue-current-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(176,34,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'midnight-signal-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(177,34,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,58,'midnight-signal-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(178,34,NULL,'cassette','Cassette','Transparent Shell','',14.0,44,'midnight-signal-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(179,NULL,33,'shirt','Tour Tee','S','Black',29.0,35,'copper-bloom-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(180,NULL,33,'shirt','Tour Tee','M','Black',29.0,36,'copper-bloom-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(181,NULL,33,'shirt','Tour Tee','L','Black',29.0,37,'copper-bloom-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(182,NULL,33,'shirt','Tour Tee','XL','Black',29.0,38,'copper-bloom-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(183,NULL,34,'tote','Carry Tote','Natural','',22.0,36,'copper-bloom-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(184,NULL,34,'tote','Carry Tote','Black','',22.0,37,'copper-bloom-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(185,35,NULL,'digital','Digital Album','MP3 + FLAC','',7.0,9999,'late-boulevard-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(186,35,NULL,'cassette','Cassette','Transparent Shell','',13.5,45,'late-boulevard-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(187,36,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'glass-mosaic-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(188,36,NULL,'cassette','Cassette','Transparent Shell','',14.5,45,'glass-mosaic-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(189,NULL,35,'shirt','Tour Tee','S','Black',30.0,36,'ladder-choir-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(190,NULL,35,'shirt','Tour Tee','M','Black',30.0,37,'ladder-choir-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(191,NULL,35,'shirt','Tour Tee','L','Black',30.0,38,'ladder-choir-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(192,NULL,35,'shirt','Tour Tee','XL','Black',30.0,39,'ladder-choir-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(193,NULL,36,'tote','Carry Tote','Natural','',23.0,37,'ladder-choir-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(194,NULL,36,'tote','Carry Tote','Black','',23.0,38,'ladder-choir-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(195,37,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'golden-garden-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(196,37,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,60,'golden-garden-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(197,37,NULL,'cd','Compact Disc','Gatefold','',15.5,35,'golden-garden-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(198,38,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'motel-harbor-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(199,38,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,60,'motel-harbor-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(200,38,NULL,'cd','Compact Disc','Gatefold','',16.5,35,'motel-harbor-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(201,NULL,37,'shirt','Tour Tee','S','Black',31.0,37,'hour-motel-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(202,NULL,37,'shirt','Tour Tee','M','Black',31.0,38,'hour-motel-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(203,NULL,37,'shirt','Tour Tee','L','Black',31.0,39,'hour-motel-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(204,NULL,37,'shirt','Tour Tee','XL','Black',31.0,40,'hour-motel-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(205,NULL,38,'tote','Carry Tote','Natural','',21.0,38,'hour-motel-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(206,NULL,38,'tote','Carry Tote','Black','',21.0,39,'hour-motel-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(207,39,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'quiet-transit-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(208,39,NULL,'cassette','Cassette','Transparent Shell','',14.5,47,'quiet-transit-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(209,40,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'stone-circuit-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(210,40,NULL,'cassette','Cassette','Transparent Shell','',15.5,47,'stone-circuit-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(211,NULL,39,'shirt','Tour Tee','S','Black',32.0,38,'signal-lake-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(212,NULL,39,'shirt','Tour Tee','M','Black',32.0,39,'signal-lake-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(213,NULL,39,'shirt','Tour Tee','L','Black',32.0,40,'signal-lake-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(214,NULL,39,'shirt','Tour Tee','XL','Black',32.0,41,'signal-lake-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(215,NULL,40,'tote','Carry Tote','Natural','',22.0,39,'signal-lake-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(216,NULL,40,'tote','Carry Tote','Black','',22.0,40,'signal-lake-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(217,41,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'paper-archive-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(218,41,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,62,'paper-archive-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(219,41,NULL,'cd','Compact Disc','Gatefold','',16.5,37,'paper-archive-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(220,42,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'delta-minutes-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(221,42,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,62,'delta-minutes-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(222,42,NULL,'cd','Compact Disc','Gatefold','',15.5,37,'delta-minutes-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(223,NULL,41,'shirt','Tour Tee','S','Black',29.0,39,'delta-hall-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(224,NULL,41,'shirt','Tour Tee','M','Black',29.0,40,'delta-hall-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(225,NULL,41,'shirt','Tour Tee','L','Black',29.0,41,'delta-hall-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(226,NULL,41,'shirt','Tour Tee','XL','Black',29.0,18,'delta-hall-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(227,NULL,42,'tote','Carry Tote','Natural','',23.0,40,'delta-hall-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(228,NULL,42,'tote','Carry Tote','Black','',23.0,41,'delta-hall-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(229,43,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'chrome-pattern-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(230,43,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,63,'chrome-pattern-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(231,43,NULL,'cassette','Cassette','Transparent Shell','',15.5,28,'chrome-pattern-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(232,44,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'velvet-weather-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(233,44,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,63,'velvet-weather-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(234,44,NULL,'cassette','Cassette','Transparent Shell','',14.5,28,'velvet-weather-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(235,NULL,43,'shirt','Tour Tee','S','Black',30.0,40,'mirror-union-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(236,NULL,43,'shirt','Tour Tee','M','Black',30.0,41,'mirror-union-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(237,NULL,43,'shirt','Tour Tee','L','Black',30.0,18,'mirror-union-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(238,NULL,43,'shirt','Tour Tee','XL','Black',30.0,19,'mirror-union-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(239,NULL,44,'tote','Carry Tote','Natural','',21.0,41,'mirror-union-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(240,NULL,44,'tote','Carry Tote','Black','',21.0,18,'mirror-union-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(241,45,NULL,'digital','Digital Album','MP3 + FLAC','',7.0,9999,'south-dusk-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(242,45,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',22.5,64,'south-dusk-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(243,45,NULL,'cd','Compact Disc','Gatefold','',15.0,39,'south-dusk-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(244,46,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'open-ledger-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(245,46,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,64,'open-ledger-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(246,46,NULL,'cd','Compact Disc','Gatefold','',16.5,39,'open-ledger-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(247,NULL,45,'shirt','Tour Tee','S','Black',31.0,41,'quiet-metric-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(248,NULL,45,'shirt','Tour Tee','M','Black',31.0,18,'quiet-metric-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(249,NULL,45,'shirt','Tour Tee','L','Black',31.0,19,'quiet-metric-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(250,NULL,45,'shirt','Tour Tee','XL','Black',31.0,20,'quiet-metric-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(251,NULL,46,'tote','Carry Tote','Natural','',22.0,18,'quiet-metric-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(252,NULL,46,'tote','Carry Tote','Black','',22.0,19,'quiet-metric-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(253,47,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'midnight-vector-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(254,47,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,65,'midnight-vector-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(255,48,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'after-cinema-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(256,48,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,65,'after-cinema-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(257,NULL,47,'shirt','Tour Tee','S','Black',32.0,18,'blue-archive-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(258,NULL,47,'shirt','Tour Tee','M','Black',32.0,19,'blue-archive-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(259,NULL,47,'shirt','Tour Tee','L','Black',32.0,20,'blue-archive-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(260,NULL,47,'shirt','Tour Tee','XL','Black',32.0,21,'blue-archive-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(261,NULL,48,'tote','Carry Tote','Natural','',23.0,19,'blue-archive-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(262,NULL,48,'tote','Carry Tote','Black','',23.0,20,'blue-archive-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(263,49,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'glass-thread-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(264,49,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,66,'glass-thread-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(265,49,NULL,'cassette','Cassette','Transparent Shell','',14.5,31,'glass-thread-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(266,50,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'broken-method-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(267,50,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,66,'broken-method-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(268,50,NULL,'cassette','Cassette','Transparent Shell','',14.0,31,'broken-method-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(269,NULL,49,'shirt','Tour Tee','S','Black',29.0,19,'stair-pattern-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(270,NULL,49,'shirt','Tour Tee','M','Black',29.0,20,'stair-pattern-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(271,NULL,49,'shirt','Tour Tee','L','Black',29.0,21,'stair-pattern-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(272,NULL,49,'shirt','Tour Tee','XL','Black',29.0,22,'stair-pattern-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(273,NULL,50,'tote','Carry Tote','Natural','',21.0,20,'stair-pattern-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(274,NULL,50,'tote','Carry Tote','Black','',21.0,21,'stair-pattern-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(275,51,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'motel-parade-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(276,51,NULL,'cassette','Cassette','Transparent Shell','',15.0,32,'motel-parade-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(277,52,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'static-current-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(278,52,NULL,'cassette','Cassette','Transparent Shell','',14.5,32,'static-current-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(279,NULL,51,'shirt','Tour Tee','S','Black',30.0,20,'velvet-current-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(280,NULL,51,'shirt','Tour Tee','M','Black',30.0,21,'velvet-current-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(281,NULL,51,'shirt','Tour Tee','L','Black',30.0,22,'velvet-current-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(282,NULL,51,'shirt','Tour Tee','XL','Black',30.0,23,'velvet-current-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(283,NULL,52,'tote','Carry Tote','Natural','',22.0,21,'velvet-current-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(284,NULL,52,'tote','Carry Tote','Black','',22.0,22,'velvet-current-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(285,53,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'stone-engine-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(286,53,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,68,'stone-engine-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(287,53,NULL,'cd','Compact Disc','Gatefold','',17.0,43,'stone-engine-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(288,54,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'harbor-boulevard-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(289,54,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,68,'harbor-boulevard-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(290,54,NULL,'cd','Compact Disc','Gatefold','',16.5,43,'harbor-boulevard-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(291,NULL,53,'shirt','Tour Tee','S','Black',31.0,21,'harbor-study-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(292,NULL,53,'shirt','Tour Tee','M','Black',31.0,22,'harbor-study-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(293,NULL,53,'shirt','Tour Tee','L','Black',31.0,23,'harbor-study-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(294,NULL,53,'shirt','Tour Tee','XL','Black',31.0,24,'harbor-study-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(295,NULL,54,'tote','Carry Tote','Natural','',23.0,22,'harbor-study-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(296,NULL,54,'tote','Carry Tote','Black','',23.0,23,'harbor-study-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(297,55,NULL,'digital','Digital Album','MP3 + FLAC','',7.0,9999,'delta-signal-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(298,55,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',22.5,69,'delta-signal-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(299,55,NULL,'cd','Compact Disc','Gatefold','',15.0,44,'delta-signal-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(300,56,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'river-garden-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(301,56,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,69,'river-garden-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(302,56,NULL,'cd','Compact Disc','Gatefold','',17.0,44,'river-garden-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(303,NULL,55,'shirt','Tour Tee','S','Black',32.0,22,'street-lantern-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(304,NULL,55,'shirt','Tour Tee','M','Black',32.0,23,'street-lantern-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(305,NULL,55,'shirt','Tour Tee','L','Black',32.0,24,'street-lantern-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(306,NULL,55,'shirt','Tour Tee','XL','Black',32.0,25,'street-lantern-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(307,NULL,56,'tote','Carry Tote','Natural','',21.0,23,'street-lantern-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(308,NULL,56,'tote','Carry Tote','Black','',21.0,24,'street-lantern-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(309,57,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'velvet-mosaic-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(310,57,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,70,'velvet-mosaic-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(311,57,NULL,'cassette','Cassette','Transparent Shell','',14.0,35,'velvet-mosaic-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(312,58,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'signal-transit-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(313,58,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,70,'signal-transit-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(314,58,NULL,'cassette','Cassette','Transparent Shell','',14.0,35,'signal-transit-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(315,NULL,57,'shirt','Tour Tee','S','Black',29.0,23,'noon-frame-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(316,NULL,57,'shirt','Tour Tee','M','Black',29.0,24,'noon-frame-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(317,NULL,57,'shirt','Tour Tee','L','Black',29.0,25,'noon-frame-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(318,NULL,57,'shirt','Tour Tee','XL','Black',29.0,26,'noon-frame-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(319,NULL,58,'tote','Carry Tote','Natural','',22.0,24,'noon-frame-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(320,NULL,58,'tote','Carry Tote','Black','',22.0,25,'noon-frame-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(321,59,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'open-harbor-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(322,59,NULL,'cassette','Cassette','Transparent Shell','',14.5,36,'open-harbor-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(323,60,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'blue-archive-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(324,60,NULL,'cassette','Cassette','Transparent Shell','',14.5,36,'blue-archive-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(325,NULL,59,'shirt','Tour Tee','S','Black',30.0,24,'low-atlas-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(326,NULL,59,'shirt','Tour Tee','M','Black',30.0,25,'low-atlas-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(327,NULL,59,'shirt','Tour Tee','L','Black',30.0,26,'low-atlas-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(328,NULL,59,'shirt','Tour Tee','XL','Black',30.0,27,'low-atlas-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(329,NULL,60,'tote','Carry Tote','Natural','',23.0,25,'low-atlas-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(330,NULL,60,'tote','Carry Tote','Black','',23.0,26,'low-atlas-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(331,61,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'after-circuit-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(332,61,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,72,'after-circuit-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(333,61,NULL,'cd','Compact Disc','Gatefold','',16.5,47,'after-circuit-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(334,62,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'late-pattern-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(335,62,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,72,'late-pattern-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(336,62,NULL,'cd','Compact Disc','Gatefold','',16.5,47,'late-pattern-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(337,NULL,61,'shirt','Tour Tee','S','Black',31.0,25,'chrome-willow-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(338,NULL,61,'shirt','Tour Tee','M','Black',31.0,26,'chrome-willow-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(339,NULL,61,'shirt','Tour Tee','L','Black',31.0,27,'chrome-willow-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(340,NULL,61,'shirt','Tour Tee','XL','Black',31.0,28,'chrome-willow-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(341,NULL,62,'tote','Carry Tote','Natural','',21.0,26,'chrome-willow-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(342,NULL,62,'tote','Carry Tote','Black','',21.0,27,'chrome-willow-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(343,63,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'broken-minutes-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(344,63,NULL,'cassette','Cassette','Transparent Shell','',15.5,38,'broken-minutes-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(345,64,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'golden-dusk-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(346,64,NULL,'cassette','Cassette','Transparent Shell','',15.5,38,'golden-dusk-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(347,NULL,63,'shirt','Tour Tee','S','Black',32.0,26,'radio-meadow-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(348,NULL,63,'shirt','Tour Tee','M','Black',32.0,27,'radio-meadow-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(349,NULL,63,'shirt','Tour Tee','L','Black',32.0,28,'radio-meadow-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(350,NULL,63,'shirt','Tour Tee','XL','Black',32.0,29,'radio-meadow-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(351,NULL,64,'tote','Carry Tote','Natural','',22.0,27,'radio-meadow-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(352,NULL,64,'tote','Carry Tote','Black','',22.0,28,'radio-meadow-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(353,65,NULL,'digital','Digital Album','MP3 + FLAC','',7.0,9999,'static-weather-fault-parade-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(354,65,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',22.5,74,'static-weather-fault-parade-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(355,65,NULL,'cd','Compact Disc','Gatefold','',15.0,49,'static-weather-fault-parade-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(356,66,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'quiet-vector-fault-parade-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(357,66,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,74,'quiet-vector-fault-parade-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(358,66,NULL,'cd','Compact Disc','Gatefold','',15.5,49,'quiet-vector-fault-parade-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(359,NULL,65,'shirt','Tour Tee','S','Black',29.0,27,'fault-parade-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(360,NULL,65,'shirt','Tour Tee','M','Black',29.0,28,'fault-parade-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(361,NULL,65,'shirt','Tour Tee','L','Black',29.0,29,'fault-parade-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(362,NULL,65,'shirt','Tour Tee','XL','Black',29.0,30,'fault-parade-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(363,NULL,66,'tote','Carry Tote','Natural','',23.0,28,'fault-parade-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(364,NULL,66,'tote','Carry Tote','Black','',23.0,29,'fault-parade-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(365,67,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'harbor-ledger-static-ledger-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(366,67,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,75,'harbor-ledger-static-ledger-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(367,67,NULL,'cassette','Cassette','Transparent Shell','',14.0,40,'harbor-ledger-static-ledger-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(368,68,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'paper-thread-static-ledger-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(369,68,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,75,'paper-thread-static-ledger-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(370,68,NULL,'cassette','Cassette','Transparent Shell','',14.5,40,'paper-thread-static-ledger-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(371,NULL,67,'shirt','Tour Tee','S','Black',30.0,28,'static-ledger-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(372,NULL,67,'shirt','Tour Tee','M','Black',30.0,29,'static-ledger-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(373,NULL,67,'shirt','Tour Tee','L','Black',30.0,30,'static-ledger-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(374,NULL,67,'shirt','Tour Tee','XL','Black',30.0,31,'static-ledger-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(375,NULL,68,'tote','Carry Tote','Natural','',21.0,29,'static-ledger-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(376,NULL,68,'tote','Carry Tote','Black','',21.0,30,'static-ledger-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(377,69,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'river-cinema-stone-balcony-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(378,69,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,76,'river-cinema-stone-balcony-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(379,69,NULL,'cd','Compact Disc','Gatefold','',16.0,34,'river-cinema-stone-balcony-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(380,70,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'chrome-parade-stone-balcony-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(381,70,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,76,'chrome-parade-stone-balcony-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(382,70,NULL,'cd','Compact Disc','Gatefold','',16.5,34,'chrome-parade-stone-balcony-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(383,NULL,69,'shirt','Tour Tee','S','Black',31.0,29,'stone-balcony-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(384,NULL,69,'shirt','Tour Tee','M','Black',31.0,30,'stone-balcony-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(385,NULL,69,'shirt','Tour Tee','L','Black',31.0,31,'stone-balcony-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(386,NULL,69,'shirt','Tour Tee','XL','Black',31.0,32,'stone-balcony-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(387,NULL,70,'tote','Carry Tote','Natural','',22.0,30,'stone-balcony-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(388,NULL,70,'tote','Carry Tote','Black','',22.0,31,'stone-balcony-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(389,71,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'signal-method-west-transit-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(390,71,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,42,'signal-method-west-transit-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(391,72,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'south-engine-west-transit-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(392,72,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,42,'south-engine-west-transit-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(393,NULL,71,'shirt','Tour Tee','S','Black',32.0,30,'west-transit-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(394,NULL,71,'shirt','Tour Tee','M','Black',32.0,31,'west-transit-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(395,NULL,71,'shirt','Tour Tee','L','Black',32.0,32,'west-transit-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(396,NULL,71,'shirt','Tour Tee','XL','Black',32.0,33,'west-transit-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(397,NULL,72,'tote','Carry Tote','Natural','',23.0,31,'west-transit-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(398,NULL,72,'tote','Carry Tote','Black','',23.0,32,'west-transit-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(399,73,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'blue-current-echo-dividend-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(400,73,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,43,'blue-current-echo-dividend-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(401,73,NULL,'cassette','Cassette','Transparent Shell','',15.5,43,'blue-current-echo-dividend-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(402,74,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'midnight-signal-echo-dividend-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(403,74,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,43,'midnight-signal-echo-dividend-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(404,74,NULL,'cassette','Cassette','Transparent Shell','',14.0,43,'midnight-signal-echo-dividend-cassette-3','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(405,NULL,73,'shirt','Tour Tee','S','Black',29.0,31,'echo-dividend-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(406,NULL,73,'shirt','Tour Tee','M','Black',29.0,32,'echo-dividend-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(407,NULL,73,'shirt','Tour Tee','L','Black',29.0,33,'echo-dividend-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(408,NULL,73,'shirt','Tour Tee','XL','Black',29.0,34,'echo-dividend-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(409,NULL,74,'tote','Carry Tote','Natural','',21.0,32,'echo-dividend-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(410,NULL,74,'tote','Carry Tote','Black','',21.0,33,'echo-dividend-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(411,75,NULL,'digital','Digital Album','MP3 + FLAC','',7.0,9999,'late-boulevard-cloud-bureau-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(412,75,NULL,'cassette','Cassette','Transparent Shell','',13.5,44,'late-boulevard-cloud-bureau-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(413,76,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'glass-mosaic-cloud-bureau-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(414,76,NULL,'cassette','Cassette','Transparent Shell','',14.5,44,'glass-mosaic-cloud-bureau-cassette-2','Ships in 2-4 days','Numbered shell edition.',0); +INSERT INTO "format_variants" VALUES(415,NULL,75,'shirt','Tour Tee','S','Black',30.0,32,'cloud-bureau-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(416,NULL,75,'shirt','Tour Tee','M','Black',30.0,33,'cloud-bureau-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(417,NULL,75,'shirt','Tour Tee','L','Black',30.0,34,'cloud-bureau-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(418,NULL,75,'shirt','Tour Tee','XL','Black',30.0,35,'cloud-bureau-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(419,NULL,76,'tote','Carry Tote','Natural','',22.0,33,'cloud-bureau-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(420,NULL,76,'tote','Carry Tote','Black','',22.0,34,'cloud-bureau-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(421,77,NULL,'digital','Digital Album','MP3 + FLAC','',7.5,9999,'golden-garden-ridge-cinema-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(422,77,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.0,45,'golden-garden-ridge-cinema-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(423,77,NULL,'cd','Compact Disc','Gatefold','',15.5,38,'golden-garden-ridge-cinema-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(424,78,NULL,'digital','Digital Album','MP3 + FLAC','',8.5,9999,'motel-harbor-ridge-cinema-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(425,78,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.0,45,'motel-harbor-ridge-cinema-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(426,78,NULL,'cd','Compact Disc','Gatefold','',16.5,38,'motel-harbor-ridge-cinema-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(427,NULL,77,'shirt','Tour Tee','S','Black',31.0,33,'ridge-cinema-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(428,NULL,77,'shirt','Tour Tee','M','Black',31.0,34,'ridge-cinema-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(429,NULL,77,'shirt','Tour Tee','L','Black',31.0,35,'ridge-cinema-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(430,NULL,77,'shirt','Tour Tee','XL','Black',31.0,36,'ridge-cinema-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(431,NULL,78,'tote','Carry Tote','Natural','',23.0,34,'ridge-cinema-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(432,NULL,78,'tote','Carry Tote','Black','',23.0,35,'ridge-cinema-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(433,79,NULL,'digital','Digital Album','MP3 + FLAC','',8.0,9999,'quiet-transit-hollow-method-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(434,79,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',23.5,46,'quiet-transit-hollow-method-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(435,79,NULL,'cd','Compact Disc','Gatefold','',16.0,39,'quiet-transit-hollow-method-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(436,80,NULL,'digital','Digital Album','MP3 + FLAC','',9.0,9999,'stone-circuit-hollow-method-digital-1','Instant download','Unlimited streams in the mirror.',1); +INSERT INTO "format_variants" VALUES(437,80,NULL,'vinyl','Colored Vinyl','12-inch','Ocean Blue',24.5,46,'stone-circuit-hollow-method-vinyl-2','Ships in 3-5 days','Limited mirror pressing.',0); +INSERT INTO "format_variants" VALUES(438,80,NULL,'cd','Compact Disc','Gatefold','',17.0,39,'stone-circuit-hollow-method-cd-3','Ships in 2-4 days','Includes lyric foldout.',0); +INSERT INTO "format_variants" VALUES(439,NULL,79,'shirt','Tour Tee','S','Black',32.0,34,'hollow-method-tour-tee-shirt-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(440,NULL,79,'shirt','Tour Tee','M','Black',32.0,35,'hollow-method-tour-tee-shirt-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(441,NULL,79,'shirt','Tour Tee','L','Black',32.0,36,'hollow-method-tour-tee-shirt-3','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(442,NULL,79,'shirt','Tour Tee','XL','Black',32.0,37,'hollow-method-tour-tee-shirt-4','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +INSERT INTO "format_variants" VALUES(443,NULL,80,'tote','Carry Tote','Natural','',21.0,35,'hollow-method-carry-tote-tote-1','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',1); +INSERT INTO "format_variants" VALUES(444,NULL,80,'tote','Carry Tote','Black','',21.0,36,'hollow-method-carry-tote-tote-2','Ships in 2-5 days','Locally generated merch photo and deterministic variant data.',0); +CREATE TABLE genres ( + id INTEGER NOT NULL, + name VARCHAR(60) NOT NULL, + slug VARCHAR(80) NOT NULL, + description TEXT, + accent_color VARCHAR(20), + PRIMARY KEY (id), + UNIQUE (name) +); +INSERT INTO "genres" VALUES(1,'electronic','electronic','Clubs, synth architecture, and digital fog.','#0f5ea8'); +INSERT INTO "genres" VALUES(2,'experimental','experimental','Boundary-pushing releases with noise, tape, and collage impulses.','#6b2fb3'); +INSERT INTO "genres" VALUES(3,'alternative','alternative','Hook-heavy independent records, dream pop, and guitar shimmer.','#274ab8'); +INSERT INTO "genres" VALUES(4,'rock','rock','Fuzz, motorik rhythm sections, and widescreen choruses.','#a63b34'); +INSERT INTO "genres" VALUES(5,'ambient','ambient','Slow-moving drift, field recordings, and restorative detail.','#145c4b'); +INSERT INTO "genres" VALUES(6,'hip-hop/rap','hip-hop-rap','Sharp lyric sheets, beat experiments, and street-level memoir.','#9333ea'); +INSERT INTO "genres" VALUES(7,'metal','metal','Dense distortion, ritual percussion, and high-pressure dynamics.','#7c2d12'); +INSERT INTO "genres" VALUES(8,'punk','punk','Fast, bright, political, and built for tiny rooms.','#be123c'); +INSERT INTO "genres" VALUES(9,'jazz','jazz','Late-night improvisation, spiritual harmony, and room sound.','#0f766e'); +INSERT INTO "genres" VALUES(10,'folk','folk','Acoustic storytelling, communal choruses, and road-worn detail.','#7c2d12'); +INSERT INTO "genres" VALUES(11,'pop','pop','Polished melodies, neon hooks, and emotional lift.','#1d4ed8'); +INSERT INTO "genres" VALUES(12,'techno','techno','Machine pulse, analog grit, and hypnotic low-end.','#0f5ea8'); +CREATE TABLE labels ( + id INTEGER NOT NULL, + name VARCHAR(120) NOT NULL, + slug VARCHAR(120) NOT NULL, + location VARCHAR(120), + description TEXT, + PRIMARY KEY (id), + UNIQUE (name) +); +INSERT INTO "labels" VALUES(1,'Aperture Tapes','aperture-tapes','Berlin, Germany','Pressings that lean toward dub techno, electro, and humid afterhours ambience.'); +INSERT INTO "labels" VALUES(2,'Midnight Service','midnight-service','London, United Kingdom','Independent label focused on guitar records and night-bus pop.'); +INSERT INTO "labels" VALUES(3,'Motor Relay','motor-relay','Detroit, United States','Hardware-forward dance music and disciplined machine funk.'); +INSERT INTO "labels" VALUES(4,'Inland Weather','inland-weather','Tokyo, Japan','Ambient and electro-acoustic labels with carefully built physical editions.'); +INSERT INTO "labels" VALUES(5,'South District','south-district','Sao Paulo, Brazil','Razor-wire punk, no-wave experiments, and scene-documentation merch.'); +INSERT INTO "labels" VALUES(6,'Sun Trace','sun-trace','Los Angeles, United States','Beat music, jazz crossover, and cinematic low-end.'); +INSERT INTO "labels" VALUES(7,'Night School Annex','night-school-annex','New York, United States','Jazz-leaning independents with tactile design systems.'); +INSERT INTO "labels" VALUES(8,'Lantern Union','lantern-union','Melbourne, Australia','Songwriter records and small-batch merch with printshop charm.'); +INSERT INTO "labels" VALUES(9,'Obsidian Bloom','obsidian-bloom','Paris, France','Heavy records with monochrome art direction and deluxe inserts.'); +INSERT INTO "labels" VALUES(10,'Harbor Circuit','harbor-circuit','Global','Cross-scene collaborations curated for the mirror benchmark.'); +CREATE TABLE merch_items ( + id INTEGER NOT NULL, + artist_id INTEGER NOT NULL, + album_id INTEGER, + title VARCHAR(160) NOT NULL, + slug VARCHAR(180) NOT NULL, + item_type VARCHAR(50), + description TEXT, + short_blurb VARCHAR(200), + image VARCHAR(255), + price FLOAT, + inventory INTEGER, + release_date DATE NOT NULL, + is_featured BOOLEAN, + PRIMARY KEY (id), + FOREIGN KEY(artist_id) REFERENCES artists (id), + FOREIGN KEY(album_id) REFERENCES albums (id) +); +INSERT INTO "merch_items" VALUES(1,1,1,'Neon Harbor Studio Tee','neon-harbor-studio-tee','shirt','Neon Harbor Studio Tee is a shirt release tied to Tidal Memory, printed locally for Neon Harbor with clean benchmark-ready variant information.','Garment-dyed heavyweight tee with the Tidal Memory mark.','images/merch/neon-harbor-studio-tee.svg',32.0,55,'2025-09-30',1); +INSERT INTO "merch_items" VALUES(2,1,2,'Neon Harbor Breakwater Slipmat','neon-harbor-breakwater-slipmat','slipmat','Neon Harbor Breakwater Slipmat is a slipmat release tied to Night Ferry, printed locally for Neon Harbor with clean benchmark-ready variant information.','Pair of felt slipmats printed with breakwater geometry.','images/merch/neon-harbor-breakwater-slipmat.svg',24.0,61,'2025-10-05',1); +INSERT INTO "merch_items" VALUES(3,2,3,'Glass Choir Stairwell Tee','glass-choir-stairwell-tee','shirt','Glass Choir Stairwell Tee is a shirt release tied to Static Bloom, printed locally for Glass Choir with clean benchmark-ready variant information.','Soft grey shirt with stairwell photo treatment.','images/merch/glass-choir-stairwell-tee.svg',30.0,57,'2026-02-06',1); +INSERT INTO "merch_items" VALUES(4,2,4,'Glass Choir Balcony Poster','glass-choir-balcony-poster','poster','Glass Choir Balcony Poster is a poster release tied to Paper Signal, printed locally for Glass Choir with clean benchmark-ready variant information.','Risograph poster pulled from the Paper Signal cover session.','images/merch/glass-choir-balcony-poster.svg',18.0,63,'2026-02-11',1); +INSERT INTO "merch_items" VALUES(5,3,6,'Ashen Circuit Grid Slipmat','ashen-circuit-grid-slipmat','slipmat','Ashen Circuit Grid Slipmat is a slipmat release tied to Machine Prayer, printed locally for Ashen Circuit with clean benchmark-ready variant information.','Dense white-on-black slipmat pair with the Machine Prayer grid.','images/merch/ashen-circuit-grid-slipmat.svg',22.0,59,'2025-05-16',1); +INSERT INTO "merch_items" VALUES(6,3,5,'Ashen Circuit Weld Patch','ashen-circuit-weld-patch','patch','Ashen Circuit Weld Patch is a patch release tied to Redline Ritual, printed locally for Ashen Circuit with clean benchmark-ready variant information.','Embroidered patch cut from the Redline Ritual symbol language.','images/merch/ashen-circuit-weld-patch.svg',10.0,65,'2025-05-21',1); +INSERT INTO "merch_items" VALUES(7,4,7,'Soft Locale Drift Hoodie','soft-locale-drift-hoodie','hoodie','Soft Locale Drift Hoodie is a hoodie release tied to Between Stations, printed locally for Soft Locale with clean benchmark-ready variant information.','Midweight hoodie with a reflective commuter-grid chest print.','images/merch/soft-locale-drift-hoodie.svg',48.0,61,'2025-07-18',1); +INSERT INTO "merch_items" VALUES(8,4,8,'Soft Locale Rain Map Cassette Box','soft-locale-rain-map-cassette-box','cassette','Soft Locale Rain Map Cassette Box is a cassette release tied to Sleep Maps, printed locally for Soft Locale with clean benchmark-ready variant information.','Cassette shell and booklet edition with a fold-out station map.','images/merch/soft-locale-rain-map-cassette-box.svg',26.0,67,'2025-07-23',1); +INSERT INTO "merch_items" VALUES(9,5,9,'South Exit Flyer Tee','south-exit-flyer-tee','shirt','South Exit Flyer Tee is a shirt release tied to Concrete Carnival, printed locally for South Exit with clean benchmark-ready variant information.','Cracked-print tee based on a xeroxed show flyer.','images/merch/south-exit-flyer-tee.svg',28.0,63,'2025-03-28',1); +INSERT INTO "merch_items" VALUES(10,5,10,'South Exit Poster Pack','south-exit-poster-pack','poster','South Exit Poster Pack is a poster release tied to Siren Economy, printed locally for South Exit with clean benchmark-ready variant information.','Three-poster bundle featuring concrete stencil variants.','images/merch/south-exit-poster-pack.svg',16.0,69,'2025-04-02',1); +INSERT INTO "merch_items" VALUES(11,6,12,'Cinder Plaza Blueprint Fever Poster','cinder-plaza-blueprint-fever-poster','poster','Cinder Plaza Blueprint Fever Poster is a poster release tied to Blueprint Fever, printed locally for Cinder Plaza with clean benchmark-ready variant information.','Blueprint grid poster with a signed edition for collectors.','images/merch/cinder-plaza-blueprint-fever-poster.svg',20.0,65,'2025-09-05',1); +INSERT INTO "merch_items" VALUES(12,6,11,'Cinder Plaza Signal Debt Cap','cinder-plaza-signal-debt-cap','cap','Cinder Plaza Signal Debt Cap is a cap release tied to Signal Debt, printed locally for Cinder Plaza with clean benchmark-ready variant information.','Low-profile cap embroidered with the Signal Debt skyline.','images/merch/cinder-plaza-signal-debt-cap.svg',26.0,71,'2025-09-10',1); +INSERT INTO "merch_items" VALUES(13,7,13,'Velvet Avenue Night Shift Poster','velvet-avenue-night-shift-poster','poster','Velvet Avenue Night Shift Poster is a poster release tied to Blue Hour Broadcast, printed locally for Velvet Avenue with clean benchmark-ready variant information.','Matte poster with the Blue Hour Broadcast room diagram.','images/merch/velvet-avenue-night-shift-poster.svg',19.0,67,'2025-01-17',1); +INSERT INTO "merch_items" VALUES(14,7,14,'Velvet Avenue Setlist Tote','velvet-avenue-setlist-tote','tote','Velvet Avenue Setlist Tote is a tote release tied to Lobby Mirage, printed locally for Velvet Avenue with clean benchmark-ready variant information.','Natural cotton tote printed with a handwritten setlist.','images/merch/velvet-avenue-setlist-tote.svg',24.0,73,'2025-01-22',1); +INSERT INTO "merch_items" VALUES(15,8,15,'Salt Meadow Field Notes Tote','salt-meadow-field-notes-tote','tote','Salt Meadow Field Notes Tote is a tote release tied to Riverlights, printed locally for Salt Meadow with clean benchmark-ready variant information.','Natural tote printed with Riverlights notebook fragments.','images/merch/salt-meadow-field-notes-tote.svg',22.0,69,'2025-06-27',1); +INSERT INTO "merch_items" VALUES(16,8,15,'Salt Meadow Riverlights Lyric Zine','salt-meadow-riverlights-lyric-zine','zine','Salt Meadow Riverlights Lyric Zine is a zine release tied to Riverlights, printed locally for Salt Meadow with clean benchmark-ready variant information.','Staple-bound lyric zine with recording notes and Polaroids.','images/merch/salt-meadow-riverlights-lyric-zine.svg',14.0,75,'2025-07-02',1); +INSERT INTO "merch_items" VALUES(17,9,17,'Iron Veil Chapel Longsleeve','iron-veil-chapel-longsleeve','shirt','Iron Veil Chapel Longsleeve is a shirt release tied to Iron Sleep, printed locally for Iron Veil with clean benchmark-ready variant information.','Longsleeve with metallic ink front and sleeve glyphs.','images/merch/iron-veil-chapel-longsleeve.svg',36.0,71,'2025-11-07',1); +INSERT INTO "merch_items" VALUES(18,9,18,'Iron Veil Noise Patch Set','iron-veil-noise-patch-set','patch','Iron Veil Noise Patch Set is a patch release tied to Saint of Noise, printed locally for Iron Veil with clean benchmark-ready variant information.','Three embroidered patches with silver overlock.','images/merch/iron-veil-noise-patch-set.svg',12.0,77,'2025-11-12',1); +INSERT INTO "merch_items" VALUES(19,10,19,'Fever Arcade Gloss Tee','fever-arcade-gloss-tee','shirt','Fever Arcade Gloss Tee is a shirt release tied to Elastic Hearts, printed locally for Fever Arcade with clean benchmark-ready variant information.','Bright print tee with split-tone Elastic Hearts graphic.','images/merch/fever-arcade-gloss-tee.svg',29.0,73,'2025-11-21',1); +INSERT INTO "merch_items" VALUES(20,10,20,'Fever Arcade Mirror Pin Set','fever-arcade-mirror-pin-set','pin','Fever Arcade Mirror Pin Set is a pin release tied to Mirror Mosaic, printed locally for Fever Arcade with clean benchmark-ready variant information.','Two hard-enamel pins shaped like mirror fragments.','images/merch/fever-arcade-mirror-pin-set.svg',15.0,79,'2025-11-26',1); +INSERT INTO "merch_items" VALUES(21,11,22,'Mono Shrine Fault Choir Zine','mono-shrine-fault-choir-zine','zine','Mono Shrine Fault Choir Zine is a zine release tied to Fault Choir, printed locally for Mono Shrine with clean benchmark-ready variant information.','Eight-page foldout zine of lyric fragments and cassette labels.','images/merch/mono-shrine-fault-choir-zine.svg',13.0,75,'2025-03-14',1); +INSERT INTO "merch_items" VALUES(22,11,21,'Mono Shrine Resin Poster','mono-shrine-resin-poster','poster','Mono Shrine Resin Poster is a poster release tied to Resin Language, printed locally for Mono Shrine with clean benchmark-ready variant information.','A2 poster using the album''s resin-grid art.','images/merch/mono-shrine-resin-poster.svg',17.0,81,'2025-03-19',0); +INSERT INTO "merch_items" VALUES(23,12,23,'Tide Static Burn Tee','tide-static-burn-tee','shirt','Tide Static Burn Tee is a shirt release tied to Harbor Burn, printed locally for Tide Static with clean benchmark-ready variant information.','Vintage white tee with a cracked orange harbor-burn print.','images/merch/tide-static-burn-tee.svg',30.0,77,'2025-06-13',1); +INSERT INTO "merch_items" VALUES(24,12,24,'Tide Static Quiet Engine Tote','tide-static-quiet-engine-tote','tote','Tide Static Quiet Engine Tote is a tote release tied to Quiet Engine, printed locally for Tide Static with clean benchmark-ready variant information.','Canvas tote featuring the Quiet Engine highway diagram.','images/merch/tide-static-quiet-engine-tote.svg',21.0,83,'2025-06-18',0); +INSERT INTO "merch_items" VALUES(25,13,25,'Amber Relay Tour Tee','amber-relay-tour-tee','shirt','Amber Relay Tour Tee is a shirt release tied to Static Weather, printed locally for Amber Relay with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/amber-relay-tour-tee.svg',29.0,79,'2024-04-17',1); +INSERT INTO "merch_items" VALUES(26,13,26,'Amber Relay Carry Tote','amber-relay-carry-tote','tote','Amber Relay Carry Tote is a tote release tied to Quiet Vector, printed locally for Amber Relay with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/amber-relay-carry-tote.svg',21.0,85,'2024-04-22',0); +INSERT INTO "merch_items" VALUES(27,14,27,'Paper Current Tour Tee','paper-current-tour-tee','shirt','Paper Current Tour Tee is a shirt release tied to Harbor Ledger, printed locally for Paper Current with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/paper-current-tour-tee.svg',30.0,81,'2025-05-23',1); +INSERT INTO "merch_items" VALUES(28,14,28,'Paper Current Carry Tote','paper-current-carry-tote','tote','Paper Current Carry Tote is a tote release tied to Paper Thread, printed locally for Paper Current with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/paper-current-carry-tote.svg',22.0,87,'2025-05-28',0); +INSERT INTO "merch_items" VALUES(29,15,29,'Silver Weather Tour Tee','silver-weather-tour-tee','shirt','Silver Weather Tour Tee is a shirt release tied to River Cinema, printed locally for Silver Weather with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/silver-weather-tour-tee.svg',31.0,83,'2026-06-27',1); +INSERT INTO "merch_items" VALUES(30,15,30,'Silver Weather Carry Tote','silver-weather-carry-tote','tote','Silver Weather Carry Tote is a tote release tied to Chrome Parade, printed locally for Silver Weather with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/silver-weather-carry-tote.svg',23.0,89,'2026-07-02',0); +INSERT INTO "merch_items" VALUES(31,16,31,'North Routine Tour Tee','north-routine-tour-tee','shirt','North Routine Tour Tee is a shirt release tied to Signal Method, printed locally for North Routine with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/north-routine-tour-tee.svg',32.0,85,'2026-03-27',1); +INSERT INTO "merch_items" VALUES(32,16,32,'North Routine Carry Tote','north-routine-carry-tote','tote','North Routine Carry Tote is a tote release tied to South Engine, printed locally for North Routine with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/north-routine-carry-tote.svg',21.0,91,'2026-04-01',0); +INSERT INTO "merch_items" VALUES(33,17,33,'Copper Bloom Tour Tee','copper-bloom-tour-tee','shirt','Copper Bloom Tour Tee is a shirt release tied to Blue Current, printed locally for Copper Bloom with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/copper-bloom-tour-tee.svg',29.0,87,'2025-09-07',1); +INSERT INTO "merch_items" VALUES(34,17,34,'Copper Bloom Carry Tote','copper-bloom-carry-tote','tote','Copper Bloom Carry Tote is a tote release tied to Midnight Signal, printed locally for Copper Bloom with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/copper-bloom-carry-tote.svg',22.0,93,'2025-09-12',0); +INSERT INTO "merch_items" VALUES(35,18,35,'Ladder Choir Tour Tee','ladder-choir-tour-tee','shirt','Ladder Choir Tour Tee is a shirt release tied to Late Boulevard, printed locally for Ladder Choir with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/ladder-choir-tour-tee.svg',30.0,89,'2026-09-18',1); +INSERT INTO "merch_items" VALUES(36,18,36,'Ladder Choir Carry Tote','ladder-choir-carry-tote','tote','Ladder Choir Carry Tote is a tote release tied to Glass Mosaic, printed locally for Ladder Choir with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/ladder-choir-carry-tote.svg',23.0,95,'2026-09-23',0); +INSERT INTO "merch_items" VALUES(37,19,37,'Hour Motel Tour Tee','hour-motel-tour-tee','shirt','Hour Motel Tour Tee is a shirt release tied to Golden Garden, printed locally for Hour Motel with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/hour-motel-tour-tee.svg',31.0,91,'2025-07-05',1); +INSERT INTO "merch_items" VALUES(38,19,38,'Hour Motel Carry Tote','hour-motel-carry-tote','tote','Hour Motel Carry Tote is a tote release tied to Motel Harbor, printed locally for Hour Motel with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/hour-motel-carry-tote.svg',21.0,97,'2025-07-10',0); +INSERT INTO "merch_items" VALUES(39,20,39,'Signal Lake Tour Tee','signal-lake-tour-tee','shirt','Signal Lake Tour Tee is a shirt release tied to Quiet Transit, printed locally for Signal Lake with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/signal-lake-tour-tee.svg',32.0,93,'2026-08-08',1); +INSERT INTO "merch_items" VALUES(40,20,40,'Signal Lake Carry Tote','signal-lake-carry-tote','tote','Signal Lake Carry Tote is a tote release tied to Stone Circuit, printed locally for Signal Lake with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/signal-lake-carry-tote.svg',22.0,99,'2026-08-13',0); +INSERT INTO "merch_items" VALUES(41,21,41,'Delta Hall Tour Tee','delta-hall-tour-tee','shirt','Delta Hall Tour Tee is a shirt release tied to Paper Archive, printed locally for Delta Hall with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/delta-hall-tour-tee.svg',29.0,95,'2026-02-03',1); +INSERT INTO "merch_items" VALUES(42,21,42,'Delta Hall Carry Tote','delta-hall-carry-tote','tote','Delta Hall Carry Tote is a tote release tied to Delta Minutes, printed locally for Delta Hall with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/delta-hall-carry-tote.svg',23.0,101,'2026-02-08',0); +INSERT INTO "merch_items" VALUES(43,22,43,'Mirror Union Tour Tee','mirror-union-tour-tee','shirt','Mirror Union Tour Tee is a shirt release tied to Chrome Pattern, printed locally for Mirror Union with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/mirror-union-tour-tee.svg',30.0,97,'2024-09-20',1); +INSERT INTO "merch_items" VALUES(44,22,44,'Mirror Union Carry Tote','mirror-union-carry-tote','tote','Mirror Union Carry Tote is a tote release tied to Velvet Weather, printed locally for Mirror Union with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/mirror-union-carry-tote.svg',21.0,103,'2024-09-25',0); +INSERT INTO "merch_items" VALUES(45,23,45,'Quiet Metric Tour Tee','quiet-metric-tour-tee','shirt','Quiet Metric Tour Tee is a shirt release tied to South Dusk, printed locally for Quiet Metric with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/quiet-metric-tour-tee.svg',31.0,99,'2025-10-24',1); +INSERT INTO "merch_items" VALUES(46,23,46,'Quiet Metric Carry Tote','quiet-metric-carry-tote','tote','Quiet Metric Carry Tote is a tote release tied to Open Ledger, printed locally for Quiet Metric with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/quiet-metric-carry-tote.svg',22.0,105,'2025-10-29',0); +INSERT INTO "merch_items" VALUES(47,24,47,'Blue Archive Tour Tee','blue-archive-tour-tee','shirt','Blue Archive Tour Tee is a shirt release tied to Midnight Vector, printed locally for Blue Archive with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/blue-archive-tour-tee.svg',32.0,101,'2026-04-24',1); +INSERT INTO "merch_items" VALUES(48,24,48,'Blue Archive Carry Tote','blue-archive-carry-tote','tote','Blue Archive Carry Tote is a tote release tied to After Cinema, printed locally for Blue Archive with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/blue-archive-carry-tote.svg',23.0,107,'2026-04-29',0); +INSERT INTO "merch_items" VALUES(49,25,49,'Stair Pattern Tour Tee','stair-pattern-tour-tee','shirt','Stair Pattern Tour Tee is a shirt release tied to Glass Thread, printed locally for Stair Pattern with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/stair-pattern-tour-tee.svg',29.0,103,'2024-05-30',1); +INSERT INTO "merch_items" VALUES(50,25,50,'Stair Pattern Carry Tote','stair-pattern-carry-tote','tote','Stair Pattern Carry Tote is a tote release tied to Broken Method, printed locally for Stair Pattern with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/stair-pattern-carry-tote.svg',21.0,109,'2024-06-04',0); +INSERT INTO "merch_items" VALUES(51,26,51,'Velvet Current Tour Tee','velvet-current-tour-tee','shirt','Velvet Current Tour Tee is a shirt release tied to Motel Parade, printed locally for Velvet Current with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/velvet-current-tour-tee.svg',30.0,105,'2025-07-04',1); +INSERT INTO "merch_items" VALUES(52,26,52,'Velvet Current Carry Tote','velvet-current-carry-tote','tote','Velvet Current Carry Tote is a tote release tied to Static Current, printed locally for Velvet Current with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/velvet-current-carry-tote.svg',22.0,111,'2025-07-09',0); +INSERT INTO "merch_items" VALUES(53,27,53,'Harbor Study Tour Tee','harbor-study-tour-tee','shirt','Harbor Study Tour Tee is a shirt release tied to Stone Engine, printed locally for Harbor Study with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/harbor-study-tour-tee.svg',31.0,107,'2026-08-09',1); +INSERT INTO "merch_items" VALUES(54,27,54,'Harbor Study Carry Tote','harbor-study-carry-tote','tote','Harbor Study Carry Tote is a tote release tied to Harbor Boulevard, printed locally for Harbor Study with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/harbor-study-carry-tote.svg',23.0,113,'2026-08-14',0); +INSERT INTO "merch_items" VALUES(55,28,55,'Street Lantern Tour Tee','street-lantern-tour-tee','shirt','Street Lantern Tour Tee is a shirt release tied to Delta Signal, printed locally for Street Lantern with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/street-lantern-tour-tee.svg',32.0,109,'2026-05-08',1); +INSERT INTO "merch_items" VALUES(56,28,56,'Street Lantern Carry Tote','street-lantern-carry-tote','tote','Street Lantern Carry Tote is a tote release tied to River Garden, printed locally for Street Lantern with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/street-lantern-carry-tote.svg',21.0,115,'2026-05-13',0); +INSERT INTO "merch_items" VALUES(57,29,57,'Noon Frame Tour Tee','noon-frame-tour-tee','shirt','Noon Frame Tour Tee is a shirt release tied to Velvet Mosaic, printed locally for Noon Frame with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/noon-frame-tour-tee.svg',29.0,111,'2025-09-25',1); +INSERT INTO "merch_items" VALUES(58,29,58,'Noon Frame Carry Tote','noon-frame-carry-tote','tote','Noon Frame Carry Tote is a tote release tied to Signal Transit, printed locally for Noon Frame with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/noon-frame-carry-tote.svg',22.0,117,'2025-09-30',0); +INSERT INTO "merch_items" VALUES(59,30,59,'Low Atlas Tour Tee','low-atlas-tour-tee','shirt','Low Atlas Tour Tee is a shirt release tied to Open Harbor, printed locally for Low Atlas with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/low-atlas-tour-tee.svg',30.0,113,'2026-10-31',1); +INSERT INTO "merch_items" VALUES(60,30,60,'Low Atlas Carry Tote','low-atlas-carry-tote','tote','Low Atlas Carry Tote is a tote release tied to Blue Archive, printed locally for Low Atlas with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/low-atlas-carry-tote.svg',23.0,119,'2026-11-05',0); +INSERT INTO "merch_items" VALUES(61,31,61,'Chrome Willow Tour Tee','chrome-willow-tour-tee','shirt','Chrome Willow Tour Tee is a shirt release tied to After Circuit, printed locally for Chrome Willow with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/chrome-willow-tour-tee.svg',31.0,115,'2025-07-24',1); +INSERT INTO "merch_items" VALUES(62,31,62,'Chrome Willow Carry Tote','chrome-willow-carry-tote','tote','Chrome Willow Carry Tote is a tote release tied to Late Pattern, printed locally for Chrome Willow with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/chrome-willow-carry-tote.svg',21.0,121,'2025-07-29',0); +INSERT INTO "merch_items" VALUES(63,32,63,'Radio Meadow Tour Tee','radio-meadow-tour-tee','shirt','Radio Meadow Tour Tee is a shirt release tied to Broken Minutes, printed locally for Radio Meadow with clean benchmark-ready variant information.','Standard artist tee with benchmark-ready sizing.','images/merch/radio-meadow-tour-tee.svg',32.0,117,'2026-08-27',1); +INSERT INTO "merch_items" VALUES(64,32,64,'Radio Meadow Carry Tote','radio-meadow-carry-tote','tote','Radio Meadow Carry Tote is a tote release tied to Golden Dusk, printed locally for Radio Meadow with clean benchmark-ready variant information.','Canvas tote with scene-specific line work.','images/merch/radio-meadow-carry-tote.svg',22.0,123,'2026-09-01',0); +INSERT INTO "merch_items" VALUES(65,33,NULL,'Fault Parade Tour Tee','fault-parade-tour-tee','shirt','Fault Parade Tour Tee is a shirt item from Fault Parade, created for the local mirror with deterministic colors and edition notes.','Standard artist tee with benchmark-ready sizing.','images/merch/fault-parade-tour-tee.svg',29.0,119,'2026-02-19',1); +INSERT INTO "merch_items" VALUES(66,33,NULL,'Fault Parade Carry Tote','fault-parade-carry-tote','tote','Fault Parade Carry Tote is a tote item from Fault Parade, created for the local mirror with deterministic colors and edition notes.','Canvas tote with scene-specific line work.','images/merch/fault-parade-carry-tote.svg',23.0,125,'2026-02-24',0); +INSERT INTO "merch_items" VALUES(67,34,NULL,'Static Ledger Tour Tee','static-ledger-tour-tee','shirt','Static Ledger Tour Tee is a shirt item from Static Ledger, created for the local mirror with deterministic colors and edition notes.','Standard artist tee with benchmark-ready sizing.','images/merch/static-ledger-tour-tee.svg',30.0,121,'2024-11-02',1); +INSERT INTO "merch_items" VALUES(68,34,NULL,'Static Ledger Carry Tote','static-ledger-carry-tote','tote','Static Ledger Carry Tote is a tote item from Static Ledger, created for the local mirror with deterministic colors and edition notes.','Canvas tote with scene-specific line work.','images/merch/static-ledger-carry-tote.svg',21.0,127,'2024-11-07',0); +INSERT INTO "merch_items" VALUES(69,35,NULL,'Stone Balcony Tour Tee','stone-balcony-tour-tee','shirt','Stone Balcony Tour Tee is a shirt item from Stone Balcony, created for the local mirror with deterministic colors and edition notes.','Standard artist tee with benchmark-ready sizing.','images/merch/stone-balcony-tour-tee.svg',31.0,123,'2025-05-01',1); +INSERT INTO "merch_items" VALUES(70,35,NULL,'Stone Balcony Carry Tote','stone-balcony-carry-tote','tote','Stone Balcony Carry Tote is a tote item from Stone Balcony, created for the local mirror with deterministic colors and edition notes.','Canvas tote with scene-specific line work.','images/merch/stone-balcony-carry-tote.svg',22.0,129,'2025-05-06',0); +INSERT INTO "merch_items" VALUES(71,36,NULL,'West Transit Tour Tee','west-transit-tour-tee','shirt','West Transit Tour Tee is a shirt item from West Transit, created for the local mirror with deterministic colors and edition notes.','Standard artist tee with benchmark-ready sizing.','images/merch/west-transit-tour-tee.svg',32.0,125,'2026-06-06',1); +INSERT INTO "merch_items" VALUES(72,36,NULL,'West Transit Carry Tote','west-transit-carry-tote','tote','West Transit Carry Tote is a tote item from West Transit, created for the local mirror with deterministic colors and edition notes.','Canvas tote with scene-specific line work.','images/merch/west-transit-carry-tote.svg',23.0,131,'2026-06-11',0); +INSERT INTO "merch_items" VALUES(73,37,NULL,'Echo Dividend Tour Tee','echo-dividend-tour-tee','shirt','Echo Dividend Tour Tee is a shirt item from Echo Dividend, created for the local mirror with deterministic colors and edition notes.','Standard artist tee with benchmark-ready sizing.','images/merch/echo-dividend-tour-tee.svg',29.0,127,'2024-06-17',1); +INSERT INTO "merch_items" VALUES(74,37,NULL,'Echo Dividend Carry Tote','echo-dividend-carry-tote','tote','Echo Dividend Carry Tote is a tote item from Echo Dividend, created for the local mirror with deterministic colors and edition notes.','Canvas tote with scene-specific line work.','images/merch/echo-dividend-carry-tote.svg',21.0,133,'2024-06-22',0); +INSERT INTO "merch_items" VALUES(75,38,NULL,'Cloud Bureau Tour Tee','cloud-bureau-tour-tee','shirt','Cloud Bureau Tour Tee is a shirt item from Cloud Bureau, created for the local mirror with deterministic colors and edition notes.','Standard artist tee with benchmark-ready sizing.','images/merch/cloud-bureau-tour-tee.svg',30.0,129,'2025-07-23',1); +INSERT INTO "merch_items" VALUES(76,38,NULL,'Cloud Bureau Carry Tote','cloud-bureau-carry-tote','tote','Cloud Bureau Carry Tote is a tote item from Cloud Bureau, created for the local mirror with deterministic colors and edition notes.','Canvas tote with scene-specific line work.','images/merch/cloud-bureau-carry-tote.svg',22.0,135,'2025-07-28',0); +INSERT INTO "merch_items" VALUES(77,39,NULL,'Ridge Cinema Tour Tee','ridge-cinema-tour-tee','shirt','Ridge Cinema Tour Tee is a shirt item from Ridge Cinema, created for the local mirror with deterministic colors and edition notes.','Standard artist tee with benchmark-ready sizing.','images/merch/ridge-cinema-tour-tee.svg',31.0,131,'2026-08-28',1); +INSERT INTO "merch_items" VALUES(78,39,NULL,'Ridge Cinema Carry Tote','ridge-cinema-carry-tote','tote','Ridge Cinema Carry Tote is a tote item from Ridge Cinema, created for the local mirror with deterministic colors and edition notes.','Canvas tote with scene-specific line work.','images/merch/ridge-cinema-carry-tote.svg',23.0,137,'2026-09-02',0); +INSERT INTO "merch_items" VALUES(79,40,NULL,'Hollow Method Tour Tee','hollow-method-tour-tee','shirt','Hollow Method Tour Tee is a shirt item from Hollow Method, created for the local mirror with deterministic colors and edition notes.','Standard artist tee with benchmark-ready sizing.','images/merch/hollow-method-tour-tee.svg',32.0,133,'2026-05-27',1); +INSERT INTO "merch_items" VALUES(80,40,NULL,'Hollow Method Carry Tote','hollow-method-carry-tote','tote','Hollow Method Carry Tote is a tote item from Hollow Method, created for the local mirror with deterministic colors and edition notes.','Canvas tote with scene-specific line work.','images/merch/hollow-method-carry-tote.svg',21.0,139,'2026-06-01',0); +CREATE TABLE order_items ( + id INTEGER NOT NULL, + order_id INTEGER NOT NULL, + album_id INTEGER, + merch_item_id INTEGER, + format_variant_id INTEGER, + title VARCHAR(200) NOT NULL, + artist_name VARCHAR(140), + image_path VARCHAR(255), + variant_label VARCHAR(160), + quantity INTEGER, + unit_price FLOAT, + PRIMARY KEY (id), + FOREIGN KEY(order_id) REFERENCES orders (id), + FOREIGN KEY(album_id) REFERENCES albums (id), + FOREIGN KEY(merch_item_id) REFERENCES merch_items (id), + FOREIGN KEY(format_variant_id) REFERENCES format_variants (id) +); +INSERT INTO "order_items" VALUES(1,1,13,NULL,64,'Blue Hour Broadcast','Velvet Avenue','images/covers/blue-hour-broadcast.svg','Colored Vinyl · 12-inch · Ocean Blue',1,25.0); +INSERT INTO "order_items" VALUES(2,1,NULL,13,70,'Velvet Avenue Night Shift Poster','Velvet Avenue','images/merch/velvet-avenue-night-shift-poster.svg','Signed · 18x24',1,27.0); +INSERT INTO "order_items" VALUES(3,2,1,NULL,1,'Tidal Memory','Neon Harbor','images/covers/tidal-memory.svg','Digital Album · MP3 + FLAC',1,8.5); +INSERT INTO "order_items" VALUES(4,3,11,NULL,55,'Signal Debt','Cinder Plaza','images/covers/signal-debt.svg','Digital Album · MP3 + FLAC',1,8.5); +INSERT INTO "order_items" VALUES(5,3,21,NULL,108,'Resin Language','Mono Shrine','images/covers/resin-language.svg','Cassette · Transparent Shell',1,15.0); +INSERT INTO "order_items" VALUES(6,4,7,NULL,35,'Between Stations','Soft Locale','images/covers/between-stations.svg','Cassette · Transparent Shell',1,14.5); +INSERT INTO "order_items" VALUES(7,4,NULL,7,40,'Soft Locale Drift Hoodie','Soft Locale','images/merch/soft-locale-drift-hoodie.svg','Drift Hoodie · M · Stone',1,48.0); +INSERT INTO "order_items" VALUES(8,5,19,NULL,97,'Elastic Hearts','Fever Arcade','images/covers/elastic-hearts.svg','Compact Disc · Gatefold',1,17.0); +INSERT INTO "order_items" VALUES(9,5,NULL,15,79,'Salt Meadow Field Notes Tote','Salt Meadow','images/merch/salt-meadow-field-notes-tote.svg','Field Notes Tote · Natural',1,22.0); +CREATE TABLE orders ( + id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + order_number VARCHAR(60) NOT NULL, + status VARCHAR(40), + subtotal FLOAT, + shipping FLOAT, + tax FLOAT, + total FLOAT, + shipping_name VARCHAR(140), + shipping_line1 VARCHAR(200), + shipping_city VARCHAR(120), + shipping_country VARCHAR(120), + payment_label VARCHAR(120), + note VARCHAR(240), + placed_at DATETIME, + PRIMARY KEY (id), + FOREIGN KEY(user_id) REFERENCES users (id), + UNIQUE (order_number) +); +INSERT INTO "orders" VALUES(1,1,'BC-20260414-0001','delivered',52.0,0.0,4.29,56.29,'Alice Johnson','128 Lake Union Ave','Seattle','United States','Visa ending in 4242','Deterministic benchmark seed order.','2026-04-14 12:00:00.000000'); +INSERT INTO "orders" VALUES(2,1,'BC-20260426-0002','processing',8.5,6.5,0.7,15.7,'Alice Johnson','128 Lake Union Ave','Seattle','United States','Visa ending in 1177','Deterministic benchmark seed order.','2026-04-26 12:00:00.000000'); +INSERT INTO "orders" VALUES(3,2,'BC-20260409-0003','delivered',23.5,6.5,1.94,31.94,'Bob Chen','77 Fulton Market','Chicago','United States','Mastercard ending in 7788','Deterministic benchmark seed order.','2026-04-09 12:00:00.000000'); +INSERT INTO "orders" VALUES(4,3,'BC-20260421-0004','delivered',62.5,0.0,5.16,67.66,'Carol Davis','55 Bergen St','Brooklyn','United States','Visa ending in 9901','Deterministic benchmark seed order.','2026-04-21 12:00:00.000000'); +INSERT INTO "orders" VALUES(5,4,'BC-20260428-0005','shipped',39.0,6.5,3.22,48.72,'David Kim','204 Burnside St','Portland','United States','Amex ending in 3401','Deterministic benchmark seed order.','2026-04-28 12:00:00.000000'); +CREATE TABLE scenes ( + id INTEGER NOT NULL, + name VARCHAR(120) NOT NULL, + slug VARCHAR(120) NOT NULL, + country VARCHAR(80), + description TEXT, + PRIMARY KEY (id), + UNIQUE (name) +); +INSERT INTO "scenes" VALUES(1,'Los Angeles, United States','los-angeles-united-states','United States','Sun-bleached studios, late-night FM nostalgia, and movie-score sheen.'); +INSERT INTO "scenes" VALUES(2,'Berlin, Germany','berlin-germany','Germany','Dub chambers, warehouse drums, and experimental club cross-pollination.'); +INSERT INTO "scenes" VALUES(3,'Tokyo, Japan','tokyo-japan','Japan','Compact detail, commuter ambience, and hyper-precise sound design.'); +INSERT INTO "scenes" VALUES(4,'London, United Kingdom','london-united-kingdom','United Kingdom','Independent label culture with left turns into post-punk, jazz, and pop.'); +INSERT INTO "scenes" VALUES(5,'New York, United States','new-york-united-states','United States','Small-room virtuosity, art-school hooks, and downtown improvisation.'); +INSERT INTO "scenes" VALUES(6,'Melbourne, Australia','melbourne-australia','Australia','Open-hearted songwriting, DIY scenes, and tactile physical editions.'); +INSERT INTO "scenes" VALUES(7,'Sao Paulo, Brazil','sao-paulo-brazil','Brazil','Percussive movement, raw punk energy, and bright visual identity.'); +INSERT INTO "scenes" VALUES(8,'Detroit, United States','detroit-united-states','United States','Machine rhythm, soul memory, and durable underground infrastructure.'); +INSERT INTO "scenes" VALUES(9,'Paris, France','paris-france','France','Elegant arrangements, metallic tension, and art-book presentation.'); +CREATE TABLE tags ( + id INTEGER NOT NULL, + name VARCHAR(80) NOT NULL, + slug VARCHAR(80) NOT NULL, + PRIMARY KEY (id), + UNIQUE (name) +); +INSERT INTO "tags" VALUES(1,'dub techno','dub-techno'); +INSERT INTO "tags" VALUES(2,'afterhours','afterhours'); +INSERT INTO "tags" VALUES(3,'submerged','submerged'); +INSERT INTO "tags" VALUES(4,'Berlin','berlin'); +INSERT INTO "tags" VALUES(5,'deep groove','deep-groove'); +INSERT INTO "tags" VALUES(6,'modular','modular'); +INSERT INTO "tags" VALUES(7,'late deck','late-deck'); +INSERT INTO "tags" VALUES(8,'dream pop','dream-pop'); +INSERT INTO "tags" VALUES(9,'reverb','reverb'); +INSERT INTO "tags" VALUES(10,'overcast hooks','overcast-hooks'); +INSERT INTO "tags" VALUES(11,'London','london'); +INSERT INTO "tags" VALUES(12,'shoegaze','shoegaze'); +INSERT INTO "tags" VALUES(13,'jangle','jangle'); +INSERT INTO "tags" VALUES(14,'bedroom','bedroom'); +INSERT INTO "tags" VALUES(15,'warehouse','warehouse'); +INSERT INTO "tags" VALUES(16,'analog','analog'); +INSERT INTO "tags" VALUES(17,'strobe','strobe'); +INSERT INTO "tags" VALUES(18,'Detroit','detroit'); +INSERT INTO "tags" VALUES(19,'acid','acid'); +INSERT INTO "tags" VALUES(20,'tool track','tool-track'); +INSERT INTO "tags" VALUES(21,'ferrous','ferrous'); +INSERT INTO "tags" VALUES(22,'commuter','commuter'); +INSERT INTO "tags" VALUES(23,'late train','late-train'); +INSERT INTO "tags" VALUES(24,'sleep tape','sleep-tape'); +INSERT INTO "tags" VALUES(25,'Tokyo','tokyo'); +INSERT INTO "tags" VALUES(26,'drone','drone'); +INSERT INTO "tags" VALUES(27,'meditation','meditation'); +INSERT INTO "tags" VALUES(28,'field recordings','field-recordings'); +INSERT INTO "tags" VALUES(29,'d-beat','d-beat'); +INSERT INTO "tags" VALUES(30,'DIY','diy'); +INSERT INTO "tags" VALUES(31,'street flyer','street-flyer'); +INSERT INTO "tags" VALUES(32,'Sao Paulo','sao-paulo'); +INSERT INTO "tags" VALUES(33,'basement','basement'); +INSERT INTO "tags" VALUES(34,'agitprop','agitprop'); +INSERT INTO "tags" VALUES(35,'sprint','sprint'); +INSERT INTO "tags" VALUES(36,'lyric sheet','lyric-sheet'); +INSERT INTO "tags" VALUES(37,'left field','left-field'); +INSERT INTO "tags" VALUES(38,'city pressure','city-pressure'); +INSERT INTO "tags" VALUES(39,'Los Angeles','los-angeles'); +INSERT INTO "tags" VALUES(40,'jazz rap','jazz-rap'); +INSERT INTO "tags" VALUES(41,'loop heavy','loop-heavy'); +INSERT INTO "tags" VALUES(42,'basement tape','basement-tape'); +INSERT INTO "tags" VALUES(43,'late set','late-set'); +INSERT INTO "tags" VALUES(44,'trio','trio'); +INSERT INTO "tags" VALUES(45,'blue room','blue-room'); +INSERT INTO "tags" VALUES(46,'New York','new-york'); +INSERT INTO "tags" VALUES(47,'modal','modal'); +INSERT INTO "tags" VALUES(48,'horn blend','horn-blend'); +INSERT INTO "tags" VALUES(49,'improv','improv'); +INSERT INTO "tags" VALUES(50,'story song','story-song'); +INSERT INTO "tags" VALUES(51,'field note','field-note'); +INSERT INTO "tags" VALUES(52,'river road','river-road'); +INSERT INTO "tags" VALUES(53,'Melbourne','melbourne'); +INSERT INTO "tags" VALUES(54,'americana','americana'); +INSERT INTO "tags" VALUES(55,'soft harmonies','soft-harmonies'); +INSERT INTO "tags" VALUES(56,'slow weather','slow-weather'); +INSERT INTO "tags" VALUES(57,'doom','doom'); +INSERT INTO "tags" VALUES(58,'ritual','ritual'); +INSERT INTO "tags" VALUES(59,'cathedral reverb','cathedral-reverb'); +INSERT INTO "tags" VALUES(60,'Paris','paris'); +INSERT INTO "tags" VALUES(61,'blackened','blackened'); +INSERT INTO "tags" VALUES(62,'ash cloud','ash-cloud'); +INSERT INTO "tags" VALUES(63,'blast beat','blast-beat'); +INSERT INTO "tags" VALUES(64,'hook','hook'); +INSERT INTO "tags" VALUES(65,'night drive','night-drive'); +INSERT INTO "tags" VALUES(66,'bright chorus','bright-chorus'); +INSERT INTO "tags" VALUES(67,'synth pop','synth-pop'); +INSERT INTO "tags" VALUES(68,'gloss','gloss'); +INSERT INTO "tags" VALUES(69,'dancefloor','dancefloor'); +INSERT INTO "tags" VALUES(70,'collage','collage'); +INSERT INTO "tags" VALUES(71,'tape hiss','tape-hiss'); +INSERT INTO "tags" VALUES(72,'microtone','microtone'); +INSERT INTO "tags" VALUES(73,'glitch','glitch'); +INSERT INTO "tags" VALUES(74,'avant pop','avant-pop'); +INSERT INTO "tags" VALUES(75,'noise drift','noise-drift'); +INSERT INTO "tags" VALUES(76,'psych rock','psych-rock'); +INSERT INTO "tags" VALUES(77,'motorik','motorik'); +INSERT INTO "tags" VALUES(78,'widescreen','widescreen'); +INSERT INTO "tags" VALUES(79,'garage','garage'); +INSERT INTO "tags" VALUES(80,'riff driven','riff-driven'); +INSERT INTO "tags" VALUES(81,'festival ready','festival-ready'); +INSERT INTO "tags" VALUES(82,'drum machine','drum-machine'); +INSERT INTO "tags" VALUES(83,'indie rock','indie-rock'); +INSERT INTO "tags" VALUES(84,'burnt amp','burnt-amp'); +INSERT INTO "tags" VALUES(85,'soundscape','soundscape'); +INSERT INTO "tags" VALUES(86,'boom bap','boom-bap'); +INSERT INTO "tags" VALUES(87,'spiritual','spiritual'); +INSERT INTO "tags" VALUES(88,'acoustic','acoustic'); +INSERT INTO "tags" VALUES(89,'heartbreak','heartbreak'); +INSERT INTO "tags" VALUES(90,'four on the floor','four-on-the-floor'); +CREATE TABLE tracks ( + id INTEGER NOT NULL, + album_id INTEGER NOT NULL, + title VARCHAR(160) NOT NULL, + slug VARCHAR(180) NOT NULL, + track_number INTEGER NOT NULL, + duration_seconds INTEGER, + preview_hook VARCHAR(255), + lyrics_excerpt TEXT, + is_focus_track BOOLEAN, + PRIMARY KEY (id), + FOREIGN KEY(album_id) REFERENCES albums (id) +); +INSERT INTO "tracks" VALUES(1,1,'Incoming Tide','tidal-memory-incoming-tide',1,184,'Incoming Tide is the preview focus from Tidal Memory.','Incoming Tide traces the emotional contour of tidal memory in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(2,1,'Mooring Light','tidal-memory-mooring-light',2,213,'Mooring Light is the preview focus from Tidal Memory.','Mooring Light traces the emotional contour of tidal memory in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(3,1,'Breakwater','tidal-memory-breakwater',3,242,'Breakwater is the preview focus from Tidal Memory.','Breakwater traces the emotional contour of tidal memory in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(4,1,'Low Pier','tidal-memory-low-pier',4,271,'Low Pier is the preview focus from Tidal Memory.','Low Pier traces the emotional contour of tidal memory in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(5,1,'Morning Channel','tidal-memory-morning-channel',5,300,'Morning Channel is the preview focus from Tidal Memory.','Morning Channel traces the emotional contour of tidal memory in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(6,2,'Platform Sleep','night-ferry-platform-sleep',1,205,'Platform Sleep is the preview focus from Night Ferry.','Platform Sleep traces the emotional contour of night ferry in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(7,2,'Signal Two','night-ferry-signal-two',2,234,'Signal Two is the preview focus from Night Ferry.','Signal Two traces the emotional contour of night ferry in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(8,2,'Dock Exchange','night-ferry-dock-exchange',3,263,'Dock Exchange is the preview focus from Night Ferry.','Dock Exchange traces the emotional contour of night ferry in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(9,2,'Wake Window','night-ferry-wake-window',4,292,'Wake Window is the preview focus from Night Ferry.','Wake Window traces the emotional contour of night ferry in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(10,2,'Westbound Static','night-ferry-westbound-static',5,321,'Westbound Static is the preview focus from Night Ferry.','Westbound Static traces the emotional contour of night ferry in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(11,3,'Glasshouse Lobby','static-bloom-glasshouse-lobby',1,197,'Glasshouse Lobby is the preview focus from Static Bloom.','Glasshouse Lobby traces the emotional contour of static bloom in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(12,3,'Northbound Blue','static-bloom-northbound-blue',2,226,'Northbound Blue is the preview focus from Static Bloom.','Northbound Blue traces the emotional contour of static bloom in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(13,3,'Tin Roof Weather','static-bloom-tin-roof-weather',3,255,'Tin Roof Weather is the preview focus from Static Bloom.','Tin Roof Weather traces the emotional contour of static bloom in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(14,3,'Run the Balcony','static-bloom-run-the-balcony',4,284,'Run the Balcony is the preview focus from Static Bloom.','Run the Balcony traces the emotional contour of static bloom in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(15,3,'Static Bloom','static-bloom-static-bloom',5,313,'Static Bloom is the preview focus from Static Bloom.','Static Bloom traces the emotional contour of static bloom in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(16,4,'Sunday Turnstile','paper-signal-sunday-turnstile',1,218,'Sunday Turnstile is the preview focus from Paper Signal.','Sunday Turnstile traces the emotional contour of paper signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(17,4,'Fluorescent Map','paper-signal-fluorescent-map',2,247,'Fluorescent Map is the preview focus from Paper Signal.','Fluorescent Map traces the emotional contour of paper signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(18,4,'Paper Signal','paper-signal-paper-signal',3,276,'Paper Signal is the preview focus from Paper Signal.','Paper Signal traces the emotional contour of paper signal in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(19,4,'Taxi Dust','paper-signal-taxi-dust',4,305,'Taxi Dust is the preview focus from Paper Signal.','Taxi Dust traces the emotional contour of paper signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(20,4,'Window Figures','paper-signal-window-figures',5,164,'Window Figures is the preview focus from Paper Signal.','Window Figures traces the emotional contour of paper signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(21,5,'Factory Dawn','redline-ritual-factory-dawn',1,210,'Factory Dawn is the preview focus from Redline Ritual.','Factory Dawn traces the emotional contour of redline ritual in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(22,5,'Heater Coil','redline-ritual-heater-coil',2,239,'Heater Coil is the preview focus from Redline Ritual.','Heater Coil traces the emotional contour of redline ritual in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(23,5,'Redline Ritual','redline-ritual-redline-ritual',3,268,'Redline Ritual is the preview focus from Redline Ritual.','Redline Ritual traces the emotional contour of redline ritual in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(24,5,'Locked Loop','redline-ritual-locked-loop',4,297,'Locked Loop is the preview focus from Redline Ritual.','Locked Loop traces the emotional contour of redline ritual in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(25,5,'Night Shift Press','redline-ritual-night-shift-press',5,156,'Night Shift Press is the preview focus from Redline Ritual.','Night Shift Press traces the emotional contour of redline ritual in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(26,6,'Servo Chant','machine-prayer-servo-chant',1,231,'Servo Chant is the preview focus from Machine Prayer.','Servo Chant traces the emotional contour of machine prayer in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(27,6,'Welded Saints','machine-prayer-welded-saints',2,260,'Welded Saints is the preview focus from Machine Prayer.','Welded Saints traces the emotional contour of machine prayer in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(28,6,'Machine Prayer','machine-prayer-machine-prayer',3,289,'Machine Prayer is the preview focus from Machine Prayer.','Machine Prayer traces the emotional contour of machine prayer in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(29,6,'Bulkhead Glow','machine-prayer-bulkhead-glow',4,318,'Bulkhead Glow is the preview focus from Machine Prayer.','Bulkhead Glow traces the emotional contour of machine prayer in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(30,6,'After Conveyor','machine-prayer-after-conveyor',5,177,'After Conveyor is the preview focus from Machine Prayer.','After Conveyor traces the emotional contour of machine prayer in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(31,7,'Transfer Bell','between-stations-transfer-bell',1,223,'Transfer Bell is the preview focus from Between Stations.','Transfer Bell traces the emotional contour of between stations in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(32,7,'Quiet Platform','between-stations-quiet-platform',2,252,'Quiet Platform is the preview focus from Between Stations.','Quiet Platform traces the emotional contour of between stations in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(33,7,'Between Stations','between-stations-between-stations',3,281,'Between Stations is the preview focus from Between Stations.','Between Stations traces the emotional contour of between stations in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(34,7,'River Line','between-stations-river-line',4,310,'River Line is the preview focus from Between Stations.','River Line traces the emotional contour of between stations in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(35,7,'Window Heat','between-stations-window-heat',5,169,'Window Heat is the preview focus from Between Stations.','Window Heat traces the emotional contour of between stations in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(36,8,'Pocket Lantern','sleep-maps-pocket-lantern',1,244,'Pocket Lantern is the preview focus from Sleep Maps.','Pocket Lantern traces the emotional contour of sleep maps in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(37,8,'Hallway Air','sleep-maps-hallway-air',2,273,'Hallway Air is the preview focus from Sleep Maps.','Hallway Air traces the emotional contour of sleep maps in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(38,8,'Sleep Maps','sleep-maps-sleep-maps',3,302,'Sleep Maps is the preview focus from Sleep Maps.','Sleep Maps traces the emotional contour of sleep maps in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(39,8,'Paper Screen','sleep-maps-paper-screen',4,161,'Paper Screen is the preview focus from Sleep Maps.','Paper Screen traces the emotional contour of sleep maps in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(40,8,'End of Service','sleep-maps-end-of-service',5,190,'End of Service is the preview focus from Sleep Maps.','End of Service traces the emotional contour of sleep maps in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(41,9,'Bus Lane','concrete-carnival-bus-lane',1,236,'Bus Lane is the preview focus from Concrete Carnival.','Bus Lane traces the emotional contour of concrete carnival in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(42,9,'Concrete Carnival','concrete-carnival-concrete-carnival',2,265,'Concrete Carnival is the preview focus from Concrete Carnival.','Concrete Carnival traces the emotional contour of concrete carnival in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(43,9,'Sticker Wall','concrete-carnival-sticker-wall',3,294,'Sticker Wall is the preview focus from Concrete Carnival.','Sticker Wall traces the emotional contour of concrete carnival in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(44,9,'No Permit','concrete-carnival-no-permit',4,323,'No Permit is the preview focus from Concrete Carnival.','No Permit traces the emotional contour of concrete carnival in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(45,9,'Two Minute Exit','concrete-carnival-two-minute-exit',5,182,'Two Minute Exit is the preview focus from Concrete Carnival.','Two Minute Exit traces the emotional contour of concrete carnival in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(46,10,'Median Strip','siren-economy-median-strip',1,257,'Median Strip is the preview focus from Siren Economy.','Median Strip traces the emotional contour of siren economy in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(47,10,'Siren Economy','siren-economy-siren-economy',2,286,'Siren Economy is the preview focus from Siren Economy.','Siren Economy traces the emotional contour of siren economy in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(48,10,'Paper Badge','siren-economy-paper-badge',3,315,'Paper Badge is the preview focus from Siren Economy.','Paper Badge traces the emotional contour of siren economy in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(49,10,'Turn the Bolts','siren-economy-turn-the-bolts',4,174,'Turn the Bolts is the preview focus from Siren Economy.','Turn the Bolts traces the emotional contour of siren economy in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(50,10,'Close the Gate','siren-economy-close-the-gate',5,203,'Close the Gate is the preview focus from Siren Economy.','Close the Gate traces the emotional contour of siren economy in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(51,11,'Signal Debt','signal-debt-signal-debt',1,249,'Signal Debt is the preview focus from Signal Debt.','Signal Debt traces the emotional contour of signal debt in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(52,11,'Median Palm','signal-debt-median-palm',2,278,'Median Palm is the preview focus from Signal Debt.','Median Palm traces the emotional contour of signal debt in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(53,11,'Overpass Dialtone','signal-debt-overpass-dialtone',3,307,'Overpass Dialtone is the preview focus from Signal Debt.','Overpass Dialtone traces the emotional contour of signal debt in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(54,11,'Small Claims','signal-debt-small-claims',4,166,'Small Claims is the preview focus from Signal Debt.','Small Claims traces the emotional contour of signal debt in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(55,11,'Interchange Prayer','signal-debt-interchange-prayer',5,195,'Interchange Prayer is the preview focus from Signal Debt.','Interchange Prayer traces the emotional contour of signal debt in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(56,12,'Rollout Plan','blueprint-fever-rollout-plan',1,270,'Rollout Plan is the preview focus from Blueprint Fever.','Rollout Plan traces the emotional contour of blueprint fever in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(57,12,'Blueprint Fever','blueprint-fever-blueprint-fever',2,299,'Blueprint Fever is the preview focus from Blueprint Fever.','Blueprint Fever traces the emotional contour of blueprint fever in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(58,12,'Room Tone','blueprint-fever-room-tone',3,158,'Room Tone is the preview focus from Blueprint Fever.','Room Tone traces the emotional contour of blueprint fever in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(59,12,'Silver Marker','blueprint-fever-silver-marker',4,187,'Silver Marker is the preview focus from Blueprint Fever.','Silver Marker traces the emotional contour of blueprint fever in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(60,12,'Exit Column','blueprint-fever-exit-column',5,216,'Exit Column is the preview focus from Blueprint Fever.','Exit Column traces the emotional contour of blueprint fever in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(61,13,'Blue Hour Broadcast','blue-hour-broadcast-blue-hour-broadcast',1,262,'Blue Hour Broadcast is the preview focus from Blue Hour Broadcast.','Blue Hour Broadcast traces the emotional contour of blue hour broadcast in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(62,13,'Canal Echo','blue-hour-broadcast-canal-echo',2,291,'Canal Echo is the preview focus from Blue Hour Broadcast.','Canal Echo traces the emotional contour of blue hour broadcast in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(63,13,'Fifth Table','blue-hour-broadcast-fifth-table',3,320,'Fifth Table is the preview focus from Blue Hour Broadcast.','Fifth Table traces the emotional contour of blue hour broadcast in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(64,13,'Smoke Ladder','blue-hour-broadcast-smoke-ladder',4,179,'Smoke Ladder is the preview focus from Blue Hour Broadcast.','Smoke Ladder traces the emotional contour of blue hour broadcast in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(65,13,'After Set Receipt','blue-hour-broadcast-after-set-receipt',5,208,'After Set Receipt is the preview focus from Blue Hour Broadcast.','After Set Receipt traces the emotional contour of blue hour broadcast in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(66,14,'Lobby Mirage','lobby-mirage-lobby-mirage',1,283,'Lobby Mirage is the preview focus from Lobby Mirage.','Lobby Mirage traces the emotional contour of lobby mirage in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(67,14,'Quarter Note Rain','lobby-mirage-quarter-note-rain',2,312,'Quarter Note Rain is the preview focus from Lobby Mirage.','Quarter Note Rain traces the emotional contour of lobby mirage in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(68,14,'Overnight Guest','lobby-mirage-overnight-guest',3,171,'Overnight Guest is the preview focus from Lobby Mirage.','Overnight Guest traces the emotional contour of lobby mirage in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(69,14,'Stairwell Vibraphone','lobby-mirage-stairwell-vibraphone',4,200,'Stairwell Vibraphone is the preview focus from Lobby Mirage.','Stairwell Vibraphone traces the emotional contour of lobby mirage in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(70,14,'Quiet Receipt','lobby-mirage-quiet-receipt',5,229,'Quiet Receipt is the preview focus from Lobby Mirage.','Quiet Receipt traces the emotional contour of lobby mirage in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(71,15,'Riverlights','riverlights-riverlights',1,275,'Riverlights is the preview focus from Riverlights.','Riverlights traces the emotional contour of riverlights in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(72,15,'Fence Post August','riverlights-fence-post-august',2,304,'Fence Post August is the preview focus from Riverlights.','Fence Post August traces the emotional contour of riverlights in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(73,15,'Borrowed Kettle','riverlights-borrowed-kettle',3,163,'Borrowed Kettle is the preview focus from Riverlights.','Borrowed Kettle traces the emotional contour of riverlights in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(74,15,'Downwind Choir','riverlights-downwind-choir',4,192,'Downwind Choir is the preview focus from Riverlights.','Downwind Choir traces the emotional contour of riverlights in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(75,15,'Common Thread','riverlights-common-thread',5,221,'Common Thread is the preview focus from Riverlights.','Common Thread traces the emotional contour of riverlights in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(76,16,'Handrail','common-thread-handrail',1,296,'Handrail is the preview focus from Common Thread.','Handrail traces the emotional contour of common thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(77,16,'Common Thread','common-thread-common-thread',2,155,'Common Thread is the preview focus from Common Thread.','Common Thread traces the emotional contour of common thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(78,16,'Old Union Hall','common-thread-old-union-hall',3,184,'Old Union Hall is the preview focus from Common Thread.','Old Union Hall traces the emotional contour of common thread in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(79,16,'Weather Note','common-thread-weather-note',4,213,'Weather Note is the preview focus from Common Thread.','Weather Note traces the emotional contour of common thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(80,16,'West Creek','common-thread-west-creek',5,242,'West Creek is the preview focus from Common Thread.','West Creek traces the emotional contour of common thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(81,17,'Bell Ash','iron-sleep-bell-ash',1,288,'Bell Ash is the preview focus from Iron Sleep.','Bell Ash traces the emotional contour of iron sleep in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(82,17,'Iron Sleep','iron-sleep-iron-sleep',2,317,'Iron Sleep is the preview focus from Iron Sleep.','Iron Sleep traces the emotional contour of iron sleep in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(83,17,'Procession Stair','iron-sleep-procession-stair',3,176,'Procession Stair is the preview focus from Iron Sleep.','Procession Stair traces the emotional contour of iron sleep in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(84,17,'Shutter Psalm','iron-sleep-shutter-psalm',4,205,'Shutter Psalm is the preview focus from Iron Sleep.','Shutter Psalm traces the emotional contour of iron sleep in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(85,17,'Ember Chapel','iron-sleep-ember-chapel',5,234,'Ember Chapel is the preview focus from Iron Sleep.','Ember Chapel traces the emotional contour of iron sleep in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(86,18,'Noisework','saint-of-noise-noisework',1,309,'Noisework is the preview focus from Saint of Noise.','Noisework traces the emotional contour of saint of noise in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(87,18,'Saint of Noise','saint-of-noise-saint-of-noise',2,168,'Saint of Noise is the preview focus from Saint of Noise.','Saint of Noise traces the emotional contour of saint of noise in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(88,18,'Window Soot','saint-of-noise-window-soot',3,197,'Window Soot is the preview focus from Saint of Noise.','Window Soot traces the emotional contour of saint of noise in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(89,18,'Tower Rain','saint-of-noise-tower-rain',4,226,'Tower Rain is the preview focus from Saint of Noise.','Tower Rain traces the emotional contour of saint of noise in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(90,18,'Rust Halo','saint-of-noise-rust-halo',5,255,'Rust Halo is the preview focus from Saint of Noise.','Rust Halo traces the emotional contour of saint of noise in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(91,19,'Elastic Hearts','elastic-hearts-elastic-hearts',1,301,'Elastic Hearts is the preview focus from Elastic Hearts.','Elastic Hearts traces the emotional contour of elastic hearts in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(92,19,'Flicker Map','elastic-hearts-flicker-map',2,160,'Flicker Map is the preview focus from Elastic Hearts.','Flicker Map traces the emotional contour of elastic hearts in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(93,19,'Checkout Lights','elastic-hearts-checkout-lights',3,189,'Checkout Lights is the preview focus from Elastic Hearts.','Checkout Lights traces the emotional contour of elastic hearts in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(94,19,'Half Fare','elastic-hearts-half-fare',4,218,'Half Fare is the preview focus from Elastic Hearts.','Half Fare traces the emotional contour of elastic hearts in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(95,19,'Aftercare FM','elastic-hearts-aftercare-fm',5,247,'Aftercare FM is the preview focus from Elastic Hearts.','Aftercare FM traces the emotional contour of elastic hearts in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(96,20,'Mirror Mosaic','mirror-mosaic-mirror-mosaic',1,322,'Mirror Mosaic is the preview focus from Mirror Mosaic.','Mirror Mosaic traces the emotional contour of mirror mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(97,20,'South Loop','mirror-mosaic-south-loop',2,181,'South Loop is the preview focus from Mirror Mosaic.','South Loop traces the emotional contour of mirror mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(98,20,'Cab Floor Glitter','mirror-mosaic-cab-floor-glitter',3,210,'Cab Floor Glitter is the preview focus from Mirror Mosaic.','Cab Floor Glitter traces the emotional contour of mirror mosaic in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(99,20,'Rain Delay','mirror-mosaic-rain-delay',4,239,'Rain Delay is the preview focus from Mirror Mosaic.','Rain Delay traces the emotional contour of mirror mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(100,20,'Window Waltz','mirror-mosaic-window-waltz',5,268,'Window Waltz is the preview focus from Mirror Mosaic.','Window Waltz traces the emotional contour of mirror mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(101,21,'Resin Language','resin-language-resin-language',1,314,'Resin Language is the preview focus from Resin Language.','Resin Language traces the emotional contour of resin language in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(102,21,'Circuit Teeth','resin-language-circuit-teeth',2,173,'Circuit Teeth is the preview focus from Resin Language.','Circuit Teeth traces the emotional contour of resin language in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(103,21,'Broken Caption','resin-language-broken-caption',3,202,'Broken Caption is the preview focus from Resin Language.','Broken Caption traces the emotional contour of resin language in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(104,21,'Choir Exit','resin-language-choir-exit',4,231,'Choir Exit is the preview focus from Resin Language.','Choir Exit traces the emotional contour of resin language in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(105,21,'Warm Static','resin-language-warm-static',5,260,'Warm Static is the preview focus from Resin Language.','Warm Static traces the emotional contour of resin language in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(106,22,'Fault Choir','fault-choir-fault-choir',1,165,'Fault Choir is the preview focus from Fault Choir.','Fault Choir traces the emotional contour of fault choir in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(107,22,'Signal Prayer','fault-choir-signal-prayer',2,194,'Signal Prayer is the preview focus from Fault Choir.','Signal Prayer traces the emotional contour of fault choir in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(108,22,'Paper Mouth','fault-choir-paper-mouth',3,223,'Paper Mouth is the preview focus from Fault Choir.','Paper Mouth traces the emotional contour of fault choir in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(109,22,'Room Dust','fault-choir-room-dust',4,252,'Room Dust is the preview focus from Fault Choir.','Room Dust traces the emotional contour of fault choir in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(110,22,'Small Collapse','fault-choir-small-collapse',5,281,'Small Collapse is the preview focus from Fault Choir.','Small Collapse traces the emotional contour of fault choir in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(111,23,'Harbor Burn','harbor-burn-harbor-burn',1,157,'Harbor Burn is the preview focus from Harbor Burn.','Harbor Burn traces the emotional contour of harbor burn in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(112,23,'Median Gold','harbor-burn-median-gold',2,186,'Median Gold is the preview focus from Harbor Burn.','Median Gold traces the emotional contour of harbor burn in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(113,23,'Glass Toll','harbor-burn-glass-toll',3,215,'Glass Toll is the preview focus from Harbor Burn.','Glass Toll traces the emotional contour of harbor burn in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(114,23,'Breaker Motel','harbor-burn-breaker-motel',4,244,'Breaker Motel is the preview focus from Harbor Burn.','Breaker Motel traces the emotional contour of harbor burn in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(115,23,'Wide Exit','harbor-burn-wide-exit',5,273,'Wide Exit is the preview focus from Harbor Burn.','Wide Exit traces the emotional contour of harbor burn in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(116,24,'Quiet Engine','quiet-engine-quiet-engine',1,178,'Quiet Engine is the preview focus from Quiet Engine.','Quiet Engine traces the emotional contour of quiet engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(117,24,'Chrome Valley','quiet-engine-chrome-valley',2,207,'Chrome Valley is the preview focus from Quiet Engine.','Chrome Valley traces the emotional contour of quiet engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(118,24,'Heat Map','quiet-engine-heat-map',3,236,'Heat Map is the preview focus from Quiet Engine.','Heat Map traces the emotional contour of quiet engine in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(119,24,'August Freight','quiet-engine-august-freight',4,265,'August Freight is the preview focus from Quiet Engine.','August Freight traces the emotional contour of quiet engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(120,24,'Left Signal','quiet-engine-left-signal',5,294,'Left Signal is the preview focus from Quiet Engine.','Left Signal traces the emotional contour of quiet engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(121,25,'Static Weather','static-weather-static-weather',1,170,'Static Weather is the preview focus from Static Weather.','Static Weather traces the emotional contour of static weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(122,25,'Platform Map','static-weather-platform-map',2,199,'Platform Map is the preview focus from Static Weather.','Platform Map traces the emotional contour of static weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(123,25,'August Ledger','static-weather-august-ledger',3,228,'August Ledger is the preview focus from Static Weather.','August Ledger traces the emotional contour of static weather in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(124,25,'Pattern Glow','static-weather-pattern-glow',4,257,'Pattern Glow is the preview focus from Static Weather.','Pattern Glow traces the emotional contour of static weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(125,25,'Window Transit','static-weather-window-transit',5,286,'Window Transit is the preview focus from Static Weather.','Window Transit traces the emotional contour of static weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(126,26,'Anchor Weather','quiet-vector-anchor-weather',1,191,'Anchor Weather is the preview focus from Quiet Vector.','Anchor Weather traces the emotional contour of quiet vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(127,26,'Static Map','quiet-vector-static-map',2,220,'Static Map is the preview focus from Quiet Vector.','Static Map traces the emotional contour of quiet vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(128,26,'Platform Ledger','quiet-vector-platform-ledger',3,249,'Platform Ledger is the preview focus from Quiet Vector.','Platform Ledger traces the emotional contour of quiet vector in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(129,26,'August Glow','quiet-vector-august-glow',4,278,'August Glow is the preview focus from Quiet Vector.','August Glow traces the emotional contour of quiet vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(130,26,'Pattern Transit','quiet-vector-pattern-transit',5,307,'Pattern Transit is the preview focus from Quiet Vector.','Pattern Transit traces the emotional contour of quiet vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(131,27,'Transfer Note','harbor-ledger-transfer-note',1,183,'Transfer Note is the preview focus from Harbor Ledger.','Transfer Note traces the emotional contour of harbor ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(132,27,'Rain Line','harbor-ledger-rain-line',2,212,'Rain Line is the preview focus from Harbor Ledger.','Rain Line traces the emotional contour of harbor ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(133,27,'Invoice Receipt','harbor-ledger-invoice-receipt',3,241,'Invoice Receipt is the preview focus from Harbor Ledger.','Invoice Receipt traces the emotional contour of harbor ledger in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(134,27,'Current Dust','harbor-ledger-current-dust',4,270,'Current Dust is the preview focus from Harbor Ledger.','Current Dust traces the emotional contour of harbor ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(135,27,'Median Dialtone','harbor-ledger-median-dialtone',5,299,'Median Dialtone is the preview focus from Harbor Ledger.','Median Dialtone traces the emotional contour of harbor ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(136,28,'Anchor Map','paper-thread-anchor-map',1,204,'Anchor Map is the preview focus from Paper Thread.','Anchor Map traces the emotional contour of paper thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(137,28,'Static Ledger','paper-thread-static-ledger',2,233,'Static Ledger is the preview focus from Paper Thread.','Static Ledger traces the emotional contour of paper thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(138,28,'Platform Glow','paper-thread-platform-glow',3,262,'Platform Glow is the preview focus from Paper Thread.','Platform Glow traces the emotional contour of paper thread in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(139,28,'August Transit','paper-thread-august-transit',4,291,'August Transit is the preview focus from Paper Thread.','August Transit traces the emotional contour of paper thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(140,28,'Pattern Machine','paper-thread-pattern-machine',5,320,'Pattern Machine is the preview focus from Paper Thread.','Pattern Machine traces the emotional contour of paper thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(141,29,'Platform Glow','river-cinema-platform-glow',1,196,'Platform Glow is the preview focus from River Cinema.','Platform Glow traces the emotional contour of river cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(142,29,'August Transit','river-cinema-august-transit',2,225,'August Transit is the preview focus from River Cinema.','August Transit traces the emotional contour of river cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(143,29,'Pattern Machine','river-cinema-pattern-machine',3,254,'Pattern Machine is the preview focus from River Cinema.','Pattern Machine traces the emotional contour of river cinema in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(144,29,'Window Murmur','river-cinema-window-murmur',4,283,'Window Murmur is the preview focus from River Cinema.','Window Murmur traces the emotional contour of river cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(145,29,'Breaker Light','river-cinema-breaker-light',5,312,'Breaker Light is the preview focus from River Cinema.','Breaker Light traces the emotional contour of river cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(146,30,'Median Signal','chrome-parade-median-signal',1,217,'Median Signal is the preview focus from Chrome Parade.','Median Signal traces the emotional contour of chrome parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(147,30,'Receipt Weather','chrome-parade-receipt-weather',2,246,'Receipt Weather is the preview focus from Chrome Parade.','Receipt Weather traces the emotional contour of chrome parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(148,30,'Basin Map','chrome-parade-basin-map',3,275,'Basin Map is the preview focus from Chrome Parade.','Basin Map traces the emotional contour of chrome parade in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(149,30,'Anchor Ledger','chrome-parade-anchor-ledger',4,304,'Anchor Ledger is the preview focus from Chrome Parade.','Anchor Ledger traces the emotional contour of chrome parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(150,30,'Static Glow','chrome-parade-static-glow',5,163,'Static Glow is the preview focus from Chrome Parade.','Static Glow traces the emotional contour of chrome parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(151,31,'Receipt Weather','signal-method-receipt-weather',1,209,'Receipt Weather is the preview focus from Signal Method.','Receipt Weather traces the emotional contour of signal method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(152,31,'Basin Map','signal-method-basin-map',2,238,'Basin Map is the preview focus from Signal Method.','Basin Map traces the emotional contour of signal method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(153,31,'Anchor Ledger','signal-method-anchor-ledger',3,267,'Anchor Ledger is the preview focus from Signal Method.','Anchor Ledger traces the emotional contour of signal method in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(154,31,'Static Glow','signal-method-static-glow',4,296,'Static Glow is the preview focus from Signal Method.','Static Glow traces the emotional contour of signal method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(155,31,'Platform Transit','signal-method-platform-transit',5,155,'Platform Transit is the preview focus from Signal Method.','Platform Transit traces the emotional contour of signal method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(156,32,'Transfer Dust','south-engine-transfer-dust',1,230,'Transfer Dust is the preview focus from South Engine.','Transfer Dust traces the emotional contour of south engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(157,32,'Rain Dialtone','south-engine-rain-dialtone',2,259,'Rain Dialtone is the preview focus from South Engine.','Rain Dialtone traces the emotional contour of south engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(158,32,'Invoice Index','south-engine-invoice-index',3,288,'Invoice Index is the preview focus from South Engine.','Invoice Index traces the emotional contour of south engine in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(159,32,'Current Signal','south-engine-current-signal',4,317,'Current Signal is the preview focus from South Engine.','Current Signal traces the emotional contour of south engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(160,32,'Median Weather','south-engine-median-weather',5,176,'Median Weather is the preview focus from South Engine.','Median Weather traces the emotional contour of south engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(161,33,'Transfer Dust','blue-current-transfer-dust',1,222,'Transfer Dust is the preview focus from Blue Current.','Transfer Dust traces the emotional contour of blue current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(162,33,'Rain Dialtone','blue-current-rain-dialtone',2,251,'Rain Dialtone is the preview focus from Blue Current.','Rain Dialtone traces the emotional contour of blue current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(163,33,'Invoice Index','blue-current-invoice-index',3,280,'Invoice Index is the preview focus from Blue Current.','Invoice Index traces the emotional contour of blue current in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(164,33,'Current Signal','blue-current-current-signal',4,309,'Current Signal is the preview focus from Blue Current.','Current Signal traces the emotional contour of blue current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(165,33,'Median Weather','blue-current-median-weather',5,168,'Median Weather is the preview focus from Blue Current.','Median Weather traces the emotional contour of blue current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(166,34,'Invoice Signal','midnight-signal-invoice-signal',1,243,'Invoice Signal is the preview focus from Midnight Signal.','Invoice Signal traces the emotional contour of midnight signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(167,34,'Current Weather','midnight-signal-current-weather',2,272,'Current Weather is the preview focus from Midnight Signal.','Current Weather traces the emotional contour of midnight signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(168,34,'Median Map','midnight-signal-median-map',3,301,'Median Map is the preview focus from Midnight Signal.','Median Map traces the emotional contour of midnight signal in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(169,34,'Receipt Ledger','midnight-signal-receipt-ledger',4,160,'Receipt Ledger is the preview focus from Midnight Signal.','Receipt Ledger traces the emotional contour of midnight signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(170,34,'Basin Glow','midnight-signal-basin-glow',5,189,'Basin Glow is the preview focus from Midnight Signal.','Basin Glow traces the emotional contour of midnight signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(171,35,'August Light','late-boulevard-august-light',1,235,'August Light is the preview focus from Late Boulevard.','August Light traces the emotional contour of late boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(172,35,'Pattern Note','late-boulevard-pattern-note',2,264,'Pattern Note is the preview focus from Late Boulevard.','Pattern Note traces the emotional contour of late boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(173,35,'Window Line','late-boulevard-window-line',3,293,'Window Line is the preview focus from Late Boulevard.','Window Line traces the emotional contour of late boulevard in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(174,35,'Breaker Receipt','late-boulevard-breaker-receipt',4,322,'Breaker Receipt is the preview focus from Late Boulevard.','Breaker Receipt traces the emotional contour of late boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(175,35,'Hallway Dust','late-boulevard-hallway-dust',5,181,'Hallway Dust is the preview focus from Late Boulevard.','Hallway Dust traces the emotional contour of late boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(176,36,'Basin Transit','glass-mosaic-basin-transit',1,256,'Basin Transit is the preview focus from Glass Mosaic.','Basin Transit traces the emotional contour of glass mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(177,36,'Anchor Machine','glass-mosaic-anchor-machine',2,285,'Anchor Machine is the preview focus from Glass Mosaic.','Anchor Machine traces the emotional contour of glass mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(178,36,'Static Murmur','glass-mosaic-static-murmur',3,314,'Static Murmur is the preview focus from Glass Mosaic.','Static Murmur traces the emotional contour of glass mosaic in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(179,36,'Platform Light','glass-mosaic-platform-light',4,173,'Platform Light is the preview focus from Glass Mosaic.','Platform Light traces the emotional contour of glass mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(180,36,'August Note','glass-mosaic-august-note',5,202,'August Note is the preview focus from Glass Mosaic.','August Note traces the emotional contour of glass mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(181,37,'August Note','golden-garden-august-note',1,248,'August Note is the preview focus from Golden Garden.','August Note traces the emotional contour of golden garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(182,37,'Pattern Line','golden-garden-pattern-line',2,277,'Pattern Line is the preview focus from Golden Garden.','Pattern Line traces the emotional contour of golden garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(183,37,'Window Receipt','golden-garden-window-receipt',3,306,'Window Receipt is the preview focus from Golden Garden.','Window Receipt traces the emotional contour of golden garden in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(184,37,'Breaker Dust','golden-garden-breaker-dust',4,165,'Breaker Dust is the preview focus from Golden Garden.','Breaker Dust traces the emotional contour of golden garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(185,37,'Hallway Dialtone','golden-garden-hallway-dialtone',5,194,'Hallway Dialtone is the preview focus from Golden Garden.','Hallway Dialtone traces the emotional contour of golden garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(186,38,'Pattern Receipt','motel-harbor-pattern-receipt',1,269,'Pattern Receipt is the preview focus from Motel Harbor.','Pattern Receipt traces the emotional contour of motel harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(187,38,'Window Dust','motel-harbor-window-dust',2,298,'Window Dust is the preview focus from Motel Harbor.','Window Dust traces the emotional contour of motel harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(188,38,'Breaker Dialtone','motel-harbor-breaker-dialtone',3,157,'Breaker Dialtone is the preview focus from Motel Harbor.','Breaker Dialtone traces the emotional contour of motel harbor in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(189,38,'Hallway Index','motel-harbor-hallway-index',4,186,'Hallway Index is the preview focus from Motel Harbor.','Hallway Index traces the emotional contour of motel harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(190,38,'Transfer Signal','motel-harbor-transfer-signal',5,215,'Transfer Signal is the preview focus from Motel Harbor.','Transfer Signal traces the emotional contour of motel harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(191,39,'Anchor Murmur','quiet-transit-anchor-murmur',1,261,'Anchor Murmur is the preview focus from Quiet Transit.','Anchor Murmur traces the emotional contour of quiet transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(192,39,'Static Light','quiet-transit-static-light',2,290,'Static Light is the preview focus from Quiet Transit.','Static Light traces the emotional contour of quiet transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(193,39,'Platform Note','quiet-transit-platform-note',3,319,'Platform Note is the preview focus from Quiet Transit.','Platform Note traces the emotional contour of quiet transit in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(194,39,'August Line','quiet-transit-august-line',4,178,'August Line is the preview focus from Quiet Transit.','August Line traces the emotional contour of quiet transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(195,39,'Pattern Receipt','quiet-transit-pattern-receipt',5,207,'Pattern Receipt is the preview focus from Quiet Transit.','Pattern Receipt traces the emotional contour of quiet transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(196,40,'August Receipt','stone-circuit-august-receipt',1,282,'August Receipt is the preview focus from Stone Circuit.','August Receipt traces the emotional contour of stone circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(197,40,'Pattern Dust','stone-circuit-pattern-dust',2,311,'Pattern Dust is the preview focus from Stone Circuit.','Pattern Dust traces the emotional contour of stone circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(198,40,'Window Dialtone','stone-circuit-window-dialtone',3,170,'Window Dialtone is the preview focus from Stone Circuit.','Window Dialtone traces the emotional contour of stone circuit in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(199,40,'Breaker Index','stone-circuit-breaker-index',4,199,'Breaker Index is the preview focus from Stone Circuit.','Breaker Index traces the emotional contour of stone circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(200,40,'Hallway Signal','stone-circuit-hallway-signal',5,228,'Hallway Signal is the preview focus from Stone Circuit.','Hallway Signal traces the emotional contour of stone circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(201,41,'Breaker Index','paper-archive-breaker-index',1,274,'Breaker Index is the preview focus from Paper Archive.','Breaker Index traces the emotional contour of paper archive in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(202,41,'Hallway Signal','paper-archive-hallway-signal',2,303,'Hallway Signal is the preview focus from Paper Archive.','Hallway Signal traces the emotional contour of paper archive in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(203,41,'Transfer Weather','paper-archive-transfer-weather',3,162,'Transfer Weather is the preview focus from Paper Archive.','Transfer Weather traces the emotional contour of paper archive in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(204,41,'Rain Map','paper-archive-rain-map',4,191,'Rain Map is the preview focus from Paper Archive.','Rain Map traces the emotional contour of paper archive in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(205,41,'Invoice Ledger','paper-archive-invoice-ledger',5,220,'Invoice Ledger is the preview focus from Paper Archive.','Invoice Ledger traces the emotional contour of paper archive in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(206,42,'Window Index','delta-minutes-window-index',1,295,'Window Index is the preview focus from Delta Minutes.','Window Index traces the emotional contour of delta minutes in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(207,42,'Breaker Signal','delta-minutes-breaker-signal',2,324,'Breaker Signal is the preview focus from Delta Minutes.','Breaker Signal traces the emotional contour of delta minutes in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(208,42,'Hallway Weather','delta-minutes-hallway-weather',3,183,'Hallway Weather is the preview focus from Delta Minutes.','Hallway Weather traces the emotional contour of delta minutes in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(209,42,'Transfer Map','delta-minutes-transfer-map',4,212,'Transfer Map is the preview focus from Delta Minutes.','Transfer Map traces the emotional contour of delta minutes in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(210,42,'Rain Ledger','delta-minutes-rain-ledger',5,241,'Rain Ledger is the preview focus from Delta Minutes.','Rain Ledger traces the emotional contour of delta minutes in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(211,43,'Median Machine','chrome-pattern-median-machine',1,287,'Median Machine is the preview focus from Chrome Pattern.','Median Machine traces the emotional contour of chrome pattern in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(212,43,'Receipt Murmur','chrome-pattern-receipt-murmur',2,316,'Receipt Murmur is the preview focus from Chrome Pattern.','Receipt Murmur traces the emotional contour of chrome pattern in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(213,43,'Basin Light','chrome-pattern-basin-light',3,175,'Basin Light is the preview focus from Chrome Pattern.','Basin Light traces the emotional contour of chrome pattern in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(214,43,'Anchor Note','chrome-pattern-anchor-note',4,204,'Anchor Note is the preview focus from Chrome Pattern.','Anchor Note traces the emotional contour of chrome pattern in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(215,43,'Static Line','chrome-pattern-static-line',5,233,'Static Line is the preview focus from Chrome Pattern.','Static Line traces the emotional contour of chrome pattern in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(216,44,'Window Signal','velvet-weather-window-signal',1,308,'Window Signal is the preview focus from Velvet Weather.','Window Signal traces the emotional contour of velvet weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(217,44,'Breaker Weather','velvet-weather-breaker-weather',2,167,'Breaker Weather is the preview focus from Velvet Weather.','Breaker Weather traces the emotional contour of velvet weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(218,44,'Hallway Map','velvet-weather-hallway-map',3,196,'Hallway Map is the preview focus from Velvet Weather.','Hallway Map traces the emotional contour of velvet weather in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(219,44,'Transfer Ledger','velvet-weather-transfer-ledger',4,225,'Transfer Ledger is the preview focus from Velvet Weather.','Transfer Ledger traces the emotional contour of velvet weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(220,44,'Rain Glow','velvet-weather-rain-glow',5,254,'Rain Glow is the preview focus from Velvet Weather.','Rain Glow traces the emotional contour of velvet weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(221,45,'Receipt Light','south-dusk-receipt-light',1,300,'Receipt Light is the preview focus from South Dusk.','Receipt Light traces the emotional contour of south dusk in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(222,45,'Basin Note','south-dusk-basin-note',2,159,'Basin Note is the preview focus from South Dusk.','Basin Note traces the emotional contour of south dusk in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(223,45,'Anchor Line','south-dusk-anchor-line',3,188,'Anchor Line is the preview focus from South Dusk.','Anchor Line traces the emotional contour of south dusk in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(224,45,'Static Receipt','south-dusk-static-receipt',4,217,'Static Receipt is the preview focus from South Dusk.','Static Receipt traces the emotional contour of south dusk in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(225,45,'Platform Dust','south-dusk-platform-dust',5,246,'Platform Dust is the preview focus from South Dusk.','Platform Dust traces the emotional contour of south dusk in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(226,46,'Static Dust','open-ledger-static-dust',1,321,'Static Dust is the preview focus from Open Ledger.','Static Dust traces the emotional contour of open ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(227,46,'Platform Dialtone','open-ledger-platform-dialtone',2,180,'Platform Dialtone is the preview focus from Open Ledger.','Platform Dialtone traces the emotional contour of open ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(228,46,'August Index','open-ledger-august-index',3,209,'August Index is the preview focus from Open Ledger.','August Index traces the emotional contour of open ledger in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(229,46,'Pattern Signal','open-ledger-pattern-signal',4,238,'Pattern Signal is the preview focus from Open Ledger.','Pattern Signal traces the emotional contour of open ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(230,46,'Window Weather','open-ledger-window-weather',5,267,'Window Weather is the preview focus from Open Ledger.','Window Weather traces the emotional contour of open ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(231,47,'Basin Line','midnight-vector-basin-line',1,313,'Basin Line is the preview focus from Midnight Vector.','Basin Line traces the emotional contour of midnight vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(232,47,'Anchor Receipt','midnight-vector-anchor-receipt',2,172,'Anchor Receipt is the preview focus from Midnight Vector.','Anchor Receipt traces the emotional contour of midnight vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(233,47,'Static Dust','midnight-vector-static-dust',3,201,'Static Dust is the preview focus from Midnight Vector.','Static Dust traces the emotional contour of midnight vector in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(234,47,'Platform Dialtone','midnight-vector-platform-dialtone',4,230,'Platform Dialtone is the preview focus from Midnight Vector.','Platform Dialtone traces the emotional contour of midnight vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(235,47,'August Index','midnight-vector-august-index',5,259,'August Index is the preview focus from Midnight Vector.','August Index traces the emotional contour of midnight vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(236,48,'Pattern Weather','after-cinema-pattern-weather',1,164,'Pattern Weather is the preview focus from After Cinema.','Pattern Weather traces the emotional contour of after cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(237,48,'Window Map','after-cinema-window-map',2,193,'Window Map is the preview focus from After Cinema.','Window Map traces the emotional contour of after cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(238,48,'Breaker Ledger','after-cinema-breaker-ledger',3,222,'Breaker Ledger is the preview focus from After Cinema.','Breaker Ledger traces the emotional contour of after cinema in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(239,48,'Hallway Glow','after-cinema-hallway-glow',4,251,'Hallway Glow is the preview focus from After Cinema.','Hallway Glow traces the emotional contour of after cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(240,48,'Transfer Transit','after-cinema-transfer-transit',5,280,'Transfer Transit is the preview focus from After Cinema.','Transfer Transit traces the emotional contour of after cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(241,49,'Hallway Glow','glass-thread-hallway-glow',1,156,'Hallway Glow is the preview focus from Glass Thread.','Hallway Glow traces the emotional contour of glass thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(242,49,'Transfer Transit','glass-thread-transfer-transit',2,185,'Transfer Transit is the preview focus from Glass Thread.','Transfer Transit traces the emotional contour of glass thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(243,49,'Rain Machine','glass-thread-rain-machine',3,214,'Rain Machine is the preview focus from Glass Thread.','Rain Machine traces the emotional contour of glass thread in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(244,49,'Invoice Murmur','glass-thread-invoice-murmur',4,243,'Invoice Murmur is the preview focus from Glass Thread.','Invoice Murmur traces the emotional contour of glass thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(245,49,'Current Light','glass-thread-current-light',5,272,'Current Light is the preview focus from Glass Thread.','Current Light traces the emotional contour of glass thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(246,50,'August Weather','broken-method-august-weather',1,177,'August Weather is the preview focus from Broken Method.','August Weather traces the emotional contour of broken method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(247,50,'Pattern Map','broken-method-pattern-map',2,206,'Pattern Map is the preview focus from Broken Method.','Pattern Map traces the emotional contour of broken method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(248,50,'Window Ledger','broken-method-window-ledger',3,235,'Window Ledger is the preview focus from Broken Method.','Window Ledger traces the emotional contour of broken method in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(249,50,'Breaker Glow','broken-method-breaker-glow',4,264,'Breaker Glow is the preview focus from Broken Method.','Breaker Glow traces the emotional contour of broken method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(250,50,'Hallway Transit','broken-method-hallway-transit',5,293,'Hallway Transit is the preview focus from Broken Method.','Hallway Transit traces the emotional contour of broken method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(251,51,'Basin Dust','motel-parade-basin-dust',1,169,'Basin Dust is the preview focus from Motel Parade.','Basin Dust traces the emotional contour of motel parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(252,51,'Anchor Dialtone','motel-parade-anchor-dialtone',2,198,'Anchor Dialtone is the preview focus from Motel Parade.','Anchor Dialtone traces the emotional contour of motel parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(253,51,'Static Index','motel-parade-static-index',3,227,'Static Index is the preview focus from Motel Parade.','Static Index traces the emotional contour of motel parade in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(254,51,'Platform Signal','motel-parade-platform-signal',4,256,'Platform Signal is the preview focus from Motel Parade.','Platform Signal traces the emotional contour of motel parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(255,51,'August Weather','motel-parade-august-weather',5,285,'August Weather is the preview focus from Motel Parade.','August Weather traces the emotional contour of motel parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(256,52,'Invoice Note','static-current-invoice-note',1,190,'Invoice Note is the preview focus from Static Current.','Invoice Note traces the emotional contour of static current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(257,52,'Current Line','static-current-current-line',2,219,'Current Line is the preview focus from Static Current.','Current Line traces the emotional contour of static current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(258,52,'Median Receipt','static-current-median-receipt',3,248,'Median Receipt is the preview focus from Static Current.','Median Receipt traces the emotional contour of static current in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(259,52,'Receipt Dust','static-current-receipt-dust',4,277,'Receipt Dust is the preview focus from Static Current.','Receipt Dust traces the emotional contour of static current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(260,52,'Basin Dialtone','static-current-basin-dialtone',5,306,'Basin Dialtone is the preview focus from Static Current.','Basin Dialtone traces the emotional contour of static current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(261,53,'August Map','stone-engine-august-map',1,182,'August Map is the preview focus from Stone Engine.','August Map traces the emotional contour of stone engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(262,53,'Pattern Ledger','stone-engine-pattern-ledger',2,211,'Pattern Ledger is the preview focus from Stone Engine.','Pattern Ledger traces the emotional contour of stone engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(263,53,'Window Glow','stone-engine-window-glow',3,240,'Window Glow is the preview focus from Stone Engine.','Window Glow traces the emotional contour of stone engine in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(264,53,'Breaker Transit','stone-engine-breaker-transit',4,269,'Breaker Transit is the preview focus from Stone Engine.','Breaker Transit traces the emotional contour of stone engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(265,53,'Hallway Machine','stone-engine-hallway-machine',5,298,'Hallway Machine is the preview focus from Stone Engine.','Hallway Machine traces the emotional contour of stone engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(266,54,'Window Transit','harbor-boulevard-window-transit',1,203,'Window Transit is the preview focus from Harbor Boulevard.','Window Transit traces the emotional contour of harbor boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(267,54,'Breaker Machine','harbor-boulevard-breaker-machine',2,232,'Breaker Machine is the preview focus from Harbor Boulevard.','Breaker Machine traces the emotional contour of harbor boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(268,54,'Hallway Murmur','harbor-boulevard-hallway-murmur',3,261,'Hallway Murmur is the preview focus from Harbor Boulevard.','Hallway Murmur traces the emotional contour of harbor boulevard in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(269,54,'Transfer Light','harbor-boulevard-transfer-light',4,290,'Transfer Light is the preview focus from Harbor Boulevard.','Transfer Light traces the emotional contour of harbor boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(270,54,'Rain Note','harbor-boulevard-rain-note',5,319,'Rain Note is the preview focus from Harbor Boulevard.','Rain Note traces the emotional contour of harbor boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(271,55,'Rain Note','delta-signal-rain-note',1,195,'Rain Note is the preview focus from Delta Signal.','Rain Note traces the emotional contour of delta signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(272,55,'Invoice Line','delta-signal-invoice-line',2,224,'Invoice Line is the preview focus from Delta Signal.','Invoice Line traces the emotional contour of delta signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(273,55,'Current Receipt','delta-signal-current-receipt',3,253,'Current Receipt is the preview focus from Delta Signal.','Current Receipt traces the emotional contour of delta signal in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(274,55,'Median Dust','delta-signal-median-dust',4,282,'Median Dust is the preview focus from Delta Signal.','Median Dust traces the emotional contour of delta signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(275,55,'Receipt Dialtone','delta-signal-receipt-dialtone',5,311,'Receipt Dialtone is the preview focus from Delta Signal.','Receipt Dialtone traces the emotional contour of delta signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(276,56,'Anchor Weather','river-garden-anchor-weather',1,216,'Anchor Weather is the preview focus from River Garden.','Anchor Weather traces the emotional contour of river garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(277,56,'Static Map','river-garden-static-map',2,245,'Static Map is the preview focus from River Garden.','Static Map traces the emotional contour of river garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(278,56,'Platform Ledger','river-garden-platform-ledger',3,274,'Platform Ledger is the preview focus from River Garden.','Platform Ledger traces the emotional contour of river garden in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(279,56,'August Glow','river-garden-august-glow',4,303,'August Glow is the preview focus from River Garden.','August Glow traces the emotional contour of river garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(280,56,'Pattern Transit','river-garden-pattern-transit',5,162,'Pattern Transit is the preview focus from River Garden.','Pattern Transit traces the emotional contour of river garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(281,57,'Anchor Weather','velvet-mosaic-anchor-weather',1,208,'Anchor Weather is the preview focus from Velvet Mosaic.','Anchor Weather traces the emotional contour of velvet mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(282,57,'Static Map','velvet-mosaic-static-map',2,237,'Static Map is the preview focus from Velvet Mosaic.','Static Map traces the emotional contour of velvet mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(283,57,'Platform Ledger','velvet-mosaic-platform-ledger',3,266,'Platform Ledger is the preview focus from Velvet Mosaic.','Platform Ledger traces the emotional contour of velvet mosaic in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(284,57,'August Glow','velvet-mosaic-august-glow',4,295,'August Glow is the preview focus from Velvet Mosaic.','August Glow traces the emotional contour of velvet mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(285,57,'Pattern Transit','velvet-mosaic-pattern-transit',5,324,'Pattern Transit is the preview focus from Velvet Mosaic.','Pattern Transit traces the emotional contour of velvet mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(286,58,'Hallway Note','signal-transit-hallway-note',1,229,'Hallway Note is the preview focus from Signal Transit.','Hallway Note traces the emotional contour of signal transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(287,58,'Transfer Line','signal-transit-transfer-line',2,258,'Transfer Line is the preview focus from Signal Transit.','Transfer Line traces the emotional contour of signal transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(288,58,'Rain Receipt','signal-transit-rain-receipt',3,287,'Rain Receipt is the preview focus from Signal Transit.','Rain Receipt traces the emotional contour of signal transit in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(289,58,'Invoice Dust','signal-transit-invoice-dust',4,316,'Invoice Dust is the preview focus from Signal Transit.','Invoice Dust traces the emotional contour of signal transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(290,58,'Current Dialtone','signal-transit-current-dialtone',5,175,'Current Dialtone is the preview focus from Signal Transit.','Current Dialtone traces the emotional contour of signal transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(291,59,'Basin Weather','open-harbor-basin-weather',1,221,'Basin Weather is the preview focus from Open Harbor.','Basin Weather traces the emotional contour of open harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(292,59,'Anchor Map','open-harbor-anchor-map',2,250,'Anchor Map is the preview focus from Open Harbor.','Anchor Map traces the emotional contour of open harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(293,59,'Static Ledger','open-harbor-static-ledger',3,279,'Static Ledger is the preview focus from Open Harbor.','Static Ledger traces the emotional contour of open harbor in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(294,59,'Platform Glow','open-harbor-platform-glow',4,308,'Platform Glow is the preview focus from Open Harbor.','Platform Glow traces the emotional contour of open harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(295,59,'August Transit','open-harbor-august-transit',5,167,'August Transit is the preview focus from Open Harbor.','August Transit traces the emotional contour of open harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(296,60,'Hallway Line','blue-archive-hallway-line',1,242,'Hallway Line is the preview focus from Blue Archive.','Hallway Line traces the emotional contour of blue archive in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(297,60,'Transfer Receipt','blue-archive-transfer-receipt',2,271,'Transfer Receipt is the preview focus from Blue Archive.','Transfer Receipt traces the emotional contour of blue archive in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(298,60,'Rain Dust','blue-archive-rain-dust',3,300,'Rain Dust is the preview focus from Blue Archive.','Rain Dust traces the emotional contour of blue archive in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(299,60,'Invoice Dialtone','blue-archive-invoice-dialtone',4,159,'Invoice Dialtone is the preview focus from Blue Archive.','Invoice Dialtone traces the emotional contour of blue archive in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(300,60,'Current Index','blue-archive-current-index',5,188,'Current Index is the preview focus from Blue Archive.','Current Index traces the emotional contour of blue archive in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(301,61,'Rain Dust','after-circuit-rain-dust',1,234,'Rain Dust is the preview focus from After Circuit.','Rain Dust traces the emotional contour of after circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(302,61,'Invoice Dialtone','after-circuit-invoice-dialtone',2,263,'Invoice Dialtone is the preview focus from After Circuit.','Invoice Dialtone traces the emotional contour of after circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(303,61,'Current Index','after-circuit-current-index',3,292,'Current Index is the preview focus from After Circuit.','Current Index traces the emotional contour of after circuit in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(304,61,'Median Signal','after-circuit-median-signal',4,321,'Median Signal is the preview focus from After Circuit.','Median Signal traces the emotional contour of after circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(305,61,'Receipt Weather','after-circuit-receipt-weather',5,180,'Receipt Weather is the preview focus from After Circuit.','Receipt Weather traces the emotional contour of after circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(306,62,'Window Note','late-pattern-window-note',1,255,'Window Note is the preview focus from Late Pattern.','Window Note traces the emotional contour of late pattern in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(307,62,'Breaker Line','late-pattern-breaker-line',2,284,'Breaker Line is the preview focus from Late Pattern.','Breaker Line traces the emotional contour of late pattern in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(308,62,'Hallway Receipt','late-pattern-hallway-receipt',3,313,'Hallway Receipt is the preview focus from Late Pattern.','Hallway Receipt traces the emotional contour of late pattern in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(309,62,'Transfer Dust','late-pattern-transfer-dust',4,172,'Transfer Dust is the preview focus from Late Pattern.','Transfer Dust traces the emotional contour of late pattern in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(310,62,'Rain Dialtone','late-pattern-rain-dialtone',5,201,'Rain Dialtone is the preview focus from Late Pattern.','Rain Dialtone traces the emotional contour of late pattern in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(311,63,'Anchor Glow','broken-minutes-anchor-glow',1,247,'Anchor Glow is the preview focus from Broken Minutes.','Anchor Glow traces the emotional contour of broken minutes in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(312,63,'Static Transit','broken-minutes-static-transit',2,276,'Static Transit is the preview focus from Broken Minutes.','Static Transit traces the emotional contour of broken minutes in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(313,63,'Platform Machine','broken-minutes-platform-machine',3,305,'Platform Machine is the preview focus from Broken Minutes.','Platform Machine traces the emotional contour of broken minutes in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(314,63,'August Murmur','broken-minutes-august-murmur',4,164,'August Murmur is the preview focus from Broken Minutes.','August Murmur traces the emotional contour of broken minutes in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(315,63,'Pattern Light','broken-minutes-pattern-light',5,193,'Pattern Light is the preview focus from Broken Minutes.','Pattern Light traces the emotional contour of broken minutes in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(316,64,'Hallway Dust','golden-dusk-hallway-dust',1,268,'Hallway Dust is the preview focus from Golden Dusk.','Hallway Dust traces the emotional contour of golden dusk in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(317,64,'Transfer Dialtone','golden-dusk-transfer-dialtone',2,297,'Transfer Dialtone is the preview focus from Golden Dusk.','Transfer Dialtone traces the emotional contour of golden dusk in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(318,64,'Rain Index','golden-dusk-rain-index',3,156,'Rain Index is the preview focus from Golden Dusk.','Rain Index traces the emotional contour of golden dusk in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(319,64,'Invoice Signal','golden-dusk-invoice-signal',4,185,'Invoice Signal is the preview focus from Golden Dusk.','Invoice Signal traces the emotional contour of golden dusk in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(320,64,'Current Weather','golden-dusk-current-weather',5,214,'Current Weather is the preview focus from Golden Dusk.','Current Weather traces the emotional contour of golden dusk in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(321,65,'Breaker Receipt','static-weather-fault-parade-breaker-receipt',1,260,'Breaker Receipt is the preview focus from Static Weather.','Breaker Receipt traces the emotional contour of static weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(322,65,'Hallway Dust','static-weather-fault-parade-hallway-dust',2,289,'Hallway Dust is the preview focus from Static Weather.','Hallway Dust traces the emotional contour of static weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(323,65,'Transfer Dialtone','static-weather-fault-parade-transfer-dialtone',3,318,'Transfer Dialtone is the preview focus from Static Weather.','Transfer Dialtone traces the emotional contour of static weather in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(324,65,'Rain Index','static-weather-fault-parade-rain-index',4,177,'Rain Index is the preview focus from Static Weather.','Rain Index traces the emotional contour of static weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(325,65,'Invoice Signal','static-weather-fault-parade-invoice-signal',5,206,'Invoice Signal is the preview focus from Static Weather.','Invoice Signal traces the emotional contour of static weather in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(326,66,'Window Receipt','quiet-vector-fault-parade-window-receipt',1,281,'Window Receipt is the preview focus from Quiet Vector.','Window Receipt traces the emotional contour of quiet vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(327,66,'Breaker Dust','quiet-vector-fault-parade-breaker-dust',2,310,'Breaker Dust is the preview focus from Quiet Vector.','Breaker Dust traces the emotional contour of quiet vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(328,66,'Hallway Dialtone','quiet-vector-fault-parade-hallway-dialtone',3,169,'Hallway Dialtone is the preview focus from Quiet Vector.','Hallway Dialtone traces the emotional contour of quiet vector in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(329,66,'Transfer Index','quiet-vector-fault-parade-transfer-index',4,198,'Transfer Index is the preview focus from Quiet Vector.','Transfer Index traces the emotional contour of quiet vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(330,66,'Rain Signal','quiet-vector-fault-parade-rain-signal',5,227,'Rain Signal is the preview focus from Quiet Vector.','Rain Signal traces the emotional contour of quiet vector in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(331,67,'Receipt Glow','harbor-ledger-static-ledger-receipt-glow',1,273,'Receipt Glow is the preview focus from Harbor Ledger.','Receipt Glow traces the emotional contour of harbor ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(332,67,'Basin Transit','harbor-ledger-static-ledger-basin-transit',2,302,'Basin Transit is the preview focus from Harbor Ledger.','Basin Transit traces the emotional contour of harbor ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(333,67,'Anchor Machine','harbor-ledger-static-ledger-anchor-machine',3,161,'Anchor Machine is the preview focus from Harbor Ledger.','Anchor Machine traces the emotional contour of harbor ledger in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(334,67,'Static Murmur','harbor-ledger-static-ledger-static-murmur',4,190,'Static Murmur is the preview focus from Harbor Ledger.','Static Murmur traces the emotional contour of harbor ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(335,67,'Platform Light','harbor-ledger-static-ledger-platform-light',5,219,'Platform Light is the preview focus from Harbor Ledger.','Platform Light traces the emotional contour of harbor ledger in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(336,68,'Window Dust','paper-thread-static-ledger-window-dust',1,294,'Window Dust is the preview focus from Paper Thread.','Window Dust traces the emotional contour of paper thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(337,68,'Breaker Dialtone','paper-thread-static-ledger-breaker-dialtone',2,323,'Breaker Dialtone is the preview focus from Paper Thread.','Breaker Dialtone traces the emotional contour of paper thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(338,68,'Hallway Index','paper-thread-static-ledger-hallway-index',3,182,'Hallway Index is the preview focus from Paper Thread.','Hallway Index traces the emotional contour of paper thread in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(339,68,'Transfer Signal','paper-thread-static-ledger-transfer-signal',4,211,'Transfer Signal is the preview focus from Paper Thread.','Transfer Signal traces the emotional contour of paper thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(340,68,'Rain Weather','paper-thread-static-ledger-rain-weather',5,240,'Rain Weather is the preview focus from Paper Thread.','Rain Weather traces the emotional contour of paper thread in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(341,69,'Hallway Index','river-cinema-stone-balcony-hallway-index',1,286,'Hallway Index is the preview focus from River Cinema.','Hallway Index traces the emotional contour of river cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(342,69,'Transfer Signal','river-cinema-stone-balcony-transfer-signal',2,315,'Transfer Signal is the preview focus from River Cinema.','Transfer Signal traces the emotional contour of river cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(343,69,'Rain Weather','river-cinema-stone-balcony-rain-weather',3,174,'Rain Weather is the preview focus from River Cinema.','Rain Weather traces the emotional contour of river cinema in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(344,69,'Invoice Map','river-cinema-stone-balcony-invoice-map',4,203,'Invoice Map is the preview focus from River Cinema.','Invoice Map traces the emotional contour of river cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(345,69,'Current Ledger','river-cinema-stone-balcony-current-ledger',5,232,'Current Ledger is the preview focus from River Cinema.','Current Ledger traces the emotional contour of river cinema in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(346,70,'Platform Line','chrome-parade-stone-balcony-platform-line',1,307,'Platform Line is the preview focus from Chrome Parade.','Platform Line traces the emotional contour of chrome parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(347,70,'August Receipt','chrome-parade-stone-balcony-august-receipt',2,166,'August Receipt is the preview focus from Chrome Parade.','August Receipt traces the emotional contour of chrome parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(348,70,'Pattern Dust','chrome-parade-stone-balcony-pattern-dust',3,195,'Pattern Dust is the preview focus from Chrome Parade.','Pattern Dust traces the emotional contour of chrome parade in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(349,70,'Window Dialtone','chrome-parade-stone-balcony-window-dialtone',4,224,'Window Dialtone is the preview focus from Chrome Parade.','Window Dialtone traces the emotional contour of chrome parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(350,70,'Breaker Index','chrome-parade-stone-balcony-breaker-index',5,253,'Breaker Index is the preview focus from Chrome Parade.','Breaker Index traces the emotional contour of chrome parade in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(351,71,'August Receipt','signal-method-west-transit-august-receipt',1,299,'August Receipt is the preview focus from Signal Method.','August Receipt traces the emotional contour of signal method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(352,71,'Pattern Dust','signal-method-west-transit-pattern-dust',2,158,'Pattern Dust is the preview focus from Signal Method.','Pattern Dust traces the emotional contour of signal method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(353,71,'Window Dialtone','signal-method-west-transit-window-dialtone',3,187,'Window Dialtone is the preview focus from Signal Method.','Window Dialtone traces the emotional contour of signal method in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(354,71,'Breaker Index','signal-method-west-transit-breaker-index',4,216,'Breaker Index is the preview focus from Signal Method.','Breaker Index traces the emotional contour of signal method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(355,71,'Hallway Signal','signal-method-west-transit-hallway-signal',5,245,'Hallway Signal is the preview focus from Signal Method.','Hallway Signal traces the emotional contour of signal method in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(356,72,'Receipt Murmur','south-engine-west-transit-receipt-murmur',1,320,'Receipt Murmur is the preview focus from South Engine.','Receipt Murmur traces the emotional contour of south engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(357,72,'Basin Light','south-engine-west-transit-basin-light',2,179,'Basin Light is the preview focus from South Engine.','Basin Light traces the emotional contour of south engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(358,72,'Anchor Note','south-engine-west-transit-anchor-note',3,208,'Anchor Note is the preview focus from South Engine.','Anchor Note traces the emotional contour of south engine in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(359,72,'Static Line','south-engine-west-transit-static-line',4,237,'Static Line is the preview focus from South Engine.','Static Line traces the emotional contour of south engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(360,72,'Platform Receipt','south-engine-west-transit-platform-receipt',5,266,'Platform Receipt is the preview focus from South Engine.','Platform Receipt traces the emotional contour of south engine in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(361,73,'Receipt Murmur','blue-current-echo-dividend-receipt-murmur',1,312,'Receipt Murmur is the preview focus from Blue Current.','Receipt Murmur traces the emotional contour of blue current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(362,73,'Basin Light','blue-current-echo-dividend-basin-light',2,171,'Basin Light is the preview focus from Blue Current.','Basin Light traces the emotional contour of blue current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(363,73,'Anchor Note','blue-current-echo-dividend-anchor-note',3,200,'Anchor Note is the preview focus from Blue Current.','Anchor Note traces the emotional contour of blue current in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(364,73,'Static Line','blue-current-echo-dividend-static-line',4,229,'Static Line is the preview focus from Blue Current.','Static Line traces the emotional contour of blue current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(365,73,'Platform Receipt','blue-current-echo-dividend-platform-receipt',5,258,'Platform Receipt is the preview focus from Blue Current.','Platform Receipt traces the emotional contour of blue current in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(366,74,'Anchor Line','midnight-signal-echo-dividend-anchor-line',1,163,'Anchor Line is the preview focus from Midnight Signal.','Anchor Line traces the emotional contour of midnight signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(367,74,'Static Receipt','midnight-signal-echo-dividend-static-receipt',2,192,'Static Receipt is the preview focus from Midnight Signal.','Static Receipt traces the emotional contour of midnight signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(368,74,'Platform Dust','midnight-signal-echo-dividend-platform-dust',3,221,'Platform Dust is the preview focus from Midnight Signal.','Platform Dust traces the emotional contour of midnight signal in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(369,74,'August Dialtone','midnight-signal-echo-dividend-august-dialtone',4,250,'August Dialtone is the preview focus from Midnight Signal.','August Dialtone traces the emotional contour of midnight signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(370,74,'Pattern Index','midnight-signal-echo-dividend-pattern-index',5,279,'Pattern Index is the preview focus from Midnight Signal.','Pattern Index traces the emotional contour of midnight signal in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(371,75,'Transfer Ledger','late-boulevard-cloud-bureau-transfer-ledger',1,155,'Transfer Ledger is the preview focus from Late Boulevard.','Transfer Ledger traces the emotional contour of late boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(372,75,'Rain Glow','late-boulevard-cloud-bureau-rain-glow',2,184,'Rain Glow is the preview focus from Late Boulevard.','Rain Glow traces the emotional contour of late boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(373,75,'Invoice Transit','late-boulevard-cloud-bureau-invoice-transit',3,213,'Invoice Transit is the preview focus from Late Boulevard.','Invoice Transit traces the emotional contour of late boulevard in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(374,75,'Current Machine','late-boulevard-cloud-bureau-current-machine',4,242,'Current Machine is the preview focus from Late Boulevard.','Current Machine traces the emotional contour of late boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(375,75,'Median Murmur','late-boulevard-cloud-bureau-median-murmur',5,271,'Median Murmur is the preview focus from Late Boulevard.','Median Murmur traces the emotional contour of late boulevard in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(376,76,'Pattern Signal','glass-mosaic-cloud-bureau-pattern-signal',1,176,'Pattern Signal is the preview focus from Glass Mosaic.','Pattern Signal traces the emotional contour of glass mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(377,76,'Window Weather','glass-mosaic-cloud-bureau-window-weather',2,205,'Window Weather is the preview focus from Glass Mosaic.','Window Weather traces the emotional contour of glass mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(378,76,'Breaker Map','glass-mosaic-cloud-bureau-breaker-map',3,234,'Breaker Map is the preview focus from Glass Mosaic.','Breaker Map traces the emotional contour of glass mosaic in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(379,76,'Hallway Ledger','glass-mosaic-cloud-bureau-hallway-ledger',4,263,'Hallway Ledger is the preview focus from Glass Mosaic.','Hallway Ledger traces the emotional contour of glass mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(380,76,'Transfer Glow','glass-mosaic-cloud-bureau-transfer-glow',5,292,'Transfer Glow is the preview focus from Glass Mosaic.','Transfer Glow traces the emotional contour of glass mosaic in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(381,77,'Transfer Glow','golden-garden-ridge-cinema-transfer-glow',1,168,'Transfer Glow is the preview focus from Golden Garden.','Transfer Glow traces the emotional contour of golden garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(382,77,'Rain Transit','golden-garden-ridge-cinema-rain-transit',2,197,'Rain Transit is the preview focus from Golden Garden.','Rain Transit traces the emotional contour of golden garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(383,77,'Invoice Machine','golden-garden-ridge-cinema-invoice-machine',3,226,'Invoice Machine is the preview focus from Golden Garden.','Invoice Machine traces the emotional contour of golden garden in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(384,77,'Current Murmur','golden-garden-ridge-cinema-current-murmur',4,255,'Current Murmur is the preview focus from Golden Garden.','Current Murmur traces the emotional contour of golden garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(385,77,'Median Light','golden-garden-ridge-cinema-median-light',5,284,'Median Light is the preview focus from Golden Garden.','Median Light traces the emotional contour of golden garden in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(386,78,'Rain Machine','motel-harbor-ridge-cinema-rain-machine',1,189,'Rain Machine is the preview focus from Motel Harbor.','Rain Machine traces the emotional contour of motel harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(387,78,'Invoice Murmur','motel-harbor-ridge-cinema-invoice-murmur',2,218,'Invoice Murmur is the preview focus from Motel Harbor.','Invoice Murmur traces the emotional contour of motel harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(388,78,'Current Light','motel-harbor-ridge-cinema-current-light',3,247,'Current Light is the preview focus from Motel Harbor.','Current Light traces the emotional contour of motel harbor in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(389,78,'Median Note','motel-harbor-ridge-cinema-median-note',4,276,'Median Note is the preview focus from Motel Harbor.','Median Note traces the emotional contour of motel harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(390,78,'Receipt Line','motel-harbor-ridge-cinema-receipt-line',5,305,'Receipt Line is the preview focus from Motel Harbor.','Receipt Line traces the emotional contour of motel harbor in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(391,79,'Window Map','quiet-transit-hollow-method-window-map',1,181,'Window Map is the preview focus from Quiet Transit.','Window Map traces the emotional contour of quiet transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(392,79,'Breaker Ledger','quiet-transit-hollow-method-breaker-ledger',2,210,'Breaker Ledger is the preview focus from Quiet Transit.','Breaker Ledger traces the emotional contour of quiet transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(393,79,'Hallway Glow','quiet-transit-hollow-method-hallway-glow',3,239,'Hallway Glow is the preview focus from Quiet Transit.','Hallway Glow traces the emotional contour of quiet transit in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(394,79,'Transfer Transit','quiet-transit-hollow-method-transfer-transit',4,268,'Transfer Transit is the preview focus from Quiet Transit.','Transfer Transit traces the emotional contour of quiet transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(395,79,'Rain Machine','quiet-transit-hollow-method-rain-machine',5,297,'Rain Machine is the preview focus from Quiet Transit.','Rain Machine traces the emotional contour of quiet transit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(396,80,'Transfer Machine','stone-circuit-hollow-method-transfer-machine',1,202,'Transfer Machine is the preview focus from Stone Circuit.','Transfer Machine traces the emotional contour of stone circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(397,80,'Rain Murmur','stone-circuit-hollow-method-rain-murmur',2,231,'Rain Murmur is the preview focus from Stone Circuit.','Rain Murmur traces the emotional contour of stone circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(398,80,'Invoice Light','stone-circuit-hollow-method-invoice-light',3,260,'Invoice Light is the preview focus from Stone Circuit.','Invoice Light traces the emotional contour of stone circuit in one sharp phrase.',1); +INSERT INTO "tracks" VALUES(399,80,'Current Note','stone-circuit-hollow-method-current-note',4,289,'Current Note is the preview focus from Stone Circuit.','Current Note traces the emotional contour of stone circuit in one sharp phrase.',0); +INSERT INTO "tracks" VALUES(400,80,'Median Line','stone-circuit-hollow-method-median-line',5,318,'Median Line is the preview focus from Stone Circuit.','Median Line traces the emotional contour of stone circuit in one sharp phrase.',0); +CREATE TABLE users ( + id INTEGER NOT NULL, + username VARCHAR(80) NOT NULL, + email VARCHAR(120) NOT NULL, + password_hash VARCHAR(255) NOT NULL, + display_name VARCHAR(120) NOT NULL, + bio TEXT, + city VARCHAR(80), + country VARCHAR(80), + address_line1 VARCHAR(200), + postal_code VARCHAR(30), + favorite_format VARCHAR(40), + favorite_scene_id INTEGER, + created_at DATETIME, + PRIMARY KEY (id), + FOREIGN KEY(favorite_scene_id) REFERENCES scenes (id) +); +INSERT INTO "users" VALUES(1,'alice_j','alice.j@test.com','scrypt:32768:8:1$jattGpMscXJ483Xf$61fbe0ceaf7fa6442bc0964b55c2a96c633840b142719eca04af08ad55d34c75b083f18d92430185a587261b49b5fbd655cbc8c166d4512938d0a695213b6b52','Alice Johnson','Collects ambient and dub techno 12-inches.','Seattle','United States','128 Lake Union Ave','98109','vinyl',2,'2026-05-27 10:33:28.999466'); +INSERT INTO "users" VALUES(2,'bob_c','bob.c@test.com','scrypt:32768:8:1$CKhedapqOu5J0hbN$9b89da1bc0d9b8e36ddb60b163448583047b86443f94b61d0b96a7201eed43b5d29901a0f38302ad78d6b12588016d6a055faaec345ddadbb316d2ac6f1c0f4a','Bob Chen','Hip-hop fan chasing sharp lyric sheets and live bootlegs.','Chicago','United States','77 Fulton Market','60607','digital',8,'2026-05-27 10:33:29.242393'); +INSERT INTO "users" VALUES(3,'carol_d','carol.d@test.com','scrypt:32768:8:1$96ZrgZnFHeDAWyeY$17e84c179f7e0ba44cb6ac5b4cf86f90511cdc44a55619d31a88ea27e8a71b9397238f3eac0d0f29a681da8a063559664e758b23b9873c83c7442f16c3d8e82d','Carol Davis','Tags every purchase with the scene where she found it.','Brooklyn','United States','55 Bergen St','11201','cassette',3,'2026-05-27 10:33:29.477079'); +INSERT INTO "users" VALUES(4,'david_k','david.k@test.com','scrypt:32768:8:1$KHZNQFtyEDiBWJ8u$cfcd45c55b82012b00b7e5b9355cfd4c81027fa20e1f691d67e049564f553c3aec0435591bcc4eb53d3091bc6ced979d256c7737b1f35b212b92d16ef5d35770','David Kim','Merch-heavy collector who buys a tote with almost every record.','Portland','United States','204 Burnside St','97209','shirt',6,'2026-05-27 10:33:29.722056'); +CREATE TABLE wishlist_items ( + id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + album_id INTEGER, + merch_item_id INTEGER, + added_at DATETIME, + PRIMARY KEY (id), + FOREIGN KEY(user_id) REFERENCES users (id), + FOREIGN KEY(album_id) REFERENCES albums (id), + FOREIGN KEY(merch_item_id) REFERENCES merch_items (id) +); +INSERT INTO "wishlist_items" VALUES(1,1,3,NULL,'2026-05-27 10:33:29.732420'); +INSERT INTO "wishlist_items" VALUES(2,1,13,NULL,'2026-05-27 10:33:29.732921'); +INSERT INTO "wishlist_items" VALUES(3,1,21,NULL,'2026-05-27 10:33:29.733260'); +INSERT INTO "wishlist_items" VALUES(4,1,NULL,13,'2026-05-27 10:33:29.733576'); +INSERT INTO "wishlist_items" VALUES(5,2,17,NULL,'2026-05-27 10:33:29.733903'); +INSERT INTO "wishlist_items" VALUES(6,2,6,NULL,'2026-05-27 10:33:29.734219'); +INSERT INTO "wishlist_items" VALUES(7,2,NULL,5,'2026-05-27 10:33:29.734545'); +INSERT INTO "wishlist_items" VALUES(8,2,NULL,21,'2026-05-27 10:33:29.734848'); +INSERT INTO "wishlist_items" VALUES(9,3,15,NULL,'2026-05-27 10:33:29.735187'); +INSERT INTO "wishlist_items" VALUES(10,3,4,NULL,'2026-05-27 10:33:29.735541'); +INSERT INTO "wishlist_items" VALUES(11,3,23,NULL,'2026-05-27 10:33:29.735848'); +INSERT INTO "wishlist_items" VALUES(12,3,NULL,3,'2026-05-27 10:33:29.736139'); +INSERT INTO "wishlist_items" VALUES(13,4,19,NULL,'2026-05-27 10:33:29.736441'); +INSERT INTO "wishlist_items" VALUES(14,4,8,NULL,'2026-05-27 10:33:29.736766'); +INSERT INTO "wishlist_items" VALUES(15,4,NULL,7,'2026-05-27 10:33:29.737082'); +INSERT INTO "wishlist_items" VALUES(16,4,NULL,15,'2026-05-27 10:33:29.737901'); +CREATE UNIQUE INDEX ix_genres_slug ON genres (slug); +CREATE UNIQUE INDEX ix_scenes_slug ON scenes (slug); +CREATE UNIQUE INDEX ix_labels_slug ON labels (slug); +CREATE UNIQUE INDEX ix_tags_slug ON tags (slug); +CREATE UNIQUE INDEX ix_users_username ON users (username); +CREATE UNIQUE INDEX ix_users_email ON users (email); +CREATE UNIQUE INDEX ix_artists_slug ON artists (slug); +CREATE UNIQUE INDEX ix_albums_slug ON albums (slug); +CREATE UNIQUE INDEX ix_tracks_slug ON tracks (slug); +CREATE UNIQUE INDEX ix_merch_items_slug ON merch_items (slug); +COMMIT; diff --git a/sites/bandcamp/verify/test_task4_schema.sql b/sites/bandcamp/verify/test_task4_schema.sql new file mode 100644 index 00000000..9b4e3e83 --- /dev/null +++ b/sites/bandcamp/verify/test_task4_schema.sql @@ -0,0 +1,29 @@ +-- Full Bandcamp SQLite schema, including nullable fields, FKs and indexes. +-- Synthetic test data is inserted by make_task4_db; no runtime/seed writes. +CREATE TABLE album_tags (album_id INTEGER NOT NULL, tag_id INTEGER NOT NULL, PRIMARY KEY (album_id, tag_id), FOREIGN KEY(album_id) REFERENCES albums (id), FOREIGN KEY(tag_id) REFERENCES tags (id)); +CREATE TABLE albums (id INTEGER NOT NULL, artist_id INTEGER NOT NULL, label_id INTEGER NOT NULL, primary_genre_id INTEGER NOT NULL, scene_id INTEGER NOT NULL, title VARCHAR(160) NOT NULL, slug VARCHAR(180) NOT NULL, description TEXT, story TEXT, cover_image VARCHAR(255), header_image VARCHAR(255), price FLOAT, release_date DATE NOT NULL, track_count INTEGER, duration_seconds INTEGER, fan_count INTEGER, catalog_no VARCHAR(40), is_featured BOOLEAN, is_new BOOLEAN, is_editorial BOOLEAN, PRIMARY KEY (id), FOREIGN KEY(artist_id) REFERENCES artists (id), FOREIGN KEY(label_id) REFERENCES labels (id), FOREIGN KEY(primary_genre_id) REFERENCES genres (id), FOREIGN KEY(scene_id) REFERENCES scenes (id)); +CREATE TABLE artists (id INTEGER NOT NULL, name VARCHAR(140) NOT NULL, slug VARCHAR(160) NOT NULL, location VARCHAR(120), bio TEXT, headline VARCHAR(180), formed_year INTEGER, follow_count INTEGER, avatar_image VARCHAR(255), hero_image VARCHAR(255), scene_id INTEGER NOT NULL, label_id INTEGER NOT NULL, primary_genre_id INTEGER NOT NULL, PRIMARY KEY (id), FOREIGN KEY(scene_id) REFERENCES scenes (id), FOREIGN KEY(label_id) REFERENCES labels (id), FOREIGN KEY(primary_genre_id) REFERENCES genres (id)); +CREATE TABLE cart_items (id INTEGER NOT NULL, user_id INTEGER NOT NULL, album_id INTEGER, merch_item_id INTEGER, format_variant_id INTEGER, quantity INTEGER, added_at DATETIME, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id), FOREIGN KEY(album_id) REFERENCES albums (id), FOREIGN KEY(merch_item_id) REFERENCES merch_items (id), FOREIGN KEY(format_variant_id) REFERENCES format_variants (id)); +CREATE TABLE fan_collection_items (id INTEGER NOT NULL, user_id INTEGER NOT NULL, album_id INTEGER NOT NULL, format_variant_id INTEGER, favorite_track_id INTEGER, acquired_via VARCHAR(80), notes VARCHAR(240), added_at DATETIME, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id), FOREIGN KEY(album_id) REFERENCES albums (id), FOREIGN KEY(format_variant_id) REFERENCES format_variants (id), FOREIGN KEY(favorite_track_id) REFERENCES tracks (id)); +CREATE TABLE fan_comments (id INTEGER NOT NULL, user_id INTEGER NOT NULL, album_id INTEGER, track_id INTEGER, headline VARCHAR(140), body TEXT, rating INTEGER, created_at DATETIME, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id), FOREIGN KEY(album_id) REFERENCES albums (id), FOREIGN KEY(track_id) REFERENCES tracks (id)); +CREATE TABLE format_variants (id INTEGER NOT NULL, album_id INTEGER, merch_item_id INTEGER, kind VARCHAR(50) NOT NULL, name VARCHAR(120) NOT NULL, option_a VARCHAR(80), option_b VARCHAR(80), price FLOAT, inventory INTEGER, sku VARCHAR(80) NOT NULL, shipping_note VARCHAR(160), edition_note VARCHAR(200), is_default BOOLEAN, PRIMARY KEY (id), FOREIGN KEY(album_id) REFERENCES albums (id), FOREIGN KEY(merch_item_id) REFERENCES merch_items (id), UNIQUE (sku)); +CREATE TABLE genres (id INTEGER NOT NULL, name VARCHAR(60) NOT NULL, slug VARCHAR(80) NOT NULL, description TEXT, accent_color VARCHAR(20), PRIMARY KEY (id), UNIQUE (name)); +CREATE TABLE labels (id INTEGER NOT NULL, name VARCHAR(120) NOT NULL, slug VARCHAR(120) NOT NULL, location VARCHAR(120), description TEXT, PRIMARY KEY (id), UNIQUE (name)); +CREATE TABLE merch_items (id INTEGER NOT NULL, artist_id INTEGER NOT NULL, album_id INTEGER, title VARCHAR(160) NOT NULL, slug VARCHAR(180) NOT NULL, item_type VARCHAR(50), description TEXT, short_blurb VARCHAR(200), image VARCHAR(255), price FLOAT, inventory INTEGER, release_date DATE NOT NULL, is_featured BOOLEAN, PRIMARY KEY (id), FOREIGN KEY(artist_id) REFERENCES artists (id), FOREIGN KEY(album_id) REFERENCES albums (id)); +CREATE TABLE order_items (id INTEGER NOT NULL, order_id INTEGER NOT NULL, album_id INTEGER, merch_item_id INTEGER, format_variant_id INTEGER, title VARCHAR(200) NOT NULL, artist_name VARCHAR(140), image_path VARCHAR(255), variant_label VARCHAR(160), quantity INTEGER, unit_price FLOAT, PRIMARY KEY (id), FOREIGN KEY(order_id) REFERENCES orders (id), FOREIGN KEY(album_id) REFERENCES albums (id), FOREIGN KEY(merch_item_id) REFERENCES merch_items (id), FOREIGN KEY(format_variant_id) REFERENCES format_variants (id)); +CREATE TABLE orders (id INTEGER NOT NULL, user_id INTEGER NOT NULL, order_number VARCHAR(60) NOT NULL, status VARCHAR(40), subtotal FLOAT, shipping FLOAT, tax FLOAT, total FLOAT, shipping_name VARCHAR(140), shipping_line1 VARCHAR(200), shipping_city VARCHAR(120), shipping_country VARCHAR(120), payment_label VARCHAR(120), note VARCHAR(240), placed_at DATETIME, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id), UNIQUE (order_number)); +CREATE TABLE scenes (id INTEGER NOT NULL, name VARCHAR(120) NOT NULL, slug VARCHAR(120) NOT NULL, country VARCHAR(80), description TEXT, PRIMARY KEY (id), UNIQUE (name)); +CREATE TABLE tags (id INTEGER NOT NULL, name VARCHAR(80) NOT NULL, slug VARCHAR(80) NOT NULL, PRIMARY KEY (id), UNIQUE (name)); +CREATE TABLE tracks (id INTEGER NOT NULL, album_id INTEGER NOT NULL, title VARCHAR(160) NOT NULL, slug VARCHAR(180) NOT NULL, track_number INTEGER NOT NULL, duration_seconds INTEGER, preview_hook VARCHAR(255), lyrics_excerpt TEXT, is_focus_track BOOLEAN, PRIMARY KEY (id), FOREIGN KEY(album_id) REFERENCES albums (id)); +CREATE TABLE users (id INTEGER NOT NULL, username VARCHAR(80) NOT NULL, email VARCHAR(120) NOT NULL, password_hash VARCHAR(255) NOT NULL, display_name VARCHAR(120) NOT NULL, bio TEXT, city VARCHAR(80), country VARCHAR(80), address_line1 VARCHAR(200), postal_code VARCHAR(30), favorite_format VARCHAR(40), favorite_scene_id INTEGER, created_at DATETIME, PRIMARY KEY (id), FOREIGN KEY(favorite_scene_id) REFERENCES scenes (id)); +CREATE TABLE wishlist_items (id INTEGER NOT NULL, user_id INTEGER NOT NULL, album_id INTEGER, merch_item_id INTEGER, added_at DATETIME, PRIMARY KEY (id), FOREIGN KEY(user_id) REFERENCES users (id), FOREIGN KEY(album_id) REFERENCES albums (id), FOREIGN KEY(merch_item_id) REFERENCES merch_items (id)); +CREATE UNIQUE INDEX ix_albums_slug ON albums (slug); +CREATE UNIQUE INDEX ix_artists_slug ON artists (slug); +CREATE UNIQUE INDEX ix_genres_slug ON genres (slug); +CREATE UNIQUE INDEX ix_labels_slug ON labels (slug); +CREATE UNIQUE INDEX ix_merch_items_slug ON merch_items (slug); +CREATE UNIQUE INDEX ix_scenes_slug ON scenes (slug); +CREATE UNIQUE INDEX ix_tags_slug ON tags (slug); +CREATE UNIQUE INDEX ix_tracks_slug ON tracks (slug); +CREATE UNIQUE INDEX ix_users_email ON users (email); +CREATE UNIQUE INDEX ix_users_username ON users (username); diff --git a/sites/bandcamp/verify/test_task9_11.py b/sites/bandcamp/verify/test_task9_11.py new file mode 100644 index 00000000..3fefeda7 --- /dev/null +++ b/sites/bandcamp/verify/test_task9_11.py @@ -0,0 +1,299 @@ +"""9/11 专项:完整 schema、变体真值、反例与独立 standalone exit 检查。""" +from contextlib import closing +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import shutil +import sqlite3 +import subprocess +import sys +import tempfile +import unittest + +import verify_lib as v +import test_verifiers as legacy + +HERE = Path(__file__).resolve().parent +NEW = 'BC-20260426-0002' +OLD = 'BC-20260414-0001' +REAL9 = 'Signed in as Alice Johnson. Her most recent seeded order number is BC-20260426-0002, dated April 26, 2026.' +REAL11 = 'The release is Elastic Hearts by Fever Arcade (London, pop), released December 5, 2025. It offers two physical formats: Colored Vinyl — 12-inch, Ocean Blue ($24.50), and Compact Disc — Gatefold ($17.00).' + +POS9 = [ + NEW, REAL9, NEW.lower(), '**bc ‑ 20260426 – 0002**', 'BC-20260426-0002', + f'Her latest order number is {NEW}.', f'{NEW} is the latest.', + f'The latest order is {NEW}, dated April 26, 2026.', + f'Latest: {NEW}, not {OLD}.', f'Latest: {NEW}; older order: {OLD}.', + f'{OLD} is older; {NEW} is latest.', f'Not {OLD}; {NEW}.', + f'Latest: {NEW}, compared with older order {OLD}.', + f'{NEW}; {NEW}.', f'{NEW} on 2026-04-26.', +] +NEG9 = [ + '', OLD, 'BC-20260425-0005', 'BC-20990101-9999', 'BC-20260426-00020', + 'XBC-20260426-0002', NEW + '-extra', NEW + 'x', 'BC-20260426-000', + f'Not {NEW}.', f'{NEW} is not latest.', f'Her latest order is not {NEW}.', + f'{NEW}, {OLD}', f'Either {NEW} or {OLD}.', f'{NEW} or unknown.', + f'Latest: {OLD}; older order: {NEW}.', f'Latest: {NEW}; latest: {OLD}.', + f'{NEW}. Not {NEW}.', f'It is false that {NEW}.', + f"Bob Chen's latest order is {NEW}.", f'Signed in as Bob Chen. {NEW}.', + f'{NEW}, dated April 14, 2026.', f'Maybe {NEW}.', f'{NEW}?', + f'Older order: {NEW}.', f'I saw {NEW} but cannot tell which is latest.', + f'Latest: {NEW}; older order BC-20260425-0005.', + f'orderref999; {NEW}', f'orderref0; {NEW}', +] +POS11 = [ + REAL11, 'CD and vinyl', 'Compact Disc and Colored Vinyl.', 'compact disk and LP', + 'CDs & vinyl records', 'CD / vinyl', 'Elastic Hearts offers CD and vinyl.', + 'The physical formats are CD and vinyl.', 'It offers 2 physical formats: CD and vinyl.', + 'It offers both physical formats: CD and vinyl.', + 'Not cassette, CD and vinyl.', 'CD and vinyl, not cassette.', + 'CD and vinyl; no MiniDisc.', 'CD and vinyl. Digital is not physical.', + 'CD and vinyl (digital is not physical).', 'CD and vinyl; digital does not count as a physical format.', + 'CD and vinyl, not digital.', 'CD is available; vinyl is offered.', + 'Colored Vinyl / 12-inch / Ocean Blue ($24.50), Compact Disc / Gatefold ($17).', + 'CD — Gatefold, vinyl — 12-inch, Ocean Blue.', 'CD and CD and vinyl.', + 'The artist is Fever Arcade, from London. CD and vinyl.', + 'CD ($17.00 USD) and LP ($24.5).', 'CD\n- Vinyl', +] +NEG11 = [ + '', 'CD', 'vinyl', 'Colored Vinyl and LP', 'CD and Compact Disc', + 'CD, vinyl and cassette.', 'CD, vinyl and digital.', 'CD, vinyl and MiniDisc.', + 'CD, vinyl and USB.', 'CD, vinyl and DVD.', 'CD, vinyl and holographic cube.', + 'CD and vinyl. It offers holographic cube.', + 'Not CD and vinyl.', 'Neither CD nor vinyl.', 'No CD, no vinyl.', + 'CD and vinyl but no CD.', 'CD and vinyl; CD is unavailable.', + 'CD is not available; vinyl is offered.', 'Vinyl and CD (not offered).', + 'CD and vinyl are not available.', 'CD and vinyl but it is false that CD is offered.', + 'Either CD or vinyl.', 'CD or vinyl.', 'Maybe CD and vinyl.', + 'Tidal Memory offers CD and vinyl.', "Tidal Memory's physical formats are CD and vinyl.", + 'For Tidal Memory: CD and vinyl.', 'Elastic Hearts has no physical formats. Tidal Memory has CD and vinyl.', + 'CD and vinyl, but not for Elastic Hearts.', 'Not Elastic Hearts. CD and vinyl.', + 'The release is Imaginary Album. Imaginary Album offers CD and vinyl.', + 'The release is Imaginary Album. CD and vinyl.', + 'CD and vinyl. None are available.', 'It offers no physical formats: CD and vinyl.', + 'Not physical formats are CD and vinyl.', 'It offers three physical formats: CD and vinyl.', + 'CD — Ocean Blue and vinyl — Gatefold.', 'CD ($24.50) and vinyl ($17.00).', + 'CD and vinyl — 12-inch, Red.', 'CD and vinyl and', + 'I cannot confirm CD and vinyl.', 'CD and vinyl?', 'CD-ROM and vinyl.', + 'CD ($17.0017.00) and vinyl.', 'CD and vinyl (12-inchOcean Blue).', +] + + +class FocusedTests(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + self.addCleanup(self.tmp.cleanup) + self.root = Path(self.tmp.name) + self.before = self.root / 'initial.db' + self.after = self.root / 'after.db' + self.sql(self.before, (HERE / 'test_seed.sql').read_text()) + shutil.copyfile(self.before, self.after) + + def sql(self, path, sql): + with closing(sqlite3.connect(path)) as db: + db.executescript(sql) + db.commit() + + def reseed(self, sql): + self.sql(self.before, sql) + shutil.copyfile(self.before, self.after) + + def verdict(self, index, answer, urls=None): + return v.evaluate(index, legacy.trajectory(index, answer, urls), self.before, self.after) + + def test9_truth_is_account_and_time_not_id_or_number(self): + # A smaller row ID and lexically smaller, differently shaped number win. + self.reseed("UPDATE orders SET order_number='ZX-7-A', placed_at='2027-01-03 00:00:00.000000' WHERE id=1; UPDATE orders SET placed_at='2099-01-01 00:00:00.000000' WHERE user_id!=1") + self.assertTrue(self.verdict(9, 'ZX-7-A')['pass']) + self.assertFalse(self.verdict(9, NEW)['pass']) + self.assertTrue(self.verdict(9, 'ZX-7-A', [legacy.ORIGIN + '/orders/ZX-7-A'])['pass']) + self.assertFalse(self.verdict(9, 'ZX-7-A', [legacy.ORIGIN + '/orders/' + NEW])['pass']) + + def test9_same_day_precise_time(self): + self.reseed("UPDATE orders SET placed_at='2026-04-26 12:00:00.000001' WHERE id=1") + self.assertTrue(self.verdict(9, OLD)['pass']) + self.assertFalse(self.verdict(9, NEW)['pass']) + + def test9_invalid_fixture_is_infra_even_for_empty_answer(self): + for sql in [ + "UPDATE users SET email='elsewhere@test.com' WHERE email='alice.j@test.com'", + "UPDATE orders SET user_id=2 WHERE user_id=1", + "UPDATE orders SET placed_at='2026-04-26 12:00:00.000000' WHERE user_id=1", + "UPDATE orders SET placed_at='not a date' WHERE id=2", + "UPDATE orders SET placed_at='2026-02-30 12:00:00' WHERE id=2", + "UPDATE orders SET placed_at='2026-04-26 12:00:00' WHERE id=1", + "UPDATE orders SET order_number='bc-20260426-0002' WHERE id=1", + ]: + with self.subTest(sql=sql): + data = v.snapshot(self.before)[0] + tmp = self.root / 'invalid.db' + shutil.copyfile(self.before, tmp) + self.sql(tmp, sql) + with self.assertRaises(ValueError): + v.evaluate(9, legacy.trajectory(9, ''), tmp, tmp) + self.assertEqual(data, v.snapshot(self.before)[0]) + + def test9_protected_paths_exact(self): + for path in ['/account', '/orders', '/orders/' + NEW]: + with self.subTest(path=path): + self.assertTrue(self.verdict(9, NEW, [legacy.ORIGIN + path])['pass']) + for path in ['/account/edit', '/accounting', '/account/child', '/account/', '/orders/' + OLD, + '/orders/' + NEW + '/extra', '/login?next=/account', '/?next=/orders', '/orderlist', '/orderdetail']: + with self.subTest(path=path): + self.assertFalse(self.verdict(9, NEW, [legacy.ORIGIN + path])['pass']) + + def test_both_origins_pages_and_identity(self): + for i, answer, path in [(9, NEW, '/account'), (11, 'CD and vinyl', '/album/elastic-hearts')]: + for url in ['https://evil.example' + path, 'http://127.0.0.1:47931' + path, + 'https://127.0.0.1:47930' + path, 'http://evil@127.0.0.1:47930' + path, + legacy.ORIGIN + '/login?next=' + path, legacy.ORIGIN + '/album/tidal-memory']: + with self.subTest(task=i, url=url): + self.assertFalse(self.verdict(i, answer, [url])['pass']) + for task_id in ['Bandcamp--0', 'wrong']: + traj = dict(legacy.trajectory(i, answer), task_id=task_id) + with self.assertRaises(ValueError): + v.evaluate(i, traj, self.before, self.after) + + def test11_different_physical_set_and_zero_stock(self): + self.reseed("UPDATE format_variants SET kind='cassette',name='Cobalt Tape',option_a='',option_b='',inventory=0 WHERE album_id=19 AND kind='cd'; UPDATE format_variants SET inventory=0 WHERE album_id=19") + self.assertTrue(self.verdict(11, 'Cobalt Tape and LP')['pass']) + self.assertTrue(self.verdict(11, 'cassette and vinyl')['pass']) + self.assertFalse(self.verdict(11, 'CD and vinyl')['pass']) + self.assertFalse(self.verdict(11, 'cassette, vinyl and digital')['pass']) + + def test11_third_kind_and_kind_dedup(self): + self.reseed("INSERT INTO format_variants SELECT 10000,album_id,merch_item_id,'minidisc','Mini Disc',option_a,option_b,price,0,'extra-md',shipping_note,edition_note,0 FROM format_variants WHERE album_id=19 AND kind='cd'") + self.assertTrue(self.verdict(11, 'CD, vinyl and MiniDisc')['pass']) + self.assertTrue(self.verdict(11, 'It offers three physical formats: CD, vinyl and MiniDisc')['pass']) + self.assertFalse(self.verdict(11, 'CD and vinyl')['pass']) + self.reseed("UPDATE format_variants SET kind='cd',name='Bonus Disc' WHERE id=10000") + self.assertTrue(self.verdict(11, 'CD and vinyl')['pass']) + self.assertTrue(self.verdict(11, 'Bonus Disc and vinyl')['pass']) + + def test11_different_name_price_and_options(self): + self.reseed("UPDATE format_variants SET name='Archival Disc',option_a='Booklet',price=19.25 WHERE album_id=19 AND kind='cd'") + self.assertTrue(self.verdict(11, 'Archival Disc / Booklet ($19.25), LP')['pass']) + self.assertTrue(self.verdict(11, 'compact disk and vinyl')['pass']) + self.assertFalse(self.verdict(11, 'CD / Gatefold ($17.00), LP')['pass']) + + def test11_kind_case_does_not_make_digital_physical(self): + self.reseed("UPDATE format_variants SET kind='Digital' WHERE album_id=19 AND kind='digital'") + self.assertTrue(self.verdict(11, 'CD and vinyl')['pass']) + self.assertFalse(self.verdict(11, 'CD, vinyl and digital')['pass']) + + def test11_fixture_errors(self): + for sql in ["UPDATE albums SET slug='elsewhere' WHERE slug='elastic-hearts'", + "UPDATE format_variants SET name='vinyl' WHERE album_id=19 AND kind='cd'", + "UPDATE format_variants SET kind='' WHERE album_id=19 AND kind='cd'"]: + with self.subTest(sql=sql): + tmp = self.root / 'invalid.db' + shutil.copyfile(self.before, tmp) + self.sql(tmp, sql) + with self.assertRaises(ValueError): + v.evaluate(11, legacy.trajectory(11, ''), tmp, tmp) + + def test_supplemental_prose_is_not_certified(self): + result = self.verdict(11, 'The artist lives on Mars. CD and vinyl.') + self.assertTrue(result['pass']) # Only the requested format proposition. + self.assertIn('not_verified_requires_review', result['evidence'][-1]) + self.assertFalse(self.verdict(11, 'CD and vinyl. It offers unknown format X.')['pass']) + + def test_snapshot_readonly_and_all_table_mutations(self): + original_hash = hashlib.sha256(self.before.read_bytes()).hexdigest() + for i, answer in [(9, REAL9), (11, REAL11)]: + for table in v.COLUMNS: + with self.subTest(task=i, table=table): + shutil.copyfile(self.before, self.after) + self.sql(self.after, f'DELETE FROM "{table}" WHERE rowid=(SELECT MIN(rowid) FROM "{table}")') + self.assertFalse(self.verdict(i, answer)['pass']) + self.assertEqual(hashlib.sha256(self.before.read_bytes()).hexdigest(), original_hash) + + def test_standalone_codes_and_explicit_paths(self): + for i, answer in [(9, REAL9), (11, REAL11)]: + run = self.root / f'cli-{i}' + run.mkdir() + cmd = [sys.executable, '-B', str(HERE / f'verify_{i}.py'), '--run_dir', str(run), + '--initial_db', str(self.before), '--after_db', str(self.after), '--no_llm', 'True'] + for expected, traj in [(0, legacy.trajectory(i, answer)), (1, legacy.trajectory(i, '', [])), + (1, legacy.trajectory(i, 'unsupported unknown answer')), (2, {'task_id': 'wrong'})]: + (run / 'trajectory.json').write_text(json.dumps(traj)) + reply = subprocess.run(cmd, capture_output=True, text=True) + with self.subTest(task=i, exit=expected, case=traj): + self.assertEqual(reply.returncode, expected, reply.stdout + reply.stderr) + (run / 'trajectory.json').write_text(json.dumps(legacy.trajectory(i, answer))) + for sql in ['ALTER TABLE users ADD COLUMN unrequested TEXT', 'DROP TABLE order_items']: + shutil.copyfile(self.before, self.after) + self.sql(self.after, sql) + reply = subprocess.run(cmd, capture_output=True, text=True) + self.assertEqual(reply.returncode, 2, reply.stdout + reply.stderr) + self.after.unlink() + self.assertEqual(subprocess.run(cmd, capture_output=True).returncode, 2) + shutil.copyfile(self.before, self.after) + missing_initial = cmd.copy() + missing_initial[missing_initial.index('--initial_db') + 1] = str(self.root / 'missing.db') + self.assertEqual(subprocess.run(missing_initial, capture_output=True).returncode, 2) + (run / 'task.json').write_text('{"id":"wrong"}') + self.assertEqual(subprocess.run(cmd, capture_output=True).returncode, 2) + + def test9_standalone_fixture_errors_exit2(self): + run = self.root / 'fixture-cli' + run.mkdir() + (run / 'trajectory.json').write_text(json.dumps(legacy.trajectory(9, ''))) + fixture = self.root / 'fixture.db' + cmd = [sys.executable, '-B', str(HERE / 'verify_9.py'), '--run_dir', str(run), + '--initial_db', str(fixture), '--after_db', str(fixture)] + for sql in ["UPDATE orders SET placed_at='2026-04-26 12:00:00.000000' WHERE user_id=1", + "UPDATE orders SET user_id=2 WHERE user_id=1", + "UPDATE users SET email='elsewhere@test.com' WHERE email='alice.j@test.com'"]: + with self.subTest(sql=sql): + shutil.copyfile(self.before, fixture) + self.sql(fixture, sql) + reply = subprocess.run(cmd, capture_output=True, text=True) + self.assertEqual(reply.returncode, 2, reply.stdout + reply.stderr) + self.assertEqual(json.loads(reply.stdout)['reason'], 'infrastructure_error') + + @unittest.skipUnless(os.environ.get('BANDCAMP_FROZEN_SESSION'), 'requires explicit frozen audit session') + def test_actual_frozen_answers_and_inputs(self): + s = Path(os.environ['BANDCAMP_FROZEN_SESSION']) + for i, job, exact in [(9, 'job-0009-3e25a850fb60', REAL9), (11, 'job-0011-4b73af1be5bd', REAL11)]: + actor = s / 'runs/bandcamp-n5-001/jobs' / job / 'actor' + traj = json.loads((actor / 'trajectory.json').read_text()) + self.assertEqual(traj['final_answer'], exact) + result = v.evaluate(i, traj, actor / 'backend/before.db', actor / 'backend/after.db') + self.assertTrue(result['pass'], result) + + @unittest.skipUnless(os.environ.get('BANDCAMP_FROZEN_SESSION'), 'requires explicit frozen audit session') + def test_other16_differential_frozen_regression(self): + s = Path(os.environ['BANDCAMP_FROZEN_SESSION']) + spec = importlib.util.spec_from_file_location('frozen_verify', s / 'builds/bandcamp-002/context/site/sites/bandcamp/verify/verify_lib.py') + old = importlib.util.module_from_spec(spec) + spec.loader.exec_module(old) + for i in set(range(18)) - {9, 11}: + shutil.copyfile(self.before, self.after) + legacy.complete(self.after, i) + for answer in [legacy.CASES[i][0], '', 'Unknown', 'It is false that ' + legacy.CASES[i][0]]: + for urls in [None, [], [legacy.ORIGIN + '/wrong'], ['https://evil.example' + legacy.CASES[i][1]]]: + with self.subTest(task=i, answer=answer, urls=urls): + traj = legacy.trajectory(i, answer, urls) + self.assertEqual(v.evaluate(i, traj, self.before, self.after), old.evaluate(i, traj, self.before, self.after)) + + +def add_case(index, answer, expected, number): + def test(self): + result = self.verdict(index, answer) + self.assertEqual(result['pass'], expected, (answer, result)) + test.__doc__ = answer + setattr(FocusedTests, f'test_{index}_{"accept" if expected else "reject"}_{number:03d}', test) + + +for index, positives, negatives in [(9, POS9, NEG9), (11, POS11, NEG11)]: + for number, answer in enumerate(positives): + add_case(index, answer, True, number) + for number, answer in enumerate(negatives): + add_case(index, answer, False, number) + + +if __name__ == '__main__': + unittest.main() diff --git a/sites/bandcamp/verify/test_verifiers.py b/sites/bandcamp/verify/test_verifiers.py new file mode 100644 index 00000000..2f833dd8 --- /dev/null +++ b/sites/bandcamp/verify/test_verifiers.py @@ -0,0 +1,224 @@ +"""Independent full-schema synthetic seed fixtures. These are not browser E2E.""" +import json +from pathlib import Path +import shutil +import sqlite3 +import subprocess +import sys +import tempfile +import unittest + +from verify_lib import evaluate, answer_ok, STATEFUL_TASKS + +HERE = Path(__file__).parent +ORIGIN = 'http://127.0.0.1:47930' +# Manually authored responses and page observations, not verifier PASS constants. +CASES = [ + ('Tidal Memory cassette: $15.00.', '/album/tidal-memory'), + ('The third track is Between Stations.', '/album/between-stations'), + ('Pair is cheaper than Glow Pair: $22 versus $27.', '/merch/ashen-circuit-grid-slipmat'), + ('Saved.', '/album/machine-prayer'), + ('Done.', '/cart'), + ('Tidal Memory is longer at 20:10; Harbor Burn is 17:55.', '/compare/releases?right=tidal-memory&left=harbor-burn'), + ('Track 4 lasts 4:31.', '/album/tidal-memory'), + ('Checkout completed.', '/checkout'), + ('Updated.', '/account'), + ('BC-20260426-0002', '/orders'), + ('Resin Language digital price: $8.50.', '/album/resin-language'), + ('The physical formats are CD and vinyl. Digital is not physical.', '/album/elastic-hearts'), + ('The signed poster costs $27.00.', '/merch/velvet-avenue-night-shift-poster'), + ('Digital Album is cheapest at $9.50.', '/album/blue-hour-broadcast'), + ('The favorite track is Between Stations.', '/collection'), + ('The newest album is Static Bloom by Glass Choir.', '/album/static-bloom'), + ('The closing track is Wide Exit.', '/album/harbor-burn'), + ('Natural and Forest.', '/merch/salt-meadow-field-notes-tote'), +] + + +def mutate(path, sql): + with sqlite3.connect(path) as db: + db.executescript(sql) + + +def complete(path, index): + if index == 3: + mutate(path, "INSERT INTO wishlist_items VALUES (17,1,6,NULL,'2026-09-09')") + elif index == 4: + mutate(path, "INSERT INTO cart_items VALUES (10,2,NULL,7,40,1,'2026-09-09')") + elif index == 8: + mutate(path, "UPDATE users SET city='Eugene',favorite_format='vinyl' WHERE id=4") + elif index == 7: + mutate(path, """ + INSERT INTO orders VALUES (6,2,'BC-20260501-0006','paid',36.5,6.5,3.01,46.01, + 'Bob Chen','77 Fulton Market','Chicago','United States','Mock card','','2026-05-01 12:06:00.000000'); + INSERT INTO order_items (id,order_id,album_id,merch_item_id,format_variant_id,title,artist_name,image_path,variant_label,quantity,unit_price) + SELECT 10,6,a.id,NULL,55,a.title,ar.name,a.cover_image,'Digital Album / MP3 + FLAC',1,8.5 + FROM albums a JOIN artists ar ON ar.id=a.artist_id WHERE a.id=11; + INSERT INTO order_items (id,order_id,album_id,merch_item_id,format_variant_id,title,artist_name,image_path,variant_label,quantity,unit_price) + SELECT 11,6,NULL,m.id,60,m.title,ar.name,m.image,'Signed / 18x24',1,28 + FROM merch_items m JOIN artists ar ON ar.id=m.artist_id WHERE m.id=11; + DELETE FROM cart_items WHERE user_id=2; + UPDATE albums SET fan_count=fan_count+1 WHERE id=11; + UPDATE format_variants SET inventory=inventory-1 WHERE id=60; + """) + + +def trajectory(index, answer=None, urls=None): + return dict(task_id=f'Bandcamp--{index}', start_url=ORIGIN + '/', + final_answer=CASES[index][0] if answer is None else answer, + steps=[dict(url=u) for u in ([ORIGIN + CASES[index][1]] if urls is None else urls)]) + + +class VerifierTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.tmp = tempfile.TemporaryDirectory() + cls.root = Path(cls.tmp.name) + cls.before = cls.root / 'initial.db' + mutate(cls.before, (HERE / 'test_seed.sql').read_text()) + cls.after = {} + for i in range(18): + cls.after[i] = cls.root / f'after-{i}.db' + shutil.copyfile(cls.before, cls.after[i]) + complete(cls.after[i], i) + + @classmethod + def tearDownClass(cls): + cls.tmp.cleanup() + + def verdict(self, i, answer=None, urls=None, after=None): + return evaluate(i, trajectory(i, answer, urls), self.before, after or self.after[i]) + + def test_all_task_matrix(self): + for i in range(18): + with self.subTest(task=i): + self.assertTrue(self.verdict(i)['pass']) + self.assertFalse(self.verdict(i, '', [ORIGIN + '/'], self.before)['pass']) + self.assertFalse(self.verdict(i, urls=[])['pass']) + self.assertFalse(self.verdict(i, urls=['https://evil.example' + CASES[i][1]])['pass']) + self.assertFalse(self.verdict(i, urls=[ORIGIN + '/?next=' + CASES[i][1]])['pass']) + self.assertFalse(self.verdict(i, urls=['http://127.0.0.1:47931' + CASES[i][1]])['pass']) + if i in STATEFUL_TASKS: + self.assertFalse(self.verdict(i, after=self.before)['pass']) + self.assertTrue(self.verdict(i, '\u5df2\u5b8c\u6210\u3002')['pass']) + # State wording does not substitute for or invalidate authoritative state. + self.assertTrue(self.verdict(i, 'An unrelated sentence.')['pass']) + else: + self.assertFalse(self.verdict(i, 'Unknown; I did not find an answer.')['pass']) + self.assertFalse(self.verdict(i, 'It is false that ' + CASES[i][0])['pass']) + wrong = trajectory(i) + wrong['task_id'] = f'Bandcamp--{(i + 1) % 18}' + with self.assertRaises(ValueError): + evaluate(i, wrong, self.before, self.after[i]) + + def test_information_near_misses(self): + negatives = { + 0: ['The cassette is not $15.00.', 'The cassette costs $8.50.', "Harbor Burn's cassette costs $15.00.", 'Cassette is $15. Cassette is not $15.'], + 1: ['The album is Between Stations.', 'The second track is Between Stations.'], + 2: ['Glow Pair is cheaper than Pair.', 'Pair is not cheaper.', 'Pair costs $27 and Glow Pair $22; Pair is cheaper.'], + 5: ['Tidal Memory is not longer.', 'Harbor Burn is longer than Tidal Memory.', 'Tidal Memory is longer at 17:55, Harbor Burn at 20:10.'], + 6: ['Track 4 is not 4:31.', 'Track 4 lasts 4:30.'], + 9: ['Not BC-20260426-0002.', 'BC-20260414-0001'], + 10: ['The digital album is not $8.50.', 'Digital: $9.50.'], + 11: ['CD, vinyl and cassette.', 'CD, vinyl and digital.', 'Neither CD nor vinyl.', 'CD, vinyl and USB.'], + 12: ['Signed is not $27.00.', 'Standard costs $27.00.'], + 13: ['Digital is the most expensive at $9.50.', 'Digital is not $9.50.'], + 14: ['The album is Between Stations.', 'The favorite track is not Between Stations.'], + 15: ['Static Bloom is not the newest album.', 'The oldest is Static Bloom.'], + 16: ['Wide Exit is not the closing track.', 'The opening track is Wide Exit.'], + 17: ['Natural, Forest and Black.', 'Neither Natural nor Forest.'], + } + for i, answers in negatives.items(): + for answer in answers: + with self.subTest(task=i, answer=answer): + self.assertFalse(self.verdict(i, answer)['pass']) + + def test_legal_alternatives(self): + self.assertTrue(self.verdict(15, urls=[ORIGIN + '/search?q=guitar+songs', ORIGIN + '/artist/glass-choir', ORIGIN + '/album/static-bloom'])['pass']) + for answer in ['Pair is cheaper, not Glow Pair.', 'Pair is cheaper than Glow Pair.', 'Glow Pair is not cheaper. Pair is cheaper at $22.']: + self.assertTrue(self.verdict(2, answer)['pass'], answer) + self.assertTrue(self.verdict(11, 'CD and vinyl, not digital.')['pass']) + self.assertTrue(self.verdict(11, 'CD and vinyl (digital is not physical).')['pass']) + self.assertTrue(self.verdict(5, 'Tidal Memory is not shorter than Harbor Burn.')['pass']) + for i, answer in [(0, '$15.00'), (2, 'Pair ($22.00)'), (5, 'Tidal Memory'), (10, '$8.50'), (12, '$27.00'), + (17, 'The two colors available for the tote are Natural and Forest.')]: + with self.subTest(task=i, answer=answer): + self.assertTrue(self.verdict(i, answer)['pass']) + + def test_extra_mutation_every_table(self): + for i in range(18): + for table in ('users', 'artists', 'albums', 'tracks', 'cart_items', 'orders', 'order_items', 'wishlist_items', + 'fan_collection_items', 'fan_comments', 'format_variants', 'genres', 'labels', 'scenes', 'tags', 'merch_items', 'album_tags'): + with self.subTest(task=i, table=table): + path = self.root / 'mutated.db' + shutil.copyfile(self.after[i], path) + mutate(path, f'DELETE FROM {table} WHERE rowid=(SELECT MIN(rowid) FROM {table})') + self.assertFalse(self.verdict(i, after=path)['pass']) + + def test_state_specific_mutations(self): + cases = { + 3: ["UPDATE wishlist_items SET user_id=2 WHERE id=17", "UPDATE wishlist_items SET album_id=1 WHERE id=17", "INSERT INTO wishlist_items VALUES (18,1,6,NULL,'extra')"], + 4: ["UPDATE cart_items SET user_id=1 WHERE id=10", "UPDATE cart_items SET quantity=2 WHERE id=10", "UPDATE cart_items SET quantity=1.5 WHERE id=10", "UPDATE cart_items SET format_variant_id=39 WHERE id=10", "UPDATE cart_items SET album_id=1 WHERE id=10", "INSERT INTO cart_items VALUES (11,2,NULL,7,40,1,'extra')"], + 7: ["UPDATE orders SET user_id=1 WHERE id=6", "UPDATE orders SET total=0 WHERE id=6", "UPDATE orders SET shipping_name='Alice' WHERE id=6", "UPDATE order_items SET quantity=2 WHERE id=11", "UPDATE order_items SET format_variant_id=59 WHERE id=11", "UPDATE order_items SET order_id=1 WHERE id=11", "UPDATE order_items SET unit_price=1 WHERE id=11", "UPDATE format_variants SET inventory=25 WHERE id=60", "UPDATE users SET bio='extra' WHERE id=2", "INSERT INTO orders SELECT 7,user_id,'extra',status,subtotal,shipping,tax,total,shipping_name,shipping_line1,shipping_city,shipping_country,payment_label,note,placed_at FROM orders WHERE id=6"], + 8: ["UPDATE users SET city='Portland' WHERE id=4", "UPDATE users SET favorite_format='Vinyl' WHERE id=4", "UPDATE users SET address_line1='extra' WHERE id=4", "UPDATE users SET city='Eugene' WHERE id=1"], + } + for i, sqls in cases.items(): + for sql in sqls: + with self.subTest(task=i, sql=sql): + path = self.root / 'mutated.db' + shutil.copyfile(self.after[i], path) + mutate(path, sql) + self.assertFalse(self.verdict(i, after=path)['pass']) + + def test_task4_full_noncart_fields_remain_protected(self): + with sqlite3.connect(self.before) as db: + columns = [(table, r[1], r[2]) for (table,) in db.execute("SELECT name FROM sqlite_master WHERE type='table' AND name!='cart_items'") + for r in db.execute(f'PRAGMA table_info("{table}")')] + for table, column, dtype in columns: + with self.subTest(table=table, column=column): + path = self.root / 'fields.db' + shutil.copyfile(self.after[4], path) + value = f'COALESCE("{column}",0)+10000' if dtype == 'INTEGER' else "'unrequested'" + mutate(path, f'UPDATE "{table}" SET "{column}"={value} WHERE rowid=(SELECT MIN(rowid) FROM "{table}")') + self.assertFalse(self.verdict(4, after=path)['pass']) + + def test_wrapper_codes_and_snapshot_discovery(self): + for i in range(18): + run = self.root / f'cli-{i}' + run.mkdir() + shutil.copyfile(self.before, run / 'before.db') + shutil.copyfile(self.after[i], run / 'after.db') + command = [sys.executable, '-B', str(HERE / f'verify_{i}.py'), '--run_dir', str(run)] + for expected, traj in [(0, trajectory(i)), (1, trajectory(i, '', [])), (2, dict(trajectory(i), task_id='wrong'))]: + (run / 'trajectory.json').write_text(json.dumps(traj)) + reply = subprocess.run(command, capture_output=True, text=True) + with self.subTest(task=i, exit=expected): + self.assertEqual(reply.returncode, expected, reply.stdout + reply.stderr) + result = json.loads(reply.stdout) + self.assertIs(result['pass'], expected == 0) + (run / 'trajectory.json').write_text(json.dumps(trajectory(i))) + (run / 'after.db').unlink() + self.assertEqual(subprocess.run(command, capture_output=True).returncode, 2) + (run / 'after.db').write_text('not sqlite') + self.assertEqual(subprocess.run(command, capture_output=True).returncode, 2) + shutil.copyfile(self.after[i], run / 'after.db') + mutate(run / 'after.db', 'ALTER TABLE users ADD COLUMN surprise TEXT') + self.assertEqual(subprocess.run(command, capture_output=True).returncode, 2) + (run / 'trajectory.json').write_text('{broken') + self.assertEqual(subprocess.run(command, capture_output=True).returncode, 2) + + def test_mapping_and_rubrics(self): + tasks = [json.loads(line) for line in (HERE.parent / 'tasks.jsonl').read_text().splitlines()] + self.assertEqual(len(tasks), 18) + for i, task in enumerate(tasks): + self.assertEqual(task['id'], f'Bandcamp--{i}') + self.assertEqual(task['verifier_path'], f'sites/bandcamp/verify/verify_{i}.py') + self.assertTrue((HERE / f'verify_{i}.py').is_file()) + self.assertNotIn('answer', task) + self.assertNotIn('$', task['judge_rubric']) + self.assertNotIn('BC-2026', task['judge_rubric']) + self.assertTrue(task['judge_rubric'].startswith('FACT CHECKPOINTS:')) + + +if __name__ == '__main__': + unittest.main() diff --git a/sites/bandcamp/verify/verify_0.py b/sites/bandcamp/verify/verify_0.py new file mode 100644 index 00000000..f050ff59 --- /dev/null +++ b/sites/bandcamp/verify/verify_0.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(0) diff --git a/sites/bandcamp/verify/verify_1.py b/sites/bandcamp/verify/verify_1.py new file mode 100644 index 00000000..0edeec9d --- /dev/null +++ b/sites/bandcamp/verify/verify_1.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(1) diff --git a/sites/bandcamp/verify/verify_10.py b/sites/bandcamp/verify/verify_10.py new file mode 100644 index 00000000..84d64164 --- /dev/null +++ b/sites/bandcamp/verify/verify_10.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(10) diff --git a/sites/bandcamp/verify/verify_11.py b/sites/bandcamp/verify/verify_11.py new file mode 100644 index 00000000..b0bb758f --- /dev/null +++ b/sites/bandcamp/verify/verify_11.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(11) diff --git a/sites/bandcamp/verify/verify_12.py b/sites/bandcamp/verify/verify_12.py new file mode 100644 index 00000000..663ce83e --- /dev/null +++ b/sites/bandcamp/verify/verify_12.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(12) diff --git a/sites/bandcamp/verify/verify_13.py b/sites/bandcamp/verify/verify_13.py new file mode 100644 index 00000000..1a1316c9 --- /dev/null +++ b/sites/bandcamp/verify/verify_13.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(13) diff --git a/sites/bandcamp/verify/verify_14.py b/sites/bandcamp/verify/verify_14.py new file mode 100644 index 00000000..5f98be65 --- /dev/null +++ b/sites/bandcamp/verify/verify_14.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(14) diff --git a/sites/bandcamp/verify/verify_15.py b/sites/bandcamp/verify/verify_15.py new file mode 100644 index 00000000..844aafe9 --- /dev/null +++ b/sites/bandcamp/verify/verify_15.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(15) diff --git a/sites/bandcamp/verify/verify_16.py b/sites/bandcamp/verify/verify_16.py new file mode 100644 index 00000000..762eb783 --- /dev/null +++ b/sites/bandcamp/verify/verify_16.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(16) diff --git a/sites/bandcamp/verify/verify_17.py b/sites/bandcamp/verify/verify_17.py new file mode 100644 index 00000000..449e41c7 --- /dev/null +++ b/sites/bandcamp/verify/verify_17.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(17) diff --git a/sites/bandcamp/verify/verify_2.py b/sites/bandcamp/verify/verify_2.py new file mode 100644 index 00000000..67bc72e6 --- /dev/null +++ b/sites/bandcamp/verify/verify_2.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(2) diff --git a/sites/bandcamp/verify/verify_3.py b/sites/bandcamp/verify/verify_3.py new file mode 100644 index 00000000..e1f8b038 --- /dev/null +++ b/sites/bandcamp/verify/verify_3.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(3) diff --git a/sites/bandcamp/verify/verify_4.py b/sites/bandcamp/verify/verify_4.py new file mode 100644 index 00000000..48176f43 --- /dev/null +++ b/sites/bandcamp/verify/verify_4.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(4) diff --git a/sites/bandcamp/verify/verify_5.py b/sites/bandcamp/verify/verify_5.py new file mode 100644 index 00000000..7e86072b --- /dev/null +++ b/sites/bandcamp/verify/verify_5.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(5) diff --git a/sites/bandcamp/verify/verify_6.py b/sites/bandcamp/verify/verify_6.py new file mode 100644 index 00000000..373abfe8 --- /dev/null +++ b/sites/bandcamp/verify/verify_6.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(6) diff --git a/sites/bandcamp/verify/verify_7.py b/sites/bandcamp/verify/verify_7.py new file mode 100644 index 00000000..6227fcea --- /dev/null +++ b/sites/bandcamp/verify/verify_7.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(7) diff --git a/sites/bandcamp/verify/verify_8.py b/sites/bandcamp/verify/verify_8.py new file mode 100644 index 00000000..96cdd664 --- /dev/null +++ b/sites/bandcamp/verify/verify_8.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(8) diff --git a/sites/bandcamp/verify/verify_9.py b/sites/bandcamp/verify/verify_9.py new file mode 100644 index 00000000..233551cf --- /dev/null +++ b/sites/bandcamp/verify/verify_9.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 +from verify_lib import main + +if __name__ == "__main__": + main(9) diff --git a/sites/bandcamp/verify/verify_lib.py b/sites/bandcamp/verify/verify_lib.py new file mode 100644 index 00000000..c30cfd21 --- /dev/null +++ b/sites/bandcamp/verify/verify_lib.py @@ -0,0 +1,633 @@ +#!/usr/bin/env python3 +"""Snapshot-only grading. Healthy task failures are distinct from input errors.""" +import argparse +from collections import Counter +from contextlib import closing +from copy import deepcopy +from datetime import datetime +import json +from pathlib import Path +import re +import sqlite3 +import sys +import unicodedata +from urllib.parse import parse_qs, urlsplit + +TASK_COUNT = 18 +STATEFUL_TASKS = {3, 4, 7, 8} +# Every business column participates in equality, including unqueried fields. +COLUMNS = { + 'album_tags': 'album_id tag_id', + 'albums': 'id artist_id label_id primary_genre_id scene_id title slug description story cover_image header_image price release_date track_count duration_seconds fan_count catalog_no is_featured is_new is_editorial', + 'artists': 'id name slug location bio headline formed_year follow_count avatar_image hero_image scene_id label_id primary_genre_id', + 'cart_items': 'id user_id album_id merch_item_id format_variant_id quantity added_at', + 'fan_collection_items': 'id user_id album_id format_variant_id favorite_track_id acquired_via notes added_at', + 'fan_comments': 'id user_id album_id track_id headline body rating created_at', + 'format_variants': 'id album_id merch_item_id kind name option_a option_b price inventory sku shipping_note edition_note is_default', + 'genres': 'id name slug description accent_color', + 'labels': 'id name slug location description', + 'merch_items': 'id artist_id album_id title slug item_type description short_blurb image price inventory release_date is_featured', + 'order_items': 'id order_id album_id merch_item_id format_variant_id title artist_name image_path variant_label quantity unit_price', + 'orders': 'id user_id order_number status subtotal shipping tax total shipping_name shipping_line1 shipping_city shipping_country payment_label note placed_at', + 'scenes': 'id name slug country description', + 'tags': 'id name slug', + 'tracks': 'id album_id title slug track_number duration_seconds preview_hook lyrics_excerpt is_focus_track', + 'users': 'id username email password_hash display_name bio city country address_line1 postal_code favorite_format favorite_scene_id created_at', + 'wishlist_items': 'id user_id album_id merch_item_id added_at', +} + + +def snapshot(path): + path = Path(path).resolve() + if not path.is_file(): + raise ValueError('missing snapshot: ' + str(path)) + with closing(sqlite3.connect(path.as_uri() + '?mode=ro', uri=True)) as db: + if db.execute('PRAGMA integrity_check').fetchall() != [('ok',)]: + raise ValueError('snapshot integrity failure') + schema = db.execute('SELECT type,name,tbl_name,sql FROM sqlite_master ORDER BY type,name').fetchall() + if {r[1] for r in schema if r[0] == 'table'} != set(COLUMNS): + raise ValueError('snapshot table contract mismatch') + result = {} + for table, columns in COLUMNS.items(): + cursor = db.execute(f'SELECT * FROM "{table}"') + names = [c[0] for c in cursor.description] + if names != columns.split(): + raise ValueError('snapshot column contract mismatch: ' + table) + result[table] = [dict(zip(names, r)) for r in cursor.fetchall()] + return result, schema, db.execute('PRAGMA foreign_key_check').fetchall() + + +def rows_equal(left, right): + return Counter(tuple(sorted(r.items())) for r in left) == Counter(tuple(sorted(r.items())) for r in right) + + +def unchanged(before, after): + return all(rows_equal(before[t], after[t]) for t in before) + + +def one(rows, **where): + matches = [r for r in rows if all(r[k] == v for k, v in where.items())] + if len(matches) != 1: + raise ValueError('missing/ambiguous authoritative target: ' + repr(where)) + return matches[0] + + +def additions(before, after): + old = Counter(tuple(r.items()) for r in before) + new = Counter(tuple(r.items()) for r in after) + if old - new: + return None + return [dict(r) for r in (new - old).elements()] + + +def state_ok(index, before, after): + expected = deepcopy(before) + email = {3: 'alice.j@test.com', 4: 'bob.c@test.com', 7: 'bob.c@test.com', 8: 'david.k@test.com'}[index] + user = one(expected['users'], email=email) + uid = user['id'] + if index in (3, 4): + table = 'wishlist_items' if index == 3 else 'cart_items' + added = additions(before[table], after[table]) + if added is None or len(added) != 1: + return False + if index == 3: + album = one(before['albums'], slug='machine-prayer') + if any(r['user_id'] == uid and r['album_id'] == album['id'] for r in before[table]): + return False + fields = dict(user_id=uid, album_id=album['id'], merch_item_id=None) + else: + merch = one(before['merch_items'], slug='soft-locale-drift-hoodie') + variant = one(before['format_variants'], merch_item_id=merch['id'], album_id=None, option_a='M', option_b='Stone') + if any(r['user_id'] == uid and r['merch_item_id'] == merch['id'] for r in before[table]): + return False + fields = dict(user_id=uid, album_id=None, merch_item_id=merch['id'], format_variant_id=variant['id'], quantity=1) + if not all(added[0][k] == v for k, v in fields.items()): + return False + expected[table].append(added[0]) # Only the new id/timestamp are generated. + elif index == 8: + if (user['city'], user['favorite_format']) != ('Portland', 'shirt'): + return False + user.update(city='Eugene', favorite_format='vinyl') + else: + cart = [r for r in before['cart_items'] if r['user_id'] == uid] + new_orders = additions(before['orders'], after['orders']) + if not cart or new_orders is None or len(new_orders) != 1: + return False + order = new_orders[0] + if (order['user_id'] != uid or order['status'] != 'paid' + or order['shipping_line1'] != '77 Fulton Market' + or order['shipping_city'] != 'Chicago' or order['shipping_country'] != 'United States' + or order['shipping_name'] != user['display_name'] + or not isinstance(order['payment_label'], str) or not order['payment_label'].strip() + or not order['order_number'] or not order['placed_at']): + return False + new_items = additions(before['order_items'], after['order_items']) + if new_items is None or len(new_items) != len(cart): + return False + wanted_items, wanted_collection = [], [] + subtotal = 0 + for item in cart: + if type(item['quantity']) is not int or item['quantity'] <= 0: + return False + album = one(expected['albums'], id=item['album_id']) if item['album_id'] else None + merch = one(expected['merch_items'], id=item['merch_item_id']) if item['merch_item_id'] else None + if bool(album) == bool(merch): + return False + subject = album or merch + variant = one(expected['format_variants'], id=item['format_variant_id']) if item['format_variant_id'] else None + if variant and (variant['album_id'], variant['merch_item_id']) != (item['album_id'], item['merch_item_id']): + return False + price = variant['price'] if variant else subject['price'] + label = ' / '.join(v for v in (variant['name'], variant['option_a'], variant['option_b']) if v) if variant else 'Standard' + artist = one(before['artists'], id=subject['artist_id']) + wanted_items.append(dict(order_id=order['id'], album_id=item['album_id'], merch_item_id=item['merch_item_id'], + format_variant_id=item['format_variant_id'], title=subject['title'], artist_name=artist['name'], + image_path=subject['cover_image'] if album else subject['image'], variant_label=label, + quantity=item['quantity'], unit_price=price)) + subtotal += price * item['quantity'] + if album: + album['fan_count'] += item['quantity'] + if not any(r['user_id'] == uid and r['album_id'] == album['id'] for r in expected['fan_collection_items'] + wanted_collection): + tracks = [r for r in before['tracks'] if r['album_id'] == album['id']] + focus = next((r for r in tracks if r['is_focus_track']), tracks[0] if tracks else None) + wanted_collection.append(dict(user_id=uid, album_id=album['id'], format_variant_id=item['format_variant_id'], + favorite_track_id=focus['id'] if focus else None, acquired_via='purchase', + notes='Added from checkout', added_at='2026-05-01 12:00:00.000000')) + if variant and variant['inventory'] < 9999: + variant['inventory'] = max(0, variant['inventory'] - item['quantity']) + elif merch: + merch['inventory'] = max(0, merch['inventory'] - item['quantity']) + # Compare full payloads, not title sets; variants, quantities and FKs matter. + if not rows_equal(wanted_items, [{k: v for k, v in r.items() if k != 'id'} for r in new_items]): + return False + collected = additions(before['fan_collection_items'], after['fan_collection_items']) + if collected is None or not rows_equal(wanted_collection, [{k: v for k, v in r.items() if k != 'id'} for r in collected]): + return False + subtotal = round(subtotal, 2) + shipping = 0.0 if subtotal >= 45 or subtotal == 0 else 6.5 + tax = round(subtotal * .0825, 2) + if any(order[k] != v for k, v in dict(subtotal=subtotal, shipping=shipping, tax=tax, total=round(subtotal + shipping + tax, 2)).items()): + return False + expected['cart_items'] = [r for r in before['cart_items'] if r['user_id'] != uid] + expected['orders'].append(order) + expected['order_items'].extend(new_items) + expected['fan_collection_items'].extend(collected) + user.update(address_line1=order['shipping_line1'], city=order['shipping_city'], country=order['shipping_country']) + return unchanged(expected, after) + + +PAGES = { + 0: ('/album/tidal-memory',), 1: ('/album/between-stations',), + 2: ('/merch/ashen-circuit-grid-slipmat',), 3: ('/album/machine-prayer', '/wishlist'), + 4: ('/merch/soft-locale-drift-hoodie', '/cart'), 5: ('/compare/releases',), + 6: ('/album/tidal-memory',), 7: ('/checkout', '/orders'), 8: ('/account/edit', '/account'), + 9: ('/orders', '/account'), 10: ('/album/resin-language',), + 11: ('/album/elastic-hearts',), 12: ('/merch/velvet-avenue-night-shift-poster',), + 13: ('/album/blue-hour-broadcast',), 14: ('/collection',), + 15: ('/artist/glass-choir', '/album/static-bloom'), 16: ('/album/harbor-burn',), + 17: ('/merch/salt-meadow-field-notes-tote',), +} + + +def navigation_ok(index, traj, before=None): + start = urlsplit(traj['start_url']) + if start.scheme not in ('http', 'https') or start.hostname not in ('localhost', '127.0.0.1', '::1') or start.username or start.password: + raise ValueError('invalid local start origin') + origin = (start.scheme, start.hostname, start.port) + pages = [] + for step in traj['steps']: + if not isinstance(step, dict) or not isinstance(step.get('url', ''), str): + raise ValueError('invalid step URL') + url = urlsplit(step.get('url', '')) + if not url.username and not url.password and (url.scheme, url.hostname, url.port) == origin: + pages.append(url) + if index == 5: + return any(p.path.rstrip('/') == '/compare/releases' and + {tuple(parse_qs(p.query).get(k, [])) for k in ('left', 'right')} == + {('tidal-memory',), ('harbor-burn',)} for p in pages) + if index == 9: + _, latest = order_context(before) + # URLs establish page relevance, NOT the authenticated principal. + paths = (*PAGES[9], '/orders/' + latest['order_number']) + return any(p.path in paths for p in pages) + return any(p.path.rstrip('/') in PAGES[index] for p in pages) + + +def norm(text): + return re.sub(r'\s+', ' ', text.casefold().replace('\u2019', "'").replace('**', '').replace('`', '')).strip() + + +def prose(text): + """Local normalization for 9/11 only; never rewrite stored answers or URLs.""" + text = unicodedata.normalize('NFKC', text) + text = text.translate(str.maketrans({c: '-' for c in '\u2010\u2011\u2012\u2013\u2014\u2212'})) + return norm(text) + + +def order_text(text): + return re.sub(r'\s*-\s*', '-', prose(text)) + + +def order_context(before): + if before is None: + raise ValueError('task9 requires initial snapshot context') + # Account is fixed by the task, not by an actor-supplied query/identity claim. + user = one(before['users'], email='alice.j@test.com') + orders = [r for r in before['orders'] if r['user_id'] == user['id']] + if not orders: + raise ValueError('task9 fixture: named account has no initial orders') + for row in orders: + stamp = row['placed_at'] + if not isinstance(stamp, str) or not re.fullmatch(r'\d{4}-\d\d-\d\d \d\d:\d\d:\d\d(?:\.\d{1,6})?', stamp): + raise ValueError('task9 fixture: unsupported placed_at') + datetime.fromisoformat(stamp) # Reject invalid calendar values, not just bad shapes. + if not isinstance(row['order_number'], str) or not row['order_number'].strip(): + raise ValueError('task9 fixture: missing order number') + # Mirrors ORDER BY placed_at DESC. No invented id tie-break for equal times. + stamp = max(r['placed_at'] for r in orders) + latest = [r for r in orders if r['placed_at'] == stamp] + times = [datetime.fromisoformat(r['placed_at']) for r in orders] + if len(latest) != 1 or times.count(max(times)) != 1 or datetime.fromisoformat(stamp) != max(times): + raise ValueError('task9 fixture: ambiguous latest order/page time ordering') + ids = [order_text(r['order_number']) for r in before['orders']] + if len(ids) != len(set(ids)): + raise ValueError('task9 fixture: ambiguous normalized order numbers') + return user, latest[0] + + +def order_answer(answer, before): + """Bounded affirmative-ID grammar; unknown prose fails closed (exit 1).""" + user, latest = order_context(before) + text = order_text(answer) + if re.search(r'\b(?:either|or|maybe|perhaps|possibly|guess|false|incorrect|wrong|orderref\d+)\b|\?', text): + return False, 'unsupported_or_ambiguous_order_claim' + known = {order_text(r['order_number']): r for r in before['orders']} + wanted = order_text(latest['order_number']) + # Match complete identifiers, including plausible unknown candidates. A suffix + # or prefix cannot turn a wrong ID into a match of the expected ID. + pattern = r'(?