buy.py: automatically add purchased songs to /mix

Hunter
Tuesday, March 10th, 2026 at 10:27 AM

buy.py+52-4

buy.py
 14  import json
 15  import platform
 16  import webbrowser
 17 +import os
 18 +import shutil
 19 +import time
 20 +
 21 +
 22 +ITUNES_MUSIC_DIR = os.path.expanduser(
 23 +	"~/Music/Music/Media.localized/Music"
 24 +)
 25 +MIX_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "mix")
 26 +POLL_INTERVAL = 1  # seconds between directory scans
 27  
 28  
 29  def search_itunes(query):
 30  	"""Search iTunes for a song and return results."""
 31  	encoded_query = urllib.parse.quote(query)
 32  	url = f"https://itunes.apple.com/search?term={encoded_query}&media=music&entity=song&limit=10"
    -	
 33 +
 34  	try:
 35  		with urllib.request.urlopen(url) as response:
 36  			data = json.loads(response.read().decode())

 68  	# Use macOS 'open' command to bypass browser entirely
 69  	if platform.system() == 'Darwin':  # macOS
 70  		subprocess.run(['open', itunes_url])
    -		return itunes_url
 71 +		return True  # opened iTunes Store
 72  	else:
 73  		# For non-macOS systems, open song.link page with all platform options
 74  		songlink_url = f"https://song.link/i/{track_id}"
 75  		webbrowser.open(songlink_url)
    -		return songlink_url
 76 +		return False  # not iTunes Store
 77 +
 78 +
 79 +def snapshot_audio_files(root):
 80 +	"""Return a set of all audio file paths found recursively under root."""
 81 +	audio_exts = {'.mp3', '.m4a', '.ogg', '.flac', '.wav', '.aiff', '.aif'}
 82 +	found = set()
 83 +	for dirpath, _dirnames, filenames in os.walk(root):
 84 +		for name in filenames:
 85 +			if os.path.splitext(name)[1].lower() in audio_exts:
 86 +				found.add(os.path.join(dirpath, name))
 87 +	return found
 88  
 89  
 90 +def wait_for_new_file(watch_dir, before):
 91 +	"""
 92 +	Poll watch_dir recursively until an audio file appears that wasn't in the
 93 +	before snapshot. Returns the full path of the new file.
 94 +	"""
 95 +	print(f"Watching for new file in:\n  {watch_dir}")
 96 +	print("(complete your purchase in iTunes — press Ctrl+C to cancel)")
 97 +
 98 +	while True:
 99 +		after = snapshot_audio_files(watch_dir)
100 +		new_files = after - before
101 +		if new_files:
102 +			return next(iter(new_files))
103 +		time.sleep(POLL_INTERVAL)
104 +
105  def main():
106  	"""Main function to run the music search CLI."""
107  	# Get search query from command line args or prompt user

143  	else:
144  		print("(Opening song.link with all platform options)")
145  	
    -	open_itunes_link(itunes_url, track_id)
146 +	opened_itunes = open_itunes_link(itunes_url, track_id)
147 +
148 +	# Only watch for the downloaded file when we opened the iTunes Store
149 +	if not opened_itunes:
150 +		return
151 +	
152 +	before = snapshot_audio_files(ITUNES_MUSIC_DIR)
153 +	new_file = wait_for_new_file(ITUNES_MUSIC_DIR, before)
154 +	
155 +	filename = os.path.basename(new_file)
156 +	dest = os.path.join(MIX_DIR, filename)
157 +	shutil.copy2(new_file, dest)
158 +	print(f"Added to /mix: {filename}")
159  
160  
161  if __name__ == "__main__":