commit 3624390ebd76a63026c84f88540b22777b12007e
parent 3167f69a5595bd7c06221ecedcb72b4aef7e9715
Author: Hunter
Date:   Tue, 30 Dec 2025 16:41:08 -0500

add buy.py; make scripts executable

Diffstat:
Abuy.py | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgenerate_manifests.py | 0
Mhost.py | 0
Mreadme.md | 11++++++-----
Mscan.py | 0
5 files changed, 125 insertions(+), 5 deletions(-)

diff --git a/buy.py b/buy.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 +""" +Search for songs and open purchase links. + +Usage: + ./buy.py <search query> + ./buy.py beatles yesterday + ./buy.py "daft punk revolution 909" +""" + +import sys +import urllib.parse +import urllib.request +import json +import platform +import webbrowser + + +def search_itunes(query): + """Search iTunes for a song and return results.""" + encoded_query = urllib.parse.quote(query) + url = f"https://itunes.apple.com/search?term={encoded_query}&media=music&entity=song&limit=10" + + try: + with urllib.request.urlopen(url) as response: + data = json.loads(response.read().decode()) + return data.get('results', []) + except Exception as e: + print(f"Error searching iTunes: {e}") + return [] + + +def open_itunes_link(itunes_url, track_id): + """Open the iTunes purchase link directly in iTunes app on macOS, or song.link on other platforms.""" + import subprocess + + # Convert to geo-aware link and add app=itunes parameter to force iTunes Store + # This prevents opening in Apple Music (streaming) instead of iTunes Store (purchase) + + # Replace music.apple.com with geo.itunes.apple.com for better compatibility + if 'music.apple.com' in itunes_url: + itunes_url = itunes_url.replace('music.apple.com', 'geo.itunes.apple.com') + elif 'itunes.apple.com' in itunes_url and 'geo.' not in itunes_url: + itunes_url = itunes_url.replace('itunes.apple.com', 'geo.itunes.apple.com') + + # Add app=itunes parameter to force iTunes Store (not Apple Music) + if '?' in itunes_url: + # URL already has parameters, append with & + if 'app=' not in itunes_url: + itunes_url += '&app=itunes' + else: + # No parameters yet, add with ? + itunes_url += '?app=itunes' + + # Convert https:// to itms:// to open directly in iTunes app + itunes_url = itunes_url.replace('https://', 'itms://') + + # Use macOS 'open' command to bypass browser entirely + if platform.system() == 'Darwin': # macOS + subprocess.run(['open', itunes_url]) + return itunes_url + else: + # For non-macOS systems, open song.link page with all platform options + songlink_url = f"https://song.link/i/{track_id}" + webbrowser.open(songlink_url) + return songlink_url + + +def main(): + """Main function to run the music search CLI.""" + # Get search query from command line args or prompt user + if len(sys.argv) > 1: + query = ' '.join(sys.argv[1:]) + else: + query = input("Enter song name, artist, or both: ").strip() + + if not query: + print("No search query provided.") + return + + print(f"Searching for: {query}") + + # Search iTunes + results = search_itunes(query) + + if not results: + print("No results found. Try a different search term.") + return + + # Get the best (first) result + best_track = results[0] + + # Extract track info + artist = best_track.get('artistName', 'Unknown') + song = best_track.get('trackName', 'Unknown') + itunes_url = best_track.get('trackViewUrl') + track_id = best_track.get('trackId') + + if not itunes_url or not track_id: + print("Error: Could not find iTunes URL") + return + + # Open iTunes purchase page (macOS) or song.link (other platforms) + print(f"Opening: {artist} - {song}") + if platform.system() == 'Darwin': + print("(Opening iTunes Store directly)") + else: + print("(Opening song.link with all platform options)") + + open_itunes_link(itunes_url, track_id) + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("\n\nExiting...") + sys.exit(0) +\ No newline at end of file diff --git a/generate_manifests.py b/generate_manifests.py diff --git a/host.py b/host.py diff --git a/readme.md b/readme.md @@ -37,16 +37,17 @@ hits different, right?<br><br> ## quickstart 1. **prep your playlist** - - add your .mp3 files to the `/tracks` directory - - you can do this manually or run `rip.py` to rip tracks from a physical CD. - - run `scan.py` to parse `/tracks` and populate `tracks.json`, which defines the songs available to the player. after running `scan.py` once, you can manually edit `tracks.json` to refine your mix. + - add your .mp3 files to the `/tracks` directory, or use: + - `./rip.py` to rip tracks from a physical CD + - `./buy.py` to search for songs to purchase (uses iTunes on MacOS, <a href="https://song.link/i/1651294855">song.link</a> otherwise) + - run `./scan.py` to parse `/tracks` and populate `tracks.json`, which defines the songs available to the player. after running `./scan.py` once, you can manually edit `tracks.json` to refine your mix. - optionally, add an `album_art.jpg` to `/tracks` to set the cover art for your mix. 2. **soundcheck** - - run `host.py` to start a local HTTP server for testing. you can scan the QR code printed to the terminal to test the app from any device on your local network. + - run `./host.py` to start a local HTTP server for testing. you can scan the QR code printed to the terminal to test the app from any device on your local network. 3. **manifesting** - - run `generate_manifests.py` and follow the interactive prompts to specify an app name and the remote server path where your app will be hosted. + - run `./generate_manifests.py` and follow the interactive prompts to specify an app name and the remote server path where your app will be hosted. - this creates the config files that enable offline functionality: `manifest.json`, `resource-manifest.json`, and `service-worker.js`. 4. **ship it** diff --git a/scan.py b/scan.py