187 lines
4.8 KiB
Ruby
187 lines
4.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative 'light/tag'
|
|
|
|
module FicTracker::Models
|
|
class Story < Sequel::Model
|
|
# 1/week
|
|
METADATA_REFRESH_INTERVAL = 7 * 24 * 60 * 60
|
|
# 3/day
|
|
CONTENT_REFRESH_INTERVAL = 12 * 60 * 60
|
|
|
|
many_to_one :author
|
|
one_to_many :chapters, order: :index
|
|
many_to_many :collection, join_table: :collection_stories
|
|
|
|
plugin :serialization, [Light::Tag.method(:store), Light::Tag.method(:load)], :tags
|
|
plugin :serialization, :json, :data
|
|
|
|
# Defer creation of author/chapters until story requiring them is to be saved
|
|
def before_create
|
|
if @author
|
|
@author.save unless @author.id
|
|
self.author_id = @author.id
|
|
end
|
|
|
|
super
|
|
end
|
|
|
|
def after_create
|
|
return if [@author, @chapters].all?(&:nil?)
|
|
|
|
self.author = @author if @author
|
|
@author = nil
|
|
|
|
if @chapters
|
|
latest_chapter_at = self.updated_at || self.published_at || Time.at(0)
|
|
@chapters&.each do |chapter|
|
|
latest_chapter_at = [chapter.published_at || Time.at(0), latest_chapter_at].max
|
|
add_chapter chapter
|
|
end
|
|
update(updated_at: latest_chapter_at) if latest_chapter_at > Time.at(0) && (updated_at.nil? || latest_chapter_at >= updated_at)
|
|
end
|
|
@chapters = nil
|
|
end
|
|
|
|
def completed?
|
|
completed
|
|
end
|
|
|
|
# Support attaching author to a not-yet-saved story
|
|
def author
|
|
@author || super
|
|
end
|
|
|
|
def author=(author_name)
|
|
author = author_name if author_name.is_a?(FicTracker::Models::Author)
|
|
author ||= Author.find(backend_name: backend.name, slug: author_name)
|
|
author ||= backend.load_author(author_name) if id
|
|
author ||= Author.new(backend: backend, slug: author_name)
|
|
|
|
if id
|
|
@author = nil
|
|
author.save unless author.id
|
|
super(author)
|
|
else
|
|
@author = author
|
|
end
|
|
end
|
|
|
|
# Support attaching chapters to a not-yet-saved story
|
|
def chapters
|
|
@chapters || super
|
|
end
|
|
|
|
def chapters=(entries)
|
|
latest_chapter_at = self.published_at || Time.at(0)
|
|
|
|
to_add = []
|
|
to_remove = self.chapters.map(&:id)
|
|
|
|
entries.each do |entry|
|
|
chapter = self.chapters.find { |c| c.slug == entry[:slug] }
|
|
|
|
if chapter
|
|
latest_chapter_at = [chapter.published_at || Time.at(0), latest_chapter_at].max
|
|
chapter.set(**entry)
|
|
else
|
|
chapter = FicTracker::Models::Chapter.new(**entry)
|
|
to_add << chapter
|
|
end
|
|
to_remove.delete chapter.id
|
|
end
|
|
|
|
if id
|
|
@chapters = nil
|
|
to_add.each do |entry|
|
|
logger.debug "Adding new chapter #{entry.inspect} to story #{self}"
|
|
add_chapter entry
|
|
end
|
|
if to_remove.any?
|
|
logger.debug "Removing chapter(s) #{to_remove.inspect} from story #{self}"
|
|
chapter_dataset.where(id: to_remove).delete
|
|
end
|
|
|
|
update(updated_at: latest_chapter_at) if latest_chapter_at > Time.at(0) && (updated_at.nil? || latest_chapter_at >= updated_at)
|
|
else
|
|
@chapters = (@chapters || []) + to_add - to_remove
|
|
end
|
|
end
|
|
|
|
def ensure_fully_loaded
|
|
ensure_chapters
|
|
refresh_content
|
|
refresh_metadata
|
|
end
|
|
|
|
def ensure_chapters
|
|
# FIXME: Should check for a reasonable set of parameters - full load unless XX% (75%?) of chapters have content
|
|
return if chapters && chapters.any? && chapters.all? { |c| c.content? && c.content_type? }
|
|
|
|
backend.load_full_story(self)
|
|
end
|
|
|
|
def backend
|
|
return unless backend_name
|
|
|
|
@backend ||= FicTracker::Backends.get(backend_name)
|
|
end
|
|
|
|
def backend=(backend)
|
|
@backend = backend
|
|
self.backend_name = backend.name
|
|
end
|
|
|
|
def etag
|
|
chapters.select { |c| c.etag }.last
|
|
end
|
|
|
|
def cache_key
|
|
backend.cache_key + [:s, slug]
|
|
end
|
|
|
|
def safe_name
|
|
[backend_name, slug, name].join('_').downcase.gsub(/[^a-z0-9\-_]/, '_').gsub(/__+/, '_')
|
|
end
|
|
|
|
def refresh_metadata
|
|
refresh_metadata! if backend && needs_metadata_refresh?
|
|
end
|
|
|
|
def refresh_metadata!
|
|
backend.load_story(self)
|
|
end
|
|
|
|
def refresh_content
|
|
backend.find_chapters(self) if backend && needs_content_refresh?
|
|
chapters.each(&:refresh_content)
|
|
end
|
|
|
|
def refresh_content!
|
|
backend.find_chapters(self)
|
|
chapters.each(&:refresh_content!)
|
|
end
|
|
|
|
def needs_metadata_refresh?
|
|
Time.now - (last_metadata_refresh || Time.at(0)) >= METADATA_REFRESH_INTERVAL
|
|
end
|
|
|
|
def needs_content_refresh?
|
|
Time.now - (last_content_refresh || Time.at(0)) >= (completed? ? METADATA_REFRESH_INTERVAL : CONTENT_REFRESH_INTERVAL)
|
|
end
|
|
|
|
def to_s
|
|
"#{name}, by #{author.nil? ? '<Unknown>' : author.to_s}"
|
|
end
|
|
|
|
def uid
|
|
Digest::SHA1.hexdigest("#{backend}/#{slug}")
|
|
end
|
|
|
|
private
|
|
|
|
def logger
|
|
Logging.logger[self]
|
|
end
|
|
end
|
|
end
|