hummingbird-me/kitsu-server

View on GitHub
app/models/anime.rb

Summary

Maintainability
A
55 mins
Test Coverage
B
86%
# frozen_string_literal: true

class Anime < ApplicationRecord
  SEASONS = %w[winter spring summer fall].freeze

  include Media
  include AgeRatings
  include Episodic

  enum subtype: { TV: 0, special: 1, OVA: 2, ONA: 3, movie: 4, music: 5 }
  has_many :streaming_links, as: 'media', dependent: :destroy, inverse_of: :media
  has_many :producers, through: :anime_productions
  has_many :anime_productions, dependent: :destroy
  has_many :anime_characters, dependent: :destroy
  has_many :anime_staff, dependent: :destroy
  has_many :anime_media_attributes
  has_many :media_attributes, through: :anime_media_attributes
  accepts_nested_attributes_for :streaming_links
  alias_attribute :show_type, :subtype

  rails_admin { fields :episode_count }

  update_index('media#anime') { self }

  def slug_candidates
    # Prefer the canonical title or romaji title before anything else
    candidates = [
      -> { canonical_title } # attack-on-titan
    ]
    if subtype == :TV
      # If it's a TV show with a name collision, common practice is to
      # specify the year (ex: kanon-2006)
      candidates << -> { [canonical_title, year] }
    else
      # If it's not TV and it's having a name collision, it's probably the
      # movie or OVA for a series (ex: attack-on-titan-movie)
      candidates << -> { [canonical_title, subtype] }
      candidates << -> { [canonical_title, subtype, year] }
    end
    candidates
  end

  def season
    case start_date.try(:month)
    when 12, 1, 2 then :winter
    when 3, 4, 5 then :spring
    when 6, 7, 8 then :summer
    when 9, 10, 11 then :fall
    end
  end

  def season_changed?
    start_date_changed?
  end

  # Season year is the year, adjusted so that December is part of the next year
  def season_year
    if start_date.try(:month) == 12
      year + 1
    else
      year
    end
  end
  alias_method :season_year_changed?, :season_changed?
  alias_method :year_changed?, :season_changed?

  def update_unit_count_guess(guess)
    return if episode_count || (episode_count_guess && episode_count_guess > guess)
    update(episode_count_guess: guess)
  end

  def self.unit_class
    Episode
  end

  def self.fuzzy_find(title)
    MediaIndex::Anime.query(multi_match: {
      fields: %w[titles.* abbreviated_titles description actors characters],
      query: title,
      fuzziness: 2,
      max_expansions: 15,
      prefix_length: 2
    }).preload.first
  end

  def self.rails_admin_search(keyword)
    where(id: AlgoliaMediaIndex.search(keyword, filters: 'kind:anime').map(&:id))
  end

  before_save do
    self.episode_count_guess = nil if episode_count

    if episode_count == 1
      self.start_date = end_date if start_date.nil? && !end_date.nil?
      self.end_date = start_date if end_date.nil? && !start_date.nil?
    end
  end

  after_commit(on: %i[create update]) do
    TypesenseAnimeIndex.index_one(id) if TypesenseAnimeIndex.should_sync?(saved_changes)
  end

  after_commit(on: :destroy) do
    TypesenseAnimeIndex.remove_one(id)
  end

  after_save do
    if (saved_change_to_episode_count_guess? && !episode_count_guess.nil?) &&
       episodes.length != episode_count_guess
      episodes.create_defaults(episode_count_guess || 0)
    elsif saved_change_to_episode_count? && episodes.length != episode_count
      episodes.create_defaults(episode_count || 0)
    end
  end
end