rauversion/rauversion-ror

View on GitHub
app/models/track.rb

Summary

Maintainability
B
5 hrs
Test Coverage
class Track < ApplicationRecord
  extend FriendlyId
  friendly_id :title, use: :slugged

  belongs_to :user
  has_many :track_comments
  has_many :track_playlists
  has_many :playlists, through: :track_playlists
  has_many :listening_events
  has_many :reposts, dependent: :destroy
  has_many :purchased_items, as: :purchased_item
  has_many :likes, as: :likeable, dependent: :destroy
  has_many :comments, as: :commentable, dependent: :destroy
  has_one :track_peak, dependent: :destroy
  has_many :spotlights, as: :spotlightable, dependent: :destroy
  # has_many :spotlighted_tracks, through: :spotlight_tracks

  belongs_to :label, class_name: "User", optional: true
  attr_accessor :enable_label
  before_save :check_label

  def check_label
   self.label_id = Current.label_user.id if enable_label && Current.label_user 
  end

  has_one_attached :cover
  has_one_attached :audio
  has_one_attached :mp3_audio
  has_one_attached :zip

  validates :cover, presence: false, blob: {content_type: :web_image} # supported options: :web_image, :image, :audio, :video, :text

  validates :audio, presence: false, blob: {content_type: :audio, size_range: 1..(400.megabytes)} # supported options: :web_image, :image, :audio, :video, :text

  acts_as_likeable

  attr_accessor :step, :tab

  scope :published, -> { where(private: false) }
  # scope :private, -> { where.not(:private => true)}
  scope :latests, -> { order("id desc") }

  # store_attribute :metadata, :ratio, :integer, limit: 1
  # store_attribute :metadata, :login_at, :datetime
  # #store_attribute :metadata, :active, :boolean
  # store_attribute :metadata, :color, :string, default: "red"
  # store_attribute :metadata, :colors, :json, default: ["red", "blue"]
  # store_attribute :metadata, :data, :datetime, default: -> { Time.now }

  # store_attribute :metadata, :peaks, :json, default: []
  store_attribute :metadata, :contains_music, :boolean
  store_attribute :metadata, :artist, :string
  store_attribute :metadata, :publisher, :string
  store_attribute :metadata, :isrc, :string
  store_attribute :metadata, :composer, :string
  store_attribute :metadata, :release_title, :string
  store_attribute :metadata, :buy_link, :string
  store_attribute :metadata, :buy_link_title, :string
  store_attribute :metadata, :buy, :boolean
  store_attribute :metadata, :album_title, :string
  store_attribute :metadata, :record_label, :string
  store_attribute :metadata, :release_date, :date
  store_attribute :metadata, :barcode, :string
  store_attribute :metadata, :iswc, :string
  store_attribute :metadata, :p_line, :string
  store_attribute :metadata, :contains_explicit_content, :boolean
  store_attribute :metadata, :copyright, :string
  store_attribute :metadata, :genre, :string
  store_attribute :metadata, :direct_download, :boolean
  store_attribute :metadata, :display_embed, :boolean
  store_attribute :metadata, :enable_comments, :boolean, default: true
  store_attribute :metadata, :display_comments, :boolean, default: true
  store_attribute :metadata, :display_stats, :boolean
  store_attribute :metadata, :include_in_rss, :boolean
  store_attribute :metadata, :offline_listening, :boolean
  store_attribute :metadata, :enable_app_playblack, :boolean
  store_attribute :metadata, :attribution, :boolean
  store_attribute :metadata, :noncommercial, :boolean
  store_attribute :metadata, :non_derivative_works, :boolean
  store_attribute :metadata, :share_alike, :boolean
  store_attribute :metadata, :copies, :string
  store_attribute :metadata, :price, :decimal
  store_attribute :metadata, :name_your_price, :boolean

  include AASM

  after_create :reprocess_async

  aasm column: :state do
    state :pending, initial: true
    state :processed

    event :run do
      transitions from: :pending, to: :processed
    end

    event :sleep do
      transitions from: :running, to: :sleeping
    end
  end

  def peaks
    self.track_peak&.data || []
  end

  def peaks=(peaks)
    if self.track_peak.blank?
      self.build_track_peak(data: peaks) 
    else
      self.track_peak.update(data: peaks) 
    end
  end

  def cover_url(size = nil)
    url = case size
    when :medium
      cover.variant(resize_to_limit: [200, 200])

    when :large
      cover.variant(resize_to_limit: [500, 500])

    when :small
      cover.variant(resize_to_limit: [50, 50])

    else
      cover.variant(resize_to_limit: [200, 200])
    end

    return Rails.application.routes.url_helpers.rails_storage_proxy_url(url) if url.present?

    url || "daniel-schludi-mbGxz7pt0jM-unsplash-sqr-s-bn.png"
  end

  def self.permission_definitions
    [
      {
        name: :direct_download,
        wrapper_class: "sm:col-span-2",
        type: :checkbox,
        checked_hint:
          "This track will be available for direct download in the original format it was uploaded.",
        unchecked_hint:
          "This track will not be available for direct download in the original format it was uploaded."
      },
      {
        name: :display_embed,
        wrapper_class: "sm:col-span-2",
        type: :checkbox,
        checked_hint: "This track’s embedded-player code will be displayed publicly.",
        unchecked_hint: "This track’s embedded-player code will only be displayed to you."
      },
      {
        name: :enable_comments,
        wrapper_class: "sm:col-span-2",
        type: :checkbox,
        checked_hint: "Enable comments",
        unchecked_hint: "Comments disabled."
      },
      {
        name: :display_comments,
        wrapper_class: "sm:col-span-2",
        type: :checkbox,
        checked_hint: "Display comments",
        unchecked_hint: "Don't display public comments."
      },
      {
        name: :display_stats,
        wrapper_class: "sm:col-span-2",
        type: :checkbox,
        checked_hint: "Display public stats",
        unchecked_hint: "Don't display public stats."
      },
      {
        name: :include_in_rss,
        wrapper_class: "sm:col-span-2",
        type: :checkbox,
        checked_hint: "This track will be included in your RSS feed if it is public.",
        unchecked_hint: "This track will not be included in your RSS feed."
      },
      {
        name: :offline_listening,
        wrapper_class: "sm:col-span-2",
        type: :checkbox,
        checked_hint: "This track can be played on devices without an internet connection.",
        unchecked_hint:
          "Playing this track will not be possible on devices without an internet connection."
      },
      {
        name: :enable_app_playblack,
        field_type: :text_fiel,
        wrapper_class: "sm:col-span-2",
        type: :checkbox,
        checked_hint: "This track will be playable outside of Rauversion and its apps.",
        unchecked_hint: "This track will not be playable outside of Rauversion and its apps."
      }
    ]
  end

  def update_peaks
    peaks = process_audio_peaks

    update(peaks: peaks, state: "processed")
  end

  def reprocess_async
    TrackProcessorJob.perform_later(id)
  end

  def reprocess!
    return if audio.blob.blank?
    update_mp3
    update_peaks
  end

  def process_with_mp3

    temp_file = Tempfile.new(["audio", ".mp3"], binmode: true)

    # Download the blob to the temp file in chunks
    mp3_audio.download do |chunk|
      temp_file.write(chunk)
    end

    temp_file.rewind

    update_peaks
  end

  def update_mp3(temp_file = nil)
    if temp_file.nil?
      # Create a temp file
      temp_file = Tempfile.new(["audio", ".mp3"], binmode: true)

      # Download the blob to the temp file in chunks
      audio.download do |chunk|
        temp_file.write(chunk)
      end

      temp_file.rewind
    end
    # Use the path of the temp file in the Mp3Converter
    mp3_path = Mp3Converter.new(temp_file.path).run
    mp3_file = File.open(mp3_path)

    # Attach the MP3 file to the Track model
    mp3_audio.attach(io: mp3_file, filename: "converted_audio.mp3", content_type: "audio/mpeg")

    FileUtils.remove_entry mp3_path
    # Ensure the temp file is removed after the Mp3Converter has finished
    temp_file.close
    temp_file.unlink
  end

  def process_audio_peaks(temp_file = nil)
    # Create a temp file
    if temp_file.nil?
      temp_file = Tempfile.new(["audio", ".mp3"], binmode: true)

      # Download the blob to the temp file in chunks
      audio.download do |chunk|
        temp_file.write(chunk)
      end

      temp_file.rewind
    end

    # Use the path of the temp file in the PeaksGeneratorService
    peaks = PeaksGenerator.new(temp_file.path).run

    # Ensure the temp file is removed after the PeaksGeneratorService has finished
    temp_file.close
    temp_file.unlink

    peaks
  end

  def self.top_tracks(profile_id)
    Track.joins(:listening_events)
      .where(listening_events: {resource_profile_id: profile_id})
      .group(:id)
      .limit(10)
      .select("tracks.*, COUNT(listening_events.id) as count")
      .order("count DESC")
      .includes(:cover_blob, user: :avatar_blob)
  end

  def self.top_listeners(profile_id)
    User.joins(:listening_events)
      .where(listening_events: {resource_profile_id: profile_id})
      .group(:id)
      .limit(10)
      .select("users.*, COUNT(listening_events.id) as count")
      .order("count DESC")
      .includes(:avatar_blob)
  end

  def self.top_countries(profile_id)
    ListeningEvent.where(resource_profile_id: profile_id)
      .group(:country)
      .limit(10)
      .select("country, COUNT(country) as count")
      .order("count DESC")
  end

  def self.series_by_month(profile_id, range: 12)
    ListeningEvent.where(resource_profile_id: profile_id)
      .group_by_month(:created_at, range: range.months.ago.midnight..Time.now).count
      .map { |k, v|
      {count: v, day: k}
    }
  end

  def tags=(list)
    self[:tags] = list.map(&:downcase).reject { |item| item.empty? }
  end

  def iframe_code_string(url)
    <<-HTML
    <iframe width="100%" height="100%" scrolling="no" frameborder="no" allow="autoplay" src="#{url}"></iframe>
    <div 
      style="font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;">
      <a href="#{user.username}" title="#{user.username}" 
        target="_blank" 
        style="color: #cccccc; text-decoration: none;">
        #{user.username}
      </a> · 
      <a href="#{url}" title="#{title}" target="_blank" style="color: #cccccc; text-decoration: none;">
        #{title}
      </a>
    </div>
    HTML
  end

  def self.get_tracks_by_tag(tag)
    tag = tag.downcase
    includes(:user).where("? = ANY (tags)", tag)
  end
end