From 7ec3dca180d8d10ffb6f573964195d71b5702ae2 Mon Sep 17 00:00:00 2001 From: poprhythm Date: Fri, 11 Sep 2026 21:56:22 +0000 Subject: [PATCH] subwave: add 4 more 1996 shows, add artist-score floor to fix false match Adds Tunecaster Rock Top 30 for 1996-01-13, 1996-04-13, 1996-07-13, and 1996-12-14, cross-checked against all 4 existing 1995 shows and each other for exact-track duplicates. Also fixes a real false-positive: "John Mellencamp - Just Another Day" was matching Jon Secada's unrelated song of the same generic title, because a perfect title score could carry a middling-but-not-absurd artist-name coincidence (0.56) over the acceptance threshold. Added a hard artist similarity floor (0.65) independent of the combined score. Claude-Session: https://claude.ai/code/session_01L7Rwa6guD5wK8F8tWQwcJX --- .../tunecaster-rock-1996-01-13.json | 116 +++++++++++++ .../tunecaster-rock-1996-04-13.json | 146 ++++++++++++++++ .../tunecaster-rock-1996-07-13.json | 151 +++++++++++++++++ .../tunecaster-rock-1996-12-14.json | 156 ++++++++++++++++++ subwave/recreate_broadcast.py | 11 ++ 5 files changed, 580 insertions(+) create mode 100644 subwave/broadcasts/tunecaster-rock-1996-01-13.json create mode 100644 subwave/broadcasts/tunecaster-rock-1996-04-13.json create mode 100644 subwave/broadcasts/tunecaster-rock-1996-07-13.json create mode 100644 subwave/broadcasts/tunecaster-rock-1996-12-14.json diff --git a/subwave/broadcasts/tunecaster-rock-1996-01-13.json b/subwave/broadcasts/tunecaster-rock-1996-01-13.json new file mode 100644 index 0000000..8e1706d --- /dev/null +++ b/subwave/broadcasts/tunecaster-rock-1996-01-13.json @@ -0,0 +1,116 @@ +{ + "station": "Tunecaster Rock Top 30", + "date": "1996-01-13", + "tracks": [ + { + "rank": 3, + "artist": "Pearl Jam", + "title": "I Got ID" + }, + { + "rank": 4, + "artist": "Bush", + "title": "Glycerine" + }, + { + "rank": 5, + "artist": "Collective Soul", + "title": "Shine" + }, + { + "rank": 6, + "artist": "Everclear", + "title": "Santa Monica (Watch The World Die)" + }, + { + "rank": 7, + "artist": "Green Day", + "title": "Longview" + }, + { + "rank": 8, + "artist": "Red Hot Chili Peppers", + "title": "Under the Bridge" + }, + { + "rank": 9, + "artist": "Tom Petty And The Heartbreakers", + "title": "Waiting For Tonight" + }, + { + "rank": 10, + "artist": "Rancid", + "title": "Ruby Soho" + }, + { + "rank": 12, + "artist": "No Doubt", + "title": "Just A Girl" + }, + { + "rank": 13, + "artist": "Dave Matthews Band", + "title": "Satellite" + }, + { + "rank": 16, + "artist": "Ozzy Osbourne", + "title": "See You On The Other Side" + }, + { + "rank": 18, + "artist": "Radiohead", + "title": "High And Dry" + }, + { + "rank": 19, + "artist": "AC/DC", + "title": "Cover You In Oil" + }, + { + "rank": 20, + "artist": "Goo Goo Dolls", + "title": "Naked" + }, + { + "rank": 22, + "artist": "Poe", + "title": "Trigger Happy Jack (Drive By A Go-Go)" + }, + { + "rank": 23, + "artist": "Alanis Morissette", + "title": "Ironic" + }, + { + "rank": 24, + "artist": "Natalie Merchant", + "title": "Wonder" + }, + { + "rank": 25, + "artist": "Tori Amos", + "title": "Caught A Lite Sneeze" + }, + { + "rank": 26, + "artist": "For Squirrels", + "title": "Mighty K.C." + }, + { + "rank": 28, + "artist": "Alice In Chains", + "title": "Grind" + }, + { + "rank": 29, + "artist": "Loud Lucy", + "title": "Ticking" + }, + { + "rank": 30, + "artist": "Silverchair", + "title": "Pure Massacre" + } + ] +} \ No newline at end of file diff --git a/subwave/broadcasts/tunecaster-rock-1996-04-13.json b/subwave/broadcasts/tunecaster-rock-1996-04-13.json new file mode 100644 index 0000000..5bf23cf --- /dev/null +++ b/subwave/broadcasts/tunecaster-rock-1996-04-13.json @@ -0,0 +1,146 @@ +{ + "station": "Tunecaster Rock Top 30", + "date": "1996-04-13", + "tracks": [ + { + "rank": 1, + "artist": "Bush", + "title": "Machinehead" + }, + { + "rank": 2, + "artist": "Stone Temple Pilots", + "title": "Big Bang Baby" + }, + { + "rank": 3, + "artist": "The Smashing Pumpkins", + "title": "Zero" + }, + { + "rank": 4, + "artist": "Oasis", + "title": "Champagne Supernova" + }, + { + "rank": 5, + "artist": "Jars Of Clay", + "title": "Flood" + }, + { + "rank": 6, + "artist": "The Cranberries", + "title": "Salvation" + }, + { + "rank": 7, + "artist": "Foo Fighters", + "title": "Big Me" + }, + { + "rank": 8, + "artist": "Alanis Morissette", + "title": "Head Over Feet" + }, + { + "rank": 9, + "artist": "The Nixons", + "title": "Sister" + }, + { + "rank": 10, + "artist": "Stabbing Westward", + "title": "What Do I Have To Do?" + }, + { + "rank": 11, + "artist": "Cracker", + "title": "I Hate My Generation" + }, + { + "rank": 12, + "artist": "The Verve Pipe", + "title": "Photograph" + }, + { + "rank": 13, + "artist": "Tracy Bonham", + "title": "Mother Mother" + }, + { + "rank": 14, + "artist": "Dave Matthews Band", + "title": "Too Much" + }, + { + "rank": 15, + "artist": "Collective Soul", + "title": "Where The River Flows" + }, + { + "rank": 16, + "artist": "Dog's Eye View", + "title": "Everything Falls Apart" + }, + { + "rank": 17, + "artist": "Dishwalla", + "title": "Counting Blue Cars" + }, + { + "rank": 18, + "artist": "Seven Mary Three", + "title": "Water's Edge" + }, + { + "rank": 19, + "artist": "Love And Rockets", + "title": "Sweet Lover Hangover" + }, + { + "rank": 20, + "artist": "Son Volt", + "title": "Drown" + }, + { + "rank": 21, + "artist": "Cowboy Junkies", + "title": "A Common Disaster" + }, + { + "rank": 22, + "artist": "Bad Religion", + "title": "A Walk" + }, + { + "rank": 24, + "artist": "AC/DC", + "title": "Ballbreaker" + }, + { + "rank": 25, + "artist": "Hootie & The Blowfish", + "title": "Old Man & Me (When I Get To Heaven)" + }, + { + "rank": 26, + "artist": "Rage Against The Machine", + "title": "Bulls On Parade" + }, + { + "rank": 27, + "artist": "Goldfinger", + "title": "Here In Your Bedroom" + }, + { + "rank": 29, + "artist": "Kiss", + "title": "Rock And Roll All Nite" + }, + { + "rank": 30, + "artist": "Garbage", + "title": "Only Happy When It Rains" + } + ] +} \ No newline at end of file diff --git a/subwave/broadcasts/tunecaster-rock-1996-07-13.json b/subwave/broadcasts/tunecaster-rock-1996-07-13.json new file mode 100644 index 0000000..2e48b57 --- /dev/null +++ b/subwave/broadcasts/tunecaster-rock-1996-07-13.json @@ -0,0 +1,151 @@ +{ + "station": "Tunecaster Rock Top 30", + "date": "1996-07-13", + "tracks": [ + { + "rank": 1, + "artist": "Primitive Radio Gods", + "title": "Standing Outside A Broken Phone Booth With Money In My Hand" + }, + { + "rank": 2, + "artist": "Stone Temple Pilots", + "title": "Trippin' On A Hole In A Paper Heart" + }, + { + "rank": 3, + "artist": "Butthole Surfers", + "title": "Pepper" + }, + { + "rank": 4, + "artist": "Metallica", + "title": "Until It Sleeps" + }, + { + "rank": 5, + "artist": "The Smashing Pumpkins", + "title": "Tonight, Tonight" + }, + { + "rank": 6, + "artist": "Garbage", + "title": "Stupid Girl" + }, + { + "rank": 7, + "artist": "Soundgarden", + "title": "Burden In My Hand" + }, + { + "rank": 8, + "artist": "Beck", + "title": "Where It's At" + }, + { + "rank": 9, + "artist": "The Cranberries", + "title": "Free To Decide" + }, + { + "rank": 10, + "artist": "Nada Surf", + "title": "Popular" + }, + { + "rank": 11, + "artist": "Sponge", + "title": "Wax Ecstatic (To Sell Angelina)" + }, + { + "rank": 12, + "artist": "Oasis", + "title": "Don't Look Back In Anger" + }, + { + "rank": 13, + "artist": "Screaming Trees", + "title": "All I Know" + }, + { + "rank": 14, + "artist": "Green Day", + "title": "Walking Contradiction" + }, + { + "rank": 15, + "artist": "Porno For Pyros", + "title": "Tahitian Moon" + }, + { + "rank": 16, + "artist": "The Cure", + "title": "Mint Car" + }, + { + "rank": 17, + "artist": "Goo Goo Dolls", + "title": "Long Way Down" + }, + { + "rank": 18, + "artist": "No Doubt", + "title": "Spiderwebs" + }, + { + "rank": 19, + "artist": "Alice In Chains", + "title": "Again" + }, + { + "rank": 20, + "artist": "The Black Crowes", + "title": "Good Friday" + }, + { + "rank": 21, + "artist": "The Hunger", + "title": "Vanishing Cream" + }, + { + "rank": 22, + "artist": "Alanis Morissette", + "title": "You Learn" + }, + { + "rank": 23, + "artist": "The Wallflowers", + "title": "6th Avenue Heartache" + }, + { + "rank": 24, + "artist": "311", + "title": "Down" + }, + { + "rank": 25, + "artist": "Jewel", + "title": "Who Will Save Your Soul" + }, + { + "rank": 26, + "artist": "Superdrag", + "title": "Sucked Out" + }, + { + "rank": 27, + "artist": "Cracker", + "title": "Nothing To Believe In" + }, + { + "rank": 28, + "artist": "The Hazies", + "title": "Skin & Bones" + }, + { + "rank": 29, + "artist": "Soundgarden", + "title": "Pretty Noose" + } + ] +} \ No newline at end of file diff --git a/subwave/broadcasts/tunecaster-rock-1996-12-14.json b/subwave/broadcasts/tunecaster-rock-1996-12-14.json new file mode 100644 index 0000000..7e3be71 --- /dev/null +++ b/subwave/broadcasts/tunecaster-rock-1996-12-14.json @@ -0,0 +1,156 @@ +{ + "station": "Tunecaster Rock Top 30", + "date": "1996-12-14", + "tracks": [ + { + "rank": 1, + "artist": "Soundgarden", + "title": "Blow Up The Outside World" + }, + { + "rank": 2, + "artist": "Garbage", + "title": "#1 Crush" + }, + { + "rank": 3, + "artist": "Stone Temple Pilots", + "title": "Lady Picture Show" + }, + { + "rank": 4, + "artist": "311", + "title": "All Mixed Up" + }, + { + "rank": 5, + "artist": "Cake", + "title": "The Distance" + }, + { + "rank": 6, + "artist": "No Doubt", + "title": "Don't Speak" + }, + { + "rank": 7, + "artist": "Tool", + "title": "Stinkfist" + }, + { + "rank": 8, + "artist": "Bush", + "title": "Swallowed" + }, + { + "rank": 9, + "artist": "Local H", + "title": "Bound For The Floor" + }, + { + "rank": 10, + "artist": "The Smashing Pumpkins", + "title": "Thirty-Three" + }, + { + "rank": 11, + "artist": "Red Hot Chili Peppers", + "title": "Love Rollercoaster" + }, + { + "rank": 12, + "artist": "Alice In Chains", + "title": "Down in a Hole" + }, + { + "rank": 13, + "artist": "Counting Crows", + "title": "A Long December" + }, + { + "rank": 14, + "artist": "The Wallflowers", + "title": "One Headlight" + }, + { + "rank": 15, + "artist": "Better Than Ezra", + "title": "Desperately Wanting" + }, + { + "rank": 16, + "artist": "The Presidents Of The United States Of America", + "title": "Mach 5" + }, + { + "rank": 17, + "artist": "Poe", + "title": "Hello" + }, + { + "rank": 18, + "artist": "Kula Shaker", + "title": "Tattva" + }, + { + "rank": 19, + "artist": "Luscious Jackson", + "title": "Naked Eye" + }, + { + "rank": 20, + "artist": "Soul Coughing", + "title": "Super Bon Bon" + }, + { + "rank": 21, + "artist": "Metallica", + "title": "Hero Of The Day" + }, + { + "rank": 22, + "artist": "Sublime", + "title": "What I Got" + }, + { + "rank": 23, + "artist": "Rush", + "title": "Half The World" + }, + { + "rank": 24, + "artist": "Matchbox 20", + "title": "Long Day" + }, + { + "rank": 25, + "artist": "The Bloodhound Gang", + "title": "Fire Water Burn" + }, + { + "rank": 26, + "artist": "Phish", + "title": "Free" + }, + { + "rank": 27, + "artist": "Fountains Of Wayne", + "title": "Radiation Vibe" + }, + { + "rank": 28, + "artist": "Sponge", + "title": "Have You Seen Mary" + }, + { + "rank": 29, + "artist": "John Mellencamp", + "title": "Just Another Day" + }, + { + "rank": 30, + "artist": "Fiona Apple", + "title": "Shadowboxer" + } + ] +} \ No newline at end of file diff --git a/subwave/recreate_broadcast.py b/subwave/recreate_broadcast.py index f629378..8ee8f6c 100755 --- a/subwave/recreate_broadcast.py +++ b/subwave/recreate_broadcast.py @@ -172,6 +172,15 @@ def contains_normalized(a, b): return bool(na.strip()) and bool(nb.strip()) and (na in nb or nb in na) +# A hard floor, independent of the combined score: without it a generic title +# match (e.g. two different "Just Another Day"s) can drag a middling but not +# absurd artist-name coincidence (e.g. "Jon Secada" vs "John Mellencamp", +# 0.56) over the acceptance threshold on title strength alone. Legitimate +# matches -- exact names or the containment-boosted composite-credit/subtitle +# cases -- always clear this by a wide margin. +ARTIST_SCORE_FLOOR = 0.65 + + def score_candidate(song, artist, title): title_score = similarity(song.get("title", ""), title) if contains_normalized(song.get("title", ""), title): @@ -179,6 +188,8 @@ def score_candidate(song, artist, title): artist_score = similarity(song.get("artist", ""), artist) if contains_normalized(song.get("artist", ""), artist): artist_score = 1.0 + if artist_score < ARTIST_SCORE_FLOOR: + return 0.0 return title_score * 0.6 + artist_score * 0.4