sources: add artist profile links to fetch source data box.

Add site icons linking to all the artist's sites in the fetch source
data box.

Some artist entries have a large number of URLs. Various heuristics are
applied to try to present the most useful URLs first. Dead URLs and
redundant URLs (Pixiv stacc and Twitter intent URLs) are filtered out.
Remaining URLs are sorted first by site (to put sites like Pixiv and
Twitter first), then by URL (to break ties when an artist has multiple
accounts on the same site).

Some sites have shitty hard-to-read icons. It can't be helped. The icons
are the official favicons of each site.
This commit is contained in:
evazion
2021-02-26 01:04:44 -06:00
parent e1ef94faf7
commit 7b60a476e5
35 changed files with 248 additions and 64 deletions

View File

@@ -2,9 +2,15 @@
class SourceDataComponent < ApplicationComponent
attr_reader :source
delegate :spinner_icon, to: :helpers
delegate :spinner_icon, :external_site_icon, to: :helpers
def initialize(source:)
@source = source
end
def profile_urls(artist)
artist.urls.active.reject(&:secondary_url?).sort_by do |artist_url|
[artist_url.priority, artist_url.domain, artist_url.url]
end
end
end

View File

@@ -3,46 +3,55 @@
<%= spinner_icon class: "source-data-loading" %>
<% if @source.present? %>
<dl class="source-data-content">
<div class="source-data-artist">
<dt>Artist</dt>
<dd>
<% if @source.artist_name.blank? %>
<em>None</em>
<% else %>
<%= external_link_to @source.profile_url, @source.artist_name, class: "source-data-artist-profile" %>
<table class="source-data-content mt-2">
<tbody>
<% if @source.artist_name.blank? %>
<tr>
<th>Artist</th>
<td><em>None</em></td>
</tr>
<% elsif @source.artists.empty? %>
<tr>
<th>Artist</th>
<td>
<%= external_link_to @source.profile_url, @source.artist_name %>
(<%= link_to "Create new artist", new_artist_path(artist: { source: @source.canonical_url }) %>)
</td>
</tr>
<% else %>
<% @source.artists.each do |artist| %>
<tr>
<th>Artist</th>
<td>
<%= link_to artist.name, artist_path(artist), class: tag_class(artist.tag) %>
<% if @source.artists.empty? %>
(<%= link_to "Create new artist", new_artist_path(artist: { source: @source.canonical_url }), class: "source-data-create-new-artist" %>)
<ul class="list-inline">
<% profile_urls(artist).each do |artist_url| %>
<%= external_link_to artist_url.url, external_site_icon(artist_url.site_name), title: artist_url.url %>
<% end %>
</ul>
</td>
</tr>
<% end %>
<% end %>
<tr class="source-data-tags">
<th>Tags</th>
<td>
<% if @source.tags.empty? %>
<em>None</em>
<% else %>
(<ul class="source-data-translated-artists">
<% @source.artists.each do |artist| %>
<li><%= link_to artist.name, artist_path(artist), class: tag_class(artist.tag) %></li>
<ul class="list-inline">
<% @source.tags.each do |tag, href| %>
<li>
<%= external_link_to href, tag, class: "source-data-tag" %>
</li>
<% end %>
</ul>)
</ul>
<% end %>
<% end %>
</dd>
</div>
<div class="source-data-tags">
<dt>Tags</dt>
<dd>
<% if @source.tags.empty? %>
<em>None</em>
<% else %>
<ul>
<% @source.tags.each do |tag, href| %>
<li><%= external_link_to href, tag %></li>
<% end %>
</ul>
<% if @source.image_urls.length > 1 %>
<p class="source-data-gallery-warning">Gallery. Tags may not apply to all images.</p>
<% end %>
<% end %>
</dd>
</li>
</dl>
</td>
</tr>
</tbody>
</table>
<% end %>
</div>

View File

