diff --git a/docs/supportedsites.md b/docs/supportedsites.md
index f779217e18..a6b5cfb140 100644
--- a/docs/supportedsites.md
+++ b/docs/supportedsites.md
@@ -547,6 +547,12 @@ Consider all listed sites to potentially be NSFW.
Blogs, Posts |
|
+
+ LOFTER |
+ https://www.lofter.com/ |
+ Blog Posts, Posts |
+ |
+
Luscious |
https://members.luscious.net/ |
diff --git a/gallery_dl/extractor/lofter.py b/gallery_dl/extractor/lofter.py
index bb167761d3..412b6b9fbb 100644
--- a/gallery_dl/extractor/lofter.py
+++ b/gallery_dl/extractor/lofter.py
@@ -27,9 +27,8 @@ def items(self):
post = post["post"]
post["blog_name"] = post["blogInfo"]["blogName"]
-
+ post["date"] = text.parse_timestamp(post["publishTime"] // 1000)
post_type = post["type"]
- image_urls = []
# Article
if post_type == 1:
@@ -56,6 +55,7 @@ def items(self):
image_urls = [x.partition("?")[0] for x in image_urls]
else:
+ image_urls = ()
self.log.warning(
"%s: Unsupported post type '%s'.",
post["id"], post_type)
@@ -73,7 +73,7 @@ class LofterPostExtractor(LofterExtractor):
"""Extractor for a lofter post"""
subcategory = "post"
pattern = r"(?:https?://)?[\w-]+\.lofter\.com/post/([0-9a-f]+)_([0-9a-f]+)"
- example = "https://blog_name.lofter.com/post/12345678_90abcdef"
+ example = "https://BLOG.lofter.com/post/12345678_90abcdef"
def posts(self):
blog_id, post_id = self.groups
@@ -89,21 +89,39 @@ class LofterBlogPostsExtractor(LofterExtractor):
r"www\.lofter\.com/front/blog/home-page/([\w-]+)|"
# https://.lofter.com/
r"([\w-]+)\.lofter\.com"
- r")")
- example = "https://blog_name.lofter.com/"
+ r")/?(?:$|\?|#)")
+ example = "https://BLOG.lofter.com/"
def posts(self):
blog_name = self.groups[0] or self.groups[1]
- posts = self.api.blog_posts(blog_name)
- return posts
+ return self.api.blog_posts(blog_name)
class LofterAPI():
+
def __init__(self, extractor):
self.extractor = extractor
+ def blog_posts(self, blog_name):
+ endpoint = "/v2.0/blogHomePage.api"
+ params = {
+ "method": "getPostLists",
+ "offset": 0,
+ "limit": 200,
+ "blogdomain": blog_name + ".lofter.com",
+ }
+ return self._pagination(endpoint, params)
+
+ def post(self, blog_id, post_id):
+ endpoint = "/oldapi/post/detail.api"
+ params = {
+ "targetblogid": blog_id,
+ "postid": post_id,
+ }
+ return self._call(endpoint, params)["posts"][0]
+
def _call(self, endpoint, data):
- url = "https://api.lofter.com{}".format(endpoint)
+ url = "https://api.lofter.com" + endpoint
params = {
'product': 'lofter-android-7.9.10'
}
@@ -115,36 +133,15 @@ def _call(self, endpoint, data):
self.extractor.log.debug("Server response: %s", info)
raise exception.StopExtraction("API request failed")
- return info
-
- def blog_posts(self, blog_name):
- endpoint = "/v2.0/blogHomePage.api"
- params = {
- "method": "getPostLists",
- "offset": 0,
- "limit": 200,
- "blogdomain": "{}.lofter.com".format(blog_name),
- }
+ return info["response"]
+ def _pagination(self, endpoint, params):
while True:
data = self._call(endpoint, params)
- posts = data["response"]["posts"]
+ posts = data["posts"]
- for post in posts:
- yield post
+ yield from posts
- if params["offset"] + len(posts) < data["response"]["offset"]:
+ if params["offset"] + len(posts) < data["offset"]:
break
-
- params["offset"] = data["response"]["offset"]
-
- def post(self, blog_id, post_id):
- endpoint = "/oldapi/post/detail.api"
- params = {
- "targetblogid": blog_id,
- "postid": post_id,
- }
- data = self._call(endpoint, params)
- posts = data["response"]["posts"]
- post = posts[0]
- return post
+ params["offset"] = data["offset"]
diff --git a/scripts/supportedsites.py b/scripts/supportedsites.py
index aafdb0d27c..a101266575 100755
--- a/scripts/supportedsites.py
+++ b/scripts/supportedsites.py
@@ -86,6 +86,7 @@
"kemonoparty" : "Kemono",
"koharu" : "SchaleNetwork",
"livedoor" : "livedoor Blog",
+ "lofter" : "LOFTER",
"ohpolly" : "Oh Polly",
"omgmiamiswimwear": "Omg Miami Swimwear",
"mangadex" : "MangaDex",
@@ -265,6 +266,9 @@
"lensdump": {
"albums": "",
},
+ "lofter": {
+ "blog-posts": "Blog Posts",
+ },
"mangadex": {
"feed" : "Followed Feed",
},
diff --git a/test/results/lofter.py b/test/results/lofter.py
new file mode 100644
index 0000000000..99a8f570d4
--- /dev/null
+++ b/test/results/lofter.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+
+from gallery_dl.extractor import lofter
+
+
+__tests__ = (
+{
+ "#url" : "https://gengar563.lofter.com/post/1e82da8c_1c98dae1b",
+ "#class": lofter.LofterPostExtractor,
+ "#urls" : (
+ "https://imglf3.lf127.net/img/S1d2QlVsWkJhSW1qcnpIS0ZSa3ZJQ1RxY0lYaU1UUE9tQ0NvUE9rVXFpOFFEVzMwbnQ4aEFnPT0.jpg",
+ "https://imglf3.lf127.net/img/S1d2QlVsWkJhSW1qcnpIS0ZSa3ZJRWlXYTRVOEpXTU9TSGt3TjBDQ0JFZVpZMEJtWjFneVNBPT0.png",
+ "https://imglf6.lf127.net/img/S1d2QlVsWkJhSW1qcnpIS0ZSa3ZJR1d3Y2VvbTNTQlIvdFU1WWlqZHEzbjI4MFVNZVdoN3VBPT0.png",
+ "https://imglf6.lf127.net/img/S1d2QlVsWkJhSW1qcnpIS0ZSa3ZJTi83NDRDUjNvd3hySGxEZFovd2hwbi9oaG9NQ1hOUkZ3PT0.png",
+ "https://imglf4.lf127.net/img/S1d2QlVsWkJhSW1qcnpIS0ZSa3ZJUFczb2RKSVlpMHJkNy9kc3BSQVQvQm5DNzB4eVhxay9nPT0.png",
+ "https://imglf4.lf127.net/img/S1d2QlVsWkJhSW1qcnpIS0ZSa3ZJSStJZE9RYnJURktHazdIVHNNMjQ5eFJldHVTQy9XbDB3PT0.png",
+ "https://imglf3.lf127.net/img/S1d2QlVsWkJhSW1qcnpIS0ZSa3ZJSzFCWFlnUWgzb01DcUdpT1lreG5yQjJVMkhGS09HNGR3PT0.png",
+ ),
+
+ "blog_name": "gengar563",
+ "content" : "发了三次发不出有毒……
\n二部运动au 性转ac注意
\n失去耐心.jpg
",
+ "date" : "dt:2020-06-04 12:51:42",
+ "id" : 7676472859,
+},
+
+{
+ "#url" : "https://wooden-brain.lofter.com/post/1e60de5b_1c9bf8efb",
+ "#comment": "video",
+ "#class" : lofter.LofterPostExtractor,
+ "#urls" : (
+ "https://vodm2lzexwq.vod.126.net/vodm2lzexwq/Pc5jg1nL_3039990631_sd.mp4?resId=254486990bfa2cd7aa860229db639341_3039990631_1&sign=4j02HTHXqNfhaF%2B%2FO14Ny%2F9SMNZj%2FIjpJDCqXfYa4aM%3D",
+ ),
+
+ "blog_name": "wooden-brain",
+ "date" : "dt:2020-06-24 11:01:59",
+ "id" : 7679741691,
+},
+
+{
+ "#url" : "https://gengar563.lofter.com/",
+ "#class": lofter.LofterBlogPostsExtractor,
+ "#range": "1-25",
+ "#count": 25,
+
+ "blog_name": "gengar563",
+ "date" : "type:datetime",
+ "id" : int,
+},
+
+{
+ "#url" : "https://www.lofter.com/front/blog/home-page/gengar563",
+ "#class": lofter.LofterBlogPostsExtractor,
+},
+
+)