Repository: yt-dlp/yt-dlp

Issue:
Bandcamp - infinite loop failure for "merch"
### Checklist

- [X] I'm reporting a broken site
- [X] I've verified that I'm running yt-dlp version **2022.03.08.1** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
- [X] I've checked that all provided URLs are alive and playable in a browser
- [X] I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
- [X] I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues including closed ones. DO NOT post duplicates
- [X] I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
- [X] I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required

### Region

_No response_

### Description

Starting with this link <https://mattiel.bandcamp.com/>, the download works its way through to "merch" links, where the band sell t-shirts, stickers, pins, etc.

Here's an example - <https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange>

Instead of skipping these links, the downloader/extractor gets stuck in a loop, trying to download from a page that has no media to download.

In the example I used to generate a verbose log, I specified <https://mattiel.bandcamp.com/> on the command-line, and explicitly set: `--retries 3 --file-access-retries 3 --fragment-retries 3 --extractor-retries 3`.

That was working fine for media, but then got stuck in a loop when it got to "merch", and had to be stopped with `^C`.

Proposed solution: The Bandcamp extractor should fail gracefully if there's no media to download.

Workaround: Specify a starting URL with the "music" and/or "releases" - xxx.bandcamp.com/**music** - xxx.bandcamp.com/**releases**

### Verbose log

```shell
[debug] Command-line config: ['-vU', '--config-locations', '/home/atom/.config/yt-dlp/config-bandcamp', '--retries', '3', '--file-access-retries', '3', '--fragment-retries', '3', '--extractor-retries', '3', 'https://mattiel.bandcamp.com/']
[debug] | Config "/home/atom/.config/yt-dlp/config-bandcamp": ['--ignore-config', '-o', "%(album|'Singles_')s/%(track_number|)02d%(track_number&_|)s%(title)s-%(release_date,upload_date)s.%(ext)s", '-o', "thumbnail:%(album|'Singles_')s/%(album,title)s.%(ext)s", '--embed-metadata', '--write-thumbnail', '--no-embed-thumbnail', '--restrict-filenames', '--trim-filenames', '245', '--download-archive', 'archive.txt']
[debug] Encodings: locale UTF-8, fs utf-8, out utf-8, err utf-8, pref UTF-8
[debug] yt-dlp version 2022.03.08.1 [c0c2c57d3] (zip)
[debug] Plugins: ['Youtube_AgeGateBypassIE']
[debug] Python version 3.8.10 (CPython 64bit) - Linux-5.4.0-107-generic-x86_64-with-glibc2.29
[debug] exe versions: ffmpeg 4.2.4, ffprobe 4.2.4, phantomjs 2.1.1, rtmpdump 2.4
[debug] Optional libraries: Crypto, secretstorage, mutagen, sqlite
[debug] Proxy map: {}
[debug] Loading archive file 'archive.txt'
Latest version: 2022.03.08.1, Current version: 2022.03.08.1
yt-dlp is up to date (2022.03.08.1)
[debug] [Bandcamp:user] Extracting URL: https://mattiel.bandcamp.com/
[Bandcamp:user] mattiel: Downloading webpage
[download] Downloading playlist: Discography of mattiel
[Bandcamp:user] playlist Discography of mattiel: Downloading 15 videos
[download] Downloading video 1 of 15
[debug] [Bandcamp:album] Extracting URL: https://mattiel.bandcamp.com/album/georgia-gothic?label=456339536&amp;tab=merch
[Bandcamp:album] georgia-gothic: Downloading webpage
[download] Downloading playlist: Georgia Gothic
[Bandcamp:album] playlist Georgia Gothic: Collected 11 videos; downloading 11 of them
[download] Downloading video 1 of 11
[download] Jeff Goldblum has already been recorded in the archive
[download] Downloading video 2 of 11
[download] On The Run has already been recorded in the archive
[download] Downloading video 3 of 11
[download] Lighthouse has already been recorded in the archive
[download] Downloading video 4 of 11
[download] Wheels Fall Off has already been recorded in the archive
[download] Downloading video 5 of 11
[download] Subterranean Shut-In Blues has already been recorded in the archive
[download] Downloading video 6 of 11
[download] Blood in the Yolk has already been recorded in the archive
[download] Downloading video 7 of 11
[download] Cultural Criminal has already been recorded in the archive
[download] Downloading video 8 of 11
[download] You Can Have It All has already been recorded in the archive
[download] Downloading video 9 of 11
[download] Other Plans has already been recorded in the archive
[download] Downloading video 10 of 11
[download] Boomerang has already been recorded in the archive
[download] Downloading video 11 of 11
[download] How It Ends has already been recorded in the archive
[download] Finished downloading playlist: Georgia Gothic
[download] Downloading video 2 of 15
[debug] [Bandcamp:album] Extracting URL: https://mattiel.bandcamp.com/album/those-words
[Bandcamp:album] those-words: Downloading webpage
[download] Downloading playlist: Those Words
[Bandcamp:album] playlist Those Words: Collected 2 videos; downloading 2 of them
[download] Downloading video 1 of 2
[download] Those Words has already been recorded in the archive
[download] Downloading video 2 of 2
[download] Freedom Feels has already been recorded in the archive
[download] Finished downloading playlist: Those Words
[download] Downloading video 3 of 15
[debug] [Bandcamp:album] Extracting URL: https://mattiel.bandcamp.com/album/double-cover
[Bandcamp:album] double-cover: Downloading webpage
[download] Downloading playlist: Double Cover
[Bandcamp:album] playlist Double Cover: Collected 2 videos; downloading 2 of them
[download] Downloading video 1 of 2
[download] Looking Down The Barrel Of A Gun has already been recorded in the archive
[download] Downloading video 2 of 2
[download] Guns Of Brixton has already been recorded in the archive
[download] Finished downloading playlist: Double Cover
[download] Downloading video 4 of 15
[debug] [Bandcamp:album] Extracting URL: https://mattiel.bandcamp.com/album/customer-copy-ep
[Bandcamp:album] customer-copy-ep: Downloading webpage
[download] Downloading playlist: Customer Copy EP
[Bandcamp:album] playlist Customer Copy EP: Collected 6 videos; downloading 6 of them
[download] Downloading video 1 of 6
[download] Baron's Sunday Best has already been recorded in the archive
[download] Downloading video 2 of 6
[download] East Coast Swing has already been recorded in the archive
[download] Downloading video 3 of 6
[download] Follow My Name has already been recorded in the archive
[download] Downloading video 4 of 6
[download] Game Show has already been recorded in the archive
[download] Downloading video 5 of 6
[download] Midwest Hotel has already been recorded in the archive
[download] Downloading video 6 of 6
[download] Detroit Riot has already been recorded in the archive
[download] Finished downloading playlist: Customer Copy EP
[download] Downloading video 5 of 15
[debug] [Bandcamp:album] Extracting URL: https://mattiel.bandcamp.com/album/satis-factory
[Bandcamp:album] satis-factory: Downloading webpage
[download] Downloading playlist: Satis Factory
[Bandcamp:album] playlist Satis Factory: Collected 12 videos; downloading 12 of them
[download] Downloading video 1 of 12
[download] Moment Of Death has already been recorded in the archive
[download] Downloading video 2 of 12
[download] Rescue You has already been recorded in the archive
[download] Downloading video 3 of 12
[download] Je Ne Me Connais Pas has already been recorded in the archive
[download] Downloading video 4 of 12
[download] Food For Thought has already been recorded in the archive
[download] Downloading video 5 of 12
[download] Keep The Change has already been recorded in the archive
[download] Downloading video 6 of 12
[download] Millionaire has already been recorded in the archive
[download] Downloading video 7 of 12
[download] Populonia has already been recorded in the archive
[download] Downloading video 8 of 12
[download] Blisters has already been recorded in the archive
[download] Downloading video 9 of 12
[download] Athlete has already been recorded in the archive
[download] Downloading video 10 of 12
[download] Berlin Weekend has already been recorded in the archive
[download] Downloading video 11 of 12
[download] Heck Fire has already been recorded in the archive
[download] Downloading video 12 of 12
[download] Long Division has already been recorded in the archive
[download] Finished downloading playlist: Satis Factory
[download] Downloading video 6 of 15
[debug] [Bandcamp:album] Extracting URL: https://mattiel.bandcamp.com/album/mattiel-lp
[Bandcamp:album] mattiel-lp: Downloading webpage
[download] Downloading playlist: MATTIEL LP
[Bandcamp:album] playlist MATTIEL LP: Collected 12 videos; downloading 12 of them
[download] Downloading video 1 of 12
[download] Whites of Their Eyes has already been recorded in the archive
[download] Downloading video 2 of 12
[download] Send it on Over has already been recorded in the archive
[download] Downloading video 3 of 12
[download] Baby Brother has already been recorded in the archive
[download] Downloading video 4 of 12
[download] Not Today has already been recorded in the archive
[download] Downloading video 5 of 12
[download] Cass Tech has already been recorded in the archive
[download] Downloading video 6 of 12
[download] Bye Bye has already been recorded in the archive
[download] Downloading video 7 of 12
[download] Fives and Tens has already been recorded in the archive
[download] Downloading video 8 of 12
[download] Silver Pillbox has already been recorded in the archive
[download] Downloading video 9 of 12
[download] Just a Name has already been recorded in the archive
[download] Downloading video 10 of 12
[download] Count Your Blessings has already been recorded in the archive
[download] Downloading video 11 of 12
[download] Salty Words has already been recorded in the archive
[download] Downloading video 12 of 12
[download] Ready to Think has already been recorded in the archive
[download] Finished downloading playlist: MATTIEL LP
[download] Downloading video 7 of 15
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
WARNING: [generic] Falling back on generic information extractor.
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Downloading webpage
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Extracting information
[debug] Looking for video embeds
[debug] [generic] Extracting URL: https://mattiel.bandcamp.com/merch/limited-edition-2020-long-sleeve-tee-neon-orange
[generic] limited-edition-2020-long-sleeve-tee-neon-orange: Requesting header
^C
ERROR: Interrupted by user
```



Discussion:
Hello. Is it alright if I work on this issue? I'm pretty sure it can be fixed by modifying one of the regular expressions in the Bandcamp info extractor to exclude links to merch items. 
Yup, go ahead

Resolving patch:
diff --git a/yt_dlp/extractor/bandcamp.py b/yt_dlp/extractor/bandcamp.py
--- a/yt_dlp/extractor/bandcamp.py
+++ b/yt_dlp/extractor/bandcamp.py
@@ -439,7 +439,7 @@ def _real_extract(self, url):
         uploader = self._match_id(url)
         webpage = self._download_webpage(url, uploader)
 
-        discography_data = (re.findall(r'<li data-item-id=["\'][^>]+>\s*<a href=["\']([^"\']+)', webpage)
+        discography_data = (re.findall(r'<li data-item-id=["\'][^>]+>\s*<a href=["\'](?![^"\'/]*?/merch)([^"\']+)', webpage)
                             or re.findall(r'<div[^>]+trackTitle["\'][^"\']+["\']([^"\']+)', webpage))
 
         return self.playlist_from_matches(


Test patch: