diff --git a/docs/supportedsites.md b/docs/supportedsites.md
index b79d1d9f..0865c69c 100644
--- a/docs/supportedsites.md
+++ b/docs/supportedsites.md
@@ -856,7 +856,7 @@ Consider all listed sites to potentially be NSFW.
| PornPics.com |
https://www.pornpics.com/ |
- Galleries, Search Results, Tag Searches |
+ Categories, Galleries, Listings, Search Results, Tag Searches |
|
diff --git a/gallery_dl/extractor/pornpics.py b/gallery_dl/extractor/pornpics.py
index b4394ce3..9c926e8c 100644
--- a/gallery_dl/extractor/pornpics.py
+++ b/gallery_dl/extractor/pornpics.py
@@ -116,3 +116,35 @@ class PornpicsSearchExtractor(PornpicsExtractor):
"offset": 0,
}
return self._pagination(url, params)
+
+
+class PornpicsListingExtractor(PornpicsExtractor):
+ """Extractor for galleries from pornpics listing pages
+
+ These pages (popular, recent, etc.) don't support JSON pagination
+ and use single quotes in HTML, unlike category pages.
+ """
+ subcategory = "listing"
+ pattern = (rf"{BASE_PATTERN}"
+ rf"/(popular|recent|rating|likes|views|comments)/?$")
+ example = "https://www.pornpics.com/popular/"
+
+ def galleries(self):
+ url = f"{self.root}/{self.groups[0]}/"
+ page = self.request(url).text
+ return [
+ {"g_url": href}
+ for href in text.extract_iter(
+ page, "class='rel-link' href='", "'")
+ ]
+
+
+class PornpicsCategoryExtractor(PornpicsExtractor):
+ """Extractor for galleries from pornpics categories"""
+ subcategory = "category"
+ pattern = rf"{BASE_PATTERN}/([^/?#]+)/?$"
+ example = "https://www.pornpics.com/ass/"
+
+ def galleries(self):
+ url = f"{self.root}/{self.groups[0]}/"
+ return self._pagination(url)
diff --git a/test/results/pornpics.py b/test/results/pornpics.py
index f2eb3df2..0d40332c 100644
--- a/test/results/pornpics.py
+++ b/test/results/pornpics.py
@@ -145,4 +145,46 @@ __tests__ = (
"#class" : pornpics.PornpicsSearchExtractor,
},
+{
+ "#url" : "https://www.pornpics.com/ass/",
+ "#category": ("", "pornpics", "category"),
+ "#class" : pornpics.PornpicsCategoryExtractor,
+ "#pattern" : pornpics.PornpicsGalleryExtractor.pattern,
+ "#range" : "1-50",
+ "#count" : 50,
+},
+
+{
+ "#url" : "https://www.pornpics.com/milf/",
+ "#category": ("", "pornpics", "category"),
+ "#class" : pornpics.PornpicsCategoryExtractor,
+},
+
+{
+ "#url" : "https://pornpics.com/de/blonde",
+ "#category": ("", "pornpics", "category"),
+ "#class" : pornpics.PornpicsCategoryExtractor,
+},
+
+{
+ "#url" : "https://www.pornpics.com/popular/",
+ "#category": ("", "pornpics", "listing"),
+ "#class" : pornpics.PornpicsListingExtractor,
+ "#pattern" : pornpics.PornpicsGalleryExtractor.pattern,
+ "#range" : "1-20",
+ "#count" : 20,
+},
+
+{
+ "#url" : "https://www.pornpics.com/recent/",
+ "#category": ("", "pornpics", "listing"),
+ "#class" : pornpics.PornpicsListingExtractor,
+},
+
+{
+ "#url" : "https://pornpics.com/fr/rating",
+ "#category": ("", "pornpics", "listing"),
+ "#class" : pornpics.PornpicsListingExtractor,
+},
+
)