[iwara] update

- add 'include' docs
- simplify code for different result types
- provide 'count' and 'num' metadata
- remove 'search_type' metadata
This commit is contained in:
Mike Fährmann
2025-07-14 18:30:49 +02:00
parent fca1cd51f5
commit bbe7faed21
4 changed files with 65 additions and 38 deletions

View File

@@ -3197,6 +3197,26 @@ Description
Download video files.
extractor.iwara.include
-----------------------
Type
* ``string``
* ``list`` of ``strings``
Default
``["user-images", "user-videos"]``
Description
A (comma-separated) list of subcategories to include
when processing a user profile.
Possible values are
* ``"user-images"``
* ``"user-videos"``
* ``"user-playlists"``
It is possible to use ``"all"`` instead of listing all values separately.
extractor.kemono.archives
-------------------------
Type

View File

@@ -416,7 +416,9 @@
"iwara":
{
"username": "",
"password": ""
"password": "",
"include": ["user-images", "user-images"]
},
"kemono":
{

View File

@@ -45,13 +45,14 @@ class IwaraExtractor(Extractor):
image["id"], exc.__class__.__name__, exc)
continue
group_info["count"] = len(files)
yield Message.Directory, group_info
for file in files:
for num, file in enumerate(files, 1):
file_info = self.extract_media_info(file, None)
file_id = file_info["file_id"]
url = (f"https://i.iwara.tv/image/original/"
f"{file_id}/{file_id}.{file_info['extension']}")
yield Message.Url, url, {**file_info, **group_info}
yield Message.Url, url, {**file_info, **group_info, "num": num}
def items_video(self, videos, user=None):
for video in videos:
@@ -67,6 +68,7 @@ class IwaraExtractor(Extractor):
download_url = source.get('src', {}).get('download')
info = self.extract_media_info(video, "file")
info["count"] = info["num"] = 1
info["user"] = (self.extract_user_info(video)
if user is None else user)
except Exception as exc:
@@ -78,15 +80,27 @@ class IwaraExtractor(Extractor):
yield Message.Directory, info
yield Message.Url, f"https:{download_url}", info
def items_user(self, users, key):
def items_user(self, users, key=None):
base = f"{self.root}/profile/"
for user in users:
user = user[key]
if key is not None:
user = user[key]
if (username := user["username"]) is None:
continue
user["type"] = "user"
user["_extractor"] = IwaraUserExtractor
yield Message.Queue, f"{base}{username}", user
def items_by_type(self, type, results):
if type == "image":
return self.items_image(results)
if type == "video":
return self.items_video(results)
if type == "user":
return self.items_user(results)
raise exception.AbortExtraction(f"Unsupported result type '{type}'")
def extract_media_info(self, item, key, include_file_info=True):
title = t.strip() if (t := item.get("title")) else ""
@@ -182,6 +196,7 @@ class IwaraUserPlaylistsExtractor(IwaraExtractor):
base = f"{self.root}/playlist/"
for playlist in self.api.playlists(self._user_params()[1]):
playlist["type"] = "playlist"
playlist["_extractor"] = IwaraPlaylistExtractor
url = f"{base}{playlist['id']}"
yield Message.Queue, url, playlist
@@ -244,12 +259,7 @@ class IwaraFavoriteExtractor(IwaraExtractor):
def items(self):
type = self.groups[0] or "vidoo"
results = self.api.favorites(type)
if type == "image":
return self.items_image(results)
else:
return self.items_video(results)
return self.items_by_type(type, self.api.favorites(type))
class IwaraSearchExtractor(IwaraExtractor):
@@ -260,34 +270,22 @@ class IwaraSearchExtractor(IwaraExtractor):
def items(self):
params = text.parse_query(self.groups[0])
self.kwdict["search_type"] = type = params.get("type", "video")
type = params.get("type")
self.kwdict["search_tags"] = query = params.get("query")
results = self.api.search(type, query)
if type == "image":
return self.items_image(results)
if type == "video":
return self.items_video(results)
raise exception.AbortExtraction("Unsupported search type '%s'", type)
return self.items_by_type(type, self.api.search(type, query))
class IwaraTagExtractor(IwaraExtractor):
"""Extractor for iwara.tv tag search"""
subcategory = "tag"
pattern = rf"{BASE_PATTERN}/(videos|images)(?:\?([^#]+))?"
pattern = rf"{BASE_PATTERN}/(image|video)s(?:\?([^#]+))?"
example = "https://www.iwara.tv/videos?tags=TAGS"
def items(self):
type, qs = self.groups
params = text.parse_query(qs)
self.kwdict["search_type"] = type
self.kwdict["search_tags"] = params.get("tags")
if type == "images":
return self.items_image(self.api.images(params))
else:
return self.items_video(self.api.videos(params))
return self.items_by_type(type, self.api.media(type, params))
class IwaraAPI():
@@ -336,12 +334,14 @@ class IwaraAPI():
endpoint = "/playlists"
return self._pagination(endpoint, params)
def collection(self, type, params):
def media(self, type, params):
endpoint = f"/{type}s"
params.setdefault("rating", "all")
return self._pagination(endpoint, params)
def favorites(self, type):
if not self.username:
raise exception.LoginRequired("'username' and 'password' needed")
endpoint = f"/favorites/{type}s"
return self._pagination(endpoint)

View File

@@ -29,6 +29,8 @@ __tests__ = (
"extension": "png",
"type" : "image",
"count" : 1,
"num" : 1,
},
{
@@ -41,6 +43,8 @@ __tests__ = (
"extension": "mp4",
"type" : "video",
"count" : 1,
"num" : 1,
},
{
@@ -48,6 +52,8 @@ __tests__ = (
"#class" : iwara.IwaraUserPlaylistsExtractor,
"#pattern" : iwara.IwaraPlaylistExtractor.pattern,
"#count" : range(10, 20),
"type" : "playlist",
},
{
@@ -56,6 +62,8 @@ __tests__ = (
"#pattern" : iwara.IwaraUserExtractor.pattern,
"#range" : "1-100",
"#count" : 100,
"type" : "user",
},
{
@@ -64,6 +72,8 @@ __tests__ = (
"#pattern" : iwara.IwaraUserExtractor.pattern,
"#range" : "1-100",
"#count" : 100,
"type" : "user",
},
{
@@ -162,7 +172,6 @@ __tests__ = (
"height" : None,
"type" : "video",
"search_tags" : "aether,citlali",
"search_type" : "videos",
"duration" : range(90, 200),
},
@@ -170,20 +179,12 @@ __tests__ = (
"#url" : "https://www.iwara.tv/images?tags=genshin_impact%2Ccitlali",
"#category" : ("", "iwara", "tag"),
"#class" : iwara.IwaraTagExtractor,
"#results" : (
"https://i.iwara.tv/image/original/c442c69f-30fb-4fd4-8f8f-338bbc77c07d/c442c69f-30fb-4fd4-8f8f-338bbc77c07d.jpg",
"https://i.iwara.tv/image/original/7b53cc07-3640-4749-8c11-6da5f5a292a0/7b53cc07-3640-4749-8c11-6da5f5a292a0.jpg",
"https://i.iwara.tv/image/original/373cc1cb-028e-44bd-aef3-3400de4f995b/373cc1cb-028e-44bd-aef3-3400de4f995b.jpg",
"https://i.iwara.tv/image/original/0256b01b-8b4d-47f7-894d-2aceba6b8ab8/0256b01b-8b4d-47f7-894d-2aceba6b8ab8.jpg",
"https://i.iwara.tv/image/original/8541dab6-9c67-419d-8af8-2e040ae487dc/8541dab6-9c67-419d-8af8-2e040ae487dc.png",
"https://i.iwara.tv/image/original/8eba51de-c618-4853-964f-25f526b58398/8eba51de-c618-4853-964f-25f526b58398.webm",
),
"#pattern" : r"https://i.iwara.tv/image/original/[0-9a-f-]{36}/[0-9a-f-]{36}\.(jpg|png|webm)",
"duration" : None,
"extension" : {"jpg", "png", "webm"},
"mime" : {"image/jpeg", "image/png", "video/webm"},
"search_tags" : "genshin_impact,citlali",
"search_type" : "images",
"type" : "image",
},
@@ -231,6 +232,10 @@ __tests__ = (
"extension" : "png",
"mime" : "image/png",
"type" : "image",
"width" : int,
"height" : int,
"count" : 13,
"num" : range(1, 13),
"date" : "type:datetime",
"date_updated" : "type:datetime",
},