diff --git a/BeatPrints/assets/templates/banner_dark.png b/BeatPrints/assets/templates/banner_dark.png deleted file mode 100644 index 156459a..0000000 Binary files a/BeatPrints/assets/templates/banner_dark.png and /dev/null differ diff --git a/BeatPrints/assets/templates/banner_light.png b/BeatPrints/assets/templates/banner_light.png deleted file mode 100644 index 6304837..0000000 Binary files a/BeatPrints/assets/templates/banner_light.png and /dev/null differ diff --git a/BeatPrints/assets/templates/catppuccin.png b/BeatPrints/assets/templates/catppuccin.png new file mode 100644 index 0000000..702efa3 Binary files /dev/null and b/BeatPrints/assets/templates/catppuccin.png differ diff --git a/BeatPrints/assets/templates/dark.png b/BeatPrints/assets/templates/dark.png new file mode 100644 index 0000000..582bae3 Binary files /dev/null and b/BeatPrints/assets/templates/dark.png differ diff --git a/BeatPrints/assets/templates/everforest.png b/BeatPrints/assets/templates/everforest.png new file mode 100644 index 0000000..d32e3cf Binary files /dev/null and b/BeatPrints/assets/templates/everforest.png differ diff --git a/BeatPrints/assets/templates/gruvbox.png b/BeatPrints/assets/templates/gruvbox.png new file mode 100644 index 0000000..1ad3fb3 Binary files /dev/null and b/BeatPrints/assets/templates/gruvbox.png differ diff --git a/BeatPrints/assets/templates/light.png b/BeatPrints/assets/templates/light.png new file mode 100644 index 0000000..900f7cf Binary files /dev/null and b/BeatPrints/assets/templates/light.png differ diff --git a/BeatPrints/assets/templates/nord.png b/BeatPrints/assets/templates/nord.png new file mode 100644 index 0000000..624edec Binary files /dev/null and b/BeatPrints/assets/templates/nord.png differ diff --git a/BeatPrints/assets/templates/rosepine.png b/BeatPrints/assets/templates/rosepine.png new file mode 100644 index 0000000..472cfc1 Binary files /dev/null and b/BeatPrints/assets/templates/rosepine.png differ diff --git a/BeatPrints/consts.py b/BeatPrints/consts.py index 3651c42..0610317 100644 --- a/BeatPrints/consts.py +++ b/BeatPrints/consts.py @@ -9,38 +9,68 @@ S = Size C = Cords P = Path +PL = Palette CL = Color """ import os +from typing import Literal MAX_ROWS = 5 -MAX_WIDTH = 1020 - -S_TRACKS = 35 -S_SPACING = 45 -S_COVER = (1020, 1020) -S_SPOTIFY_CODE = (330, 85) -S_HEADING = 80 -S_ARTIST = 60 -S_DURATION = 45 -S_LYRICS = 42 -S_LABEL = 30 - -C_COVER = (60, 60) -C_HEADING = (60, 1275) -C_ARTIST = (60, 1350) -C_LYRICS = (60, 1395) -C_TRACKS = (60, 1390) -C_LABEL = (1080, 1615) - -C_DURATION = (1080, 1275) -C_PALETTE = (60, 1120) -C_ACCENT = (0, 1720, 1140, 1740) -C_SPOTIFY_CODE = (45, 1610) - -CL_FONT_DARK_MODE = (193, 189, 178) -CL_FONT_LIGHT_MODE = (50, 47, 48) +MAX_WIDTH = 2040 + +S_MAX_HEADING_WIDTH = 1760 +S_TRACKS = 70 +S_SPACING = 90 +S_COVER = (2040, 2040) +S_SPOTIFY_CODE = (660, 170) +S_HEADING = 160 +S_ARTIST = 120 +S_DURATION = 90 +S_LYRICS = 84 +S_LABEL = 60 + +C_COVER = (120, 120) +C_HEADING = (120, 2550) +C_ARTIST = (120, 2700) +C_LYRICS = (120, 2790) +C_TRACKS = (120, 2780) +C_LABEL = (2160, 3230) +C_DURATION = (2160, 2550) +C_PALETTE = (120, 2240) +C_ACCENT = (0, 3440, 2280, 3480) +C_SPOTIFY_CODE = (90, 3220) + +PL_BOX_WIDTH = 340 +PL_BOX_HEIGHT = 2325 + +CL_FONT_DARK = (193, 189, 178) +CL_FONT_LIGHT = (50, 47, 48) +CL_FONT_CATPPUCCIN = (205, 214, 244) +CL_FONT_GRUVBOX = (221, 199, 161) +CL_FONT_NORD = (216, 222, 233) +CL_FONT_ROSEPINE = (224, 222, 244) +CL_FONT_EVERFOREST = (211, 198, 170) + +THEMES = { + "Light": CL_FONT_LIGHT, + "Dark": CL_FONT_DARK, + "Catppuccin": CL_FONT_CATPPUCCIN, + "Gruvbox": CL_FONT_GRUVBOX, + "Nord": CL_FONT_NORD, + "RosePine": CL_FONT_ROSEPINE, + "Everforest": CL_FONT_EVERFOREST, +} + +THEME_OPTS = Literal[ + "Light", + "Dark", + "Catppuccin", + "Gruvbox", + "Nord", + "RosePine", + "Everforest", +] CL_WHITE = (255, 255, 255, 255) CL_TRANSPARENT = (0, 0, 0, 0) diff --git a/BeatPrints/errors.py b/BeatPrints/errors.py index 47a853a..5830687 100644 --- a/BeatPrints/errors.py +++ b/BeatPrints/errors.py @@ -88,3 +88,16 @@ def __init__( ): self.message = message super().__init__(self.message) + + +class ThemeNotFound(Exception): + """ + Raised when the specified theme is not found or is invalid. + """ + + def __init__( + self, + message="The specified theme could not be found. Please ensure the theme name is valid.", + ): + self.message = message + super().__init__(self.message) diff --git a/BeatPrints/image.py b/BeatPrints/image.py index 2e20184..2c85b2a 100644 --- a/BeatPrints/image.py +++ b/BeatPrints/image.py @@ -55,10 +55,10 @@ def draw_palette( # Draw each color in the palette as a box for i in range(6): x, y = C_PALETTE - start, end = 170 * i, 170 * (i + 1) + start, end = PL_BOX_WIDTH * i, PL_BOX_WIDTH * (i + 1) # Draw the box for the current color - draw.rectangle(((x + start, y), (x + end, 1160)), fill=palette[i]) + draw.rectangle(((x + start, y), (x + end, PL_BOX_HEIGHT)), fill=palette[i]) # If accent is True, draw the accent color at the bottom if accent: @@ -117,19 +117,22 @@ def magicify(image: Image.Image) -> Image.Image: return contrast.enhance(0.8) -def scannable(id: str, darktheme: bool = False, is_album: bool = False) -> Image.Image: +def scannable( + id: str, theme: THEME_OPTS = "Light", is_album: bool = False +) -> Image.Image: """ Generates a Spotify scannable code for a track or album. Args: id (str): The Spotify track or album ID. - darktheme (bool): Whether to use dark theme. Defaults to False. + theme (str): The theme for the scannable code. Defaults to "Light". is_album (bool): If True, generates for an album. Defaults to False. Returns: Image.Image: The resized scannable code image. """ - color = CL_FONT_DARK_MODE if darktheme else CL_FONT_LIGHT_MODE + + color = THEMES[theme] item_type = "album" if is_album else "track" # Construct the URL to fetch the scannable code from Spotify @@ -186,22 +189,18 @@ def cover(image_url: str, image_path: Optional[str]) -> Image.Image: return magicify(img.resize(S_COVER)) -def get_theme(dark_theme: bool): +def get_theme(theme: THEME_OPTS = "Light") -> Tuple[tuple, str]: """ Returns theme-related properties based on the selected theme. Args: - dark_theme (bool): Whether to use dark theme. + theme (str): The selected theme. Default is "Light". Returns: - Tuple: The theme color and template path. + Tuple[tuple, str]: A tuple containing the theme color and the template path. """ - # Return appropriate color and template based on theme - color, template = ( - (CL_FONT_DARK_MODE, "banner_dark.png") - if dark_theme - else (CL_FONT_LIGHT_MODE, "banner_light.png") - ) - - template_path = os.path.join(P_TEMPLATES, template) + + color = THEMES[theme] + template_path = os.path.join(P_TEMPLATES, f"{theme.lower()}.png") + return color, template_path diff --git a/BeatPrints/poster.py b/BeatPrints/poster.py index 5083857..2b1f312 100644 --- a/BeatPrints/poster.py +++ b/BeatPrints/poster.py @@ -9,11 +9,12 @@ from pathlib import Path from typing import Optional, Union - from PIL import Image, ImageDraw -from .consts import * from . import image, write + +from .consts import * +from .errors import ThemeNotFound from .utils import filename, organize_tracks from .spotify import TrackMetadata, AlbumMetadata @@ -50,12 +51,14 @@ def _add_common_text( write.heading( draw, C_HEADING, - 875, + S_MAX_HEADING_WIDTH, metadata.name.upper(), color, write.font("Bold"), S_HEADING, - ) # Add artist name + ) + + # Add artist name write.text( draw, C_ARTIST, @@ -81,7 +84,7 @@ def track( metadata: TrackMetadata, lyrics: str, accent: bool = False, - dark_theme: bool = False, + theme: THEME_OPTS = "Light", custom_cover: Optional[str] = None, ) -> None: """ @@ -91,15 +94,20 @@ def track( metadata (TrackMetadata): Metadata containing details about the track. lyrics (str): The lyrics of the track. accent (bool, optional): Flag to add an accent at the bottom of the poster. Defaults to False. - dark_theme (bool, optional): Flag to use a dark theme. Defaults to False. + theme (str, optional): Specifies the theme to use. Must be one of "Light", "Dark", "Catppuccin", "Gruvbox", "Nord", "RosePine", or "Everforest". Defaults to "Light". custom_cover (Optional[str], optional): Path to a custom cover image. Defaults to None. """ + + # Check if the theme mentioned is valid or not + if theme not in THEMES: + raise ThemeNotFound + # Get theme colors and template for the poster - color, template = image.get_theme(dark_theme) + color, template = image.get_theme(theme) # Get cover art and spotify scannable code cover = image.cover(metadata.image, custom_cover) - scannable = image.scannable(metadata.id, dark_theme) + scannable = image.scannable(metadata.id, theme) with Image.open(template) as poster: poster = poster.convert("RGB") @@ -148,7 +156,7 @@ def album( metadata: AlbumMetadata, indexing: bool = False, accent: bool = False, - dark_theme: bool = False, + theme: THEME_OPTS = "Light", custom_cover: Optional[str] = None, ) -> None: """ @@ -158,16 +166,20 @@ def album( metadata (AlbumMetadata): Metadata containing details about the album. indexing (bool, optional): Flag to add index numbers to the tracks. Defaults to False. accent (bool, optional): Flag to add an accent at the bottom of the poster. Defaults to False. - dark_theme (bool, optional): Flag to use a dark theme. Defaults to False. + theme (str, optional): Specifies the theme to use. Must be one of "Light", "Dark", "Catppuccin", "Gruvbox", "Nord", "RosePine", or "Everforest". Defaults to "Light". custom_cover (Optional[str], optional): Path to a custom cover image. Defaults to None. """ + # Check if the theme mentioned is valid or not + if theme not in THEMES: + raise ThemeNotFound + # Get theme colors and template for the poster - color, template = image.get_theme(dark_theme) + color, template = image.get_theme(theme) # Get cover art and spotify scannable code cover = image.cover(metadata.image, custom_cover) - scannable = image.scannable(metadata.id, dark_theme, is_album=True) + scannable = image.scannable(metadata.id, theme, is_album=True) with Image.open(template) as poster: poster = poster.convert("RGB") diff --git a/README.md b/README.md index 42bec95..1cc919e 100644 --- a/README.md +++ b/README.md @@ -24,18 +24,25 @@ ## 📦 Installation -You can install BeatPrints via `pip`: +You can install BeatPrints via: ```bash +# For pip users pip install BeatPrints + +# For poetry users +poetry add BeatPrints ``` -Or, if you use Poetry: +Or if you prefer using just the CLI: ```bash -poetry add BeatPrints +pipx install BeatPrints ``` +This will install the CLI, making it ready for you to use. +For more more infomation, check out [pipx](https://github.com/pypa/pipx) + ## 🌱 Environment Variables To get started with BeatPrints, you’ll need a `.env` file with these keys: diff --git a/cli/prompt.py b/cli/prompt.py index f51f3c1..5210bf6 100644 --- a/cli/prompt.py +++ b/cli/prompt.py @@ -1,12 +1,11 @@ import questionary from rich import print +from BeatPrints import lyrics, spotify, poster, errors from cli.conf import * from cli import exutils, validate -from BeatPrints import lyrics, spotify, poster, errors - # Initialize components ly = lyrics.Lyrics() ps = poster.Poster(POSTERS_DIR) @@ -134,8 +133,19 @@ def poster_features(): tuple: A tuple containing theme, accent, and image path options. """ features = questionary.form( - theme=questionary.confirm( - "💫 • Enable dark mode?", default=False, style=exutils.default + theme=questionary.select( + "💫 • Which theme do you prefer?", + choices=[ + "Light", + "Dark", + "Catppuccin", + "Gruvbox", + "Nord", + "Rosepine", + "Everforest", + ], + default="Light", + style=exutils.default, ), accent=questionary.confirm( "🌈 • Add a color accent?", default=False, style=exutils.default diff --git a/docs/conf.py b/docs/conf.py index 6c21f7b..2684d84 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,7 +11,7 @@ project = "BeatPrints" copyright = "2024, elysianmyst" author = "elysianmyst" -release = "v1.0.6" +release = "v1.1.0" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/docs/index.rst b/docs/index.rst index 770aece..afe35d1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -29,17 +29,24 @@ Homepage 📦 Installation --------------- -You can install BeatPrints via ``pip``: +You can install BeatPrints via: .. code:: python + # For pip users pip install BeatPrints -Or, if you use Poetry: + # For poetry users + poetry add BeatPrints + +Or if you prefer using just the CLI: .. code:: python - poetry add BeatPrints + pipx install BeatPrints + +This will install the CLI, making it ready for you to use. +For more more infomation, check out `pipx `_ 🌱 Environment Variables ------------------------ diff --git a/pyproject.toml b/pyproject.toml index 721c5ad..2629f2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "BeatPrints" -version = "1.0.6" +version = "1.1.0" description = "Create eye-catching, pinterest-style music posters effortlessly." authors = ["elysianmyst <74355265+TrueMyst@users.noreply.github.com>"] license = "CC-BY-NC-4.0"