uploads: allow searching uploads and media assets by metatag.

Allow searching the /uploads and /media_assets pages by the following metatags:

* id:
* md5:
* width:
* height:
* duration:
* mpixels:
* ratio:
* filesize:
* filetype:
* date:
* age:
* status:<processing|active|deleted|expunged|failed> (for /media_assets)
* status:<pending|processing|active|failed> (for /uploads)
* is:<filetype>, is:<status>
* exif:

Examples:

* https://betabooru.donmai.us/media_assets?search[ai_tags_match]=filetype:png
* https://betabooru.donmai.us/uploads?search[ai_tags_match]=filetype:png

Note that in /uploads search, the id:, date:, and age: metatags refer to the upload media asset, not
the upload itself.

Note also that uploads may contain multiple assets, so for example searching uploads by
`filetype:png` will return all uploads containing at least one PNG file, even if they contain other
non-PNG files.
This commit is contained in:
evazion
2022-12-07 00:53:04 -06:00
parent 062a67086e
commit 2c33539be7
8 changed files with 232 additions and 18 deletions

View File

@@ -1,6 +1,6 @@
FactoryBot.define do
factory(:media_asset) do
md5 { SecureRandom.hex(32) }
md5 { SecureRandom.hex(16) }
file_ext { "jpg" }
file_size { 1_000_000 }
image_width { 1000 }

View File

@@ -0,0 +1,74 @@
require 'test_helper'
class MediaAssetTest < ActiveSupport::TestCase
def assert_tag_match(assets, query)
assert_equal(assets.map(&:id), MediaAsset.ai_tags_match(query).order(id: :desc).pluck("id"))
end
context "MediaAsset" do
context "searching" do
setup do
@asset1 = create(:media_asset, image_width: 720, image_height: 1280, file_size: 1.megabyte, file_ext: "jpg", created_at: Time.zone.now, media_metadata: build(:media_metadata, metadata: { "File:FileType" => "JPEG" }))
@asset2 = create(:media_asset, image_width: 1920, image_height: 1080, file_size: 2.megabytes, file_ext: "png", duration: 3.0, created_at: Time.parse("2022-01-01"), media_metadata: build(:media_metadata, metadata: { "File:FileType" => "PNG" }))
end
should "return assets for the id: metatag" do
assert_tag_match([@asset1], "id:#{@asset1.id}")
end
should "return assets for the md5: metatag" do
assert_tag_match([@asset1], "md5:#{@asset1.md5}")
end
should "return assets for the width: metatag" do
assert_tag_match([@asset1], "width:#{@asset1.image_width}")
end
should "return assets for the height: metatag" do
assert_tag_match([@asset1], "height:#{@asset1.image_height}")
end
should "return assets for the duration: metatag" do
assert_tag_match([@asset2], "duration:3")
end
should "return assets for the mpixels: metatag" do
assert_tag_match([@asset1], "mpixels:#{(@asset1.image_width * @asset1.image_height) / 1_000_000.0}")
end
should "return assets for the ratio: metatag" do
assert_tag_match([@asset1], "ratio:#{@asset1.image_width.to_f / @asset1.image_height}")
end
should "return assets for the filesize: metatag" do
assert_tag_match([@asset1], "filesize:1mb")
end
should "return assets for the filetype: metatag" do
assert_tag_match([@asset1], "filetype:jpg")
end
should "return assets for the date: tag" do
assert_tag_match([@asset2], "date:2022-01-01")
end
should "return assets for the age: tag" do
assert_tag_match([@asset1], "age:<1minute")
end
should "return assets for the status: tag" do
assert_tag_match([@asset2, @asset1], "status:active")
end
should "return assets for the is: tag" do
assert_tag_match([@asset1], "is:jpg")
assert_tag_match([@asset2, @asset1], "is:active")
end
should "return assets for the exif: tag" do
assert_tag_match([@asset2, @asset1], "exif:File:FileType")
assert_tag_match([@asset1], "exif:File:FileType=JPEG")
end
end
end
end

80
test/unit/upload_test.rb Normal file
View File

@@ -0,0 +1,80 @@
require 'test_helper'
class UploadTest < ActiveSupport::TestCase
def assert_tag_match(uploads, query)
assert_equal(uploads.map(&:id), Upload.ai_tags_match(query).order(id: :desc).pluck("id"))
end
context "Upload" do
context "searching" do
setup do
@asset1 = create(:media_asset, image_width: 720, image_height: 1280, file_size: 1.megabyte, file_ext: "jpg", media_metadata: build(:media_metadata, metadata: { "File:FileType" => "JPEG" }))
@asset2 = create(:media_asset, image_width: 1920, image_height: 1080, file_size: 2.megabytes, file_ext: "png", duration: 3.0, media_metadata: build(:media_metadata, metadata: { "File:FileType" => "PNG" }))
@uma1 = build(:upload_media_asset, media_asset: @asset1, status: "active", created_at: Time.zone.now)
@uma2 = build(:upload_media_asset, media_asset: @asset2, status: "active", created_at: Time.parse("2022-01-01"))
@upload1 = create(:upload, created_at: Time.zone.now, upload_media_assets: [@uma1])
@upload2 = create(:upload, created_at: Time.parse("2022-01-01"), upload_media_assets: [@uma2])
end
should "return assets for the id: metatag" do
assert_tag_match([@upload1], "id:#{@upload1.upload_media_assets.sole.id}")
end
should "return assets for the md5: metatag" do
assert_tag_match([@upload1], "md5:#{@asset1.md5}")
end
should "return assets for the width: metatag" do
assert_tag_match([@upload1], "width:#{@asset1.image_width}")
end
should "return assets for the height: metatag" do
assert_tag_match([@upload1], "height:#{@asset1.image_height}")
end
should "return assets for the duration: metatag" do
assert_tag_match([@upload2], "duration:3")
end
should "return assets for the mpixels: metatag" do
assert_tag_match([@upload1], "mpixels:#{(@asset1.image_width * @asset1.image_height) / 1_000_000.0}")
end
should "return assets for the ratio: metatag" do
assert_tag_match([@upload1], "ratio:#{@asset1.image_width.to_f / @asset1.image_height}")
end
should "return assets for the filesize: metatag" do
assert_tag_match([@upload1], "filesize:1mb")
end
should "return assets for the filetype: metatag" do
assert_tag_match([@upload1], "filetype:jpg")
end
should "return assets for the date: tag" do
assert_tag_match([@upload2], "date:2022-01-01")
end
should "return assets for the age: tag" do
assert_tag_match([@upload1], "age:<1minute")
end
should "return assets for the status: tag" do
assert_tag_match([@upload2, @upload1], "status:active")
end
should "return assets for the is: tag" do
assert_tag_match([@upload1], "is:jpg")
assert_tag_match([@upload2, @upload1], "is:active")
end
should "return assets for the exif: tag" do
assert_tag_match([@upload2, @upload1], "exif:File:FileType")
assert_tag_match([@upload1], "exif:File:FileType=JPEG")
end
end
end
end