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:
Eugen Rochko 2016-12-04 12:26:12 +01:00
parent 290ffb63cd
commit 80c44ed9c1
18 changed files with 34 additions and 27 deletions

View file

@ -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 (

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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}"

View file

@ -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

View file

@ -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) }

View file

@ -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) }

View file

@ -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)) }

View file

@ -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 }

View file

@ -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 }

View file

@ -6,6 +6,8 @@ if ENV['S3_ENABLED'] == 'true'
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

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 681 B

View file

@ -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