app/models/tv_show.rb

Summary

Maintainability
A
0 mins
Test Coverage
B
83%
class TvShow < ApplicationRecord
  include Base64Images

  include PgSearch::Model

  multisearchable against: [:name]

  after_commit :fetch_details, :on => :create

  has_many :seasons, dependent: :destroy
  has_many :episodes, dependent: :destroy
  has_many :news_items, as: :newsworthy, dependent: :destroy

  scope :watching, ->{where("waitlist = false AND (status = 'returning series' OR status IS NULL)").where(watching: true)}
  scope :not_watching, ->{where("waitlist = false AND (status = 'returning series' OR status IS NULL)").where(watching: false)}
  scope :ended, ->{where.not(status: "returning series")}
  scope :on_waitlist, ->{where("waitlist = true")}
  scope :ordered_by_name, ->{order(Arel.sql("regexp_replace(lower(name),'the ','') ASC"))}

  base64_image :poster_image, :backdrop_image

  def self.create_from_imdb_id(imdb_id)
    search_result = Services::Search.tv_shows.search(imdb_id).first
    find_or_create_by(imdb_id: imdb_id) do |tv_show|
      tv_show.name = search_result.title
      tv_show.tvdb_id = search_result.tvdb_id
    end
  end

  #
  # @param [Hash] entry
  #
  # @return [Episode]
  #
  def create_episode_from_entry(entry)
    episodes.create_from_entry(self, entry)
  end

  def fetch_news
    client = Faraday.new(:url => "https://www.reddit.com") do |builder|
      builder.request  :json
      builder.response :json
      builder.adapter Faraday.default_adapter
    end

    client.get("/search.json?q=subreddit%3Atelevision%20#{name}&sort=top&t=week").body["data"]["children"].each do |item|
      listing = item["data"]
      next if listing["is_self"]
      NewsItem.find_or_create_by(title: listing["title"]) do |news_item|
        news_item.attributes = {url: listing["url"], score: listing["ups"], newsworthy: self, category: "tv_shows" }
      end
    end
  end

  def poster_image(size = 1280)
    return nil unless tmdb_details && tmdb_details["poster_path"]
    image = tmdb_details["poster_path"]
    "#{Broad.tmdb_configuration.secure_base_url}w#{size}/#{image}"
  end

  def backdrop_image
    return nil unless tmdb_details&.key?("backdrop_path")
    image = tmdb_details["backdrop_path"]
    "#{Broad.tmdb_configuration.secure_base_url}original#{image}"
  end

  def sample
    if tvdb_id.blank?
      self.waitlist = true
      save!
      return self
    end

    sample_result = Services::Btn::Api.new.sample(tvdb_id)

    if sample_result.present?
      sample_result.each do |release|
        episode = create_episode_from_entry(release.to_h)
        episode.save
      end
    else
      download_season(1)
    end
    self.waitlist = episodes.with_release.none?
    save!
    self
  end

  def collect
    Broad::ServiceRegistry.trakt_shows.number_of_seasons(imdb_id).times do |season_number|
      download_season(season_number + 1)
    end
    self
  end

  def watch
    self.watching = true
    save!
    self
  end

  def unwatch
    self.watching = false
    save!
    self
  end

  # TODO: There is an uggo hack here to make Dry Struct work.
  # TODO: Move this to Season model?
  def download_season(season_number)
    season = seasons.find_or_create_by!(number: season_number)
    season.update download_requested: true
    trakt_episodes = Broad::ServiceRegistry.trakt_shows.episodes(imdb_id).select{|episode| episode.season == season_number}
    season_releases = btn_service.season(tvdb_id, season_number)
    season_releases.each do |release|
      trakt_episodes.each do |episode|
        hash_release = release.to_hash
        hash_release[:episode] = episode.number
        hash_release[:name] = episode.title
        episode = create_episode_from_entry(hash_release)
        episode.save!
      end
    end

    download_season_episodes(season_number) if season_releases.count.zero?
    self
  end

  def download_season_episodes(season_number)
    trakt_episodes = Broad::ServiceRegistry.trakt_shows.episodes(imdb_id).select{|episode| episode.season == season_number}

    trakt_episodes.each do |episode|
      releases = btn_service.episode(tvdb_id, season_number, episode.number)
      break if releases.count.zero?
      releases.each do |release|
        create_episode_from_entry(release.to_h)
      end
    end
  end

  def btn_service
    @btn_service ||= Services::Btn::Api.new
  end

  private

  def fetch_details
    FetchTvShowDetailsTmdbJob.perform_later self
    FetchTvShowDetailsTraktJob.perform_later self
  end
end