@@ -6,15 +6,27 @@ div.source-data {
&.loading .source-data-content { display: none; }
&.loading .source-data-fetch { display: none; }
ul {
th {
padding-right: 1rem;
padding-bottom: 0.25rem;
}
td {
vertical-align: top;
}
.icon {
height: 1rem;
font-size: 1rem;
vertical-align: bottom;
}
.source-data-tag {
display: inline-block;
}
dt, dd, li {
display: inline;
}
dt, .source-data-tags li {
margin-right: 1em;
background-color: var(--wiki-page-other-name-background-color);
padding: 0 0.25rem;
margin-right: 0.25rem;
margin-bottom: 0.25rem;
border-radius: 0.25rem;
}
}

View File

@@ -11,6 +11,11 @@ module IconHelper
end
end
def image_icon_tag(filename, class: nil, **options)
klass = binding.local_variable_get(:class)
tag.img(src: "/images/#{filename}", class: "icon #{klass}", **options)
end
# fontawesome.com/icons/arrow-alt-up
def upvote_icon(**options)
svg_icon_tag("upvote-icon", "M272 480h-96c-13.3 0-24-10.7-24-24V256H48.2c-21.4 0-32.1-25.8-17-41L207 39c9.4-9.4 24.6-9.4 34 0l175.8 176c15.1 15.1 4.4 41-17 41H296v200c0 13.3-10.7 24-24 24z", **options)
@@ -148,4 +153,83 @@ module IconHelper
def plus_icon(**options)
icon_tag("fas fa-plus", **options)
end
def globe_icon(**options)
icon_tag("fas fa-globe", **options)
end
def discord_icon(**options)
image_icon_tag("discord-logo.png", **options)
end
def github_icon(**options)
image_icon_tag("github-logo.png", **options)
end
def twitter_icon(**options)
image_icon_tag("twitter-logo.png", **options)
end
def external_site_icon(site_name, **options)
case site_name
when "ArtStation"
image_icon_tag("artstation-logo.png", **options)
when "BCY"
image_icon_tag("bcy-logo.png", **options)
when "Booth.pm"
image_icon_tag("booth-pm-logo.png", **options)
when "Circle.ms"
image_icon_tag("circle-ms-logo.png", **options)
when "DLSite"
image_icon_tag("dlsite-logo.png", **options)
when "Deviant Art"
image_icon_tag("deviantart-logo.png", **options)
when "Facebook"
image_icon_tag("facebook-logo.png", **options)
when "Fantia"
image_icon_tag("fantia-logo.png", **options)
when "FC2"
image_icon_tag("fc2-logo.png", **options)
when "Gumroad"
image_icon_tag("gumroad-logo.png", **options)
when "Instagram"
image_icon_tag("instagram-logo.png", **options)
when "Lofter"
image_icon_tag("lofter-logo.png", **options)
when "Melonbooks"
image_icon_tag("melonbooks-logo.png", **options)
when "Nico Seiga"
image_icon_tag("nicoseiga-logo.png", **options)
when "Nijie"
image_icon_tag("nijie-logo.png", **options)
when "Patreon"
image_icon_tag("patreon-logo.png", **options)
when "pawoo.net"
image_icon_tag("pawoo-logo.png", **options)
when "Pixiv"
image_icon_tag("pixiv-logo.png", **options)
when "Pixiv Fanbox"
image_icon_tag("pixiv-fanbox-logo.png", **options)
when "Pixiv Sketch"
image_icon_tag("pixiv-sketch-logo.png", **options)
when "Privatter"
image_icon_tag("privatter-logo.png", **options)
when "Skeb"
image_icon_tag("skeb-logo.png", **options)
when "Tinami"
image_icon_tag("tinami-logo.png", **options)
when "Tumblr"
image_icon_tag("tumblr-logo.png", **options)
when "Twitter"
image_icon_tag("twitter-logo.png", **options)
when "Toranoana"
image_icon_tag("toranoana-logo.png", **options)
when "Weibo"
image_icon_tag("weibo-logo.png", **options)
when "Youtube"
image_icon_tag("youtube-logo.png", **options)
else
globe_icon(**options)
end
end
end

View File

@@ -60,7 +60,47 @@ module Sources
end
def site_name
Addressable::URI.heuristic_parse(url)&.host
host = Addressable::URI.heuristic_parse(url)&.host
# XXX should go in dedicated strategies.
case host
when /bcy\.net\z/i
"BCY"
when /booth\.pm\z/i
"Booth.pm"
when /circle\.ms\z/i
"Circle.ms"
when /dlsite\.(com|net)\z/i
"DLSite"
when /facebook\.com\z/i
"Facebook"
when /fantia\.jp\z/i
"Fantia"
when /fc2\.com\z/i
"FC2"
when /gumroad\.com\z/i
"Gumroad"
when /instagram\.com\z/i
"Instagram"
when /lofter\.com\z/i
"Lofter"
when /melonbooks\.co\.jp\z/i
"Melonbooks"
when /patreon\.com\z/i
"Patreon"
when /privatter\.net\z/i
"Privatter"
when /skeb\.jp\z/i
"Skeb"
when /tinami\.com\z/i
"Tinami"
when /toranoana\.(jp|shop)\z/i
"Toranoana"
when /youtube\.com\z/i
"Youtube"
else
host
end
rescue Addressable::URI::InvalidURIError
nil
end

View File

@@ -90,7 +90,12 @@ module Sources
end
def site_name
"Pixiv"
# XXX pixiv sketch should be in a separate strategy
if parsed_url.host.in?(%w[sketch.pixiv.net img-sketch.pixiv.net img-sketch.pximg.net])
"Pixiv Sketch"
else
"Pixiv"
end
end
def image_urls

View File

@@ -7,6 +7,7 @@ class ArtistUrl < ApplicationRecord
scope :url_matches, ->(url) { url_attribute_matches(:url, url) }
scope :normalized_url_matches, ->(url) { url_attribute_matches(:normalized_url, url) }
scope :active, -> { where(is_active: true) }
def self.parse_prefix(url)
prefix, url = url.match(/\A(-)?(.*)/)[1, 2]
@@ -68,24 +69,51 @@ class ArtistUrl < ApplicationRecord
end
end
def priority
if normalized_url =~ /pixiv\.net\/member\.php/
10
def domain
uri = Addressable::URI.parse(normalized_url)
uri.domain
end
elsif normalized_url =~ /seiga\.nicovideo\.jp\/user\/illust/
10
elsif normalized_url =~ /twitter\.com/ && normalized_url !~ /status/
15
elsif normalized_url =~ /tumblr|patreon|deviantart|artstation/
20
def site_name
source = Sources::Strategies.find(normalized_url)
source.site_name
end
# A secondary URL is an artist URL that we don't normally want to display,
# usually because it's redundant with the primary profile URL.
def secondary_url?
case url
when %r!pixiv\.net/stacc!i
true
when %r!pixiv\.net/fanbox!i
true
when %r!twitter\.com/intent!i
true
when %r!lohas\.nicoseiga\.jp!i
true
when %r!(?:www|com|dic)\.nicovideo\.jp!i
true
when %r!pawoo\.net/web/accounts!i
true
when %r!www\.artstation\.com!i
true
else
100
false
end
end
# The sort order of sites in artist URL lists.
def priority
sites = %w[
Pixiv Twitter
ArtStation Deviant\ Art Nico\ Seiga Nijie pawoo.net Pixiv\ Fanbox Pixiv\ Sketch Tinami Tumblr
Booth.pm Facebook Fantia FC2 Gumroad Instagram Lofter Patreon Privatter Skeb Weibo Youtube
Circle.ms DLSite Melonbooks Toranoana
]
sites.index(site_name) || 1000
end
def normalize
# Perform some normalization with Addressable on the URL itself
# - Converts scheme and hostname to downcase

View File

@@ -6,17 +6,17 @@
<span class="social-icons">
<% if Danbooru.config.source_code_url.present? %>
<%= link_to Danbooru.config.source_code_url, title: "Running commit: #{Rails.application.config.x.git_hash&.first(9)}", class: "social-icon" do %>
<img src="/images/github-logo.png">
<%= github_icon %>
<% end %>
<% end %>
<% if Danbooru.config.twitter_username.present? %>
<%= link_to "https://twitter.com/#{Danbooru.config.twitter_username}", class: "social-icon" do %>
<img src="/images/twitter-logo.png">
<%= twitter_icon %>
<% end %>
<% end %>
<% if Danbooru.config.discord_server_url.present? %>
<%= link_to Danbooru.config.discord_server_url, class: "social-icon" do %>
<img src="/images/discord-logo.png">
<%= discord_icon %>
<% end %>
<% end %>
</span>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/images/bcy-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
public/images/fantia-logo.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
public/images/fc2-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
public/images/skeb-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB