Skip to content

Commit 40ff611

Browse files
committed
+fantia downloader
1 parent 138babf commit 40ff611

2 files changed

Lines changed: 88 additions & 1 deletion

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,5 @@ dmypy.json
125125
.pyre/
126126
_test.py
127127
/.vscode
128-
key.txt
128+
key.txt
129+
_test*.py

fantia.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
from util import download
2+
import requests
3+
import re
4+
from pathlib import Path
5+
from urllib.parse import unquote, urljoin
6+
import concurrent.futures
7+
8+
API_POSTS = "https://fantia.jp/api/v1/posts/{}"
9+
API_FANCLUB = "https://fantia.jp/api/v1/fanclubs/{}" # Not used for now
10+
HTML_POSTLIST = "https://fantia.jp/fanclubs/{}/posts?page={}"
11+
12+
class FantiaDownloader:
13+
14+
def __init__(self, fanclub, output, key):
15+
super().__init__()
16+
self.key = key
17+
self.fanclub = fanclub
18+
self.output = Path(output)
19+
self.skip_existing = True
20+
21+
def fetch(self, url):
22+
return requests.get(url, cookies={"_session_id": self.key})
23+
24+
def downloadAll(self):
25+
if not self.output.exists():
26+
self.output.exists.mkdir(parents=True, exist_ok=True)
27+
existing_ids = [m[1] for f in self.output.iterdir() if (m := re.match(r'(\d+) *', f.name))]
28+
existing_ids = list(dict.fromkeys(existing_ids))
29+
print(f'{len(existing_ids)} IDs have already been downloaded.')
30+
31+
page = 1
32+
results = []
33+
34+
while True:
35+
print(f"Attempting to fetch page {page}...")
36+
out = self.fetchGalleryPage(page)
37+
if out:
38+
results.extend(out)
39+
page += 1
40+
else:
41+
break
42+
43+
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as ex:
44+
for id in results:
45+
if self.skip_existing and id in existing_ids:
46+
print(f'{id} is already downloaded. Skip.')
47+
continue
48+
ex.submit(self.getPostPhotos, id)
49+
50+
def fetchGalleryPage(self, page):
51+
url = HTML_POSTLIST.format(self.fanclub, page)
52+
r = self.fetch(url)
53+
r.encoding = 'utf-8'
54+
html = r.text
55+
return re.findall(r'\/posts\/(?P<id>[0-9]{1,8})', html)
56+
57+
def getPostPhotos(self, id):
58+
def getWebName(url):
59+
name = unquote(url.split('?')[0].split('/')[-1])
60+
return name.rsplit('.', 1)
61+
62+
print(f'Fetching post {id}...')
63+
d = self.fetch(API_POSTS.format(id)).json()
64+
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as ex:
65+
if thumb := d['post'].get('thumb', None):
66+
img_url = thumb['original']
67+
stem, ext = getWebName(img_url)
68+
ex.submit(download, img_url, filename= self.output / f'{id} !cover.{ext}', verbose=1)
69+
if post_contents := d['post'].get('post_contents', None):
70+
for c in post_contents:
71+
cid = c['id']
72+
if photos := c.get('post_content_photos', None):
73+
for idx, p in enumerate(photos, 1):
74+
img_url = p['url']['original']
75+
stem, ext = getWebName(img_url)
76+
stem = re.sub(r'^[0-9a-fA-F]{8}_(.+)$', r'\1', stem)
77+
stem = re.sub(r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}', '', stem)
78+
idx_string = '_' + str(idx).zfill(len(str(len(photos)))) if len(photos) > 1 else ''
79+
stem = f'{id} {cid}{idx_string} {stem}'.strip()
80+
ex.submit(download, img_url, filename=self.output / f'{stem}.{ext}', verbose=1)
81+
if 'download_uri' in c: #TODO: needs testing
82+
dl_url = urljoin('https://fantia.jp', c['download_uri']) + '?ofn=' + c['filename']
83+
ex.submit(download, dl_url, prefix=id, save_path=self.output, verbose=1)
84+
85+
if __name__ == "__main__":
86+
pass

0 commit comments

Comments
 (0)