Do not autoplay videos, display play button instead. Use expiring links when using S3. Do not keep originals
for avatars/headers, resize avatars down to 120x120 instead of 300x300. Set cache headers on S3 stuff, also make it private (aka only accessible via expiring links to prevent hotlinking)
This commit is contained in:
parent
290ffb63cd
commit
80c44ed9c1
18 changed files with 34 additions and 27 deletions
app
assets/javascripts/components/components
helpers
models
views
accounts
api/v1
stream_entries
config/initializers
public/avatars
spec/helpers
|
@ -53,7 +53,8 @@ const VideoPlayer = React.createClass({
|
|||
propTypes: {
|
||||
media: ImmutablePropTypes.map.isRequired,
|
||||
width: React.PropTypes.number,
|
||||
height: React.PropTypes.number
|
||||
height: React.PropTypes.number,
|
||||
sensitive: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getDefaultProps () {
|
||||
|
@ -102,6 +103,12 @@ const VideoPlayer = React.createClass({
|
|||
<span style={spoilerSubSpanStyle}><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
|
||||
</div>
|
||||
);
|
||||
} else if (!sensitive && !this.state.visible) {
|
||||
return (
|
||||
<div style={{ cursor: 'pointer', position: 'relative', marginTop: '8px', width: `${width}px`, height: `${height}px`, background: `url(${media.get('preview_url')}) no-repeat center`, backgroundSize: 'cover' }} onClick={this.handleOpen}>
|
||||
<div style={{ position: 'absolute', top: '50%', left: '50%', fontSize: '36px', transform: 'translate(-50%, -50%)', padding: '5px', borderRadius: '100px', color: 'rgba(255, 255, 255, 0.8)' }}><i className='fa fa-play' /></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -112,13 +112,11 @@ module AtomBuilderHelper
|
|||
end
|
||||
|
||||
def link_enclosure(xml, media)
|
||||
xml.link(rel: 'enclosure', href: full_asset_url(media.file.url), type: media.file_content_type, length: media.file_file_size)
|
||||
xml.link(rel: 'enclosure', href: full_asset_url(media.file.url(:original, false)), type: media.file_content_type, length: media.file_file_size)
|
||||
end
|
||||
|
||||
def link_avatar(xml, account)
|
||||
single_link_avatar(xml, account, :large, 300)
|
||||
# single_link_avatar(xml, account, :medium, 96)
|
||||
# single_link_avatar(xml, account, :small, 48)
|
||||
single_link_avatar(xml, account, :original, 120)
|
||||
end
|
||||
|
||||
def logo(xml, url)
|
||||
|
|
|
@ -6,7 +6,7 @@ module StreamEntriesHelper
|
|||
end
|
||||
|
||||
def avatar_for_status_url(status)
|
||||
status.reblog? ? status.reblog.account.avatar.url(:large) : status.account.avatar.url(:large)
|
||||
status.reblog? ? status.reblog.account.avatar.expiring_url(3600, :original) : status.account.avatar.expiring_url(3600, :original)
|
||||
end
|
||||
|
||||
def entry_classes(status, is_predecessor, is_successor, include_threads)
|
||||
|
|
|
@ -13,12 +13,12 @@ class Account < ApplicationRecord
|
|||
validates :username, presence: true, uniqueness: { scope: :domain, case_sensitive: true }, unless: 'local?'
|
||||
|
||||
# Avatar upload
|
||||
has_attached_file :avatar, styles: { large: '300x300#' }, convert_options: { all: '-strip' }
|
||||
has_attached_file :avatar, styles: { original: '120x120#' }, convert_options: { all: '-quality 80 -strip' }
|
||||
validates_attachment_content_type :avatar, content_type: IMAGE_MIME_TYPES
|
||||
validates_attachment_size :avatar, less_than: 2.megabytes
|
||||
|
||||
# Header upload
|
||||
has_attached_file :header, styles: { medium: '700x335#' }, convert_options: { all: '-strip' }
|
||||
has_attached_file :header, styles: { original: '700x335#' }, convert_options: { all: '-quality 80 -strip' }
|
||||
validates_attachment_content_type :header, content_type: IMAGE_MIME_TYPES
|
||||
validates_attachment_size :header, less_than: 2.megabytes
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ class MediaAttachment < ApplicationRecord
|
|||
has_attached_file :file,
|
||||
styles: -> (f) { file_styles f },
|
||||
processors: -> (f) { f.video? ? [:transcoder] : [:thumbnail] },
|
||||
convert_options: { all: '-strip' }
|
||||
convert_options: { all: '-quality 80 -strip' }
|
||||
validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES
|
||||
validates_attachment_size :file, less_than: 4.megabytes
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.account-grid-card
|
||||
.account-grid-card__header
|
||||
.avatar= image_tag account.avatar.url(:medium)
|
||||
.avatar= image_tag account.avatar.expiring_url(3600, :original)
|
||||
.name
|
||||
= link_to TagManager.instance.url_for(account) do
|
||||
%span.display_name= display_name(account)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.card{ style: "background-image: url(#{@account.header.url(:medium)})" }
|
||||
.card{ style: "background-image: url(#{@account.header.expiring_url(3600, :original)})" }
|
||||
- if user_signed_in? && current_account.id != @account.id
|
||||
.controls
|
||||
- if current_account.following?(@account)
|
||||
|
@ -6,7 +6,7 @@
|
|||
- else
|
||||
= link_to t('accounts.follow'), follow_account_path(@account), data: { method: :post }, class: 'button'
|
||||
|
||||
.avatar= image_tag @account.avatar.url(:large)
|
||||
.avatar= image_tag @account.avatar.expiring_url(3600, :original)
|
||||
%h1.name
|
||||
= display_name(@account)
|
||||
%small= "@#{@account.username}"
|
||||
|
|
|
@ -6,7 +6,7 @@ Nokogiri::XML::Builder.new do |xml|
|
|||
title xml, @account.display_name
|
||||
subtitle xml, @account.note
|
||||
updated_at xml, stream_updated_at
|
||||
logo xml, full_asset_url(@account.avatar.url(:medium, false))
|
||||
logo xml, full_asset_url(@account.avatar.expiring_url(3600, :original))
|
||||
|
||||
author(xml) do
|
||||
include_author xml, @account
|
||||
|
|
|
@ -4,8 +4,8 @@ attributes :id, :username, :acct, :display_name
|
|||
|
||||
node(:note) { |account| Formatter.instance.simplified_format(account) }
|
||||
node(:url) { |account| TagManager.instance.url_for(account) }
|
||||
node(:avatar) { |account| full_asset_url(account.avatar.url(:large, false)) }
|
||||
node(:header) { |account| full_asset_url(account.header.url(:medium, false)) }
|
||||
node(:avatar) { |account| full_asset_url(account.avatar.expiring_url(3600, :original)) }
|
||||
node(:header) { |account| full_asset_url(account.header.expiring_url(3600, :original)) }
|
||||
node(:followers_count) { |account| defined?(@followers_counts_map) ? (@followers_counts_map[account.id] || 0) : (account.try(:followers_count) || account.followers.count) }
|
||||
node(:following_count) { |account| defined?(@following_counts_map) ? (@following_counts_map[account.id] || 0) : (account.try(:following_count) || account.following.count) }
|
||||
node(:statuses_count) { |account| defined?(@statuses_counts_map) ? (@statuses_counts_map[account.id] || 0) : (account.try(:statuses_count) || account.statuses.count) }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
object @media
|
||||
attribute :id, :type
|
||||
node(:url) { |media| full_asset_url(media.file.url) }
|
||||
node(:preview_url) { |media| full_asset_url(media.file.url(:small)) }
|
||||
node(:url) { |media| full_asset_url(media.file.expiring_url(3600, :original)) }
|
||||
node(:preview_url) { |media| full_asset_url(media.file.expiring_url(3600, :small)) }
|
||||
node(:text_url) { |media| medium_url(media) }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
attributes :id, :remote_url, :type
|
||||
|
||||
node(:url) { |media| full_asset_url(media.file.url) }
|
||||
node(:preview_url) { |media| full_asset_url(media.file.url(:small)) }
|
||||
node(:url) { |media| full_asset_url(media.file.expiring_url(3600, :original)) }
|
||||
node(:preview_url) { |media| full_asset_url(media.file.expiring_url(3600, :small)) }
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
- if (status.reblog? ? status.reblog : status).media_attachments.size > 0
|
||||
%ul.media-attachments
|
||||
- (status.reblog? ? status.reblog : status).media_attachments.each do |media|
|
||||
%li.transparent-background= link_to '', media.file.url, style: "background-image: url(#{media.file.url(:small)})", target: '_blank'
|
||||
%li.transparent-background= link_to '', media.file.expiring_url(3600, :original), style: "background-image: url(#{media.file.expiring_url(3600, :small)})", target: '_blank'
|
||||
|
||||
- if include_threads
|
||||
= render partial: 'status', collection: @descendants, as: :status, locals: { is_successor: true }
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
%meta{ name: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
|
||||
%meta{ name: 'og:article:author', content: @account.username }/
|
||||
%meta{ name: 'og:description', content: @stream_entry.activity.content }/
|
||||
%meta{ name: 'og:image', content: @stream_entry.activity.is_a?(Status) && @stream_entry.activity.media_attachments.size > 0 ? full_asset_url(@stream_entry.activity.media_attachments.first.file.url(:small)) : full_asset_url(@account.avatar.url(:large)) }/
|
||||
%meta{ name: 'og:image', content: @stream_entry.activity.is_a?(Status) && @stream_entry.activity.media_attachments.size > 0 ? full_asset_url(@stream_entry.activity.media_attachments.first.file.expiring_url(3600, :small)) : full_asset_url(@account.avatar.expiring_url(3600, :original)) }/
|
||||
|
||||
.activity-stream.activity-stream-headless
|
||||
= render partial: @type, locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
if ENV['S3_ENABLED'] == 'true'
|
||||
Aws.eager_autoload!(services: %w(S3))
|
||||
|
||||
Paperclip::Attachment.default_options[:storage] = :s3
|
||||
Paperclip::Attachment.default_options[:s3_protocol] = 'https'
|
||||
Paperclip::Attachment.default_options[:url] = ':s3_domain_url'
|
||||
Paperclip::Attachment.default_options[:s3_host_name] = "s3-#{ENV.fetch('S3_REGION')}.amazonaws.com"
|
||||
Paperclip::Attachment.default_options[:path] = '/:class/:attachment/:id_partition/:style/:filename'
|
||||
Paperclip::Attachment.default_options[:storage] = :s3
|
||||
Paperclip::Attachment.default_options[:s3_protocol] = 'https'
|
||||
Paperclip::Attachment.default_options[:url] = ':s3_domain_url'
|
||||
Paperclip::Attachment.default_options[:s3_host_name] = "s3-#{ENV.fetch('S3_REGION')}.amazonaws.com"
|
||||
Paperclip::Attachment.default_options[:path] = '/:class/:attachment/:id_partition/:style/:filename'
|
||||
Paperclip::Attachment.default_options[:s3_headers] = { 'Cache-Control' => 'max-age=315576000', 'Expires' => 10.years.from_now.httpdate }
|
||||
Paperclip::Attachment.default_options[:s3_permissions] = :private
|
||||
|
||||
unless ENV['S3_CLOUDFRONT_HOST'].blank?
|
||||
Paperclip::Attachment.default_options[:url] = ':s3_alias_url'
|
||||
|
|
Binary file not shown.
Before (image error) Size: 1 KiB |
Before (image error) Size: 2.9 KiB After (image error) Size: 2.9 KiB |
Binary file not shown.
Before (image error) Size: 681 B |
|
@ -162,7 +162,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do
|
|||
let(:account) { Fabricate(:account, username: 'alice') }
|
||||
|
||||
it 'creates a link' do
|
||||
expect(used_with_namespaces { |xml| helper.link_avatar(xml, account) }).to match '<link rel="avatar" type="" media:width="300" media:height="300" href="http://test.host/avatars/large/missing.png"/>'
|
||||
expect(used_with_namespaces { |xml| helper.link_avatar(xml, account) }).to match '<link rel="avatar" type="" media:width="120" media:height="120" href="http://test.host/avatars/original/missing.png"/>'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue