This guide walks you through running and testing the ad-blocking POC with real podcast episodes.
- MP3 files placed in
public/test-podcasts/ - Ad timecodes updated in fixture files
- GUID normalization implemented (handles CDATA)
- All code implemented and ready to test
Open a terminal in the podverse-web directory and run:
npm install
npm run devExpected output:
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
Note: If you get any TypeScript errors about crypto module, that's expected in the browser environment. The server-side code will use Node's crypto module correctly.
Since this is a POC and you don't have the full Podverse database set up, you'll need to test in one of these ways:
The MP3 files are publicly accessible at:
- http://localhost:3000/test-podcasts/AUDIO---DEFAULT---dcfdc6d6-489e-4ac7-b9a4-5629cc632395.mp3
- http://localhost:3000/test-podcasts/AUDIO---DEFAULT---34eb3a45-d135-453e-a790-58a744da35ba.mp3
- http://localhost:3000/test-podcasts/PPLLC1896786790.mp3
You can verify they're accessible by opening these URLs in your browser.
- Navigate to http://localhost:3000
- Open Browser DevTools (F12)
- Go to Console tab
- Manually trigger the episode loading logic:
// Import the necessary functions
const { extractEpisodeKeyFromNowPlayingItem } = await import('/src/services/adSegments/adSegmentIdentity.ts')
const { fixtureAdSegmentProvider } = await import('/src/services/adSegments/adSegmentFixtureProvider.ts')
const { mergeAdSegments } = await import('/src/services/adSegments/adSegmentMerge.ts')
// Test with episode 1 (What Bitcoin Did)
const testEpisode1 = {
episodeGuid: 'ec8b4655-cb61-4d69-87ee-dbdba77c0a62',
podcastFeedUrl: 'https://feeds.fountain.fm/UZSKQcrOnhqYS1JopxGg',
episodeMediaUrl: 'https://feeds.fountain.fm/UZSKQcrOnhqYS1JopxGg/items/RpJavE7EEgZNW5hpgUDJ/files/AUDIO---DEFAULT---dcfdc6d6-489e-4ac7-b9a4-5629cc632395.mp3'
}
const episodeKey = extractEpisodeKeyFromNowPlayingItem(testEpisode1)
console.log('Episode Key:', episodeKey)
const adSegments = await fixtureAdSegmentProvider.fetchAdSegments(episodeKey)
console.log('Ad Segments:', adSegments)
const mergedSegments = mergeAdSegments(adSegments)
console.log('Merged Ad Segments:', mergedSegments)Expected Console Output:
Episode Key: ec8b4655-cb61-4d69-87ee-dbdba77c0a62
Ad Segments: (3) [{…}, {…}, {…}]
0: {id: 'ad-test1-001', startTime: 2, endTime: 60, …}
1: {id: 'ad-test1-002', startTime: 669, endTime: 815, …}
2: {id: 'ad-test1-003', startTime: 2263, endTime: 2403, …}
Merged Ad Segments: (3) [{…}, {…}, {…}]
If Podverse is already configured to load episodes from RSS feeds, you can:
- Navigate to the podcast page
- Find the specific episode by title:
- "Fiat Money, Inflation & the Collapse..." (What Bitcoin Did)
- "#697: The Legal Fight Against UK..." (TFTC)
- "TECH009: Data Centers in Space..." (TIP)
- Click to play
When an episode loads, you should see:
[AdSegments] Loaded 3 ad segments (merged to 3) for episode: ec8b4655-cb61-4d69-87ee-dbdba77c0a62
If you see this, ad segments are loading correctly! ✅
- Open DevTools → Network tab
- Filter by "Fetch/XHR"
- Look for requests to fixture files (though these use dynamic imports, so may not show in Network tab)
- Load an episode with ad segments
- Look for the Skip Ads toggle button in the player controls
- It should appear next to the Make Clip button
- Icon: Forward arrow (►)
- Click to toggle ON (button should show active state)
Ad: 00:00:02 - 00:01:00 (2s - 60s)
- Ensure Skip Ads is ON
- Start playback from the beginning (0:00)
- Expected behavior:
- Playback starts at 0:00
- At ~2 seconds, player automatically seeks to 1:00 (60 seconds)
- Console shows:
[AdSkip] Skipping ad segment [2.0s - 60.0s] → seeking to 60.0s
Ad: 00:11:09 - 00:13:35 (669s - 815s)
- Scrub to ~11:00 (660 seconds)
- Press play
- Expected behavior:
- At 11:09, player automatically seeks to 13:35
- Console shows skip log
- Toggle Skip Ads OFF
- Scrub to 0:00 and play
- Expected behavior:
- Pre-roll ad plays normally (no skip)
- Playback continues through ad segment
Purpose: Verify user can scrub backward into an ad and it will skip again.
- Ensure Skip Ads is ON
- Let the pre-roll ad (0:02-1:00) auto-skip
- Manually scrub backward to 0:30 (inside the ad)
- Expected behavior:
- Player should skip again to 1:00
- Console shows new skip log
- No infinite loop (debounce prevents repeated skips)
GUID: ec8b4655-cb61-4d69-87ee-dbdba77c0a62
| Ad # | Start Time | End Time | Description |
|---|---|---|---|
| 1 | 00:00:02 | 00:01:00 | Pre-roll (2s → 60s) |
| 2 | 00:11:09 | 00:13:35 | Mid-roll #1 (669s → 815s) |
| 3 | 00:37:43 | 00:40:03 | Mid-roll #2 (2263s → 2403s) |
Test:
- Enable Skip Ads
- Play from start → should skip to 1:00
- Scrub to 11:00, play → should skip to 13:35
- Scrub to 37:30, play → should skip to 40:03
GUID: 2d4217bb-799b-4192-a559-348e82e2ea0e
| Ad # | Start Time | End Time | Description |
|---|---|---|---|
| 1 | 00:00:03 | 00:00:31 | Pre-roll (3s → 31s) |
| 2 | 00:27:12 | 00:29:19 | Mid-roll #1 (1632s → 1759s) |
| 3 | 00:42:49 | 00:44:49 | Mid-roll #2 (2569s → 2689s) |
Test:
- Enable Skip Ads
- Play from start → should skip to 0:31
- Scrub to 27:00, play → should skip to 29:19
- Scrub to 42:30, play → should skip to 44:49
GUID: b47746b8-daa3-11f0-b08a-233a79f7667c (wrapped in CDATA)
| Ad # | Start Time | End Time | Description |
|---|---|---|---|
| 1 | 00:00:50 | 00:01:28 | Pre-roll (50s → 88s) |
| 2 | 00:16:03 | 00:19:56 | Mid-roll #1 (963s → 1196s) |
| 3 | 00:32:02 | 00:35:33 | Mid-roll #2 (1922s → 2133s) |
Test:
- Verify GUID normalization (CDATA stripping works)
- Enable Skip Ads
- Play from start → should skip to 1:28
- Scrub to 16:00, play → should skip to 19:56
- Scrub to 32:00, play → should skip to 35:33
- Ad segments load from fixtures (check console log)
- Skip Ads toggle appears in player controls
- Toggle ON/OFF works (visual state change)
- Auto-skip jumps to ad endTime when toggle is ON
- No skip occurs when toggle is OFF
- Each ad segment only skipped once per entry
- Re-scrubbing into ad triggers skip again
- No infinite skip loops (debounce works)
- Console logs show skip actions clearly
- Test 1 & 2: Plain GUID works
- Test 3: CDATA-wrapped GUID works (normalized correctly)
- Console shows correct episode key for all three
- Skip Ads button visible and clickable
- Button shows active/inactive state
- Seeking works smoothly after auto-skip
- No player crashes or errors
Solution:
cd podverse-web
npm install
npm run devMake sure you're in the podverse-web directory, not the root.
Check:
- Console for error messages
- Fixture filenames match episode GUIDs exactly
- JSON files are valid (no syntax errors)
Debug:
// In browser console:
const { generateEpisodeKey } = await import('/src/services/adSegments/adSegmentIdentity.ts')
const testGuid = 'ec8b4655-cb61-4d69-87ee-dbdba77c0a62'
const key = generateEpisodeKey({ guid: testGuid })
console.log('Generated key:', key)
// Should match fixture filenameCheck:
- Skip Ads toggle is ON (check button state)
- Console shows skip logs when entering ad
- Ad timecodes are correct (check fixture JSON)
- Player is using audio (not video)
Debug:
// In browser console:
const { getSkipAdsEnabled, getMergedAdSegments } = await import('/src/services/player/playerAdSkip.ts')
console.log('Skip Ads Enabled:', getSkipAdsEnabled())
console.log('Merged Segments:', getMergedAdSegments())Check:
const { generateEpisodeKey } = await import('/src/services/adSegments/adSegmentIdentity.ts')
const cdataGuid = '<![CDATA[ b47746b8-daa3-11f0-b08a-233a79f7667c ]]>'
const key = generateEpisodeKey({ guid: cdataGuid })
console.log('Normalized key:', key)
// Should be: b47746b8-daa3-11f0-b08a-233a79f7667c (without CDATA)Workaround: Since this is a POC without full database setup, you can test the core logic via console:
// Manually initialize ad skip for testing
const { initializeAdSkipForEpisode, setSkipAdsEnabled } = await import('/src/services/player/playerAdSkip.ts')
const { mergeAdSegments } = await import('/src/services/adSegments/adSegmentMerge.ts')
const { fixtureAdSegmentProvider } = await import('/src/services/adSegments/adSegmentFixtureProvider.ts')
// Load ad segments for Test 1
const segments = await fixtureAdSegmentProvider.fetchAdSegments('ec8b4655-cb61-4d69-87ee-dbdba77c0a62')
const merged = mergeAdSegments(segments)
// Initialize skip manager
initializeAdSkipForEpisode(merged)
setSkipAdsEnabled(true)
console.log('Ad skip initialized with', merged.length, 'segments')For documentation/demo purposes, capture:
- Console log showing ad segments loading
- Skip Ads toggle button in player controls
- Console log showing skip action (
[AdSkip] Skipping ad segment...) - Player timeline before and after skip
- All three test episodes successfully loading
The POC is successful if:
✅ All three episodes load ad segments correctly (console log confirms) ✅ GUID normalization works for all formats (plain + CDATA) ✅ Skip Ads toggle appears and works ✅ Auto-skip jumps to ad endTime when enabled ✅ Skip guards prevent infinite loops ✅ Re-entering ads triggers skip again ✅ Toggle OFF disables auto-skip ✅ No player crashes or critical errors
Print or use this checklist while testing:
EPISODE 1: What Bitcoin Did (ec8b4655-cb61-4d69-87ee-dbdba77c0a62)
□ Ad segments load (check console)
□ Pre-roll skip (2s → 60s)
□ Mid-roll #1 skip (669s → 815s)
□ Mid-roll #2 skip (2263s → 2403s)
□ Toggle OFF disables skip
□ Re-entry triggers skip again
EPISODE 2: TFTC (2d4217bb-799b-4192-a559-348e82e2ea0e)
□ Ad segments load (check console)
□ Pre-roll skip (3s → 31s)
□ Mid-roll #1 skip (1632s → 1759s)
□ Mid-roll #2 skip (2569s → 2689s)
□ Toggle OFF disables skip
□ Re-entry triggers skip again
EPISODE 3: TIP (b47746b8-daa3-11f0-b08a-233a79f7667c - CDATA)
□ CDATA GUID normalized correctly
□ Ad segments load (check console)
□ Pre-roll skip (50s → 88s)
□ Mid-roll #1 skip (963s → 1196s)
□ Mid-roll #2 skip (1922s → 2133s)
□ Toggle OFF disables skip
□ Re-entry triggers skip again
OVERALL
□ No infinite skip loops observed
□ Console logs clear and helpful
□ Skip Ads toggle visible and functional
□ Player remains stable (no crashes)
Once testing is complete and successful:
- Document findings - Note any bugs or improvements
- Run unit tests -
npm testto verify all tests pass - Review code - Ensure clean separation of chapters vs ads
- Plan Nostr integration - See NOSTR_INTEGRATION.md
Good luck testing! 🚀
If you encounter any issues, refer to the troubleshooting section or check the console logs for detailed error messages.