Websites/app/models/api_image.rb

201 lines
5.3 KiB
Ruby
Raw Normal View History

2024-05-03 03:04:43 +00:00
# frozen_string_literal: true
class APIImage < ApplicationRecord
belongs_to_creator
attr_accessor :file, :exception
validates :category, presence: true, inclusion: { in: -> { APIImage.categories.map(&:db) } }
validates :id, uniqueness: true, on: :file
validate on: :file do |image|
ext, mime = file_header_info(file.path)
image.errors.add(:file, "type is invalid (#{mime})") if ext == mime
end
validate do |image|
image.errors.add(:base, exception.message) if exception
end
after_create :invalidate_cache
after_update :update_files, if: :saved_change_to_category?
after_destroy :delete_files
def delete_files
Websites.config.yiffy2_storage.delete(path)
invalidate_cache
end
def update_files
file = Websites.config.yiffy2_storage.get(path_before_last_save)
Websites.config.yiffy2_storage.put(path, file)
Websites.config.yiffy2_storage.delete(path_before_last_save)
invalidate_cache
end
def file_header_info(file_path)
File.open(file_path) do |bin|
mime_type = Marcel::MimeType.for(bin)
ext = case mime_type
when "image/jpeg"
"jpg"
when "image/gif"
"gif"
when "image/png"
"png"
else
mime_type
end
[mime_type, ext]
end
end
def invalidate_cache
Cache.redis.del("yiffy2:images:#{category_before_last_save}")
Cache.redis.del("yiffy2:images:#{category}")
end
2024-05-03 03:04:43 +00:00
module SearchMethods
def random(category, limit, size_limit = nil)
q = where(category: category)
q = q.where(file_size: ..size_limit) if size_limit
q.ids.sample(limit).map(&method(:find))
end
def bulk(category_map, size_limit = nil)
data = {}
category_map.each do |category, limit|
data[category] = random(category, limit, size_limit)
end
data
end
end
extend SearchMethods
def md5
id.gsub("-", "")
end
alias stripped_md5 md5
def path_before_last_save
"/#{category_before_last_save.tr('.', '/')}/#{md5}.#{file_ext}"
end
def path
"/#{category.tr('.', '/')}/#{md5}.#{file_ext}"
end
2024-05-03 03:04:43 +00:00
def url
Websites.config.yiffy2_storage.url_for(self)
2024-05-03 03:04:43 +00:00
end
def short_url
ShortUrl.override(md5, url).shorturl
end
def sources_string
sources.join("\n")
end
def sources_string=(str)
self.sources = str.split("\n").map(&:strip)
end
def artists_string
artists.join("\n")
end
def artists_string=(str)
self.artists = str.split("\n").map(&:strip)
end
2024-05-03 03:04:43 +00:00
def serializable_hash(*)
{
artists: artists,
sources: sources,
width: width,
height: height,
url: url,
type: mime_type,
name: "#{md5}.#{file_ext}",
id: md5,
ext: file_ext,
size: file_size,
reportURL: nil,
shortURL: short_url,
}
end
def self.categories
sfw = %w[animals.birb animals.blep animals.dikdik furry.boop furry.cuddle furry.flop furry.fursuit furry.hold furry.howl furry.hug furry.kiss furry.lick furry.propose]
nsfw = %w[furry.butts furry.bulge furry.yiff.andromorph furry.yiff.gay furry.yiff.gynomorph furry.yiff.lesbian furry.yiff.straight]
titles = {
**sfw.index_with { |c| c.split(".").map(&:capitalize).join(" > ") },
"animals.dikdik" => "Animals > Dik Dik",
**nsfw.index_with { |c| c.split(".").map(&:capitalize).join(" > ") },
}
sfw.map { |cat| APICategory.new(titles[cat], cat, true) }
.concat(nsfw.map { |cat| APICategory.new(titles[cat], cat, false) })
2024-05-03 03:04:43 +00:00
end
def self.category_title(db)
categories.find { |k| k == db }.try(:db) || db.split(".").map(&:capitalize).join(" > ")
end
def self.cached_count(category)
count = Cache.redis.get("yiffy2:images:#{category}")
if count.nil?
count = APIImage.where(category: category).count
# images aren't changing so we really don't need any expiry, but I still want an expiry just in case
Cache.redis.set("yiffy2:images:#{category}", count.to_s, ex: 60 * 60 * 24 * 7)
end
count.to_i
end
def self.state
data = group(:category).count.map do |k, v|
{
count: v,
name: category_title(k),
category: k,
state: v < 5 ? "red" : v < 20 ? "yellow" : "green",
}
end
[*data, {
count: data.pluck(:count).reduce(:+),
name: "Total",
category: "total",
state: "total",
},]
end
def self.sync_all
sync_e621
end
# TODO
def self.sync_e621
images = joins("CROSS JOIN unnest(sources) AS source").where("source ILIKE ?", "%e621.net%").distinct.limit(300)
ids = []
md5s = []
images.each do |img|
img.sources.each do |source|
next unless source.include?("e621.net")
id = source.split("/").last
ids << id.to_i if id.to_i.to_s == id
end
md5s << img.md5
end
Requests::E621.get_all_posts(ids: ids, md5s: md5s) => { posts:, missing: }
Rails.logger.info("Failed to find #{missing[:ids].length} posts") unless missing[:ids].empty?
mismatch = images.select { |img| posts.none? { |post| post["file"]["md5"] == img.md5 } }
Rails.logger.info("Found #{mismatch.length} mismatched images") unless mismatch.empty?
posts
end
def sync
APIImageSyncJob.perform_later(self)
end
2024-05-03 03:04:43 +00:00
end