mirror of
https://github.com/JimmXinu/FanFicFare.git
synced 2026-04-29 18:35:13 +02:00
Add support for kakuyomu.jp
This commit is contained in:
parent
4f3af1395f
commit
7e070528a1
5 changed files with 297 additions and 1 deletions
|
|
@ -2146,6 +2146,46 @@ use_basic_cache:true
|
|||
#username:YourName
|
||||
#password:yourpassword
|
||||
|
||||
[kakuyomu.jp]
|
||||
use_basic_cache:true
|
||||
|
||||
## Clear FanFiction from defaults, site is mostly original fiction.
|
||||
extratags:
|
||||
|
||||
## Some adapters collect additional meta information beyond the
|
||||
## standard ones. They need to be defined in extra_valid_entries to
|
||||
## tell the rest of the FanFicFare system about them. They can be
|
||||
## used in include_subject_tags, titlepage_entries,
|
||||
## extra_titlepage_entries, logpage_entries, extra_logpage_entries,
|
||||
## and include_in_* config items. You can also add additional entries
|
||||
## here to build up composite metadata entries.
|
||||
|
||||
extra_valid_entries: catchphrase, freeformtags, reviews, points,
|
||||
comments, views, follows, collections, events, published
|
||||
|
||||
catchphrase_label:キャッチコピー
|
||||
freeformtags_label:タグ
|
||||
reviews_label:レビュー
|
||||
points_label:評価ポイント
|
||||
comments_label:応援コメント
|
||||
views_label:アクセス数
|
||||
follows_label:小説のフォロワー
|
||||
collections_label:コレクション
|
||||
events_label:コンテスト・イベント
|
||||
published_label:書籍化
|
||||
|
||||
## adds to titlepage_entries instead of replacing it.
|
||||
#extra_titlepage_entries: catchphrase,freeformtags,reviews,points,comments,views,follows,collections,events,published
|
||||
|
||||
## adds to include_subject_tags instead of replacing it.
|
||||
#extra_subject_tags: freeformtags
|
||||
|
||||
## kakuyomu allows authors to group chapters (episodes) by titled sections.
|
||||
## true prepends the section titles to every episode title.
|
||||
## firstepisode prepends the section titles to the first episode title only. (default)
|
||||
## false means section titles are not present in the end result.
|
||||
#prepend_section_titles:firstepisode
|
||||
|
||||
[ksarchive.com]
|
||||
## Site dedicated to these categories/characters/ships
|
||||
extracategories:Star Trek
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ from . import adapter_deviantartcom
|
|||
from . import adapter_readonlymindcom
|
||||
from . import adapter_wwwsunnydaleafterdarkcom
|
||||
from . import adapter_syosetucom
|
||||
from . import adapter_kakuyomujp
|
||||
|
||||
## This bit of complexity allows adapters to be added by just adding
|
||||
## importing. It eliminates the long if/else clauses we used to need
|
||||
|
|
|
|||
215
fanficfare/adapters/adapter_kakuyomujp.py
Normal file
215
fanficfare/adapters/adapter_kakuyomujp.py
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2013 Fanficdownloader team, 2018 FanFicFare team
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
import logging, time
|
||||
logger = logging.getLogger(__name__)
|
||||
import re, json
|
||||
|
||||
from .. import exceptions as exceptions
|
||||
|
||||
# py2 vs py3 transition
|
||||
from ..six.moves import http_cookiejar as cl
|
||||
|
||||
from .base_adapter import BaseSiteAdapter, makeDate
|
||||
|
||||
def getClass():
|
||||
return KakuyomuJpAdapter
|
||||
|
||||
genres = {
|
||||
'FANTASY': '異世界ファンタジー',
|
||||
'ACTION': '現代ファンタジー',
|
||||
'SF': 'SF',
|
||||
'LOVE_STORY': '恋愛',
|
||||
'ROMANCE': 'ラブコメ',
|
||||
'DRAMA': '現代ドラマ',
|
||||
'HORROR': 'ホラー',
|
||||
'MYSTERY': 'ミステリー',
|
||||
'NONFICTION': 'エッセイ・ノンフィクション',
|
||||
'HISTORY': '歴史・時代・伝奇',
|
||||
'CRITICISM': '創作論・評論',
|
||||
'OTHERS': '詩・童話・その他',
|
||||
'FAN_FICTION': '二次創作',
|
||||
}
|
||||
|
||||
class KakuyomuJpAdapter(BaseSiteAdapter):
|
||||
|
||||
def __init__(self, config, url):
|
||||
BaseSiteAdapter.__init__(self, config, url)
|
||||
|
||||
self.story.setMetadata('siteabbrev', 'kakuyomu')
|
||||
self.story.setMetadata('language', 'Japanese')
|
||||
|
||||
self.storyId = self.path.split('/')[-1]
|
||||
self.story.setMetadata('storyId', self.storyId)
|
||||
|
||||
@staticmethod
|
||||
def getSiteDomain():
|
||||
return 'kakuyomu.jp'
|
||||
|
||||
@classmethod
|
||||
def getSiteExampleURLs(cls):
|
||||
return ("https://kakuyomu.jp/works/12341234123412341234")
|
||||
|
||||
def getSiteURLPattern(self):
|
||||
return r"^https?://kakuyomu\.jp/works/[0-9]+$"
|
||||
|
||||
def extractChapterUrlsAndMetadata(self):
|
||||
data = self.get_request(self.url)
|
||||
|
||||
# Page could not be found
|
||||
if 'お探しのページは見つかりませんでした' in data:
|
||||
raise exceptions.StoryDoesNotExist(self.url)
|
||||
|
||||
soup = self.make_soup(data)
|
||||
info = json.loads(soup.find(id='__NEXT_DATA__').contents[0])['props']['pageProps']['__APOLLO_STATE__']
|
||||
|
||||
workKey = 'Work:%s' % self.storyId
|
||||
|
||||
# Title
|
||||
self.story.setMetadata('title', info[workKey]['title'])
|
||||
|
||||
# Author
|
||||
authorKey = info[workKey]['author']['__ref']
|
||||
self.story.setMetadata('authorId', authorKey.split(':')[1])
|
||||
self.story.setMetadata('authorUrl', 'https://kakuyomu.jp/users/%s' % info[authorKey]['name'])
|
||||
self.story.setMetadata('author', info[authorKey]['activityName'])
|
||||
|
||||
# Description
|
||||
self.setDescription(self.url, info[workKey]['introduction'])
|
||||
self.story.setMetadata('catchphrase', info[workKey]['catchphrase'])
|
||||
|
||||
# Date Published and Updated
|
||||
# 2024-01-01T03:00:12Z
|
||||
self.story.setMetadata('datePublished',
|
||||
makeDate(info[workKey]['publishedAt'], '%Y-%m-%dT%H:%M:%SZ'))
|
||||
self.story.setMetadata('dateUpdated',
|
||||
makeDate(info[workKey]['editedAt'], '%Y-%m-%dT%H:%M:%SZ'))
|
||||
|
||||
# Character count
|
||||
self.story.setMetadata('numWords', info[workKey]['totalCharacterCount'])
|
||||
|
||||
# Status
|
||||
completed = info[workKey]['serialStatus'] == 'COMPLETED'
|
||||
self.story.setMetadata('status', 'Completed' if completed else 'In-Progress')
|
||||
|
||||
# Warnings
|
||||
rating = 'G'
|
||||
if info[workKey]['isCruel']:
|
||||
rating = 'R15'
|
||||
self.story.addToList('warnings', '残酷描写有り')
|
||||
if info[workKey]['isViolent']:
|
||||
rating = 'R15'
|
||||
self.story.addToList('warnings', '暴力描写有り')
|
||||
if info[workKey]['isSexual']:
|
||||
rating = 'R15'
|
||||
self.story.addToList('warnings', '性描写有り')
|
||||
|
||||
# Tags
|
||||
for tag in info[workKey]['tagLabels']:
|
||||
if re.match(r'[RrR].?[11][55]', tag) is None:
|
||||
self.story.addToList('freeformtags', tag)
|
||||
else:
|
||||
rating = 'R15'
|
||||
|
||||
# Rating
|
||||
self.story.setMetadata('rating', rating)
|
||||
|
||||
# Genre
|
||||
self.story.setMetadata('genre', genres[info[workKey]['genre']])
|
||||
|
||||
if info[workKey]['genre'] == 'FAN_FICTION':
|
||||
fandomKey = info[workKey]['fanFictionSource']['__ref']
|
||||
self.story.addToList('fandoms', info[fandomKey]['title'])
|
||||
|
||||
# Ratings, Comments, Etc.
|
||||
self.story.setMetadata('reviews', info[workKey]['reviewCount'])
|
||||
self.story.setMetadata('points', info[workKey]['totalReviewPoint'])
|
||||
self.story.setMetadata('comments', info[workKey]['totalPublicEpisodeCommentCount'])
|
||||
self.story.setMetadata('views', info[workKey]['totalReadCount'])
|
||||
self.story.setMetadata('follows', info[workKey]['totalFollowers'])
|
||||
self.story.setMetadata('collections', len(info[workKey]['publicWorkCollections']))
|
||||
self.story.setMetadata('events', info[workKey]['totalWorkContestCount'] + info[workKey]['totalUserEventCount'])
|
||||
self.story.setMetadata('published', info[workKey]['hasPublication'])
|
||||
|
||||
# visitorWorkFollowing
|
||||
# workReviewByVisitor
|
||||
|
||||
# Chapters, Episodes
|
||||
|
||||
# TOC nodes are in a list
|
||||
# each have a list of named episodes
|
||||
# each can have a named chapter
|
||||
# named chapters can be at depth 1 or 2
|
||||
# episodes might be empty (premium subscription)
|
||||
|
||||
prependSectionTitles = self.getConfig('prepend_section_titles', 'firstepisode')
|
||||
|
||||
numEpisodes = 0
|
||||
titles = []
|
||||
nestingLevel = 0
|
||||
newSection = False
|
||||
for tocNodeRef in info[workKey]['tableOfContents']:
|
||||
tocNode = info[tocNodeRef['__ref']]
|
||||
|
||||
if tocNode['chapter'] is not None:
|
||||
chapter = info[tocNode['chapter']['__ref']]
|
||||
while chapter['level'] <= nestingLevel:
|
||||
titles.pop()
|
||||
nestingLevel -= 1
|
||||
titles.append(chapter['title'])
|
||||
nestingLevel = chapter['level']
|
||||
newSection = True
|
||||
else:
|
||||
titles = []
|
||||
nestingLevel = 0
|
||||
newSection = False
|
||||
|
||||
for episodeRef in tocNode['episodeUnions']:
|
||||
if not episodeRef['__ref'].startswith('EmptyEpisode'):
|
||||
numEpisodes += 1
|
||||
episode = info[episodeRef['__ref']]
|
||||
epUrl = 'https://kakuyomu.jp/works/' + self.storyId + '/episodes/' + episode['id']
|
||||
epTitle = episode['title']
|
||||
|
||||
if ((len(titles) > 0) and
|
||||
((newSection and prependSectionTitles == 'firstepisode') or
|
||||
prependSectionTitles == 'true')):
|
||||
titles.append(epTitle)
|
||||
# bracket with ZWSP to mark presence of section titles
|
||||
epTitle = u'\u200b' + u'\u3000\u200b'.join(titles)
|
||||
titles.pop()
|
||||
|
||||
self.add_chapter(epTitle, epUrl)
|
||||
newSection = False
|
||||
|
||||
self.story.setMetadata('numChapters', numEpisodes)
|
||||
|
||||
logger.debug("Story: <%s>", self.story)
|
||||
return
|
||||
|
||||
def getChapterText(self, url):
|
||||
logger.debug('Getting chapter text from <%s>' % url)
|
||||
|
||||
soup = self.make_soup(self.get_request(url))
|
||||
soup = soup.find('div', {'class':'widget-episodeBody js-episode-body'})
|
||||
if soup is None:
|
||||
raise exceptions.FailedToDownload("Error downloading Chapter: %s! Missing required element!" % url)
|
||||
soup.attrs = {'class':'episode-body'}
|
||||
|
||||
return self.utf8FromSoup(url, soup)
|
||||
|
||||
|
|
@ -312,7 +312,7 @@ def get_valid_set_options():
|
|||
'dedup_order_chapter_list': (['wuxiaworld.xyz', 'novelupdates.cc'], None, boollist),
|
||||
'show_nsfw_cover_images': (['fiction.live'], None, boollist),
|
||||
'show_timestamps': (['fiction.live'], None, boollist),
|
||||
'prepend_section_titles': (['syosetu.com'], None, boollist+['firstepisode']),
|
||||
'prepend_section_titles': (['syosetu.com','kakuyomu.jp'], None, boollist+['firstepisode']),
|
||||
}
|
||||
|
||||
return dict(valdict)
|
||||
|
|
|
|||
|
|
@ -2141,6 +2141,46 @@ use_basic_cache:true
|
|||
#username:YourName
|
||||
#password:yourpassword
|
||||
|
||||
[kakuyomu.jp]
|
||||
use_basic_cache:true
|
||||
|
||||
## Clear FanFiction from defaults, site is mostly original fiction.
|
||||
extratags:
|
||||
|
||||
## Some adapters collect additional meta information beyond the
|
||||
## standard ones. They need to be defined in extra_valid_entries to
|
||||
## tell the rest of the FanFicFare system about them. They can be
|
||||
## used in include_subject_tags, titlepage_entries,
|
||||
## extra_titlepage_entries, logpage_entries, extra_logpage_entries,
|
||||
## and include_in_* config items. You can also add additional entries
|
||||
## here to build up composite metadata entries.
|
||||
|
||||
extra_valid_entries: catchphrase, freeformtags, reviews, points,
|
||||
comments, views, follows, collections, events, published
|
||||
|
||||
catchphrase_label:キャッチコピー
|
||||
freeformtags_label:タグ
|
||||
reviews_label:レビュー
|
||||
points_label:評価ポイント
|
||||
comments_label:応援コメント
|
||||
views_label:アクセス数
|
||||
follows_label:小説のフォロワー
|
||||
collections_label:コレクション
|
||||
events_label:コンテスト・イベント
|
||||
published_label:書籍化
|
||||
|
||||
## adds to titlepage_entries instead of replacing it.
|
||||
#extra_titlepage_entries: catchphrase,freeformtags,reviews,points,comments,views,follows,collections,events,published
|
||||
|
||||
## adds to include_subject_tags instead of replacing it.
|
||||
#extra_subject_tags: freeformtags
|
||||
|
||||
## kakuyomu allows authors to group chapters (episodes) by titled sections.
|
||||
## true prepends the section titles to every episode title.
|
||||
## firstepisode prepends the section titles to the first episode title only. (default)
|
||||
## false means section titles are not present in the end result.
|
||||
#prepend_section_titles:firstepisode
|
||||
|
||||
[ksarchive.com]
|
||||
## Site dedicated to these categories/characters/ships
|
||||
extracategories:Star Trek
|
||||
|
|
|
|||
Loading…
Reference in a new issue