Add support for kakuyomu.jp

This commit is contained in:
praschke 2024-03-13 18:43:12 +00:00 committed by Jim Miller
parent 4f3af1395f
commit 7e070528a1
5 changed files with 297 additions and 1 deletions

View file

@ -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

View file

@ -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

View 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'[Rr].?[1][5]', 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)

View file

@ -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)

View file

@ -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