mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-27 13:47:58 +01:00
verified uploaders
This commit is contained in:
parent
84abf98b30
commit
ae7f3834fd
24 changed files with 170 additions and 61 deletions
|
@ -18,6 +18,7 @@ config :philomena,
|
||||||
anonymous_name_salt: System.get_env("ANONYMOUS_NAME_SALT"),
|
anonymous_name_salt: System.get_env("ANONYMOUS_NAME_SALT"),
|
||||||
password_pepper: System.get_env("PASSWORD_PEPPER"),
|
password_pepper: System.get_env("PASSWORD_PEPPER"),
|
||||||
avatar_url_root: System.get_env("AVATAR_URL_ROOT"),
|
avatar_url_root: System.get_env("AVATAR_URL_ROOT"),
|
||||||
|
image_file_root: System.get_env("IMAGE_FILE_ROOT"),
|
||||||
otp_secret_key: System.get_env("OTP_SECRET_KEY"),
|
otp_secret_key: System.get_env("OTP_SECRET_KEY"),
|
||||||
image_url_root: System.get_env("IMAGE_URL_ROOT"),
|
image_url_root: System.get_env("IMAGE_URL_ROOT"),
|
||||||
badge_url_root: System.get_env("BADGE_URL_ROOT"),
|
badge_url_root: System.get_env("BADGE_URL_ROOT"),
|
||||||
|
|
|
@ -46,6 +46,8 @@ defmodule Philomena.Analyzers.Gif do
|
||||||
{output, 0} ->
|
{output, 0} ->
|
||||||
[width, height] =
|
[width, height] =
|
||||||
output
|
output
|
||||||
|
|> String.split("\n", trim: true)
|
||||||
|
|> hd()
|
||||||
|> String.trim()
|
|> String.trim()
|
||||||
|> String.split(" ")
|
|> String.split(" ")
|
||||||
|> Enum.map(&String.to_integer/1)
|
|> Enum.map(&String.to_integer/1)
|
||||||
|
|
|
@ -14,8 +14,8 @@ defmodule Philomena.Application do
|
||||||
PhilomenaWeb.Endpoint,
|
PhilomenaWeb.Endpoint,
|
||||||
# Starts a worker by calling: Philomena.Worker.start_link(arg)
|
# Starts a worker by calling: Philomena.Worker.start_link(arg)
|
||||||
# {Philomena.Worker, arg},
|
# {Philomena.Worker, arg},
|
||||||
Pow.Store.Backend.MnesiaCache,
|
|
||||||
Philomena.Servers.ImageProcessor,
|
Philomena.Servers.ImageProcessor,
|
||||||
|
Pow.Store.Backend.MnesiaCache,
|
||||||
{Redix, name: :redix}
|
{Redix, name: :redix}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,31 @@ defmodule Philomena.DuplicateReports do
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
|
|
||||||
alias Philomena.DuplicateReports.DuplicateReport
|
alias Philomena.DuplicateReports.DuplicateReport
|
||||||
|
alias Philomena.ImageIntensities.ImageIntensity
|
||||||
|
alias Philomena.Images.Image
|
||||||
|
|
||||||
@doc """
|
def generate_reports(source) do
|
||||||
Returns the list of duplicate_reports.
|
source = Repo.preload(source, :intensity)
|
||||||
|
|
||||||
## Examples
|
duplicates_of(source.intensity, source.image_aspect_ratio, 0.2, 0.05)
|
||||||
|
|> where([i, _it], i.id != ^source.id)
|
||||||
|
|> where([i, _it], i.duplication_checked != true)
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(fn target ->
|
||||||
|
create_duplicate_report(source, target, %{}, %{"reason" => "Automated Perceptual dedupe match"})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
iex> list_duplicate_reports()
|
def duplicates_of(intensities, aspect_ratio, dist \\ 0.25, aspect_dist \\ 0.05) do
|
||||||
[%DuplicateReport{}, ...]
|
from i in Image,
|
||||||
|
inner_join: it in ImageIntensity,
|
||||||
"""
|
on: it.image_id == i.id,
|
||||||
def list_duplicate_reports do
|
where: it.nw >= ^(intensities.nw - dist) and it.nw <= ^(intensities.nw + dist),
|
||||||
Repo.all(DuplicateReport)
|
where: it.ne >= ^(intensities.ne - dist) and it.ne <= ^(intensities.ne + dist),
|
||||||
|
where: it.sw >= ^(intensities.sw - dist) and it.sw <= ^(intensities.sw + dist),
|
||||||
|
where: it.se >= ^(intensities.se - dist) and it.se <= ^(intensities.se + dist),
|
||||||
|
where: i.image_aspect_ratio >= ^(aspect_ratio - aspect_dist) and i.image_aspect_ratio >= ^(aspect_ratio + aspect_dist),
|
||||||
|
limit: 20
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -49,9 +62,9 @@ defmodule Philomena.DuplicateReports do
|
||||||
{:error, %Ecto.Changeset{}}
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def create_duplicate_report(attrs \\ %{}) do
|
def create_duplicate_report(source, target, attribution, attrs \\ %{}) do
|
||||||
%DuplicateReport{}
|
%DuplicateReport{image_id: source.id, duplicate_of_image_id: target.id}
|
||||||
|> DuplicateReport.changeset(attrs)
|
|> DuplicateReport.creation_changeset(attrs, attribution)
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,4 +23,12 @@ defmodule Philomena.DuplicateReports.DuplicateReport do
|
||||||
|> cast(attrs, [])
|
|> cast(attrs, [])
|
||||||
|> validate_required([])
|
|> validate_required([])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def creation_changeset(duplicate_report, attrs, attribution) do
|
||||||
|
duplicate_report
|
||||||
|
|> cast(attrs, [:reason])
|
||||||
|
|> put_assoc(:user, attribution[:user])
|
||||||
|
|> validate_length(:reason, max: 250, count: :bytes)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,12 +56,20 @@ defmodule Philomena.Images do
|
||||||
|
|
||||||
Multi.new
|
Multi.new
|
||||||
|> Multi.insert(:image, image)
|
|> Multi.insert(:image, image)
|
||||||
|
|> Multi.run(:added_tag_count, fn repo, %{image: image} ->
|
||||||
|
tag_ids = image.added_tags |> Enum.map(& &1.id)
|
||||||
|
tags = Tag |> where([t], t.id in ^tag_ids)
|
||||||
|
|
||||||
|
{count, nil} = repo.update_all(tags, inc: [images_count: 1])
|
||||||
|
|
||||||
|
{:ok, count}
|
||||||
|
end)
|
||||||
|> Multi.run(:after, fn _repo, %{image: image} ->
|
|> Multi.run(:after, fn _repo, %{image: image} ->
|
||||||
Processors.after_insert(image)
|
Processors.after_insert(image)
|
||||||
|
|
||||||
{:ok, nil}
|
{:ok, nil}
|
||||||
end)
|
end)
|
||||||
|> Repo.transaction()
|
|> Repo.isolated_transaction(:serializable)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Philomena.Images.Image do
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
alias Philomena.ImageIntensities.ImageIntensity
|
||||||
alias Philomena.ImageVotes.ImageVote
|
alias Philomena.ImageVotes.ImageVote
|
||||||
alias Philomena.ImageFaves.ImageFave
|
alias Philomena.ImageFaves.ImageFave
|
||||||
alias Philomena.ImageHides.ImageHide
|
alias Philomena.ImageHides.ImageHide
|
||||||
|
@ -39,6 +40,7 @@ defmodule Philomena.Images.Image do
|
||||||
has_many :favers, through: [:faves, :user]
|
has_many :favers, through: [:faves, :user]
|
||||||
has_many :hiders, through: [:hides, :user]
|
has_many :hiders, through: [:hides, :user]
|
||||||
many_to_many :tags, Tag, join_through: "image_taggings", on_replace: :delete
|
many_to_many :tags, Tag, join_through: "image_taggings", on_replace: :delete
|
||||||
|
has_one :intensity, ImageIntensity
|
||||||
|
|
||||||
field :image, :string
|
field :image, :string
|
||||||
field :image_name, :string
|
field :image_name, :string
|
||||||
|
@ -112,7 +114,7 @@ defmodule Philomena.Images.Image do
|
||||||
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||||
|
|
||||||
image
|
image
|
||||||
|> cast(attrs, [:source_url, :description])
|
|> cast(attrs, [:anonymous, :source_url, :description])
|
||||||
|> change(first_seen_at: now)
|
|> change(first_seen_at: now)
|
||||||
|> change(attribution)
|
|> change(attribution)
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
old_set = to_set(old_tags)
|
old_set = to_set(old_tags)
|
||||||
new_set = to_set(new_tags)
|
new_set = to_set(new_tags)
|
||||||
|
|
||||||
|
image_id = changeset |> get_field(:id)
|
||||||
tags = changeset |> get_field(:tags)
|
tags = changeset |> get_field(:tags)
|
||||||
added_tags = added_set(old_set, new_set)
|
added_tags = added_set(old_set, new_set)
|
||||||
removed_tags = removed_set(old_set, new_set)
|
removed_tags = removed_set(old_set, new_set)
|
||||||
|
@ -16,14 +17,15 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
{tags, actually_added, actually_removed} =
|
{tags, actually_added, actually_removed} =
|
||||||
apply_changes(tags, added_tags, removed_tags)
|
apply_changes(tags, added_tags, removed_tags)
|
||||||
|
|
||||||
{tag_list_cache, tag_list_plus_alias_cache} =
|
{tag_list_cache, tag_list_plus_alias_cache, file_name_cache} =
|
||||||
create_caches(tags)
|
create_caches(image_id, tags)
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|> put_change(:added_tags, actually_added)
|
|> put_change(:added_tags, actually_added)
|
||||||
|> put_change(:removed_tags, actually_removed)
|
|> put_change(:removed_tags, actually_removed)
|
||||||
|> put_change(:tag_list_cache, tag_list_cache)
|
|> put_change(:tag_list_cache, tag_list_cache)
|
||||||
|> put_change(:tag_list_plus_alias_cache, tag_list_plus_alias_cache)
|
|> put_change(:tag_list_plus_alias_cache, tag_list_plus_alias_cache)
|
||||||
|
|> put_change(:file_name_cache, file_name_cache)
|
||||||
|> put_assoc(:tags, tags)
|
|> put_assoc(:tags, tags)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -94,10 +96,11 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
{tags, actually_added, actually_removed}
|
{tags, actually_added, actually_removed}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_caches(tags) do
|
defp create_caches(image_id, tags) do
|
||||||
|
tags = Tag.display_order(tags)
|
||||||
|
|
||||||
tag_list_cache =
|
tag_list_cache =
|
||||||
tags
|
tags
|
||||||
|> Tag.display_order()
|
|
||||||
|> Enum.map_join(", ", & &1.name)
|
|> Enum.map_join(", ", & &1.name)
|
||||||
|
|
||||||
tag_ids =
|
tag_ids =
|
||||||
|
@ -113,6 +116,17 @@ defmodule Philomena.Images.TagDiffer do
|
||||||
|> Tag.display_order()
|
|> Tag.display_order()
|
||||||
|> Enum.map_join(", ", & &1.name)
|
|> Enum.map_join(", ", & &1.name)
|
||||||
|
|
||||||
{tag_list_cache, tag_list_plus_alias_cache}
|
# Trunate filename to 150 characters, making room for the path + filename on Windows
|
||||||
|
# https://stackoverflow.com/questions/265769/maximum-filename-length-in-ntfs-windows-xp-and-windows-vista
|
||||||
|
file_name_slug_fragment =
|
||||||
|
tags
|
||||||
|
|> Enum.map_join("_", & &1.slug)
|
||||||
|
|> String.replace("%2F", "")
|
||||||
|
|> String.replace("/", "")
|
||||||
|
|> String.slice(0..150)
|
||||||
|
|
||||||
|
file_name_cache = "#{image_id}__#{file_name_slug_fragment}"
|
||||||
|
|
||||||
|
{tag_list_cache, tag_list_plus_alias_cache, file_name_cache}
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -1,10 +1,10 @@
|
||||||
defmodule Philomena.Processors do
|
defmodule Philomena.Processors do
|
||||||
alias Philomena.Images.Image
|
alias Philomena.Images.Image
|
||||||
|
alias Philomena.DuplicateReports
|
||||||
alias Philomena.ImageIntensities
|
alias Philomena.ImageIntensities
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
alias Philomena.Mime
|
alias Philomena.Mime
|
||||||
alias Philomena.Sha512
|
alias Philomena.Sha512
|
||||||
alias Philomena.Servers.ImageProcessor
|
|
||||||
|
|
||||||
@mimes %{
|
@mimes %{
|
||||||
"image/gif" => "image/gif",
|
"image/gif" => "image/gif",
|
||||||
|
@ -66,7 +66,6 @@ defmodule Philomena.Processors do
|
||||||
dir = Path.dirname(file)
|
dir = Path.dirname(file)
|
||||||
File.mkdir_p!(dir)
|
File.mkdir_p!(dir)
|
||||||
File.cp!(image.uploaded_image, file)
|
File.cp!(image.uploaded_image, file)
|
||||||
ImageProcessor.cast(self(), image.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_image(image_id) do
|
def process_image(image_id) do
|
||||||
|
@ -83,8 +82,11 @@ defmodule Philomena.Processors do
|
||||||
sha512 = Sha512.file(file)
|
sha512 = Sha512.file(file)
|
||||||
changeset = Image.thumbnail_changeset(image, %{"image_sha512_hash" => sha512})
|
changeset = Image.thumbnail_changeset(image, %{"image_sha512_hash" => sha512})
|
||||||
image = Repo.update!(changeset)
|
image = Repo.update!(changeset)
|
||||||
|
spawn fn -> DuplicateReports.generate_reports(image) end
|
||||||
|
|
||||||
processor.post_process(analysis, file)
|
process = processor.post_process(analysis, file)
|
||||||
|
|
||||||
|
apply_edit_script(image, process)
|
||||||
sha512 = Sha512.file(file)
|
sha512 = Sha512.file(file)
|
||||||
changeset = Image.process_changeset(image, %{"image_sha512_hash" => sha512})
|
changeset = Image.process_changeset(image, %{"image_sha512_hash" => sha512})
|
||||||
Repo.update!(changeset)
|
Repo.update!(changeset)
|
||||||
|
|
|
@ -68,7 +68,7 @@ defmodule Philomena.Processors.Gif do
|
||||||
|
|
||||||
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
||||||
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
|
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
|
||||||
filter_graph = "#{scale_filter} [x]; [x][1:v] #{palette_filter}"
|
filter_graph = "[0:v] #{scale_filter} [x]; [x][1:v] #{palette_filter}"
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-i", palette, "-lavfi", filter_graph, scaled])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-i", palette, "-lavfi", filter_graph, scaled])
|
||||||
|
|
|
@ -22,22 +22,22 @@ defmodule Philomena.Processors.Png do
|
||||||
optimized = Briefly.create!(extname: ".png")
|
optimized = Briefly.create!(extname: ".png")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("optipng", ["-fix", "-i0", "-o2", file, "-out", optimized])
|
System.cmd("optipng", ["-fix", "-i0", "-o2", "-quiet", "-clobber", file, "-out", optimized])
|
||||||
|
|
||||||
optimized
|
optimized
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scale_if_smaller(_file, _dimensions, {:full, _target_dim}) do
|
defp scale_if_smaller(_file, _dimensions, {:full, _target_dim}) do
|
||||||
[{:symlink_original, "full.jpg"}]
|
[{:symlink_original, "full.png"}]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scale_if_smaller(file, {width, height}, {thumb_name, {target_width, target_height}}) do
|
defp scale_if_smaller(file, {width, height}, {thumb_name, {target_width, target_height}}) do
|
||||||
if width > target_width or height > target_height do
|
if width > target_width or height > target_height do
|
||||||
scaled = scale(file, {target_width, target_height})
|
scaled = scale(file, {target_width, target_height})
|
||||||
|
|
||||||
[{:copy, scaled, "#{thumb_name}.jpg"}]
|
[{:copy, scaled, "#{thumb_name}.png"}]
|
||||||
else
|
else
|
||||||
[{:symlink_original, "#{thumb_name}.jpg"}]
|
[{:symlink_original, "#{thumb_name}.png"}]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ defmodule Philomena.Processors.Png do
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", scale_filter, scaled])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-vf", scale_filter, scaled])
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("optipng", ["-i0", "-o1", scaled])
|
System.cmd("optipng", ["-i0", "-o1", "-quiet", "-clobber", scaled])
|
||||||
|
|
||||||
scaled
|
scaled
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,7 @@ defmodule Philomena.Processors.Svg do
|
||||||
preview = Briefly.create!(extname: ".png")
|
preview = Briefly.create!(extname: ".png")
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("inkscape", [file, "--export-png", preview])
|
System.cmd("safe-rsvg-convert", [file, preview])
|
||||||
|
|
||||||
preview
|
preview
|
||||||
end
|
end
|
||||||
|
@ -40,9 +40,9 @@ defmodule Philomena.Processors.Svg do
|
||||||
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-y", "-i", preview, "-vf", scale_filter, scaled])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", preview, "-vf", scale_filter, scaled])
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("optipng", ["-i0", "-o1", scaled])
|
System.cmd("optipng", ["-i0", "-o1", "-quiet", "-clobber", scaled])
|
||||||
|
|
||||||
scaled
|
scaled
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,8 +29,8 @@ defmodule Philomena.Processors.Webm do
|
||||||
preview
|
preview
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scale_if_smaller(palette, file, dimensions, {:full, _target_dim}) do
|
defp scale_if_smaller(file, palette, dimensions, {:full, _target_dim}) do
|
||||||
{webm, mp4} = scale_videos(file, palette, dimensions)
|
{webm, mp4} = scale_videos(file, palette, dimensions, dimensions)
|
||||||
|
|
||||||
[
|
[
|
||||||
{:copy, webm, "full.webm"},
|
{:copy, webm, "full.webm"},
|
||||||
|
@ -38,8 +38,8 @@ defmodule Philomena.Processors.Webm do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scale_if_smaller(palette, file, _dimensions, {thumb_name, {target_width, target_height}}) do
|
defp scale_if_smaller(file, palette, dimensions, {thumb_name, {target_width, target_height}}) do
|
||||||
{webm, mp4} = scale_videos(file, palette, {target_width, target_height})
|
{webm, mp4} = scale_videos(file, palette, dimensions, {target_width, target_height})
|
||||||
|
|
||||||
cond do
|
cond do
|
||||||
thumb_name in [:thumb, :thumb_small, :thumb_tiny] ->
|
thumb_name in [:thumb, :thumb_small, :thumb_tiny] ->
|
||||||
|
@ -59,14 +59,14 @@ defmodule Philomena.Processors.Webm do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp scale_videos(file, _palette, dimensions) do
|
defp scale_videos(file, _palette, dimensions, target_dimensions) do
|
||||||
{width, height} = normalize_dimensions(dimensions)
|
{width, height} = box_dimensions(dimensions, target_dimensions)
|
||||||
webm = Briefly.create!(extname: ".webm")
|
webm = Briefly.create!(extname: ".webm")
|
||||||
mp4 = Briefly.create!(extname: ".mp4")
|
mp4 = Briefly.create!(extname: ".mp4")
|
||||||
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
scale_filter = "scale=w=#{width}:h=#{height}"
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-c:v", "libvpx", "-crf", "10", "-b:v", "5M", "-vf", scale_filter, webm])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-c:v", "libvpx", "-auto-alt-ref", "0", "-crf", "10", "-b:v", "5M", "-vf", scale_filter, webm])
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-c:v", "libx264", "-preset", "medium", "-crf", "18", "-b:v", "5M", "-vf", scale_filter, mp4])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-c:v", "libx264", "-preset", "medium", "-crf", "18", "-b:v", "5M", "-vf", scale_filter, mp4])
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ defmodule Philomena.Processors.Webm do
|
||||||
gif = Briefly.create!(extname: ".gif")
|
gif = Briefly.create!(extname: ".gif")
|
||||||
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
scale_filter = "scale=w=#{width}:h=#{height}:force_original_aspect_ratio=decrease"
|
||||||
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
|
palette_filter = "paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle"
|
||||||
filter_graph = "#{scale_filter} [x]; [x][1:v] #{palette_filter}"
|
filter_graph = "[0:v] #{scale_filter} [x]; [x][1:v] #{palette_filter}"
|
||||||
|
|
||||||
{_output, 0} =
|
{_output, 0} =
|
||||||
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-i", palette, "-lavfi", filter_graph, gif])
|
System.cmd("ffmpeg", ["-loglevel", "0", "-y", "-i", file, "-i", palette, "-lavfi", filter_graph, gif])
|
||||||
|
@ -94,9 +94,13 @@ defmodule Philomena.Processors.Webm do
|
||||||
palette
|
palette
|
||||||
end
|
end
|
||||||
|
|
||||||
# Force dimensions to be a multiple of 2. This is required by the
|
# x264 requires image dimensions to be a multiple of 2
|
||||||
# libvpx and x264 encoders.
|
# -2 = ~1
|
||||||
defp normalize_dimensions({width, height}) do
|
def box_dimensions({width, height}, {target_width, target_height}) do
|
||||||
{width &&& (~~~1), height &&& (~~~1)}
|
ratio = min(target_width / width, target_height / height)
|
||||||
|
new_width = min(max(trunc(width * ratio) &&& -2, 2), target_width)
|
||||||
|
new_height = min(max(trunc(height * ratio) &&& -2, 2), target_height)
|
||||||
|
|
||||||
|
{new_width, new_height}
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -5,12 +5,14 @@ defmodule Philomena.Servers.ImageProcessor do
|
||||||
GenServer.start_link(__MODULE__, default)
|
GenServer.start_link(__MODULE__, default)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast(pid, image_id) do
|
def cast(image_id) do
|
||||||
|
pid = Process.whereis(:processor)
|
||||||
GenServer.cast(pid, {:enqueue, image_id})
|
GenServer.cast(pid, {:enqueue, image_id})
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def init([]) do
|
def init([]) do
|
||||||
|
Process.register(self(), :processor)
|
||||||
{:ok, []}
|
{:ok, []}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -19,7 +21,7 @@ defmodule Philomena.Servers.ImageProcessor do
|
||||||
# Ensure that tempfiles get cleaned up by reaping
|
# Ensure that tempfiles get cleaned up by reaping
|
||||||
# the process after it is done
|
# the process after it is done
|
||||||
Task.async(fn -> process(image_id) end)
|
Task.async(fn -> process(image_id) end)
|
||||||
|> Task.await()
|
|> Task.await(:infinity)
|
||||||
|
|
||||||
{:noreply, []}
|
{:noreply, []}
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,8 +2,10 @@ defmodule PhilomenaWeb.ImageController do
|
||||||
use PhilomenaWeb, :controller
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
alias Philomena.{Images, Images.Image, Comments.Comment, Textile.Renderer}
|
alias Philomena.{Images, Images.Image, Comments.Comment, Textile.Renderer}
|
||||||
|
alias Philomena.Servers.ImageProcessor
|
||||||
alias Philomena.Interactions
|
alias Philomena.Interactions
|
||||||
alias Philomena.Comments
|
alias Philomena.Comments
|
||||||
|
alias Philomena.Tags
|
||||||
alias Philomena.Repo
|
alias Philomena.Repo
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
@ -95,7 +97,9 @@ defmodule PhilomenaWeb.ImageController do
|
||||||
|
|
||||||
case Images.create_image(attributes, image_params) do
|
case Images.create_image(attributes, image_params) do
|
||||||
{:ok, %{image: image}} ->
|
{:ok, %{image: image}} ->
|
||||||
|
ImageProcessor.cast(image.id)
|
||||||
Images.reindex_image(image)
|
Images.reindex_image(image)
|
||||||
|
Tags.reindex_tags(image.added_tags)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "Image created successfully.")
|
|> put_flash(:info, "Image created successfully.")
|
||||||
|
|
|
@ -30,7 +30,7 @@ defmodule PhilomenaWeb.Endpoint do
|
||||||
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
|
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
|
||||||
|
|
||||||
plug Plug.Parsers,
|
plug Plug.Parsers,
|
||||||
parsers: [:urlencoded, :multipart, :json],
|
parsers: [:urlencoded, {:multipart, length: 30_000_000}, :json],
|
||||||
pass: ["*/*"],
|
pass: ["*/*"],
|
||||||
json_decoder: Phoenix.json_library()
|
json_decoder: Phoenix.json_library()
|
||||||
|
|
||||||
|
|
|
@ -13,16 +13,19 @@ div
|
||||||
/ | because:
|
/ | because:
|
||||||
/ =<> comment.edit_reason
|
/ =<> comment.edit_reason
|
||||||
div
|
div
|
||||||
- link_path = "/images/#{@comment.image.id}#comment_#{@comment.id}"
|
- link_path = Routes.image_path(@conn, :show, @comment.image) <> "#comment_#{@comment.id}"
|
||||||
|
- safe_author = PhilomenaWeb.PostView.textile_safe_author(@comment)
|
||||||
|
- quote_body = if @comment.hidden_from_users, do: "", else: @comment.body
|
||||||
|
|
||||||
a.communication__interaction title="Link to comment" href=link_path
|
a.communication__interaction title="Link to comment" href=link_path
|
||||||
i.fa.fa-link>
|
i.fa.fa-link>
|
||||||
' Link
|
' Link
|
||||||
/=<> link_to link_path, 'data-author': safe_author(comment), 'data-reply-url': link_path, 'data-post': (comment.hidden_from_users ? '' : comment.body), class: 'communication__interaction post-reply post-reply-quote' do
|
|
||||||
a.communication__interaction.post-reply.post-reply-quote href=link_path data-reply-url=link_path data-author="" data-post=""
|
a.communication__interaction.post-reply.post-reply-quote href=link_path data-reply-url=link_path data-author=safe_author data-post=quote_body
|
||||||
i.fa.fa-quote-right>
|
i.fa.fa-quote-right>
|
||||||
' Quote
|
' Quote
|
||||||
/=<> link_to link_path, 'data-author': safe_author(comment), 'data-reply-url': link_path, class: 'communication__interaction post-reply' do
|
|
||||||
a.communication__interaction.post-reply href=link_path data-reply-url=link_path data-author=""
|
a.communication__interaction.post-reply href=link_path data-reply-url=link_path data-author=safe_author
|
||||||
i.fa.fa-reply
|
i.fa.fa-reply
|
||||||
' Reply
|
' Reply
|
||||||
/span.owner-options.hidden
|
/span.owner-options.hidden
|
||||||
|
|
|
@ -22,4 +22,6 @@
|
||||||
' [Loading preview...]
|
' [Loading preview...]
|
||||||
|
|
||||||
.block__content.communication-edit__actions
|
.block__content.communication-edit__actions
|
||||||
= submit "Post", class: "button"
|
=> submit "Post", class: "button"
|
||||||
|
= checkbox f, :anonymous
|
||||||
|
= label f, :anonymous, "Anonymous"
|
|
@ -14,15 +14,18 @@ div
|
||||||
/ =<> post.edit_reason
|
/ =<> post.edit_reason
|
||||||
div
|
div
|
||||||
- link_path = Routes.forum_topic_path(@conn, :show, @post.topic.forum, @post.topic, post_id: @post.id) <> "#post_#{@post.id}"
|
- link_path = Routes.forum_topic_path(@conn, :show, @post.topic.forum, @post.topic, post_id: @post.id) <> "#post_#{@post.id}"
|
||||||
|
- safe_author = textile_safe_author(@post)
|
||||||
|
- quote_body = if @post.hidden_from_users, do: "", else: @post.body
|
||||||
|
|
||||||
a.communication__interaction title="Link to post" href=link_path
|
a.communication__interaction title="Link to post" href=link_path
|
||||||
i.fa.fa-link>
|
i.fa.fa-link>
|
||||||
' Link
|
' Link
|
||||||
/=<> link_to link_path, 'data-author': safe_author(post), 'data-reply-url': link_path, 'data-post': (post.hidden_from_users ? '' : post.body), class: 'communication__interaction post-reply post-reply-quote' do
|
|
||||||
a.communication__interaction.post-reply.post-reply-quote href=link_path data-reply-url=link_path data-author="" data-post=""
|
a.communication__interaction.post-reply.post-reply-quote href=link_path data-reply-url=link_path data-author=safe_author data-post=quote_body
|
||||||
i.fa.fa-quote-right>
|
i.fa.fa-quote-right>
|
||||||
' Quote
|
' Quote
|
||||||
/=<> link_to link_path, 'data-author': safe_author(post), 'data-reply-url': link_path, class: 'communication__interaction post-reply' do
|
|
||||||
a.communication__interaction.post-reply href=link_path data-reply-url=link_path data-author=""
|
a.communication__interaction.post-reply href=link_path data-reply-url=link_path data-author=safe_author
|
||||||
i.fa.fa-reply
|
i.fa.fa-reply
|
||||||
' Reply
|
' Reply
|
||||||
/span.owner-options.hidden
|
/span.owner-options.hidden
|
||||||
|
|
|
@ -5,5 +5,4 @@
|
||||||
= render PhilomenaWeb.ProfileView, "_awards.html", awards: @object.user.awards
|
= render PhilomenaWeb.ProfileView, "_awards.html", awards: @object.user.awards
|
||||||
- else
|
- else
|
||||||
strong<>
|
strong<>
|
||||||
| Background Pony #
|
|
||||||
= anonymous_name(@object)
|
= anonymous_name(@object)
|
|
@ -1,3 +1,31 @@
|
||||||
defmodule PhilomenaWeb.PostView do
|
defmodule PhilomenaWeb.PostView do
|
||||||
|
alias Philomena.Attribution
|
||||||
|
alias Textile.Parser
|
||||||
|
|
||||||
use PhilomenaWeb, :view
|
use PhilomenaWeb, :view
|
||||||
|
|
||||||
|
def textile_safe_author(object) do
|
||||||
|
author_name = author_name(object)
|
||||||
|
|
||||||
|
Parser.parse(%Parser{image_transform: & &1}, author_name)
|
||||||
|
|> case do
|
||||||
|
[{:text, ^author_name}] ->
|
||||||
|
author_name
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
# Cover *all* possibilities.
|
||||||
|
literal =
|
||||||
|
author_name
|
||||||
|
|> String.replace("==]", "==]==][==")
|
||||||
|
|
||||||
|
"[==#{literal}==]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp author_name(object) do
|
||||||
|
case Attribution.anonymous?(object) do
|
||||||
|
true -> PhilomenaWeb.UserAttributionView.anonymous_name(object)
|
||||||
|
false -> object.user.name
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,8 +12,11 @@ defmodule PhilomenaWeb.UserAttributionView do
|
||||||
id = Attribution.object_identifier(object)
|
id = Attribution.object_identifier(object)
|
||||||
user_id = Attribution.best_user_identifier(object)
|
user_id = Attribution.best_user_identifier(object)
|
||||||
|
|
||||||
(:erlang.crc32(salt <> id <> user_id) &&& 0xffff)
|
hash =
|
||||||
|> Integer.to_string(16)
|
(:erlang.crc32(salt <> id <> user_id) &&& 0xffff)
|
||||||
|
|> Integer.to_string(16)
|
||||||
|
|
||||||
|
"Background Pony ##{hash}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def anonymous_avatar(_object, class \\ "avatar--100px") do
|
def anonymous_avatar(_object, class \\ "avatar--100px") do
|
||||||
|
|
1
mix.lock
1
mix.lock
|
@ -44,6 +44,7 @@
|
||||||
"plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
"plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"plug_cowboy": {:hex, :plug_cowboy, "2.1.0", "b75768153c3a8a9e8039d4b25bb9b14efbc58e9c4a6e6a270abff1cd30cbe320", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
||||||
|
"porcelain": {:git, "https://github.com/walkr/porcelain.git", "4a497495beb8cab7af0d47ae50f720d31a13f039", [ref: "4a49749"]},
|
||||||
"postgrex": {:hex, :postgrex, "0.15.1", "23ce3417de70f4c0e9e7419ad85bdabcc6860a6925fe2c6f3b1b5b1e8e47bf2f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
"postgrex": {:hex, :postgrex, "0.15.1", "23ce3417de70f4c0e9e7419ad85bdabcc6860a6925fe2c6f3b1b5b1e8e47bf2f", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"pot": {:hex, :pot, "0.10.1", "af7dc220fd45478719b821fb4c1222975132516478483213507f95026298d8ab", [:rebar3], [], "hexpm"},
|
"pot": {:hex, :pot, "0.10.1", "af7dc220fd45478719b821fb4c1222975132516478483213507f95026298d8ab", [:rebar3], [], "hexpm"},
|
||||||
"pow": {:hex, :pow, "1.0.15", "9267b5c75df2d59968585c042e2a0ec6217b1959d3afd629817461f0a20e903c", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3.0 or ~> 1.4.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 2.0.0 and <= 3.0.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, ">= 1.5.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
|
"pow": {:hex, :pow, "1.0.15", "9267b5c75df2d59968585c042e2a0ec6217b1959d3afd629817461f0a20e903c", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3.0 or ~> 1.4.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 2.0.0 and <= 3.0.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, ">= 1.5.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
|
|
|
@ -69,6 +69,16 @@ if [ ! -f /usr/local/bin/image-intensities ]; then
|
||||||
popd
|
popd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /usr/local/bin/safe-rsvg-convert ]; then
|
||||||
|
# passing input on stdin prevents the loading of any
|
||||||
|
# external resources
|
||||||
|
echo '
|
||||||
|
#!/bin/sh
|
||||||
|
rsvg-convert < "$1" -o "$2"
|
||||||
|
' > /usr/local/bin/safe-rsvg-convert
|
||||||
|
chmod +x /usr/local/bin/safe-rsvg-convert
|
||||||
|
fi
|
||||||
|
|
||||||
sed -i -e 's/\(-Xm[sx]\)1g/\1256m/' /etc/elasticsearch/jvm.options
|
sed -i -e 's/\(-Xm[sx]\)1g/\1256m/' /etc/elasticsearch/jvm.options
|
||||||
systemctl enable elasticsearch 2>/dev/null
|
systemctl enable elasticsearch 2>/dev/null
|
||||||
service elasticsearch start
|
service elasticsearch start
|
||||||
|
|
Loading…
Reference in a new issue