Skip to main content

๐ŸŽต lastfm-collage-generator

Production-grade Python library for generating high-resolution visual composite image collages from Last.fm scrobble histories.

PyPI version Python Versions License: MIT uv Code style: black Checked with mypy Downloads PRs Welcome

Key Features โ€ข Grid Geometry โ€ข Architecture โ€ข Installation โ€ข Quickstart โ€ข API Reference โ€ข Developer Workflows โ€ข Testing & QA โ€ข Caching & Social Presets โ€ข GitHub Actions โ€ข Roadmap โ€ข Defect Catalog โ€ข Contributing


โœจ Key Features

  • Multi-Entity Composite Grids:
    • ๐Ÿ’ฟ Top Albums (album): Queries user scrobble history via the Last.fm Audioscrobbler REST API (pylast), downloads album cover artwork, and composites them into an aligned matrix.
    • ๐ŸŽค Top Artists (artist): Fetches top artists via the API and scrapes high-resolution artist hero imagery directly from https://www.last.fm/music/<artist> (bypassing the historical deprecation of artist images in the Last.fm API), thumbnails images to 300x300px, and composites the grid.
    • ๐ŸŽต Top Tracks (track): Queries top tracks, resolves associated album artwork or applies solid black fallbacks, and renders the composite.
  • Configurable Grid Geometry: Generates rectangular or square grids from 1x1 up to 5x5 (e.g. 3x3 standard 9-tile grid, 5x5 25-tile poster, or asymmetric 3x5). Standard tile resolution is 300 x 300 pixels.
  • Comprehensive Time Horizons: Full support for all Last.fm aggregation periods: 7day (Weekly), 1month (Monthly), 3month (Quarterly), 6month (Semi-Annual), 12month (Yearly), and overall (All-Time).
  • Translucent Monospace Typography Banners: Renders a dark translucent banner overlay (RGBA(0, 0, 0, 123)) on the bottom 65px of each tile displaying entity name, artist, and scrobble playcount in bundled TrueType monospace font (DejaVuSansMono.ttf).
  • Concurrent Image Acquisition: Utilizes Python's concurrent.futures.ThreadPoolExecutor for parallel, non-blocking downloads of artwork assets across worker threads.
  • Resilient Fallback Handling: Gracefully handles missing artwork, HTTP 404s, or network timeouts by rendering solid black 300x300 blank tiles without failing the entire collage generation process.
  • Zero External System Dependencies: TrueType fonts are bundled directly in the distribution package and loaded dynamically relative to module path.
  • Modern Tooling & Developer Experience: Built with uv and hatchling, featuring offline synthetic mock debugging runners, VS Code F5 launch profiles, and 100% offline pytest fixtures.

๐Ÿ“ Grid Dimensions & Geometry Reference

The library supports arbitrary square and rectangular grids from $1 \times 1$ up to $20 \times 20$ (up to 400 tiles), with dynamic resolution auto-scaling and configurable tile sizes:

Grid Size Total Tiles Default Tile Resolution Canvas Dimensions Aspect Ratio Megapixels Typical Use Case
1x1 1 tile 300 x 300 px 300 x 300 px 1:1 Square 0.09 MP Single item avatar / badge
3x3 9 tiles 300 x 300 px 900 x 900 px 1:1 Square 0.81 MP Standard social media card (#LastFmFriday, Instagram)
5x5 25 tiles 300 x 300 px 1500 x 1500 px 1:1 Square 2.25 MP Classic weekly / monthly listening card
8x8 64 tiles 150 x 150 px (Auto) 1200 x 1200 px 1:1 Square 1.44 MP Detailed monthly recap poster
10x10 100 tiles 150 x 150 px (Auto) 1500 x 1500 px 1:1 Square 2.25 MP High-density 100-album annual overview
20x20 400 tiles 100 x 100 px (Auto) 2000 x 2000 px 1:1 Square 4.00 MP Comprehensive mega-recap poster
3x10 30 tiles 150 x 150 px (Auto) 450 x 1500 px 3:10 Portrait 0.68 MP Mobile sidebar / Story banner
12x6 72 tiles 100 x 100 px (Auto) 1200 x 600 px 2:1 Landscape 0.72 MP Ultra-wide desktop banner

Dynamic Resolution Auto-Scaling Tiers

To keep memory footprint safe and execution fast without sacrificing visual detail:

  • Standard Density ($\le 5 \times 5$): Defaults to 300x300 px per tile.
  • Medium Density ($6 \times 6$ to $10 \times 10$): Automatically scales to 150x150 px per tile.
  • High Density ($> 10 \times 10$, up to $20 \times 20$): Automatically scales to 100x100 px per tile.
  • Custom Resolution: Explicitly supply tile_size (between 50 and 600 px) to override automatic scaling.

Proportional Tile Layout Anatomy

Overlay typography and dark translucent banners automatically scale proportionally with tile size:

(x, y) โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ”‚                                                              โ”‚
       โ”‚                   Tile Cover Artwork                         โ”‚
       โ”‚                 (Downloaded or Scraped)                      โ”‚
       โ”‚                   (S x S px, e.g. 300px)                     โ”‚
       โ”‚                                                              โ”‚
y+h_0  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ—„โ”€โ”€ Banner Top: y + (S - h_banner)
       โ”‚ Translucent Dark Banner: RGBA(0, 0, 0, 123)                  โ”‚
y+y_t  โ”‚ Monospace Typography: DejaVuSansMono.ttf (Proportional Font) โ”‚ โ—„โ”€โ”€ Text Origin: x + pad_x, y_0 + pad_y
       โ”‚ "Artist Name - Album / Track Title. (42 scrobbles)"          โ”‚
(x+S,  โ”‚                                                              โ”‚
  y+S) โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ—„โ”€โ”€ Tile Bottom (y + S)

๐Ÿ—๏ธ System Architecture & Design Patterns

The library strictly implements a 4-layer object-oriented design: Facade โ†’ Factory โ†’ Builder โ†’ Client Adapter.

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Client / Consumer Application                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                     โ”‚
                                     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 1. Facade Layer: CollageGenerator (src/collage_generator.py)           โ”‚
โ”‚    - Encapsulates LastfmConfig (API key and API secret)                 โ”‚
โ”‚    - Validates grid bounds (1 <= cols/rows <= 5), entity, and period    โ”‚
โ”‚    - Dispatches generation request to CollageBuilderFactory             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                     โ”‚
                                     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 2. Factory Layer: CollageBuilderFactory (src/collage.py)                โ”‚
โ”‚    - Inspects entity string ("album", "artist", "track")                โ”‚
โ”‚    - Instantiates and returns the corresponding concrete Builder        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                     โ”‚
        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚                            โ”‚                            โ”‚
        โ–ผ                            โ–ผ                            โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  AlbumCollageBuilder  โ”‚  โ”‚  ArtistCollageBuilder โ”‚  โ”‚  TrackCollageBuilder  โ”‚
โ”‚  - Queries pylast     โ”‚  โ”‚  - Queries pylast     โ”‚  โ”‚  - Queries pylast     โ”‚
โ”‚  - Downloads cover artโ”‚  โ”‚  - Scrapes Last.fm DOMโ”‚  โ”‚  - Resolves album art โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
            โ”‚                          โ”‚                          โ”‚
            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                       โ”‚ Inherits
                                       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 3. Builder Layer: BaseCollageBuilder (src/collage.py)                   โ”‚
โ”‚    - Template Method create(username):                                  โ”‚
โ”‚      1. _get_tiles_from_top_items() -> Parallel ThreadPoolExecutor      โ”‚
โ”‚      2. _create_image() -> Pillow RGB canvas allocation & tile pasting  โ”‚
โ”‚      3. _insert_tile_title() -> Translucent RGBA banner overlay         โ”‚
โ”‚    - Font loading (DejaVuSansMono.ttf) & text wrapping                  โ”‚
โ”‚    - Solid black (300x300) fallback tile generator                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                     โ”‚
                                     โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 4. Client Adapter Layer: LastfmClient (src/lastfm/client.py)            โ”‚
โ”‚    - Wraps pylast.LastFMNetwork, isolating API network credentials      โ”‚
โ”‚    - Exposes get_user(), get_top_albums(), get_top_artists(),           โ”‚
โ”‚      and get_top_tracks()                                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Module Responsibilities Matrix

Module Component Pattern / Architectural Role Key Responsibilities
collage_generator.py CollageGenerator Facade Public API entrypoint; credential management; input parameter boundary validation.
collage.py CollageBuilderFactory Factory Dispatches concrete builder instances based on entity string key.
collage.py BaseCollageBuilder Base Builder / Template Method Canvas lifecycle; concurrent worker pool; coordinate math; text wrapping; banner overlays.
collage.py AlbumCollageBuilder Concrete Builder Fetches top albums via pylast; downloads cover images from Last.fm CDN.
collage.py ArtistCollageBuilder Concrete Builder Scrapes last.fm/music/<artist> DOM for .header-new-background-image; thumbnails to 300x300px.
collage.py TrackCollageBuilder Concrete Builder Fetches top tracks; inherits album cover extraction and fallback mechanics.
lastfm/client.py LastfmClient Client Adapter Wraps pylast.LastFMNetwork calls; isolates third-party API types from core builders.
constants.py ENTITIES, PERIODS Domain Constants Defines supported entity tuples and Last.fm aggregation horizons.
exceptions.py ArtistNotFound, etc. Exception Hierarchy Custom domain errors for missing artists or unresolvable artwork.

๐Ÿ“ฆ Installation

Using uv (Recommended)

uv add lastfmcollagegenerator

Using pip

pip install lastfmcollagegenerator

Using pipx (Standalone execution)

pipx install lastfmcollagegenerator

Runtime Requirements

  • Python: >= 3.8 (Fully tested and verified on Python 3.8, 3.9, 3.10, 3.11, and 3.12).
  • Core Dependencies: Pillow >= 10.4.0, pylast >= 5.3.0, requests >= 2.32.3, beautifulsoup4 >= 4.12.3, html5lib >= 1.1.
  • Last.fm API Account: Free API Key & Secret obtained from the Last.fm API Account Creation Page.

๐Ÿš€ Quickstart

Create and save a high-resolution 3x3 album collage in just a few lines of Python:

from lastfmcollagegenerator.collage_generator import CollageGenerator

# 1. Initialize the generator with your Last.fm API credentials
generator = CollageGenerator(
    lastfm_api_key="YOUR_LASTFM_API_KEY",
    lastfm_api_secret="YOUR_LASTFM_API_SECRET"
)

# 2. Generate a 3x3 album collage for the past 7 days (returns a PIL.Image object)
image = generator.generate(
    entity="album",
    username="your_lastfm_username",
    cols=3,
    rows=3,
    period="7day",
)

# 3. Save the resulting composite image to disk
image.save("my_weekly_collage.png", format="PNG")
print(f"Collage saved successfully! Canvas size: {image.width}x{image.height}px")

# 4. Or generate directly using dedicated convenience methods
album_collage = generator.generate_top_albums_collage(
    username="your_lastfm_username", cols=5, rows=5, period="7day"
)
artist_collage = generator.generate_top_artists_collage(
    username="your_lastfm_username", cols=3, rows=3, period="overall"
)
track_collage = generator.generate_top_tracks_collage(
    username="your_lastfm_username", cols=4, rows=4, period="1month"
)

๐Ÿ“– Python API Reference

CollageGenerator

lastfmcollagegenerator.collage_generator.CollageGenerator

The primary public entrypoint and facade for configuring and executing collage creation.

Constructor

CollageGenerator(lastfm_api_key: str, lastfm_api_secret: str)

Parameters:

  • lastfm_api_key (str): Valid Last.fm Audioscrobbler REST API key.
  • lastfm_api_secret (str): Valid Last.fm API secret.

Method: generate()

def generate(
    self,
    entity: str,
    username: str,
    cols: int,
    rows: int,
    period: str = "overall",
    tile_size: Optional[int] = None,
    theme: Union[str, Theme, Dict[str, Any]] = "dark",
    overlay_style: str = "banner",
    show_text: bool = True,
    font_path: Optional[str] = None
) -> PIL.Image.Image

Generates a composite image collage for the specified entity and time horizon.

Parameter Matrix:

Parameter Type Allowed Values Default Description
entity str "album", "artist", "track" Required Type of Last.fm listening entity to composite.
username str Any valid Last.fm username string Required The target Last.fm user account.
cols int 1 <= cols <= 20 Required Number of horizontal grid columns.
rows int 1 <= rows <= 20 Required Number of vertical grid rows (max 400 total tiles).
period str "7day", "1month", "3month", "6month", "12month", "overall" "overall" Scrobble aggregation time window.
tile_size Optional[int] 50 <= tile_size <= 600 None (Auto) Explicit tile dimension in px. If None, dynamically auto-scaled.
theme Union[str, Theme, dict] "dark", "light", "glassmorphic", "sunset", "neon", or custom Theme "dark" Visual color scheme for overlay banners and typography.
overlay_style str "banner", "full_tint", "gradient", "pill", "clean" "banner" Overlay visual presentation layout mode.
show_text bool True, False True Whether to render title and playcount overlays.
font_path Optional[str] Filesystem path to .ttf/.otf None Custom font file path override.

Returns:

  • PIL.Image.Image: An allocated Pillow 24-bit RGB raster canvas with dimensions (cols * tile_size, rows * tile_size) pixels.

Exceptions Raised:

  • ValueError: If entity is not in ENTITIES, period is not in PERIODS, cols/rows are outside 1..20, total tiles exceed 400, tile_size is outside 50..600, or theme/overlay_style are invalid.
  • TypeError: If cols, rows, or tile_size are not integers, or parameter types are invalid.
  • FileNotFoundError: If custom font_path does not exist on disk.
  • pylast.WSError: If Last.fm API authentication fails or the requested username does not exist.
  • pylast.NetworkError: If network connection to Last.fm API endpoints cannot be established.

Convenience Methods

For intuitive syntax, CollageGenerator provides dedicated helper methods:

# Generate Top Albums Collage (3x3, last 7 days)
album_collage = generator.generate_top_albums_collage(
    username="rj",
    cols=3,
    rows=3,
    period="7day"
)

# Generate High-Density Top Artists Collage (10x10, monthly, auto 150px tiles)
artist_collage = generator.generate_top_artists_collage(
    username="rj",
    cols=10,
    rows=10,
    period="1month"
)

# Generate Custom High-Res Top Tracks Collage (4x3, overall history, explicit 300px tiles)
track_collage = generator.generate_top_tracks_collage(
    username="rj",
    cols=4,
    rows=3,
    period="overall",
    tile_size=300
)

Working with Returned PIL.Image Objects

The returned object is a standard Pillow PIL.Image.Image instance, offering full flexibility for saving, converting, streaming, or displaying:

import io
from lastfmcollagegenerator.collage_generator import CollageGenerator

generator = CollageGenerator("YOUR_API_KEY", "YOUR_API_SECRET")
image = generator.generate(entity="album", username="user", cols=3, rows=3, period="7day")

# 1. Save as Lossless PNG
image.save("collage.png", format="PNG")

# 2. Save as Optimized JPEG with custom compression quality
image.convert("RGB").save("collage.jpg", format="JPEG", quality=90, optimize=True)

# 3. Save as Modern WebP (reduces size by 60-80% relative to PNG)
image.save("collage.webp", format="WEBP", quality=85)

# 4. Extract In-Memory Binary Buffer (for FastAPI / Flask / Discord Bot responses)
buffer = io.BytesIO()
image.save(buffer, format="PNG")
buffer.seek(0)
raw_bytes = buffer.getvalue()

# 5. Generate a Thumbnail / Downscaled Version
thumbnail = image.copy()
thumbnail.thumbnail((450, 450))
thumbnail.save("collage_thumbnail.png")

# 6. Display inline within Jupyter / IPython Notebooks
display(image)

Error Handling & Exception Management

from lastfmcollagegenerator.collage_generator import CollageGenerator
from lastfmcollagegenerator.exceptions import ArtistNotFound, ArtistImageNotFound
import pylast

generator = CollageGenerator(
    lastfm_api_key="your_api_key",
    lastfm_api_secret="your_api_secret"
)

try:
    image = generator.generate(
        entity="album",
        username="some_lastfm_user",
        cols=3,
        rows=3,
        period="7day"
    )
    image.save("collage.png")
except ValueError as e:
    print(f"Validation error (invalid parameters): {e}")
except pylast.WSError as e:
    print(f"Last.fm API Service error: {e}")
except pylast.NetworkError as e:
    print(f"Network connectivity error: {e}")
except (ArtistNotFound, ArtistImageNotFound) as e:
    print(f"Artist scraping warning: {e}")
except Exception as e:
    print(f"Unexpected error during collage generation: {e}")

๐Ÿ› ๏ธ Developer & Debugging Workflows

The repository includes a comprehensive zero-build development runner (scripts/debug_collage.py) and preconfigured debug profiles.

1. Offline Mock Mode (0 Network Calls / Instant Rendering)

Generates synthetic in-memory colored tiles with geometric artwork to test Pillow canvas allocation, font rendering, coordinate math, and text wrapping instantly without API keys or an internet connection:

# Generate 3x3 mock album collage
uv run python scripts/debug_collage.py --mock -g 3x3

# Generate 5x5 mock artist collage and automatically open in system viewer
uv run python scripts/debug_collage.py --mock -e artist -g 5x5 --open

# Generate asymmetric 3x5 track collage without playcount banners
uv run python scripts/debug_collage.py --mock -e track -c 3 -r 5 --no-title -o output/mock_track.png

2. Live Last.fm Mode with .env Configuration

Configure credentials once in a .env file:

cp .env.example .env
# .env
LASTFM_API_KEY=your_api_key_here
LASTFM_API_SECRET=your_api_secret_here
LASTFM_USERNAME=your_username_here

Execute live queries:

# Run live generation using .env credentials and username
uv run python scripts/debug_collage.py --live

# Customize entity, period, grid size, and open viewer
uv run python scripts/debug_collage.py --live -e artist -g 4x4 -p 1month --open

# Override username or period on the fly
uv run python scripts/debug_collage.py --live -u different_user -e track -p overall

3. Debug Runner CLI Options Reference

Flag Short Type Default Description
--mock flag False Run offline with synthetic colored tiles (0 network calls).
--live flag False Run live Last.fm API queries and web scraping.
--username -u str .env / testuser Target Last.fm username.
--entity -e str album Musical entity: album, artist, track.
--grid -g str 3x3 Shorthand grid dimension (e.g. 3x3, 5x5, 4x3, 3x5).
--cols / --rows -c / -r int 3 / 3 Explicit column and row counts (1 to 5).
--period -p str 7day Aggregation period (7day, 1month, 3month, 6month, 12month, overall).
--output -o str output/... Custom destination PNG filepath.
--open flag False Automatically open result in default operating system image viewer.
--no-title flag False Disable title and playcount overlay banners.
--api-key str .env / "" Last.fm API Key override.
--api-secret str .env / "" Last.fm API Secret override.

4. VS Code F5 Breakpoint Debugging

The repository includes preconfigured .vscode/launch.json debug profiles:

  • ๐ŸŽจ Debug: Mock Album Collage (3x3): Step through Pillow rendering offline.
  • ๐ŸŽจ Debug: Mock Artist Collage (5x5): Step through 5x5 multi-row rendering.
  • ๐ŸŒ Debug: Live Album Collage (.env): Step through live Last.fm API calls.
  • ๐ŸŒ Debug: Live Artist Collage (.env): Step through web scraping pipeline.
  • ๐ŸŽต Debug: Live Track Collage (.env): Step through track cover fallbacks.
  • ๐Ÿงช Debug: Current Test File (Pytest): Step through active test cases.

5. Local Editable Installation for Consumer Applications

If you are developing a consuming application (e.g. a Discord bot or web service) alongside this library:

# In your consumer application's virtual environment:
uv pip install -e /path/to/lastfm-collage-generator
# or
pip install -e /path/to/lastfm-collage-generator

Any edits made inside src/ are immediately active in the consumer application without reinstalling.


๐Ÿงช Testing & Quality Assurance

The project enforces a zero live network calls policy for automated tests. All tests execute 100% offline using synthetic in-memory fixtures.

Running the Pytest Suite

# Run all unit and integration tests
uv run pytest tests/ -v

# Run with line and branch coverage report
uv run pytest --cov=lastfmcollagegenerator --cov-report=term-missing tests/

# Enforce minimum 90% coverage quality gate
uv run pytest --cov=lastfmcollagegenerator --cov-fail-under=90 tests/

Static Analysis & Linters

# Lint code syntax and PEP 8 compliance
uv run flake8 src/ tests/

# Verify Black code formatting
uv run black --check src/ tests/

# Run static type checking
uv run mypy src/

Unified QA Runner Skill

Execute the entire QA pipeline with a single command:

uv run python .gemini/skills/poetry-test-runner/scripts/run_tests.py --all

๐Ÿ“ฆ Packaging & Publishing to PyPI

The package is built and published using uv with the hatchling build backend.

1. Build Distribution Artifacts

Always ensure only clean, current release artifacts exist in dist/ prior to building:

# Clean previous build artifacts
rm -rf dist/

# Build source distribution (.tar.gz) and binary wheel (.whl)
uv build

2. Configure PyPI Authentication

uv publish authenticates automatically using the UV_PUBLISH_TOKEN environment variable or standard ~/.pypirc / ~/.netrc config:

  • Shell profile (~/.zshrc):
    export UV_PUBLISH_TOKEN="pypi-AgEI..."
    
  • Project .env:
    UV_PUBLISH_TOKEN=pypi-AgEI...
    
  • Global ~/.pypirc:
    [distutils]
    index-servers = pypi
    
    [pypi]
    username = __token__
    password = pypi-AgEI...
    

3. Publish to PyPI

# Publish built release to PyPI
uv publish

๐Ÿ—ƒ๏ธ Caching, Social Presets & Tile Geometry

Multi-Tier Artwork Caching

Artwork is cached across two tiers: an in-memory LRU store (256 entries) and a persistent SQLite cache at ~/.cache/lastfm-collage/artwork.db. Album/track covers expire after 30 days, scraped artist hero images after 7 days. On every cache hit, no HTTP request is issued.

generator.generate(
    entity="album",
    username="your_username",
    cols=5,
    rows=5,
    cache_dir="/custom/cache/dir",  # Default: ~/.cache/lastfm-collage/
    cache_ttl_override=14,          # Override TTL (days) for all artwork kinds
    rate_limit=10,                  # HTTP requests per second (default 5.0)
)

All HTTP acquisition flows through a resilience middleware: a token-bucket rate limiter, exponential backoff with full jitter on transient errors, and a per-host circuit breaker. Unavailable artwork falls back to a deterministic algorithmic gradient tile (SHA-256-derived pastel gradient + initials) instead of a black square; pass fallback_style="black" for the legacy solid tile.

Social Media Dimension Presets

One-click output sizing for common platforms. When a preset is set it overrides cols, rows and tile_size, and letterboxed regions are filled with an acrylic backdrop (Gaussian-blurred, darkened rendition of your #1 artwork):

generator.generate(
    entity="album",
    username="your_username",
    cols=5,
    rows=5,
    preset="instagram-story",
)
Preset Dimensions Grid
instagram-story 1080 ร— 1920 (9:16) 3ร—5 @ 360px
instagram-post 1080 ร— 1080 (1:1) 3ร—3 @ 360px
twitter-header 1500 ร— 500 (3:1) 5ร—1 @ 300px
desktop-wallpaper 1920 ร— 1080 (16:9) 6ร—3 @ 320px
desktop-wallpaper-4k 3840 ร— 2160 (16:9) 6ร—3 @ 600px

Tile Geometry

Rounded corners, border strokes and inter-tile spacing are available with defaults preserving the classic edge-to-edge look:

generator.generate(
    entity="album",
    username="your_username",
    cols=3,
    rows=3,
    corner_radius=12,
    border_width=3,
    border_color="#FF5A5F",
    spacing=8,
)

๐Ÿค– GitHub Actions Automation

The repository ships a reusable composite action (action.yml) that generates a collage inside a workflow and writes it into the repository โ€” ideal for keeping a GitHub profile README fresh with weekly listening recaps.

Inputs: username (required), entity, cols, rows, period, output-path, mock, api-key, api-secret.

name: Weekly Last.fm Recap
on:
  schedule:
    - cron: "0 9 * * 1"
  workflow_dispatch:

permissions:
  contents: write

jobs:
  recap:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Generate weekly collage
        uses: paurieraf/lastfm-collage-generator@v0.8.0
        with:
          username: ${{ secrets.LASTFM_USERNAME }}
          entity: album
          cols: "5"
          rows: "5"
          period: 7day
          output-path: weekly-recap.png
          api-key: ${{ secrets.LASTFM_API_KEY }}
          api-secret: ${{ secrets.LASTFM_API_SECRET }}
      - name: Commit updated collage
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add weekly-recap.png
          git commit -m "chore: update weekly Last.fm recap" || echo "No changes"
          git push

See .github/workflows/weekly-recap.yml.example for a ready-to-copy workflow. Set mock: "true" to validate workflows without Last.fm credentials.


๐Ÿ”ค Font Handling & Asset Packaging

The library bundles official TrueType fonts directly inside the distribution package at src/lastfmcollagegenerator/fonts/:

  • DejaVuSansMono.ttf (340 KB): Default monospace font for tile title and playcount rendering.
  • DejaVuSansMono-Bold.ttf (332 KB): Bold monospace font variant.

Fonts are loaded dynamically relative to the package directory (os.path.dirname(__file__)), ensuring zero dependencies on host system fonts across macOS, Linux, and containerized Docker environments. Assets are declared in MANIFEST.in via recursive-include src/lastfmcollagegenerator/fonts *.ttf.


๐Ÿ—บ๏ธ Multi-Phase Feature Roadmap

Our development roadmap is organized across 4 strategic pillars and versioned milestones:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                       STRATEGIC ROADMAP PILLARS                             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ 1. Visual Styling     โ”‚ 2. Performance, Caching โ”‚ 3. Advanced Layouts       โ”‚ 4. Ecosystem &
โ”‚    & Custom Themes    โ”‚    & Resilience         โ”‚    & Modern Formats       โ”‚    CLI Integrations
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐ŸŽจ Pillar 1: Visual Styling & Custom Themes

  • Phase 1 (v0.5.0 โ€” Immediate Stability):
    • Correct multi-row overlay geometry bug (y_1 = y + self.TILE_HEIGHT).
    • Implement generate_top_albums_collage(), generate_top_artists_collage(), generate_top_tracks_collage() convenience methods on CollageGenerator.
    • Implement strict lower boundary validation (1 <= cols <= 5, 1 <= rows <= 5).
    • Author comprehensive 100% offline pytest suite (>90% coverage).
  • Phase 2 (v0.6.0 โ€” Visual Personalization):
    • Dynamic Theme Engine: Pre-packaged themes (Dark, Light, Glassmorphic, Sunset, Neon, and custom user hex palettes/Theme objects).
    • Typography & Auto-Scaling Engine: Word-boundary line breaking via textwrap, dynamic font downscaling for long titles, and custom .ttf/.otf font path support.
    • Tile Geometry & Rounded Corners: Rounded squircle corner masking (radius=12), configurable border stroke widths and colors, and inter-tile spacing margins.
    • Versatile Overlay Styles: Banner (lower third), Full Tint (centered text), Gradient Fade, Minimalist Badge / Pill (rank + playcount chip), and Clean Mode (show_text=False for pure artwork grids).

โšก Pillar 2: Performance, Caching & Scraping Resilience

  • Phase 2 (v0.6.0 โ€” Fallbacks & Determinism):
    • Dynamic Fallback Artwork Engine: Algorithmic two-color pastel gradients and initials typography derived from SHA-256 entity hashes to replace solid black tiles.
    • Deterministic Secondary Sorting: Secondary sort key (int(playcount), title) to ensure byte-for-byte identical collages on tied scrobble counts.
  • Phase 3 (v0.7.0 โ€” Caching & Network Resilience):
    • Multi-Tier Caching Subsystem: Tier-1 in-memory LRU cache (maxsize=256) + Tier-2 SQLite persistent disk cache (~/.cache/lastfm-collage/) with 30-day TTL for album covers and 7-day TTL for scraped artist hero images.
    • Network Resilience Middleware: Token-bucket rate limiter (5 req/sec), exponential backoff with full jitter for transient HTTP errors, and circuit breaker for web scraping fallbacks.
  • Phase 4 (v1.0.0 โ€” Asynchronous Architecture):
    • Native AsyncIO Pipeline: Non-blocking concurrent asset acquisition via httpx (async def generate_async()) with async semaphore concurrency throttling.

๐Ÿ“ Pillar 3: Advanced Layouts & Modern Formats

  • Phase 2 (v0.6.0 โ€” High-Density Grids):
    • Arbitrary $N \times M$ Matrix Grids: Expand grid size beyond 5x5 (up to 20x20, max 400 tiles) with dynamic resolution auto-scaling (300px $\to$ 150px $\to$ 100px), configurable explicit tile sizes, and proportional typography.
  • Phase 3 (v0.7.0 โ€” Social Presets & Backdrop Decorators):
    • Social Media Dimension Presets: One-click generation for Instagram Story (9:16 $1080\times1920$), Instagram Post (1:1 $1080\times1080$), Twitter Header (3:1 $1500\times500$), and Desktop Wallpaper (16:9 $1920\times1080$ / 4K).
    • Acrylic Backdrop Blur: Automatically fill non-square letterboxing with an acrylic Gaussian-blurred backdrop derived from the user's #1 top artwork.
  • Phase 4 (v1.0.0 โ€” Modern Formats & Asymmetric Grids):
    • Modern Export Formats: Direct export to WebP (lossy/lossless), SVG vector containers with crisp <text> nodes, and 300 DPI print-ready PDF posters.
    • Asymmetrical Layout Strategies: Hero Grid (#1 item in $2\times2$ block, surrounded by $1\times1$ and $0.5\times0.5$ tiles), Bento Box editorial grids, and Honeycomb Hexagon tessellations.
  • Phase 5 (v1.1.0 โ€” Motion Recaps):
    • Animated Transitions: Generate animated GIF / MP4 listening recaps smoothly crossfading across time horizons (7day $\to$ 1month $\to$ 3month $\to$ 12month).

๐ŸŒ Pillar 4: Ecosystem & CLI Integrations

  • Phase 2 (v0.6.0 โ€” Standalone CLI):
    • Rich CLI Tool (lastfm-collage): Global console script with rich terminal UI, colorized progress bars, download speed metrics, and ASCII art terminal previews.
  • Phase 3 (v0.8.0 โ€” GitHub Actions Automation):
    • GitHub Profile README Action (action.yml): Automated scheduled workflow updating developer GitHub Profile READMEs with weekly listening recaps on cron.
  • Phase 5 (v1.1.0 - v1.2.0 โ€” Web Services & Chatbots):
    • FastAPI Microservice Wrapper: Containerized REST API with OpenAPI docs and binary image streaming endpoints (GET /api/v1/collage).
    • Discord, Telegram & Slack Bot Connectors: Slash commands (/collage), user account binding, and direct embed image attachments.

๐Ÿ› Known Bugs & Defect Catalog

The following known defects in legacy versions (<= 0.4.13) have been diagnosed with root causes and remediation:

Defect ID Component Severity Description & Root Cause Resolution Status
BUG-01 collage.py:126-130 Critical Multi-Row Overlay Coordinate Drift: y_1 = y * 2 + TILE_WIDTH causes exponential coordinate inflation on rows 1..4 (Row 1 banner height is 365px, completely covering Row 2). Fixed in v0.5.0 (y_1 = y + self.TILE_HEIGHT).
BUG-02 collage_generator.py High Documentation & API Signature Drift: generate_top_albums_collage(), generate_top_artists_collage(), and generate_top_tracks_collage() advertised in documentation but missing in code. Added in v0.5.0 facade.
BUG-03 collage_generator.py:69 Medium Incomplete Boundary Validation: _validate_parameters checks cols > 5 but allows cols <= 0 or rows <= 0, causing zero-dimension canvas crashes inside PIL. Enforced 1 <= cols <= 5 and 1 <= rows <= 5.
BUG-04 collage.py:234, 251, 308 Medium Scraping Fragility & Missing Timeouts: requests.get() lacks custom User-Agent, request timeouts, and catches only limited exceptions, crashing worker threads on CDN 502/503. Wrapped with DEFAULT_HEADERS, timeout=(3.05, 10.0), and blank tile fallbacks.
BUG-05 collage.py:191 Low Non-Deterministic Tile Ordering on Tied Plays: as_completed arrival jitter combined with single-key sort produces non-deterministic tile order on identical playcounts. Secondary sort key added: (int(playcount), title).

๐Ÿค Contributing

We welcome contributions from the community! To contribute:

  1. Fork the Repository and clone your fork locally.
  2. Initialize the Virtual Environment with uv:
    uv sync
    
  3. Create a Feature Branch:
    git checkout -b feature/your-feature-name
    
  4. Author Tests: Ensure all new functionality is accompanied by 100% offline unit tests in tests/ using synthetic in-memory fixtures.
  5. Execute the QA Suite:
    uv run flake8 src/ tests/
    uv run black --check src/ tests/
    uv run mypy src/
    uv run pytest tests/ -v
    
  6. Submit a Pull Request with a detailed explanation of your changes and test verification evidence.

Pull Request Quality Checklist

  • All new methods have complete Python 3.8-compatible type annotations.
  • No live network requests are made during test execution.
  • All PIL.Image and io.BytesIO streams are cleanly closed or managed via context managers.
  • HTTP requests specify explicit timeouts and custom User-Agent headers.
  • Pytest suite passes cleanly with zero warnings or failures.

๐Ÿ“„ License

This project is open-source software licensed under the MIT License.


๐Ÿ‘ค Authors & Acknowledgments

  • Lead Author & Maintainer: @paurieraf (Pau Riera)
  • Built With & Powered By:
    • pylast โ€” Python Last.fm REST API v2.0 Client.
    • Pillow (PIL Fork) โ€” Python Imaging Library for raster 2D graphics.
    • Beautiful Soup 4 & html5lib โ€” Resilient HTML DOM scraping.
    • uv โ€” Fast Python package and dependency manager by Astral.
    • Monospace typography provided by the open-source DejaVu Fonts Project.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lastfmcollagegenerator-0.8.0.tar.gz (5.1 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lastfmcollagegenerator-0.8.0-py3-none-any.whl (438.3 kB view details)

Uploaded Python 3

File details

Details for the file lastfmcollagegenerator-0.8.0.tar.gz.

File metadata

  • Download URL: lastfmcollagegenerator-0.8.0.tar.gz
  • Upload date:
  • Size: 5.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.29 {"installer":{"name":"uv","version":"0.9.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for lastfmcollagegenerator-0.8.0.tar.gz
Algorithm Hash digest
SHA256 f81665b3d84e59a46bcc611635bba0645dc1cd6b846dc59463108e7ba2fd060b
MD5 2b4da0905c13d51b4c853e8476568a13
BLAKE2b-256 2bff814b563e784b5e5aafe6b246013a4549c2e6020641044bac01662574743d

See more details on using hashes here.

File details

Details for the file lastfmcollagegenerator-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: lastfmcollagegenerator-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 438.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.29 {"installer":{"name":"uv","version":"0.9.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for lastfmcollagegenerator-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e34f636eaca1417554dcee8b7e164a8ffce3e52fa33007c811d16dd4521be489
MD5 33dfc7a271e1ebb202d380d7b6a92a44
BLAKE2b-256 ea82e801f371518bfa7b3bd831cd2cbedef40e38f12dab33cf7e6a8d66897037

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page