about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS.md666
-rw-r--r--CHANGELOG.md33
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock33
-rw-r--r--app/controllers/api/v1/instances/activity_controller.rb1
-rw-r--r--app/controllers/api/v1/instances/peers_controller.rb1
-rw-r--r--app/controllers/api/v1/instances_controller.rb1
-rw-r--r--app/controllers/api/v1/streaming_controller.rb14
-rw-r--r--app/controllers/api/v1/timelines/home_controller.rb6
-rw-r--r--app/javascript/mastodon/actions/timelines.js2
-rw-r--r--app/javascript/mastodon/components/extended_video_player.js63
-rw-r--r--app/javascript/mastodon/components/gifv.js75
-rw-r--r--app/javascript/mastodon/components/missing_indicator.js23
-rw-r--r--app/javascript/mastodon/components/regeneration_indicator.js18
-rw-r--r--app/javascript/mastodon/components/status_content.js8
-rw-r--r--app/javascript/mastodon/components/status_list.js15
-rw-r--r--app/javascript/mastodon/features/account_timeline/index.js1
-rw-r--r--app/javascript/mastodon/features/generic_not_found/index.js2
-rw-r--r--app/javascript/mastodon/features/ui/components/focal_point_modal.js36
-rw-r--r--app/javascript/mastodon/features/ui/components/media_modal.js6
-rw-r--r--app/javascript/styles/mastodon/components.scss33
-rw-r--r--app/javascript/styles/mastodon/introduction.scss3
-rw-r--r--app/lib/feed_manager.rb106
-rw-r--r--app/lib/spam_check.rb7
-rw-r--r--app/models/account.rb9
-rw-r--r--app/models/admin/account_action.rb12
-rw-r--r--app/models/concerns/remotable.rb2
-rw-r--r--app/models/form/admin_settings.rb2
-rw-r--r--app/models/home_feed.rb16
-rw-r--r--app/models/media_attachment.rb3
-rw-r--r--app/models/status.rb4
-rw-r--r--app/models/tag.rb3
-rw-r--r--app/models/trending_tags.rb2
-rw-r--r--app/services/hashtag_query_service.rb4
-rw-r--r--app/views/about/show.html.haml13
-rw-r--r--app/views/admin/custom_emojis/_custom_emoji.html.haml4
-rw-r--r--app/views/admin/settings/edit.html.haml9
-rw-r--r--app/views/application/_sidebar.html.haml2
-rw-r--r--app/views/shared/_og.html.haml2
-rw-r--r--app/views/statuses/_detailed_status.html.haml2
-rw-r--r--app/views/statuses/_simple_status.html.haml2
-rw-r--r--config/deploy.rb2
-rw-r--r--config/initializers/json_ld.rb1
-rw-r--r--config/initializers/paperclip.rb20
-rw-r--r--config/locales/en.yml16
-rw-r--r--config/settings.yml1
-rw-r--r--db/migrate/20181024224956_migrate_account_conversations.rb2
-rw-r--r--db/migrate/20191007013357_update_pt_locales.rb11
-rw-r--r--db/schema.rb26
-rw-r--r--lib/json_ld/identity.rb87
-rw-r--r--lib/mastodon/accounts_cli.rb1
-rw-r--r--lib/mastodon/cli_helper.rb38
-rw-r--r--lib/mastodon/feeds_cli.rb1
-rw-r--r--lib/mastodon/media_cli.rb18
-rw-r--r--lib/mastodon/version.rb2
-rw-r--r--package.json4
-rw-r--r--spec/lib/spam_check_spec.rb4
-rw-r--r--spec/models/account_spec.rb4
-rw-r--r--spec/models/concerns/remotable_spec.rb13
-rw-r--r--spec/models/home_feed_spec.rb5
-rw-r--r--spec/models/status_spec.rb43
-rw-r--r--yarn.lock52
62 files changed, 837 insertions, 762 deletions
diff --git a/AUTHORS.md b/AUTHORS.md
index 8d3aaf480..5f5985fba 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -5,89 +5,100 @@ Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
 and provided thanks to the work of the following contributors:
 
 * [Gargron](https://github.com/Gargron)
-* [ykzts](https://github.com/ykzts)
 * [ThibG](https://github.com/ThibG)
+* [ykzts](https://github.com/ykzts)
+* [dependabot[bot]](https://github.com/apps/dependabot)
 * [akihikodaki](https://github.com/akihikodaki)
+* [dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
 * [mjankowski](https://github.com/mjankowski)
-* [dependabot[bot]](https://github.com/apps/dependabot)
 * [unarist](https://github.com/unarist)
-* [m4sk1n](https://github.com/m4sk1n)
 * [yiskah](https://github.com/yiskah)
 * [nolanlawson](https://github.com/nolanlawson)
 * [ysksn](https://github.com/ysksn)
-* [sorin-davidoi](https://github.com/sorin-davidoi)
 * [abcang](https://github.com/abcang)
+* [sorin-davidoi](https://github.com/sorin-davidoi)
 * [lynlynlynx](https://github.com/lynlynlynx)
 * [mayaeh](https://github.com/mayaeh)
+* [m4sk1n](mailto:me@m4sk.in)
+* [Marcin Mikołajczak](mailto:me@m4sk.in)
+* [Kjwon15](https://github.com/Kjwon15)
 * [renatolond](https://github.com/renatolond)
 * [alpaca-tc](https://github.com/alpaca-tc)
+* [jeroenpraat](https://github.com/jeroenpraat)
 * [nclm](https://github.com/nclm)
 * [ineffyble](https://github.com/ineffyble)
-* [jeroenpraat](https://github.com/jeroenpraat)
+* [mabkenar](https://github.com/mabkenar)
 * [blackle](https://github.com/blackle)
 * [Quent-in](https://github.com/Quent-in)
 * [JantsoP](https://github.com/JantsoP)
-* [Kjwon15](https://github.com/Kjwon15)
-* [mabkenar](https://github.com/mabkenar)
+* [zunda](https://github.com/zunda)
 * [nullkal](https://github.com/nullkal)
 * [yookoala](https://github.com/yookoala)
+* [Aditoo17](https://github.com/Aditoo17)
+* [Quenty31](https://github.com/Quenty31)
+* [marek-lach](https://github.com/marek-lach)
 * [shuheiktgw](https://github.com/shuheiktgw)
 * [ashfurrow](https://github.com/ashfurrow)
-* [zunda](https://github.com/zunda)
-* [Quenty31](https://github.com/Quenty31)
 * [eramdam](https://github.com/eramdam)
+* [noellabo](https://github.com/noellabo)
 * [takayamaki](https://github.com/takayamaki)
+* [danhunsaker](https://github.com/danhunsaker)
 * [masarakki](https://github.com/masarakki)
 * [ticky](https://github.com/ticky)
-* [danhunsaker](https://github.com/danhunsaker)
 * [ThisIsMissEm](https://github.com/ThisIsMissEm)
 * [hcmiya](https://github.com/hcmiya)
 * [stephenburgess8](https://github.com/stephenburgess8)
 * [Wonderfall](https://github.com/Wonderfall)
 * [matteoaquila](https://github.com/matteoaquila)
 * [yukimochi](https://github.com/yukimochi)
+* [palindromordnilap](https://github.com/palindromordnilap)
 * [rkarabut](https://github.com/rkarabut)
 * [Artoria2e5](https://github.com/Artoria2e5)
 * [nightpool](https://github.com/nightpool)
 * [marrus-sh](https://github.com/marrus-sh)
+* [hinaloe](https://github.com/hinaloe)
 * [krainboltgreene](https://github.com/krainboltgreene)
 * [pfigel](https://github.com/pfigel)
 * [Aldarone](https://github.com/Aldarone)
 * [BoFFire](https://github.com/BoFFire)
 * [clworld](https://github.com/clworld)
+* [MasterGroosha](https://github.com/MasterGroosha)
 * [dracos](https://github.com/dracos)
+* [MaciekBaron](https://github.com/MaciekBaron)
 * [SerCom_KC](mailto:sercom-kc@users.noreply.github.com)
 * [Sylvhem](https://github.com/Sylvhem)
-* [MasterGroosha](https://github.com/MasterGroosha)
+* [MitarashiDango](https://github.com/MitarashiDango)
 * [JeanGauthier](https://github.com/JeanGauthier)
 * [kschaper](https://github.com/kschaper)
-* [MaciekBaron](https://github.com/MaciekBaron)
-* [MitarashiDango](mailto:mitarashidango@users.noreply.github.com)
 * [beatrix-bitrot](https://github.com/beatrix-bitrot)
-* [Aditoo17](https://github.com/Aditoo17)
+* [angristan](https://github.com/angristan)
 * [adbelle](https://github.com/adbelle)
 * [evanminto](https://github.com/evanminto)
 * [MightyPork](https://github.com/MightyPork)
+* [ashleyhull-versent](mailto:ashley.hull@versent.com.au)
 * [yhirano55](https://github.com/yhirano55)
 * [rinsuki](https://github.com/rinsuki)
 * [camponez](https://github.com/camponez)
-* [hinaloe](https://github.com/hinaloe)
-* [SerCom-KC](https://github.com/SerCom-KC)
+* [SerCom_KC](mailto:szescxz@gmail.com)
 * [aschmitz](https://github.com/aschmitz)
+* [trwnh](https://github.com/trwnh)
 * [devkral](https://github.com/devkral)
 * [fpiesche](https://github.com/fpiesche)
+* [hugogameiro](https://github.com/hugogameiro)
 * [gandaro](https://github.com/gandaro)
 * [johnsudaar](https://github.com/johnsudaar)
+* [ariasuni](https://github.com/ariasuni)
 * [trebmuh](https://github.com/trebmuh)
-* [Rakib Hasan](mailto:rmhasan@gmail.com)
-* [ashleyhull-versent](https://github.com/ashleyhull-versent)
+* [rmhasan](https://github.com/rmhasan)
+* [kedamaDQ](https://github.com/kedamaDQ)
 * [lindwurm](https://github.com/lindwurm)
 * [victorhck](mailto:victorhck@geeko.site)
 * [voidsatisfaction](https://github.com/voidsatisfaction)
+* [BenLubar](https://github.com/BenLubar)
 * [hikari-no-yume](https://github.com/hikari-no-yume)
-* [angristan](https://github.com/angristan)
 * [seefood](https://github.com/seefood)
 * [jackjennings](https://github.com/jackjennings)
+* [koyuawsmbrtn](https://github.com/koyuawsmbrtn)
 * [spla](mailto:spla@mastodont.cat)
 * [expenses](https://github.com/expenses)
 * [walf443](https://github.com/walf443)
@@ -95,18 +106,17 @@ and provided thanks to the work of the following contributors:
 * [mistydemeo](https://github.com/mistydemeo)
 * [dunn](https://github.com/dunn)
 * [xqus](https://github.com/xqus)
-* [hugogameiro](https://github.com/hugogameiro)
-* [ariasuni](https://github.com/ariasuni)
 * [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
 * [fakenine](https://github.com/fakenine)
+* [Shleeble](https://github.com/Shleeble)
 * [tsuwatch](https://github.com/tsuwatch)
 * [victorhck](https://github.com/victorhck)
-* [kedamaDQ](https://github.com/kedamaDQ)
+* [mkljczk](https://github.com/mkljczk)
+* [manuelviens](https://github.com/manuelviens)
 * [puckipedia](https://github.com/puckipedia)
-* [trwnh](https://github.com/trwnh)
 * [fvh-P](https://github.com/fvh-P)
+* [rtucker](https://github.com/rtucker)
 * [Anna e só](mailto:contraexemplos@gmail.com)
-* [BenLubar](https://github.com/BenLubar)
 * [kazu9su](https://github.com/kazu9su)
 * [Komic](https://github.com/Komic)
 * [lmorchard](https://github.com/lmorchard)
@@ -119,6 +129,7 @@ and provided thanks to the work of the following contributors:
 * [goofy-bz](mailto:goofy@babelzilla.org)
 * [kadiix](https://github.com/kadiix)
 * [kodacs](https://github.com/kodacs)
+* [marcin mikołajczak](mailto:me@m4sk.in)
 * [JMendyk](https://github.com/JMendyk)
 * [KScl](https://github.com/KScl)
 * [sterdev](https://github.com/sterdev)
@@ -129,30 +140,31 @@ and provided thanks to the work of the following contributors:
 * [northerner](https://github.com/northerner)
 * [fhemberger](https://github.com/fhemberger)
 * [greysteil](https://github.com/greysteil)
-* [hensmith](https://github.com/hensmith)
+* [hencatsmith](https://github.com/hencatsmith)
 * [d6rkaiz](https://github.com/d6rkaiz)
 * [Reverite](https://github.com/Reverite)
 * [JohnD28](https://github.com/JohnD28)
 * [znz](https://github.com/znz)
-* [marek-lach](https://github.com/marek-lach)
 * [Naouak](https://github.com/Naouak)
 * [pawelngei](https://github.com/pawelngei)
-* [rtucker](https://github.com/rtucker)
 * [reneklacan](https://github.com/reneklacan)
 * [ekiru](https://github.com/ekiru)
-* [noellabo](https://github.com/noellabo)
 * [tcitworld](https://github.com/tcitworld)
 * [geta6](https://github.com/geta6)
 * [happycoloredbanana](https://github.com/happycoloredbanana)
 * [leopku](https://github.com/leopku)
 * [SansPseudoFix](https://github.com/SansPseudoFix)
+* [salvadorpla](https://github.com/salvadorpla)
 * [tomfhowe](https://github.com/tomfhowe)
 * [noraworld](https://github.com/noraworld)
 * [theboss](https://github.com/theboss)
+* [nzws](https://github.com/nzws)
 * [178inaba](https://github.com/178inaba)
+* [xgess](https://github.com/xgess)
 * [alyssais](https://github.com/alyssais)
-* [hiphref](https://github.com/hiphref)
+* [aablinov](https://github.com/aablinov)
 * [stalker314314](https://github.com/stalker314314)
+* [cutls](https://github.com/cutls)
 * [huertanix](https://github.com/huertanix)
 * [genesixx](https://github.com/genesixx)
 * [halkeye](https://github.com/halkeye)
@@ -162,21 +174,24 @@ and provided thanks to the work of the following contributors:
 * [kmichl](https://github.com/kmichl)
 * [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
 * [saper](https://github.com/saper)
+* [Dar13](https://github.com/Dar13)
 * [nevillepark](https://github.com/nevillepark)
 * [ornithocoder](https://github.com/ornithocoder)
+* [pwoolcoc](https://github.com/pwoolcoc)
 * [pierreozoux](https://github.com/pierreozoux)
 * [qguv](https://github.com/qguv)
 * [Ram Lmn](mailto:ramlmn@users.noreply.github.com)
-* [sascha-sl](https://github.com/sascha-sl)
+* [aurelia-sl](https://github.com/aurelia-sl)
 * [harukasan](https://github.com/harukasan)
 * [stamak](https://github.com/stamak)
-* [Technowix](mailto:technowix@users.noreply.github.com)
+* [Technowix](https://github.com/Technowix)
 * [Zoeille](https://github.com/Zoeille)
 * [Thor Harald Johansen](mailto:thj@thj.no)
 * [0x70b1a5](https://github.com/0x70b1a5)
 * [gled-rs](https://github.com/gled-rs)
 * [Valentin_NC](mailto:valentin.ouvrard@nautile.sarl)
 * [R0ckweb](https://github.com/R0ckweb)
+* [unasuke](https://github.com/unasuke)
 * [caasi](https://github.com/caasi)
 * [chr-1x](https://github.com/chr-1x)
 * [esetomo](https://github.com/esetomo)
@@ -184,8 +199,9 @@ and provided thanks to the work of the following contributors:
 * [hoodie](mailto:hoodiekitten@outlook.com)
 * [luzi82](https://github.com/luzi82)
 * [duxovni](https://github.com/duxovni)
+* [slice](https://github.com/slice)
 * [tmm576](https://github.com/tmm576)
-* [unsmell](https://github.com/unsmell)
+* [unsmell](mailto:unsmell@users.noreply.github.com)
 * [valerauko](https://github.com/valerauko)
 * [chriswmartin](https://github.com/chriswmartin)
 * [vahnj](https://github.com/vahnj)
@@ -193,21 +209,25 @@ and provided thanks to the work of the following contributors:
 * [AndreLewin](https://github.com/AndreLewin)
 * [0xflotus](https://github.com/0xflotus)
 * [redtachyons](https://github.com/redtachyons)
+* [acid-chicken](https://github.com/acid-chicken)
 * [thurloat](https://github.com/thurloat)
 * [aaribaud](https://github.com/aaribaud)
 * [pointlessone](https://github.com/pointlessone)
 * [Andrew](mailto:andrewlchronister@gmail.com)
+* [aurelien-reeves](https://github.com/aurelien-reeves)
+* [AnaGelez](https://github.com/AnaGelez)
 * [estuans](https://github.com/estuans)
 * [dissolve](https://github.com/dissolve)
 * [PurpleBooth](https://github.com/PurpleBooth)
 * [bradurani](https://github.com/bradurani)
 * [wavebeem](https://github.com/wavebeem)
 * [bruwalfas](https://github.com/bruwalfas)
-* [foxsan48](https://github.com/foxsan48)
+* [LottieVixen](https://github.com/LottieVixen)
 * [wchristian](https://github.com/wchristian)
 * [muffinista](https://github.com/muffinista)
 * [cdutson](https://github.com/cdutson)
 * [farlistener](https://github.com/farlistener)
+* [dariusk](https://github.com/dariusk)
 * [DavidLibeau](https://github.com/DavidLibeau)
 * [ddevault](https://github.com/ddevault)
 * [Fjoerfoks](https://github.com/Fjoerfoks)
@@ -216,6 +236,7 @@ and provided thanks to the work of the following contributors:
 * [Gomasy](https://github.com/Gomasy)
 * [unstabler](https://github.com/unstabler)
 * [potato4d](https://github.com/potato4d)
+* [Hanage999](https://github.com/Hanage999)
 * [h-izumi](https://github.com/h-izumi)
 * [ErikXXon](https://github.com/ErikXXon)
 * [ian-kelling](https://github.com/ian-kelling)
@@ -231,21 +252,23 @@ and provided thanks to the work of the following contributors:
 * [Kaylee](mailto:kaylee@codethat.sucks)
 * [Kazhnuz](https://github.com/Kazhnuz)
 * [connyduck](https://github.com/connyduck)
-* [Lindsey Bieda](mailto:lindseyb@users.noreply.github.com)
+* [LindseyB](https://github.com/LindseyB)
 * [Lorenz Diener](mailto:halcyon@icosahedron.website)
 * [alimony](https://github.com/alimony)
 * [mig5](https://github.com/mig5)
 * [moritzheiber](https://github.com/moritzheiber)
 * [ndarville](https://github.com/ndarville)
 * [Abzol](https://github.com/Abzol)
-* [pwoolcoc](https://github.com/pwoolcoc)
+* [PatOnTheBack](https://github.com/PatOnTheBack)
 * [xPaw](https://github.com/xPaw)
 * [petzah](https://github.com/petzah)
 * [ignisf](https://github.com/ignisf)
 * [raymestalez](https://github.com/raymestalez)
 * [remram44](https://github.com/remram44)
 * [sts10](https://github.com/sts10)
+* [SuperSandro2000](https://github.com/SuperSandro2000)
 * [u1-liquid](https://github.com/u1-liquid)
+* [rosylilly](https://github.com/rosylilly)
 * [sim6](https://github.com/sim6)
 * [Sir-Boops](https://github.com/Sir-Boops)
 * [stemid](https://github.com/stemid)
@@ -270,6 +293,7 @@ and provided thanks to the work of the following contributors:
 * [cpsdqs](https://github.com/cpsdqs)
 * [barzamin](https://github.com/barzamin)
 * [fhalna](https://github.com/fhalna)
+* [highemerly](https://github.com/highemerly)
 * [haoyayoi](https://github.com/haoyayoi)
 * [ik11235](https://github.com/ik11235)
 * [kawax](https://github.com/kawax)
@@ -279,6 +303,7 @@ and provided thanks to the work of the following contributors:
 * [mecab](https://github.com/mecab)
 * [nicobz25](https://github.com/nicobz25)
 * [oliverkeeble](https://github.com/oliverkeeble)
+* [partev](https://github.com/partev)
 * [pinfort](https://github.com/pinfort)
 * [rbaumert](https://github.com/rbaumert)
 * [rhoio](https://github.com/rhoio)
@@ -287,19 +312,17 @@ and provided thanks to the work of the following contributors:
 * [vjackson725](https://github.com/vjackson725)
 * [wxcafe](https://github.com/wxcafe)
 * [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
+* [clarfon](https://github.com/clarfon)
 * [cygnan](https://github.com/cygnan)
 * [Awea](https://github.com/Awea)
 * [halcy](https://github.com/halcy)
-* [naaaaaaaaaaaf](https://github.com/naaaaaaaaaaaf)
 * [8398a7](https://github.com/8398a7)
 * [857b](https://github.com/857b)
 * [insom](https://github.com/insom)
 * [tachyons](https://github.com/tachyons)
-* [acid-chicken](https://github.com/acid-chicken)
 * [Esteth](https://github.com/Esteth)
 * [unascribed](https://github.com/unascribed)
 * [Aguay-val](https://github.com/Aguay-val)
-* [Akihiko Odaki](mailto:nekomanma@pixiv.co.jp)
 * [knu](https://github.com/knu)
 * [h3poteto](https://github.com/h3poteto)
 * [unleashed](https://github.com/unleashed)
@@ -307,8 +330,8 @@ and provided thanks to the work of the following contributors:
 * [console-cowboy](https://github.com/console-cowboy)
 * [Alkarex](https://github.com/Alkarex)
 * [a2](https://github.com/a2)
+* [alfiedotwtf](https://github.com/alfiedotwtf)
 * [0xa](https://github.com/0xa)
-* [palindromordnilap](https://github.com/palindromordnilap)
 * [virtualpain](https://github.com/virtualpain)
 * [sapphirus](https://github.com/sapphirus)
 * [amandavisconti](https://github.com/amandavisconti)
@@ -320,10 +343,9 @@ and provided thanks to the work of the following contributors:
 * [contraexemplo](https://github.com/contraexemplo)
 * [abackstrom](https://github.com/abackstrom)
 * [armandfardeau](https://github.com/armandfardeau)
+* [raboof](https://github.com/raboof)
 * [jumbosushi](https://github.com/jumbosushi)
-* [aurelien-reeves](https://github.com/aurelien-reeves)
 * [ayumin](https://github.com/ayumin)
-* [BaptisteGelez](https://github.com/BaptisteGelez)
 * [bzg](https://github.com/bzg)
 * [benediktg](https://github.com/benediktg)
 * [blakebarnett](https://github.com/blakebarnett)
@@ -337,15 +359,15 @@ and provided thanks to the work of the following contributors:
 * [DoubleMalt](https://github.com/DoubleMalt)
 * [Moosh-be](https://github.com/Moosh-be)
 * [Motoma](https://github.com/Motoma)
-* [chriswk](https://github.com/chriswk)
+* [Christopher Kolstad](mailto:christopher.kolstad@finn.no)
 * [csu](https://github.com/csu)
-* [clarfon](https://github.com/clarfon)
 * [kklleemm](https://github.com/kklleemm)
 * [colindean](https://github.com/colindean)
 * [dachinat](https://github.com/dachinat)
 * [multiple-creatures](https://github.com/multiple-creatures)
 * [watilde](https://github.com/watilde)
 * [daprice](https://github.com/daprice)
+* [da2x](https://github.com/da2x)
 * [dar5hak](https://github.com/dar5hak)
 * [kant](https://github.com/kant)
 * [maxolasersquad](https://github.com/maxolasersquad)
@@ -354,7 +376,7 @@ and provided thanks to the work of the following contributors:
 * [davefp](https://github.com/davefp)
 * [yipdw](https://github.com/yipdw)
 * [debanshuk](https://github.com/debanshuk)
-* [Derek Lewis](mailto:derekcecillewis@gmail.com)
+* [DerekNonGeneric](https://github.com/DerekNonGeneric)
 * [dblandin](https://github.com/dblandin)
 * [Drew Gates](mailto:aranaur@users.noreply.github.com)
 * [dtschust](https://github.com/dtschust)
@@ -366,11 +388,13 @@ and provided thanks to the work of the following contributors:
 * [ericblade](https://github.com/ericblade)
 * [mikoim](https://github.com/mikoim)
 * [espenronnevik](https://github.com/espenronnevik)
+* [fabianonline](https://github.com/fabianonline)
 * [Finariel](https://github.com/Finariel)
 * [siuying](https://github.com/siuying)
 * [zoc](https://github.com/zoc)
 * [fwenzel](https://github.com/fwenzel)
 * [GenbuHase](https://github.com/GenbuHase)
+* [nilsding](https://github.com/nilsding)
 * [hattori6789](https://github.com/hattori6789)
 * [algernon](https://github.com/algernon)
 * [Fastbyte01](https://github.com/Fastbyte01)
@@ -386,17 +410,19 @@ and provided thanks to the work of the following contributors:
 * [suzukaze](https://github.com/suzukaze)
 * [Hiromi-Kai](https://github.com/Hiromi-Kai)
 * [hishamhm](https://github.com/hishamhm)
+* [Slaynash](https://github.com/Slaynash)
 * [musashino205](https://github.com/musashino205)
 * [iwaim](https://github.com/iwaim)
 * [valrus](https://github.com/valrus)
 * [IMcD23](https://github.com/IMcD23)
 * [yi0713](https://github.com/yi0713)
 * [iblech](https://github.com/iblech)
-* [usbsnowcrash](https://github.com/usbsnowcrash)
+* [J Yeary](mailto:usbsnowcrash@users.noreply.github.com)
 * [jack-michaud](https://github.com/jack-michaud)
 * [Floppy](https://github.com/Floppy)
 * [loomchild](https://github.com/loomchild)
 * [jenkr55](https://github.com/jenkr55)
+* [hyenagirl64](https://github.com/hyenagirl64)
 * [press5](https://github.com/press5)
 * [TrollDecker](https://github.com/TrollDecker)
 * [jmontane](https://github.com/jmontane)
@@ -406,17 +432,17 @@ and provided thanks to the work of the following contributors:
 * [joshuap](https://github.com/joshuap)
 * [Tiwy57](https://github.com/Tiwy57)
 * [xuv](https://github.com/xuv)
-* [June Sallou](mailto:jnsll@users.noreply.github.com)
+* [Jnsll](https://github.com/Jnsll)
 * [j0k3r](https://github.com/j0k3r)
 * [KEINOS](https://github.com/KEINOS)
 * [futoase](https://github.com/futoase)
-* [Pneumaticat](https://github.com/Pneumaticat)
+* [pot8to](https://github.com/pot8to)
 * [Kit Redgrave](mailto:qwertyitis@gmail.com)
 * [Knut Erik](mailto:abjectio@users.noreply.github.com)
 * [mkody](https://github.com/mkody)
 * [k0ta0uchi](https://github.com/k0ta0uchi)
 * [KrzysiekJ](https://github.com/KrzysiekJ)
-* [leowzukw](https://github.com/leowzukw)
+* [Leo Wzukw](mailto:leowzukw@users.noreply.github.com)
 * [Tak](https://github.com/Tak)
 * [cacheflow](https://github.com/cacheflow)
 * [ldidry](https://github.com/ldidry)
@@ -424,6 +450,7 @@ and provided thanks to the work of the following contributors:
 * [lfuelling](https://github.com/lfuelling)
 * [Grabacr07](https://github.com/Grabacr07)
 * [mistermantas](https://github.com/mistermantas)
+* [MareenaKunjachan](https://github.com/MareenaKunjachan)
 * [mareklach](https://github.com/mareklach)
 * [wirehack7](https://github.com/wirehack7)
 * [martymcguire](https://github.com/martymcguire)
@@ -431,50 +458,53 @@ and provided thanks to the work of the following contributors:
 * [otsune](https://github.com/otsune)
 * [mbugowski](https://github.com/mbugowski)
 * [Mathias B](mailto:10813340+mathias-b@users.noreply.github.com)
+* [madmath03](https://github.com/madmath03)
 * [matt-auckland](https://github.com/matt-auckland)
 * [webroo](https://github.com/webroo)
-* [matthiasbeyer](https://github.com/matthiasbeyer)
-* [mattjmattj](https://github.com/mattjmattj)
-* [mtparet](https://github.com/mtparet)
-* [maximeborges](https://github.com/maximeborges)
-* [minacle](https://github.com/minacle)
-* [michaeljdeeb](https://github.com/michaeljdeeb)
-* [Themimitoof](https://github.com/Themimitoof)
-* [cyweo](https://github.com/cyweo)
+* [Matthias Beyer](mailto:mail@beyermatthias.de)
+* [Matthias Jouan](mailto:matthias.jouan@gmail.com)
+* [Matthieu Paret](mailto:matthieuparet69@gmail.com)
+* [Maxime BORGES](mailto:maxime.borges@gmail.com)
+* [Mayu Laierlence](mailto:minacle@live.com)
+* [Michael Deeb](mailto:michaeldeeb@me.com)
+* [Michael Vieira](mailto:dtox94@gmail.com)
+* [Michel](mailto:michel@cyweo.com)
 * [Midgard](mailto:m1dgard@users.noreply.github.com)
-* [mike-burns](https://github.com/mike-burns)
-* [verymilan](https://github.com/verymilan)
-* [milmazz](https://github.com/milmazz)
-* [premist](https://github.com/premist)
-* [Mnkai](https://github.com/Mnkai)
-* [mitchhentges](https://github.com/mitchhentges)
-* [mouse-reeve](https://github.com/mouse-reeve)
-* [Mozinet-fr](https://github.com/Mozinet-fr)
-* [lae](https://github.com/lae)
-* [nosada](https://github.com/nosada)
-* [Nanamachi](https://github.com/Nanamachi)
-* [orinthe](https://github.com/orinthe)
-* [NecroTechno](https://github.com/NecroTechno)
-* [Dar13](https://github.com/Dar13)
-* [ngerakines](https://github.com/ngerakines)
-* [vonneudeck](https://github.com/vonneudeck)
-* [Ninetailed](https://github.com/Ninetailed)
-* [k24](https://github.com/k24)
-* [noiob](https://github.com/noiob)
-* [kwaio](https://github.com/kwaio)
-* [norayr](https://github.com/norayr)
-* [joyeusenoelle](https://github.com/joyeusenoelle)
-* [OlivierNicole](https://github.com/OlivierNicole)
-* [noppa](https://github.com/noppa)
-* [Otakan951](https://github.com/Otakan951)
-* [fahy](https://github.com/fahy)
+* [Mike Burns](mailto:mburns@thoughtbot.com)
+* [Milan](mailto:me@petabyteboy.de)
+* [Milan*](mailto:tchncs@vivaldi.net)
+* [Milton Mazzarri](mailto:milmazz@gmail.com)
+* [Minku Lee](mailto:premist@me.com)
+* [Minori Hiraoka](mailto:mnkai@users.noreply.github.com)
+* [Mitchell Hentges](mailto:mitch9654@gmail.com)
+* [Mostafa Ahangarha](mailto:ahangarha@users.noreply.github.com)
+* [Mouse Reeve](mailto:mousereeve@riseup.net)
+* [Mozinet](mailto:mozinet-fr@users.noreply.github.com)
+* [Musee U](mailto:lae@users.noreply.github.com)
+* [NOGISAKA Sadata](mailto:ngsksdt@gmail.com)
+* [Naf](mailto:uenok.htc@gmail.com)
+* [Nanamachi](mailto:town7.haruki@gmail.com)
+* [Nathaniel Ekoniak](mailto:nekoniak@ennate.tech)
+* [NecroTechno](mailto:necrotechno@riseup.net)
+* [Nick Gerakines](mailto:nick@gerakines.net)
+* [Nicolai von Neudeck](mailto:nicolai@vonneudeck.com)
+* [Ninetailed](mailto:ninetailed@gmail.com)
+* [Nishi, Keisuke](mailto:k24@users.noreply.github.com)
+* [Noiob](mailto:noiob@users.noreply.github.com)
+* [Nope Nope](mailto:hireme@kwaio.ninja)
+* [Norayr Chilingarian](mailto:norayr@arnet.am)
+* [Noëlle Anthony](mailto:noelle.d.anthony@gmail.com)
+* [N氏](mailto:uenok.htc@gmail.com)
+* [Olivier Nicole](mailto:olivierthnicole@gmail.com)
+* [Oskari Noppa](mailto:noppa@users.noreply.github.com)
+* [Otakan](mailto:otakan951@gmail.com)
+* [Padraig Fahy](mailto:tech@padraigfahy.com)
 * [PatrickRWells](mailto:32802366+patrickrwells@users.noreply.github.com)
 * [Paul](mailto:naydex.mc+github@gmail.com)
 * [Pete Keen](mailto:pete@petekeen.net)
 * [Pierre-Morgan Gate](mailto:pgate@users.noreply.github.com)
 * [Ratmir Karabut](mailto:rkarabut@sfmodern.ru)
 * [Reto Kromer](mailto:retokromer@users.noreply.github.com)
-* [Rey Tucker](mailto:git@reytucker.us)
 * [Rob Watson](mailto:rfwatson@users.noreply.github.com)
 * [Ryan Freebern](mailto:ryan@freebern.org)
 * [Ryan Wade](mailto:ryan.wade@protonmail.com)
@@ -482,6 +512,7 @@ and provided thanks to the work of the following contributors:
 * [S.H](mailto:gamelinks007@gmail.com)
 * [Sadiq Saif](mailto:staticsafe@users.noreply.github.com)
 * [Sam Hewitt](mailto:hewittsamuel@gmail.com)
+* [Sasha Sorokin](mailto:dafri.nochiterov8@gmail.com)
 * [Satoshi KOJIMA](mailto:skoji@mac.com)
 * [ScienJus](mailto:i@scienjus.com)
 * [Scott Larkin](mailto:scott@codeclimate.com)
@@ -492,12 +523,10 @@ and provided thanks to the work of the following contributors:
 * [Shaun Gillies](mailto:me@shaungillies.net)
 * [Shin Adachi](mailto:shn@glucose.jp)
 * [Shin Kojima](mailto:shin@kojima.org)
-* [Sho Kusano](mailto:rosylilly@aduca.org)
 * [Shouko Yu](mailto:imshouko@gmail.com)
 * [Sina Mashek](mailto:sina@mashek.xyz)
 * [Soshi Kato](mailto:mail@sossii.com)
 * [Spanky](mailto:2788886+spankyworks@users.noreply.github.com)
-* [Stanislas](mailto:angristan@pm.me)
 * [StefOfficiel](mailto:pichard.stephane@free.fr)
 * [Steven Tappert](mailto:admin@dark-it.net)
 * [Svetlozar Todorov](mailto:svetlik@users.noreply.github.com)
@@ -506,6 +535,7 @@ and provided thanks to the work of the following contributors:
 * [Takayoshi Nishida](mailto:takayoshi.nishida@gmail.com)
 * [Takayuki KUSANO](mailto:github@tkusano.jp)
 * [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
+* [Tao Bror Bojlén](mailto:brortao@users.noreply.github.com)
 * [TheInventrix](mailto:theinventrix@users.noreply.github.com)
 * [Thomas Alberola](mailto:thomas@needacoffee.fr)
 * [Toby Deshane](mailto:fortyseven@users.noreply.github.com)
@@ -515,10 +545,12 @@ and provided thanks to the work of the following contributors:
 * [Treyssat-Vincent Nino](mailto:treyssatvincent@users.noreply.github.com)
 * [Udo Kramer](mailto:optik@fluffel.io)
 * [Una](mailto:una@unascribed.com)
+* [Ushitora Anqou](mailto:ushitora@anqou.net)
 * [Ushitora Anqou](mailto:ushitora_anqou@yahoo.co.jp)
 * [Valentin Lorentz](mailto:progval+git@progval.net)
 * [Vladimir Mincev](mailto:vladimir@canicinteractive.com)
 * [Waldir Pimenta](mailto:waldyrious@gmail.com)
+* [Wenceslao Páez Chávez](mailto:wcpaez@gmail.com)
 * [Wesley Ellis](mailto:tahnok@gmail.com)
 * [Wiktor](mailto:wiktor@metacode.biz)
 * [Wonderfall](mailto:wonderfall@schrodinger.io)
@@ -529,6 +561,7 @@ and provided thanks to the work of the following contributors:
 * [YaQ](mailto:i_k_o_m_a_7@yahoo.co.jp)
 * [Yanaken](mailto:yanakend@gmail.com)
 * [Yann Klis](mailto:yann.klis@gmail.com)
+* [Yağızhan](mailto:35808275+yagizhan49@users.noreply.github.com)
 * [Yeechan Lu](mailto:wz.bluesnow@gmail.com)
 * [Yusuke Abe](mailto:moonset20@gmail.com)
 * [Zachary Spector](mailto:logicaldash@gmail.com)
@@ -542,6 +575,7 @@ and provided thanks to the work of the following contributors:
 * [chrolis](mailto:chrolis@users.noreply.github.com)
 * [cormo](mailto:cormorant2+github@gmail.com)
 * [d0p1](mailto:dopi-sama@hush.com)
+* [dxwc](mailto:dxwc@users.noreply.github.com)
 * [evilny0](mailto:evilny0@moomoocamp.net)
 * [febrezo](mailto:felixbrezo@gmail.com)
 * [fsubal](mailto:fsubal@users.noreply.github.com)
@@ -550,6 +584,7 @@ and provided thanks to the work of the following contributors:
 * [gol-cha](mailto:info@mevo.xyz)
 * [hakoai](mailto:hk--76@qa2.so-net.ne.jp)
 * [haosbvnker](mailto:github@chaosbunker.com)
+* [ichi_i](mailto:51489410+ichi-i@users.noreply.github.com)
 * [isati](mailto:phil@juchnowi.cz)
 * [jacob](mailto:jacobherringtondeveloper@gmail.com)
 * [jenn kaplan](mailto:me@jkap.io)
@@ -561,7 +596,6 @@ and provided thanks to the work of the following contributors:
 * [karlyeurl](mailto:karl.yeurl@gmail.com)
 * [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
 * [kodai](mailto:shirafuta.kodai@gmail.com)
-* [koyu](mailto:me@koyu.space)
 * [kuro5hin](mailto:rusty@kuro5hin.org)
 * [luzpaz](mailto:luzpaz@users.noreply.github.com)
 * [maxypy](mailto:maxime@mpigou.fr)
@@ -573,6 +607,7 @@ and provided thanks to the work of the following contributors:
 * [muan](mailto:muan@github.com)
 * [namelessGonbai](mailto:43787036+namelessgonbai@users.noreply.github.com)
 * [neetshin](mailto:neetshin@neetsh.in)
+* [nzws](mailto:git-yuzu@svk.jp)
 * [rch850](mailto:rich850@gmail.com)
 * [roikale](mailto:roikale@users.noreply.github.com)
 * [rysiekpl](mailto:rysiek@hackerspace.pl)
@@ -585,6 +620,8 @@ and provided thanks to the work of the following contributors:
 * [tateisu](mailto:tateisu@gmail.com)
 * [tmyt](mailto:shigure@refy.net)
 * [trevDev()](mailto:trev@trevdev.ca)
+* [tsia](mailto:github@tsia.de)
+* [umonaca](mailto:53662960+umonaca@users.noreply.github.com)
 * [utam0k](mailto:k0ma@utam0k.jp)
 * [vpzomtrrfrt](mailto:vpzomtrrfrt@gmail.com)
 * [walfie](mailto:walfington@gmail.com)
@@ -593,9 +630,10 @@ and provided thanks to the work of the following contributors:
 * [yoshipc](mailto:yoooo@yoshipc.net)
 * [Özcan Zafer AYAN](mailto:ozcanzaferayan@gmail.com)
 * [ばん](mailto:detteiu0321@gmail.com)
-* [みたらしだんご](mailto:mitarashidango@users.noreply.github.com)
+* [ふるふる](mailto:frfs@users.noreply.github.com)
 * [りんすき](mailto:6533808+rinsuki@users.noreply.github.com)
 * [ヨイツの賢狼ホロ | 3rd style](mailto:horo@yoitsu.moe)
+* [唐宗勛](mailto:tangzongxun@hotmail.com)
 * [猫吸血鬼ディフリス / 猫ロキP](mailto:deflis@gmail.com)
 * [艮 鮟鱇](mailto:ushitora_anqou@yahoo.co.jp)
 * [西小倉宏信](mailto:nishiko@mindia.jp)
@@ -607,338 +645,122 @@ This document is provided for informational purposes only. Since it is only upda
 
 Following people have contributed to translation of Mastodon:
 
-- **Albanian**
-  - Besnik Bleta
-  - Aditoo
-- **Arabic**
-  - ButterflyOfFire
-  - Aditoo
-  - Amrz0
-- **Asturian**
-  - ButterflyOfFire
-  - Enol P.
-  - Aditoo
-- **Basque**
-  - Osoitz
-  - Aditoo
-  - Aitzol
-  - ButterflyOfFire
-  - Peru Iparragirre
-  - Gorka Azkarate
-- **Bengali**
-  - dxwc
-- **Bulgarian**
-  - ButterflyOfFire
-  - Aditoo
-- **Catalan**
-  - spla
-  - Aditoo
-  - ButterflyOfFire
-  - Joan Montané
-  - Jose Luis
-- **Chinese (Hong Kong)**
-  - ButterflyOfFire
-  - Luzi Leung
-  - Aditoo
-- **Chinese (Simplified)**
-  - Allen Zhong
-  - ButterflyOfFire
-  - SerCom_KC
-  - martialarts
-  - Kaitian Xie
-  - Aditoo
-  - pan93412
-- **Chinese (Traditional)**
-  - Aditoo
-  - ButterflyOfFire
-  - James58899
-  - pan93412
-  - S1ttidoe477
-  - SHA265
-  - Jeff Huang
-- **Corsican**
-  - Alix D. R.
-  - Aditoo
-  - ButterflyOfFire
-- **Croatian**
-  - ButterflyOfFire
-  - Aditoo
-- **Czech**
-  - Aditoo
-  - Marek Ľach
-  - ButterflyOfFire
-- **Danish**
-  - Einhjeriar
-  - Rasmus Sæderup
-  - Aditoo
-  - ButterflyOfFire
-- **Dutch**
-  - Albakham
-  - ButterflyOfFire
-  - jeroenpraat
-  - rscmbbng
-  - Aditoo
-  - Jelv
-- **English**
-  - ButterflyOfFire
-  - Renato "Lond" Cerqueira
-- **English (United Kingdom)**
-  - Albakham
-- **Esperanto**
-  - Aditoo
-  - ButterflyOfFire
-  - Becci Cat
-  - Jeong Arm
-  - Mélanie Chauvel
-  - Vanege
-  - Martin Bodin
-  - tuxayo/Victor Grousset
-- **Finnish**
-  - ButterflyOfFire
-  - Mikko Poussu
-  - Taru Luojola
-  - S Heija
-  - Aditoo
-  - Jonne Arjoranta
-- **French**
-  - Albakham
-  - Alix D. R.
-  - ButterflyOfFire
-  - codl
-  - Leia
-  - Alda Marteau-Hardi
-  - Mélanie Chauvel
-  - Paul Marques Mota
-  - azenet
-  - Olivier Humbert
-  - Aditoo
-  - Jonathan Chan
-  - Letiteuf55
-  - Baptiste Jonglez
-  - goofy-mdn
-  - Jean-Baptiste Holcroft
-  - Technowix
-  - Martin Bodin
-  - Théodore
-  - Thibaut Girka
-  - Franck Paul
-  - Sylvhem
-- **Galician**
-  - ButterflyOfFire
-  - Xose M.
-  - Aditoo
-  - manequim
-- **Georgian**
-  - ButterflyOfFire
-  - Aditoo
-- **German**
-  - Aditoo
-  - ButterflyOfFire
-  - Daniel
-  - averageunicorn
-  - Koyu Berteon
-  - larsreineke
-  - koyu
-  - Austin Jones
-  - lilo
-  - Benedikt Geißler
-  - ePirat
-  - Eugen Rochko
-  - Weblate Admin
-  - Patrick Figel
-- **Greek**
-  - Dimitris Maroulidis
-  - Antonis
-  - Aditoo
-  - ButterflyOfFire
-  - Konstantinos Grevenitis
-- **Hebrew**
-  - ButterflyOfFire
-  - Aditoo
-  - Ira
-  - Yaron Shahrabani
-- **Hungarian**
-  - ButterflyOfFire
-  - Adam Paszternak
-  - Aditoo
-  - Tibike Miklós
-- **Ido**
-  - ButterflyOfFire
-  - Aditoo
-- **Indonesian**
-  - afachri
-  - ButterflyOfFire
-  - Dito Kurnia Pratama
-  - Eirworks
-  - Aditoo
-  - Alfiana Sibuea
-  - se7entime
-- **Irish**
-  - Albakham
-  - Kevin Houlihan
-- **Italian**
-  - Alessandro Levati
-  - Albakham
-  - ButterflyOfFire
-  - Marcin Mikołajczak
-  - Aditoo
-  - Giuseppe Pignataro
-  - Stefano
-- **Japanese**
-  - Hinaloe
-  - 小鳥遊まりあ
-  - mayaeh
-  - osapon
-  - 森の子リスのミーコの大冒険
-  - Kumasun Morino
-  - Yamagishi Kazutoshi
-  - Aditoo
-  - ButterflyOfFire
-  - Jeong Arm
-  - unarist
-- **Kazakh**
-  - arshat
-  - Aditoo
-- **Korean**
-  - Aditoo
-  - Jeong Arm
-  - ButterflyOfFire
-  - Minori Hiraoka
-  - Yamagishi Kazutoshi
-- **Lithuanian**
-  - Sarunas Medeikis
-- **Malay**
-  - Muhammad Nur Hidayat (MNH48)
-  - Aditoo
-  - ButterflyOfFire
-- **Norwegian (old code)**
-  - ButterflyOfFire
-  - Espen Rønnevik
-  - Aditoo
-  - Tale
-- **Occitan**
-  - Aditoo
-  - ButterflyOfFire
-  - Quenti2
-  - Quentí
-  - Maxenç
-- **Persian**
-  - Masoud Abkenar
-  - Aditoo
-  - ButterflyOfFire
-- **Polish**
-  - Aditoo
-  - Albakham
-  - ButterflyOfFire
-  - Stasiek Michalski
-  - Marcin Mikołajczak
-  - Jakub Mendyk
-  - Marek Ľach
-  - krkk
-- **Portuguese**
-  - Albakham
-  - João Pinheiro
-  - manequim
-  - Aditoo
-  - ButterflyOfFire
-  - Hugo Gameiro
-- **Portuguese (Brazil)**
-  - Aditoo
-  - Albakham
-  - Anna e só
-  - Renato "Lond" Cerqueira
-  - André Andrade
-  - ButterflyOfFire
-- **Romanian**
-  - adrianbblk
-  - ButterflyOfFire
-  - Aditoo
-- **Russian**
-  - Albakham
-  - ButterflyOfFire
-  - Evgeny Petrov
-  - Aditoo
-  - Павел Гастелло
-  - Andrew Zyabin
-  - Yaron Shahrabani
-- **Serbian**
-  - Branko Kokanovic
-  - Burekz Finezt
-  - Aditoo
-  - ButterflyOfFire
-- **Serbian (latin)**
-  - ButterflyOfFire
-  - Aditoo
-- **Slovak**
-  - Aditoo
-  - ButterflyOfFire
-  - Ivan Pleva
-  - Marek Ľach
-  - Peter
-- **Slovenian**
-  - Kristijan Tkalec
-  - Aditoo
-  - ButterflyOfFire
-- **Spanish**
-  - Albakham
-  - ButterflyOfFire
-  - Carlos Mondragon
-  - Antón López
-  - Max Winkler
-  - Pablo de la Concepción Sanz
-  - Sergio Soriano
-  - Angeles Broullón
-  - Lothar Wolf
-  - Aditoo
-  - David Charte
-  - Emmanuel
-- **Swedish**
-  - ButterflyOfFire
-  - Isak Holmström
-  - Shellkr
-  - Aditoo
-  - Elias Mårtenson
-  - Stefan Midjich
-  - Tim Stahel
-  - Jonas Hultén
-- **Telugu**
-  - avndp
-  - Ranjith Tellakula
-  - Aditoo
-  - ButterflyOfFire
-  - Joseph Nuthalapati
-- **Thai**
-  - ButterflyOfFire
-  - parnikkapore
-  - Thai Localization
-  - Aditoo
-- **Turkish**
-  - Ali Demirtas
-  - ButterflyOfFire
-  - Aditoo
-- **Ukrainian**
-  - alexcleac
-  - ButterflyOfFire
-  - Aditoo
-  - Ivan Verchenko
-- **Welsh**
-  - carl morris
-  - Jaz-Michael King
-  - Owain Rhys Lewis
-  - Rhoslyn Prys
-  - Aditoo
-  - ButterflyOfFire
-  - Renato "Lond" Cerqueira
-  - Albakham
-  - Kevin Beynon
-- **Armenian**
-  - Aditoo
-  - ButterflyOfFire
-- **Latvian**
-  - Aditoo
-  - ButterflyOfFire
-  - Maigonis
-- **Tamil**
-  - Aditoo
-  - ButterflyOfFire
-  - Prasanna Venkadesh
+- Zoltán Gera (*Hungarian*)
+- Kristijan Tkalec (*Slovenian*)
+- Evert Prants (*Estonian*)
+- borys_sh (*Ukrainian*)
+- ButterflyOfFire (*Arabic; French*)
+- Osoitz (*Basque*)
+- oɹʇuʞ (*Spanish, Argentina*)
+- koyu (*German*)
+- Jeroen (*Dutch*)
+- Muha Aliss (*Turkish*)
+- 唐宗勛 (*Chinese Simplified*)
+- Jeong Arm (*Korean; Esperanto; Japanese*)
+- Oguz Ersen (*Turkish*)
+- spla (*Catalan*)
+- Ramdziana F Y (*Indonesian*)
+- Aditoo17 (*Czech*)
+- Xosé M. (*Galician*)
+- Roboron (*Spanish*)
+- Alix Rossi (*Corsican; French*)
+- Maya Minatsuki (*Japanese*)
+- Masoud Abkenar (*Persian*)
+- Thai Localization (*Thai*)
+- Marek Ľach (*Slovak; Polish*)
+- d5Ziif3K (*Ukrainian*)
+- lamnatos (*Greek*)
+- Emyn Nant Nefydd (*Welsh*)
+- Diluns (*Occitan*)
+- atarashiako (*Chinese Simplified*)
+- 101010 (*Polish*)
+- Yi-Jyun Pan (*Chinese Traditional*)
+- silkevicious (*Italian*)
+- FédiQuébec (*French*)
+- Jaz-Michael King (*Welsh*)
+- christalleras (*Norwegian Nynorsk*)
+- tykayn (*French*)
+- Alessandro Levati (*Italian*)
+- carolinagiorno (*Portuguese, Brazilian*)
+- taoxvx (*Danish*)
+- sabri (*Spanish*)
+- Sasha Sorokin (*Russian*)
+- shioko (*Chinese Simplified*)
+- Evgeny Petrov (*Russian*)
+- ariasuni (*French; Esperanto*)
+- Tiago Epifânio (*Portuguese*)
+- dxwc (*Bengali*)
+- liffon (*Swedish*)
+- Vanege (*Esperanto*)
+- Johan Schiff (*Swedish*)
+- kat (*Ukrainian; Russian*)
+- oti4500 (*Hungarian; Ukrainian*)
+- Juan José Salvador Piedra (*Spanish*)
+- diazepan (*Spanish*)
+- SHeija (*Finnish*)
+- Jack R (*Spanish*)
+- Saederup92 (*Danish*)
+- Stasiek Michalski (*Polish*)
+- Dewi (*Breton; French*)
+- cybergene (*Japanese*)
+- AW Unad (*Indonesian*)
+- Andrea Lo Iacono (*Italian*)
+- Ray (*Spanish*)
+- Unmual (*Spanish*)
+- Ryo (*Korean*)
+- juanda097 (*Spanish*)
+- Anunnakey (*Macedonian*)
+- Cutls (*Japanese*)
+- erikstl (*Esperanto*)
+- ruine (*Japanese*)
+- MadeInSteak (*Finnish*)
+- Sokratis Alichanidis (*Greek*)
+- dragnucs2 (*Arabic*)
+- frumble (*German*)
+- Rikard Linde (*Swedish*)
+- PPNplus (*Thai*)
+- arethsu (*Swedish*)
+- EPEMA YT (*German*)
+- Rhys Harrison (*Esperanto*)
+- KEINOS (*Japanese*)
+- filippodb (*Italian*)
+- JzshAC (*Chinese Simplified*)
+- Rintan1 (*Japanese*)
+- Antillion (*Spanish*)
+- hiphipvargas (*Portuguese*)
+- Ch. (*Korean*)
+- tctovsli (*Norwegian Nynorsk*)
+- vjasiegd (*Polish*)
+- SamitiMed (*Thai*)
+- umelard (*Hebrew*)
+- 硫酸鶏 (*Japanese*)
+- Adrián Lattes (*Spanish*)
+- Hinaloe (*Japanese*)
+- Renato "Lond" Cerqueira (*Portuguese, Brazilian*)
+- parnikkapore (*Thai*)
+- Marcin Mikołajczak (*Polish*)
+- 森の子リスのミーコの大冒険 (*Japanese*)
+- Marcepanek_ (*Polish*)
+- Sahak Petrosyan (*Armenian*)
+- Daniel Dimitrov (*Bulgarian*)
+- Hugh Liu (*Chinese Simplified*)
+- Rakino (*Chinese Simplified*)
+- hussama (*Portuguese, Brazilian*)
+- ThibG (*French*)
+- SnDer (*Dutch*)
+- PifyZ (*French*)
+- eichkat3r (*German*)
+- Karol Kosek (*Polish*)
+- Akarshan Biswas (*Bengali*)
+- Tradjincal (*French*)
+- Steven Tappert (*German*)
+- sergioaraujo1 (*Portuguese, Brazilian*)
+- mmokhi (*Persian*)
+- fedot (*Russian*)
+- skaaarrr (*German*)
+- JackXu (*Chinese Simplified*)
+- Lukas Fülling (*German*)
+- Zoé Bőle (*German*)
+- Dremski (*Bulgarian*)
+- tamaina (*Japanese*)
+- OpenAlgeria (*Arabic*)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6b0d23a22..b200747b1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,39 @@ Changelog
 
 All notable changes to this project will be documented in this file.
 
+## [3.0.1] - 2019-10-10
+### Added
+
+- Add `tootctl media usage` command ([Gargron](https://github.com/tootsuite/mastodon/pull/12115))
+- Add admin setting to auto-approve trending hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/12122), [Gargron](https://github.com/tootsuite/mastodon/pull/12130))
+
+### Changed
+
+- Change `tootctl media refresh` to skip already downloaded attachments ([Gargron](https://github.com/tootsuite/mastodon/pull/12118))
+
+### Removed
+
+- Remove auto-silence behaviour from spam check ([Gargron](https://github.com/tootsuite/mastodon/pull/12117))
+- Remove HTML `lang` attribute from individual statuses in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12124))
+- Remove fallback to long description on sidebar and meta description ([Gargron](https://github.com/tootsuite/mastodon/pull/12119))
+
+### Fixed
+
+- Fix preloaded JSON-LD context for identity not being used ([Gargron](https://github.com/tootsuite/mastodon/pull/12138))
+- Fix media editing modal changing dimensions once the image loads ([Gargron](https://github.com/tootsuite/mastodon/pull/12131))
+- Fix not showing whether a custom emoji has a local counterpart in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/12135))
+- Fix attachment not being re-downloaded even if file is not stored ([Gargron](https://github.com/tootsuite/mastodon/pull/12125))
+- Fix old migration trying to use new column due to default status scope ([Gargron](https://github.com/tootsuite/mastodon/pull/12095))
+- Fix column back button missing for not found accounts ([trwnh](https://github.com/tootsuite/mastodon/pull/12094))
+- Fix issues with tootctl's parallelization and progress reporting ([Gargron](https://github.com/tootsuite/mastodon/pull/12093), [Gargron](https://github.com/tootsuite/mastodon/pull/12097))
+- Fix existing user records with now-renamed `pt` locale ([Gargron](https://github.com/tootsuite/mastodon/pull/12092))
+- Fix hashtag timeline REST API accepting too many hashtags ([Gargron](https://github.com/tootsuite/mastodon/pull/12091))
+- Fix `GET /api/v1/instance` REST APIs being unavailable in secure mode ([Gargron](https://github.com/tootsuite/mastodon/pull/12089))
+- Fix performance of home feed regeneration and merging ([Gargron](https://github.com/tootsuite/mastodon/pull/12084))
+- Fix ffmpeg performance issues due to stdout buffer overflow ([hugogameiro](https://github.com/tootsuite/mastodon/pull/12088))
+- Fix S3 adapter retrying failing uploads with exponential backoff ([Gargron](https://github.com/tootsuite/mastodon/pull/12085))
+- Fix `tootctl accounts cull` advertising unused option flag ([Kjwon15](https://github.com/tootsuite/mastodon/pull/12074))
+
 ## [3.0.0] - 2019-10-03
 ### Added
 
diff --git a/Gemfile b/Gemfile
index ccac9adc9..7dd33f312 100644
--- a/Gemfile
+++ b/Gemfile
@@ -90,7 +90,7 @@ gem 'simple_form', '~> 4.1'
 gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
 gem 'stoplight', '~> 2.1.3'
 gem 'strong_migrations', '~> 0.4'
-gem 'tty-command', '~> 0.8', require: false
+gem 'tty-command', '~> 0.9', require: false
 gem 'tty-prompt', '~> 0.19', require: false
 gem 'twitter-text', '~> 1.14'
 gem 'tzinfo-data', '~> 1.2019'
@@ -119,7 +119,7 @@ end
 group :test do
   gem 'capybara', '~> 3.29'
   gem 'climate_control', '~> 0.2'
-  gem 'faker', '~> 2.4'
+  gem 'faker', '~> 2.5'
   gem 'microformats', '~> 4.1'
   gem 'rails-controller-testing', '~> 1.0'
   gem 'rspec-sidekiq', '~> 3.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 9d506d106..8678cfa3a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -93,7 +93,7 @@ GEM
       tzinfo (~> 1.1)
     addressable (2.7.0)
       public_suffix (>= 2.0.2, < 5.0)
-    airbrussh (1.3.3)
+    airbrussh (1.3.4)
       sshkit (>= 1.6.1, != 1.7.0)
     annotate (2.7.5)
       activerecord (>= 3.2, < 7.0)
@@ -142,7 +142,7 @@ GEM
       bundler (>= 1.2.0, < 3)
       thor (~> 0.18)
     byebug (11.0.0)
-    capistrano (3.11.1)
+    capistrano (3.11.2)
       airbrussh (>= 1.0.0)
       i18n
       rake (>= 10.0.0)
@@ -188,13 +188,14 @@ GEM
     css_parser (1.7.0)
       addressable
     debug_inspector (0.0.3)
-    derailed_benchmarks (1.3.6)
+    derailed_benchmarks (1.4.0)
       benchmark-ips (~> 2)
       get_process_mem (~> 0)
       heapy (~> 0)
       memory_profiler (~> 0)
       rack (>= 1)
       rake (> 10, < 13)
+      ruby-statistics (>= 2.1)
       thor (~> 0.19)
     devise (4.7.1)
       bcrypt (~> 3.0)
@@ -233,13 +234,13 @@ GEM
       faraday
       multi_json
     encryptor (3.0.0)
-    equatable (0.5.0)
+    equatable (0.6.1)
     erubi (1.8.0)
     et-orbi (1.1.6)
       tzinfo
     excon (0.62.0)
     fabrication (2.20.2)
-    faker (2.4.0)
+    faker (2.5.0)
       i18n (~> 1.6.0)
     faraday (0.15.4)
       multipart-post (>= 1.2, < 3)
@@ -265,7 +266,8 @@ GEM
     fuubar (2.4.1)
       rspec-core (~> 3.0)
       ruby-progressbar (~> 1.4)
-    get_process_mem (0.2.3)
+    get_process_mem (0.2.4)
+      ffi (~> 1.0)
     globalid (0.4.2)
       activesupport (>= 4.2.0)
     goldfinger (2.1.0)
@@ -429,13 +431,13 @@ GEM
     parser (2.6.4.0)
       ast (~> 2.4.0)
     parslet (1.8.2)
-    pastel (0.7.2)
-      equatable (~> 0.5.0)
-      tty-color (~> 0.4.0)
+    pastel (0.7.3)
+      equatable (~> 0.6)
+      tty-color (~> 0.5)
     pg (1.1.4)
     pghero (2.3.0)
       activerecord (>= 5)
-    pkg-config (1.3.8)
+    pkg-config (1.3.9)
     premailer (1.11.1)
       addressable
       css_parser (>= 1.6.0)
@@ -571,6 +573,7 @@ GEM
     ruby-progressbar (1.10.1)
     ruby-saml (1.9.0)
       nokogiri (>= 1.5.10)
+    ruby-statistics (2.1.1)
     rufus-scheduler (3.5.2)
       fugit (~> 1.1, >= 1.1.5)
     safe_yaml (1.0.5)
@@ -629,8 +632,8 @@ GEM
     thor (0.20.3)
     thread_safe (0.3.6)
     tilt (2.0.9)
-    tty-color (0.4.3)
-    tty-command (0.8.2)
+    tty-color (0.5.0)
+    tty-command (0.9.0)
       pastel (~> 0.7.0)
     tty-cursor (0.7.0)
     tty-prompt (0.19.0)
@@ -655,7 +658,7 @@ GEM
     uniform_notifier (1.12.1)
     warden (1.2.8)
       rack (>= 2.0.6)
-    webmock (3.7.5)
+    webmock (3.7.6)
       addressable (>= 2.3.6)
       crack (>= 0.3.2)
       hashdiff (>= 0.4.0, < 2.0.0)
@@ -709,7 +712,7 @@ DEPENDENCIES
   doorkeeper (~> 5.2)
   dotenv-rails (~> 2.7)
   fabrication (~> 2.20)
-  faker (~> 2.4)
+  faker (~> 2.5)
   fast_blank (~> 1.0)
   fastimage
   fog-core (<= 2.1.0)
@@ -796,7 +799,7 @@ DEPENDENCIES
   streamio-ffmpeg (~> 3.0)
   strong_migrations (~> 0.4)
   thor (~> 0.20)
-  tty-command (~> 0.8)
+  tty-command (~> 0.9)
   tty-prompt (~> 0.19)
   twitter-text (~> 1.14)
   tzinfo-data (~> 1.2019)
diff --git a/app/controllers/api/v1/instances/activity_controller.rb b/app/controllers/api/v1/instances/activity_controller.rb
index 4fb5a69d8..b30e8464c 100644
--- a/app/controllers/api/v1/instances/activity_controller.rb
+++ b/app/controllers/api/v1/instances/activity_controller.rb
@@ -4,6 +4,7 @@ class Api::V1::Instances::ActivityController < Api::BaseController
   before_action :require_enabled_api!
 
   skip_before_action :set_cache_headers
+  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
 
   respond_to :json
 
diff --git a/app/controllers/api/v1/instances/peers_controller.rb b/app/controllers/api/v1/instances/peers_controller.rb
index 75c3cb4ba..cc00d8a6b 100644
--- a/app/controllers/api/v1/instances/peers_controller.rb
+++ b/app/controllers/api/v1/instances/peers_controller.rb
@@ -4,6 +4,7 @@ class Api::V1::Instances::PeersController < Api::BaseController
   before_action :require_enabled_api!
 
   skip_before_action :set_cache_headers
+  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
 
   respond_to :json
 
diff --git a/app/controllers/api/v1/instances_controller.rb b/app/controllers/api/v1/instances_controller.rb
index 8d8231423..c323b60b4 100644
--- a/app/controllers/api/v1/instances_controller.rb
+++ b/app/controllers/api/v1/instances_controller.rb
@@ -4,6 +4,7 @@ class Api::V1::InstancesController < Api::BaseController
   respond_to :json
 
   skip_before_action :set_cache_headers
+  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
 
   def show
     expires_in 3.minutes, public: true
diff --git a/app/controllers/api/v1/streaming_controller.rb b/app/controllers/api/v1/streaming_controller.rb
index 66b812e76..ebb17608c 100644
--- a/app/controllers/api/v1/streaming_controller.rb
+++ b/app/controllers/api/v1/streaming_controller.rb
@@ -5,11 +5,17 @@ class Api::V1::StreamingController < Api::BaseController
 
   def index
     if Rails.configuration.x.streaming_api_base_url != request.host
-      uri = URI.parse(request.url)
-      uri.host = URI.parse(Rails.configuration.x.streaming_api_base_url).host
-      redirect_to uri.to_s, status: 301
+      redirect_to streaming_api_url, status: 301
     else
-      raise ActiveRecord::RecordNotFound
+      not_found
     end
   end
+
+  private
+
+  def streaming_api_url
+    Addressable::URI.parse(request.url).tap do |uri|
+      uri.host = Addressable::URI.parse(Rails.configuration.x.streaming_api_base_url).host
+    end.to_s
+  end
 end
diff --git a/app/controllers/api/v1/timelines/home_controller.rb b/app/controllers/api/v1/timelines/home_controller.rb
index fcd0757f1..ff5ede138 100644
--- a/app/controllers/api/v1/timelines/home_controller.rb
+++ b/app/controllers/api/v1/timelines/home_controller.rb
@@ -13,7 +13,7 @@ class Api::V1::Timelines::HomeController < Api::BaseController
     render json: @statuses,
            each_serializer: REST::StatusSerializer,
            relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
-           status: regeneration_in_progress? ? 206 : 200
+           status: account_home_feed.regenerating? ? 206 : 200
   end
 
   private
@@ -62,8 +62,4 @@ class Api::V1::Timelines::HomeController < Api::BaseController
   def pagination_since_id
     @statuses.first.id
   end
-
-  def regeneration_in_progress?
-    Redis.current.exists("account:#{current_account.id}:regeneration")
-  end
 end
diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js
index 7eeba2aa7..bc2ac5e82 100644
--- a/app/javascript/mastodon/actions/timelines.js
+++ b/app/javascript/mastodon/actions/timelines.js
@@ -97,7 +97,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
     api(getState).get(path, { params }).then(response => {
       const next = getLinks(response).refs.find(link => link.rel === 'next');
       dispatch(importFetchedStatuses(response.data));
-      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
+      dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
       done();
     }).catch(error => {
       dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
diff --git a/app/javascript/mastodon/components/extended_video_player.js b/app/javascript/mastodon/components/extended_video_player.js
deleted file mode 100644
index 009c0d559..000000000
--- a/app/javascript/mastodon/components/extended_video_player.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export default class ExtendedVideoPlayer extends React.PureComponent {
-
-  static propTypes = {
-    src: PropTypes.string.isRequired,
-    alt: PropTypes.string,
-    width: PropTypes.number,
-    height: PropTypes.number,
-    time: PropTypes.number,
-    controls: PropTypes.bool.isRequired,
-    muted: PropTypes.bool.isRequired,
-    onClick: PropTypes.func,
-  };
-
-  handleLoadedData = () => {
-    if (this.props.time) {
-      this.video.currentTime = this.props.time;
-    }
-  }
-
-  componentDidMount () {
-    this.video.addEventListener('loadeddata', this.handleLoadedData);
-  }
-
-  componentWillUnmount () {
-    this.video.removeEventListener('loadeddata', this.handleLoadedData);
-  }
-
-  setRef = (c) => {
-    this.video = c;
-  }
-
-  handleClick = e => {
-    e.stopPropagation();
-    const handler = this.props.onClick;
-    if (handler) handler();
-  }
-
-  render () {
-    const { src, muted, controls, alt } = this.props;
-
-    return (
-      <div className='extended-video-player'>
-        <video
-          ref={this.setRef}
-          src={src}
-          autoPlay
-          role='button'
-          tabIndex='0'
-          aria-label={alt}
-          title={alt}
-          muted={muted}
-          controls={controls}
-          loop={!controls}
-          onClick={this.handleClick}
-        />
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/mastodon/components/gifv.js b/app/javascript/mastodon/components/gifv.js
new file mode 100644
index 000000000..83cfae49c
--- /dev/null
+++ b/app/javascript/mastodon/components/gifv.js
@@ -0,0 +1,75 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+export default class GIFV extends React.PureComponent {
+
+  static propTypes = {
+    src: PropTypes.string.isRequired,
+    alt: PropTypes.string,
+    width: PropTypes.number,
+    height: PropTypes.number,
+    onClick: PropTypes.func,
+  };
+
+  state = {
+    loading: true,
+  };
+
+  handleLoadedData = () => {
+    this.setState({ loading: false });
+  }
+
+  componentWillReceiveProps (nextProps) {
+    if (nextProps.src !== this.props.src) {
+      this.setState({ loading: true });
+    }
+  }
+
+  handleClick = e => {
+    const { onClick } = this.props;
+
+    if (onClick) {
+      e.stopPropagation();
+      onClick();
+    }
+  }
+
+  render () {
+    const { src, width, height, alt } = this.props;
+    const { loading } = this.state;
+
+    return (
+      <div className='gifv' style={{ position: 'relative' }}>
+        {loading && (
+          <canvas
+            width={width}
+            height={height}
+            role='button'
+            tabIndex='0'
+            aria-label={alt}
+            title={alt}
+            onClick={this.handleClick}
+          />
+        )}
+
+        <video
+          src={src}
+          width={width}
+          height={height}
+          role='button'
+          tabIndex='0'
+          aria-label={alt}
+          title={alt}
+          muted
+          loop
+          autoPlay
+          playsInline
+          onClick={this.handleClick}
+          onLoadedData={this.handleLoadedData}
+          style={{ position: loading ? 'absolute' : 'static', top: 0, left: 0 }}
+        />
+      </div>
+    );
+  }
+
+}
diff --git a/app/javascript/mastodon/components/missing_indicator.js b/app/javascript/mastodon/components/missing_indicator.js
index 70d8c3b98..7b0101bab 100644
--- a/app/javascript/mastodon/components/missing_indicator.js
+++ b/app/javascript/mastodon/components/missing_indicator.js
@@ -1,17 +1,24 @@
 import React from 'react';
+import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
+import illustration from 'mastodon/../images/elephant_ui_disappointed.svg';
+import classNames from 'classnames';
 
-const MissingIndicator = () => (
-  <div className='regeneration-indicator missing-indicator'>
-    <div>
-      <div className='regeneration-indicator__figure' />
+const MissingIndicator = ({ fullPage }) => (
+  <div className={classNames('regeneration-indicator', { 'regeneration-indicator--without-header': fullPage })}>
+    <div className='regeneration-indicator__figure'>
+      <img src={illustration} alt='' />
+    </div>
 
-      <div className='regeneration-indicator__label'>
-        <FormattedMessage id='missing_indicator.label' tagName='strong' defaultMessage='Not found' />
-        <FormattedMessage id='missing_indicator.sublabel' defaultMessage='This resource could not be found' />
-      </div>
+    <div className='regeneration-indicator__label'>
+      <FormattedMessage id='missing_indicator.label' tagName='strong' defaultMessage='Not found' />
+      <FormattedMessage id='missing_indicator.sublabel' defaultMessage='This resource could not be found' />
     </div>
   </div>
 );
 
+MissingIndicator.propTypes = {
+  fullPage: PropTypes.bool,
+};
+
 export default MissingIndicator;
diff --git a/app/javascript/mastodon/components/regeneration_indicator.js b/app/javascript/mastodon/components/regeneration_indicator.js
new file mode 100644
index 000000000..faf88c6b5
--- /dev/null
+++ b/app/javascript/mastodon/components/regeneration_indicator.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import { FormattedMessage } from 'react-intl';
+import illustration from 'mastodon/../images/elephant_ui_working.svg';
+
+const MissingIndicator = () => (
+  <div className='regeneration-indicator'>
+    <div className='regeneration-indicator__figure'>
+      <img src={illustration} alt='' />
+    </div>
+
+    <div className='regeneration-indicator__label'>
+      <FormattedMessage id='regeneration_indicator.label' tagName='strong' defaultMessage='Loading&hellip;' />
+      <FormattedMessage id='regeneration_indicator.sublabel' defaultMessage='Your home feed is being prepared!' />
+    </div>
+  </div>
+);
+
+export default MissingIndicator;
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index c171e7a66..4ce9ec49f 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -216,14 +216,14 @@ export default class StatusContent extends React.PureComponent {
       return (
         <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
           <p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
-            <span dangerouslySetInnerHTML={spoilerContent} lang={status.get('language')} />
+            <span dangerouslySetInnerHTML={spoilerContent} />
             {' '}
             <button tabIndex='0' className={`status__content__spoiler-link ${hidden ? 'status__content__spoiler-link--show-more' : 'status__content__spoiler-link--show-less'}`} onClick={this.handleSpoilerClick}>{toggleText}</button>
           </p>
 
           {mentionsPlaceholder}
 
-          <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} />
+          <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} />
 
           {!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
         </div>
@@ -231,7 +231,7 @@ export default class StatusContent extends React.PureComponent {
     } else if (this.props.onClick) {
       const output = [
         <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content'>
-          <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} />
+          <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} />
 
           {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
         </div>,
@@ -245,7 +245,7 @@ export default class StatusContent extends React.PureComponent {
     } else {
       return (
         <div className={classNames} ref={this.setRef} tabIndex='0' style={directionStyle}>
-          <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} />
+          <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} />
 
           {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
         </div>
diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.js
index 745e6422d..e1b370c91 100644
--- a/app/javascript/mastodon/components/status_list.js
+++ b/app/javascript/mastodon/components/status_list.js
@@ -1,12 +1,12 @@
 import { debounce } from 'lodash';
 import React from 'react';
-import { FormattedMessage } from 'react-intl';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import StatusContainer from '../containers/status_container';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import LoadGap from './load_gap';
 import ScrollableList from './scrollable_list';
+import RegenerationIndicator from 'mastodon/components/regeneration_indicator';
 
 export default class StatusList extends ImmutablePureComponent {
 
@@ -81,18 +81,7 @@ export default class StatusList extends ImmutablePureComponent {
     const { isLoading, isPartial } = other;
 
     if (isPartial) {
-      return (
-        <div className='regeneration-indicator'>
-          <div>
-            <div className='regeneration-indicator__figure' />
-
-            <div className='regeneration-indicator__label'>
-              <FormattedMessage id='regeneration_indicator.label' tagName='strong' defaultMessage='Loading&hellip;' />
-              <FormattedMessage id='regeneration_indicator.sublabel' defaultMessage='Your home feed is being prepared!' />
-            </div>
-          </div>
-        </div>
-      );
+      return <RegenerationIndicator />;
     }
 
     let scrollableContent = (isLoading || statusIds.size > 0) ? (
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.js
index 69bab1e86..8d0cbe5a1 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.js
@@ -83,6 +83,7 @@ class AccountTimeline extends ImmutablePureComponent {
     if (!isAccount) {
       return (
         <Column>
+          <ColumnBackButton multiColumn={multiColumn} />
           <MissingIndicator />
         </Column>
       );
diff --git a/app/javascript/mastodon/features/generic_not_found/index.js b/app/javascript/mastodon/features/generic_not_found/index.js
index 0290be47f..41cd61a5f 100644
--- a/app/javascript/mastodon/features/generic_not_found/index.js
+++ b/app/javascript/mastodon/features/generic_not_found/index.js
@@ -4,7 +4,7 @@ import MissingIndicator from '../../components/missing_indicator';
 
 const GenericNotFound = () => (
   <Column>
-    <MissingIndicator />
+    <MissingIndicator fullPage />
   </Column>
 );
 
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
index 1ab79a21d..3694ab904 100644
--- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js
+++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.js
@@ -16,6 +16,7 @@ import UploadProgress from 'mastodon/features/compose/components/upload_progress
 import CharacterCounter from 'mastodon/features/compose/components/character_counter';
 import { length } from 'stringz';
 import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
+import GIFV from 'mastodon/components/gifv';
 
 const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
@@ -41,6 +42,36 @@ const removeExtraLineBreaks = str => str.replace(/\n\n/g, '******')
 
 const assetHost = process.env.CDN_HOST || '';
 
+class ImageLoader extends React.PureComponent {
+
+  static propTypes = {
+    src: PropTypes.string.isRequired,
+    width: PropTypes.number,
+    height: PropTypes.number,
+  };
+
+  state = {
+    loading: true,
+  };
+
+  componentDidMount() {
+    const image = new Image();
+    image.addEventListener('load', () => this.setState({ loading: false }));
+    image.src = this.props.src;
+  }
+
+  render () {
+    const { loading } = this.state;
+
+    if (loading) {
+      return <canvas width={this.props.width} height={this.props.height} />;
+    } else {
+      return <img {...this.props} alt='' />;
+    }
+  }
+
+}
+
 export default @connect(mapStateToProps, mapDispatchToProps)
 @injectIntl
 class FocalPointModal extends ImmutablePureComponent {
@@ -60,6 +91,7 @@ class FocalPointModal extends ImmutablePureComponent {
     description: '',
     dirty: false,
     progress: 0,
+    loading: true,
   };
 
   componentWillMount () {
@@ -242,8 +274,8 @@ class FocalPointModal extends ImmutablePureComponent {
           <div className='focal-point-modal__content'>
             {focals && (
               <div className={classNames('focal-point', { dragging })} ref={this.setRef} onMouseDown={this.handleMouseDown} onTouchStart={this.handleTouchStart}>
-                {media.get('type') === 'image' && <img src={media.get('url')} width={width} height={height} alt='' />}
-                {media.get('type') === 'gifv' && <video src={media.get('url')} width={width} height={height} loop muted autoPlay />}
+                {media.get('type') === 'image' && <ImageLoader src={media.get('url')} width={width} height={height} alt='' />}
+                {media.get('type') === 'gifv' && <GIFV src={media.get('url')} width={width} height={height} />}
 
                 <div className='focal-point__preview'>
                   <strong><FormattedMessage id='upload_modal.preview_label' defaultMessage='Preview ({ratio})' values={{ ratio: '16:9' }} /></strong>
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js
index 98ebd4b41..a785551c0 100644
--- a/app/javascript/mastodon/features/ui/components/media_modal.js
+++ b/app/javascript/mastodon/features/ui/components/media_modal.js
@@ -3,13 +3,13 @@ import ReactSwipeableViews from 'react-swipeable-views';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Video from 'mastodon/features/video';
-import ExtendedVideoPlayer from 'mastodon/components/extended_video_player';
 import classNames from 'classnames';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import IconButton from 'mastodon/components/icon_button';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import ImageLoader from './image_loader';
 import Icon from 'mastodon/components/icon';
+import GIFV from 'mastodon/components/gifv';
 
 const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
@@ -169,10 +169,8 @@ class MediaModal extends ImmutablePureComponent {
         );
       } else if (image.get('type') === 'gifv') {
         return (
-          <ExtendedVideoPlayer
+          <GIFV
             src={image.get('url')}
-            muted
-            controls={false}
             width={width}
             height={height}
             key={image.get('preview_url')}
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index eaccb008c..64a6ccf17 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -3127,37 +3127,27 @@ a.status-card.compact:hover {
   cursor: default;
   display: flex;
   flex: 1 1 auto;
+  flex-direction: column;
   align-items: center;
   justify-content: center;
   padding: 20px;
 
-  & > div {
-    width: 100%;
-    background: transparent;
-    padding-top: 0;
-  }
-
   &__figure {
-    background: url('~images/elephant_ui_working.svg') no-repeat center 0;
-    width: 100%;
-    height: 160px;
-    background-size: contain;
-    position: absolute;
-    top: 50%;
-    left: 50%;
-    transform: translate(-50%, -50%);
+    &,
+    img {
+      display: block;
+      width: auto;
+      height: 160px;
+      margin: 0;
+    }
   }
 
-  &.missing-indicator {
+  &--without-header {
     padding-top: 20px + 48px;
-
-    .regeneration-indicator__figure {
-      background-image: url('~images/elephant_ui_disappointed.svg');
-    }
   }
 
   &__label {
-    margin-top: 200px;
+    margin-top: 30px;
 
     strong {
       display: block;
@@ -6102,7 +6092,8 @@ noscript {
   background: $base-shadow-color;
 
   img,
-  video {
+  video,
+  canvas {
     display: block;
     max-height: 80vh;
     width: 100%;
diff --git a/app/javascript/styles/mastodon/introduction.scss b/app/javascript/styles/mastodon/introduction.scss
index 222d8f60e..b44ae7306 100644
--- a/app/javascript/styles/mastodon/introduction.scss
+++ b/app/javascript/styles/mastodon/introduction.scss
@@ -3,9 +3,10 @@
   flex-direction: column;
   justify-content: center;
   align-items: center;
+  height: 100vh;
+  background: $ui-base-color;
 
   @media screen and (max-width: 920px) {
-    background: darken($ui-base-color, 8%);
     display: block !important;
   }
 
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 4587664b8..d109d991c 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -19,7 +19,7 @@ class FeedManager
 
   def filter?(timeline_type, status, receiver_id)
     if timeline_type == :home
-      filter_from_home?(status, receiver_id)
+      filter_from_home?(status, receiver_id, build_crutches(receiver_id, [status]))
     elsif timeline_type == :mentions
       filter_from_mentions?(status, receiver_id)
     elsif timeline_type == :direct
@@ -31,6 +31,7 @@ class FeedManager
 
   def push_to_home(account, status)
     return false unless add_to_feed(:home, account.id, status, account.user&.aggregates_reblogs?)
+
     trim(:home, account.id)
     PushUpdateWorker.perform_async(account.id, status.id, "timeline:#{account.id}") if push_update_required?("timeline:#{account.id}")
     true
@@ -38,6 +39,7 @@ class FeedManager
 
   def unpush_from_home(account, status)
     return false unless remove_from_feed(:home, account.id, status, account.user&.aggregates_reblogs?)
+
     redis.publish("timeline:#{account.id}", Oj.dump(event: :delete, payload: status.id.to_s))
     true
   end
@@ -49,7 +51,9 @@ class FeedManager
       should_filter &&= !(list.show_list_replies? && ListAccount.where(list_id: list.id, account_id: status.in_reply_to_account_id).exists?)
       return false if should_filter
     end
+
     return false unless add_to_feed(:list, list.id, status, list.account.user&.aggregates_reblogs?)
+
     trim(:list, list.id)
     PushUpdateWorker.perform_async(list.account_id, status.id, "timeline:list:#{list.id}") if push_update_required?("timeline:list:#{list.id}")
     true
@@ -57,6 +61,7 @@ class FeedManager
 
   def unpush_from_list(list, status)
     return false unless remove_from_feed(:list, list.id, status, list.account.user&.aggregates_reblogs?)
+
     redis.publish("timeline:list:#{list.id}", Oj.dump(event: :delete, payload: status.id.to_s))
     true
   end
@@ -100,16 +105,21 @@ class FeedManager
 
   def merge_into_timeline(from_account, into_account)
     timeline_key = key(:home, into_account.id)
-    query        = from_account.statuses.limit(FeedManager::MAX_ITEMS / 4)
+    aggregate    = into_account.user&.aggregates_reblogs?
+    query        = from_account.statuses.where(visibility: [:public, :unlisted, :private]).includes(:preloadable_poll, reblog: :account).limit(FeedManager::MAX_ITEMS / 4)
 
     if redis.zcard(timeline_key) >= FeedManager::MAX_ITEMS / 4
-      oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true)&.first&.last&.to_i || 0
+      oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
       query = query.where('id > ?', oldest_home_score)
     end
 
-    query.each do |status|
-      next if status.direct_visibility? || status.limited_visibility? || filter?(:home, status, into_account)
-      add_to_feed(:home, into_account.id, status, into_account.user&.aggregates_reblogs?)
+    statuses = query.to_a
+    crutches = build_crutches(into_account.id, statuses)
+
+    statuses.each do |status|
+      next if filter_from_home?(status, into_account, crutches)
+
+      add_to_feed(:home, into_account.id, status, aggregate)
     end
 
     trim(:home, into_account.id)
@@ -135,24 +145,35 @@ class FeedManager
   end
 
   def populate_feed(account)
-    added  = 0
-    limit  = FeedManager::MAX_ITEMS / 2
-    max_id = nil
+    limit        = FeedManager::MAX_ITEMS / 2
+    aggregate    = account.user&.aggregates_reblogs?
+    timeline_key = key(:home, account.id)
 
-    loop do
-      statuses = Status.as_home_timeline(account)
-                       .paginate_by_max_id(limit, max_id)
+    account.statuses.where.not(visibility: :direct).limit(limit).each do |status|
+      add_to_feed(:home, account.id, status, aggregate)
+    end
 
-      break if statuses.empty?
+    account.following.includes(:account_stat).find_each do |target_account|
+      if redis.zcard(timeline_key) >= limit
+        oldest_home_score = redis.zrange(timeline_key, 0, 0, with_scores: true).first.last.to_i
+        last_status_score = Mastodon::Snowflake.id_at(account.last_status_at)
 
-      statuses.each do |status|
-        next if filter_from_home?(status, account)
-        added += 1 if add_to_feed(:home, account.id, status, account.user&.aggregates_reblogs?)
+        # If the feed is full and this account has not posted more recently
+        # than the last item on the feed, then we can skip the whole account
+        # because none of its statuses would stay on the feed anyway
+        next if last_status_score < oldest_home_score
       end
 
-      break unless added.zero?
+      statuses = target_account.statuses.where(visibility: [:public, :unlisted, :private]).includes(:preloadable_poll, reblog: :account).limit(limit)
+      crutches = build_crutches(account.id, statuses)
 
-      max_id = statuses.last.id
+      statuses.each do |status|
+        next if filter_from_home?(status, account, crutches)
+
+        add_to_feed(:home, account.id, status, aggregate)
+      end
+
+      trim(:home, account.id)
     end
   end
 
@@ -188,31 +209,33 @@ class FeedManager
       (context == :home ? Mute.where(account_id: receiver_id, target_account_id: account_ids).any? : Mute.where(account_id: receiver_id, target_account_id: account_ids, hide_notifications: true).any?)
   end
 
-  def filter_from_home?(status, receiver_id)
+  def filter_from_home?(status, receiver_id, crutches)
     return false if receiver_id == status.account_id
     return true  if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?)
     return true  if phrase_filtered?(status, receiver_id, :home)
 
-    check_for_blocks = status.active_mentions.pluck(:account_id)
+    check_for_blocks = crutches[:active_mentions][status.id] || []
     check_for_blocks.concat([status.account_id])
 
     if status.reblog?
       check_for_blocks.concat([status.reblog.account_id])
-      check_for_blocks.concat(status.reblog.active_mentions.pluck(:account_id))
+      check_for_blocks.concat(crutches[:active_mentions][status.reblog_of_id] || [])
     end
 
-    return true if blocks_or_mutes?(receiver_id, check_for_blocks, :home)
+    return true if check_for_blocks.any? { |target_account_id| crutches[:blocking][target_account_id] || crutches[:muting][target_account_id] }
 
     if status.reply? && !status.in_reply_to_account_id.nil?                                                                      # Filter out if it's a reply
-      should_filter   = !Follow.where(account_id: receiver_id, target_account_id: status.in_reply_to_account_id).exists?         # and I'm not following the person it's a reply to
+      should_filter   = !crutches[:following][status.in_reply_to_account_id]                                                     # and I'm not following the person it's a reply to
       should_filter &&= receiver_id != status.in_reply_to_account_id                                                             # and it's not a reply to me
       should_filter &&= status.account_id != status.in_reply_to_account_id                                                       # and it's not a self-reply
-      return should_filter
+
+      return !!should_filter
     elsif status.reblog?                                                                                                         # Filter out a reblog
-      should_filter   = Follow.where(account_id: receiver_id, target_account_id: status.account_id, show_reblogs: false).exists? # if the reblogger's reblogs are suppressed
-      should_filter ||= Block.where(account_id: status.reblog.account_id, target_account_id: receiver_id).exists?                # or if the author of the reblogged status is blocking me
-      should_filter ||= AccountDomainBlock.where(account_id: receiver_id, domain: status.reblog.account.domain).exists?          # or the author's domain is blocked
-      return should_filter
+      should_filter   = crutches[:hiding_reblogs][status.account_id]                                                             # if the reblogger's reblogs are suppressed
+      should_filter ||= crutches[:blocked_by][status.reblog.account_id]                                                          # or if the author of the reblogged status is blocking me
+      should_filter ||= crutches[:domain_blocking][status.reblog.account.domain]                                                 # or the author's domain is blocked
+
+      return !!should_filter
     end
 
     false
@@ -349,4 +372,31 @@ class FeedManager
 
     redis.zrem(timeline_key, status.id)
   end
+
+  def build_crutches(receiver_id, statuses)
+    crutches = {}
+
+    crutches[:active_mentions] = Mention.active.where(status_id: statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact).pluck(:status_id, :account_id).each_with_object({}) { |(id, account_id), mapping| (mapping[id] ||= []).push(account_id) }
+
+    check_for_blocks = statuses.flat_map do |s|
+      arr = crutches[:active_mentions][s.id] || []
+      arr.concat([s.account_id])
+
+      if s.reblog?
+        arr.concat([s.reblog.account_id])
+        arr.concat(crutches[:active_mentions][s.reblog_of_id] || [])
+      end
+
+      arr
+    end
+
+    crutches[:following]       = Follow.where(account_id: receiver_id, target_account_id: statuses.map(&:in_reply_to_account_id).compact).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
+    crutches[:hiding_reblogs]  = Follow.where(account_id: receiver_id, target_account_id: statuses.map { |s| s.account_id if s.reblog? }.compact, show_reblogs: false).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
+    crutches[:blocking]        = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
+    crutches[:muting]          = Mute.where(account_id: receiver_id, target_account_id: check_for_blocks).pluck(:target_account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
+    crutches[:domain_blocking] = AccountDomainBlock.where(account_id: receiver_id, domain: statuses.map { |s| s.reblog&.account&.domain }.compact).pluck(:domain).each_with_object({}) { |domain, mapping| mapping[domain] = true }
+    crutches[:blocked_by]      = Block.where(target_account_id: receiver_id, account_id: statuses.map { |s| s.reblog&.account_id }.compact).pluck(:account_id).each_with_object({}) { |id, mapping| mapping[id] = true }
+
+    crutches
+  end
 end
diff --git a/app/lib/spam_check.rb b/app/lib/spam_check.rb
index 441697364..235e44230 100644
--- a/app/lib/spam_check.rb
+++ b/app/lib/spam_check.rb
@@ -44,7 +44,6 @@ class SpamCheck
   end
 
   def flag!
-    auto_silence_account!
     auto_report_status!
   end
 
@@ -134,17 +133,13 @@ class SpamCheck
     text.gsub(/\s+/, ' ').strip
   end
 
-  def auto_silence_account!
-    @account.silence!
-  end
-
   def auto_report_status!
     status_ids = Status.where(visibility: %i(public unlisted)).where(id: matching_status_ids).pluck(:id) + [@status.id] if @status.distributable?
     ReportService.new.call(Account.representative, @account, status_ids: status_ids, comment: I18n.t('spam_check.spam_detected_and_silenced'))
   end
 
   def already_flagged?
-    @account.silenced?
+    @account.silenced? || @account.targeted_reports.unresolved.where(account_id: -99).exists?
   end
 
   def trusted?
diff --git a/app/models/account.rb b/app/models/account.rb
index 52ce9a676..db2eb8993 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -202,7 +202,7 @@ class Account < ApplicationRecord
   end
 
   def unsilence!
-    update!(silenced_at: nil, trust_level: trust_level == TRUST_LEVELS[:untrusted] ? TRUST_LEVELS[:trusted] : trust_level)
+    update!(silenced_at: nil)
   end
 
   def suspended?
@@ -312,10 +312,9 @@ class Account < ApplicationRecord
   def save_with_optional_media!
     save!
   rescue ActiveRecord::RecordInvalid
-    self.avatar              = nil
-    self.header              = nil
-    self[:avatar_remote_url] = ''
-    self[:header_remote_url] = ''
+    self.avatar = nil
+    self.header = nil
+
     save!
   end
 
diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb
index b30a82369..e9da003a3 100644
--- a/app/models/admin/account_action.rb
+++ b/app/models/admin/account_action.rb
@@ -62,6 +62,8 @@ class Admin::AccountAction
 
   def process_action!
     case type
+    when 'none'
+      handle_resolve!
     when 'disable'
       handle_disable!
     when 'silence'
@@ -103,6 +105,16 @@ class Admin::AccountAction
     end
   end
 
+  def handle_resolve!
+    if with_report? && report.account_id == -99 && target_account.trust_level == Account::TRUST_LEVELS[:untrusted]
+      # This is an automated report and it is being dismissed, so it's
+      # a false positive, in which case update the account's trust level
+      # to prevent further spam checks
+
+      target_account.update(trust_level: Account::TRUST_LEVELS[:trusted])
+    end
+  end
+
   def handle_disable!
     authorize(target_account.user, :disable?)
     log_action(:disable, target_account.user)
diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb
index 082302619..b7a476c87 100644
--- a/app/models/concerns/remotable.rb
+++ b/app/models/concerns/remotable.rb
@@ -18,7 +18,7 @@ module Remotable
           return
         end
 
-        return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.blank? || self[attribute_name] == url
+        return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.blank? || (self[attribute_name] == url && send("#{attachment_name}_file_name").present?)
 
         begin
           Request.new(:get, url).perform do |response|
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index f1ee38325..3398af169 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -36,6 +36,7 @@ class Form::AdminSettings
     show_replies_in_public_timelines
     spam_check_enabled
     trends
+    trendable_by_default
     show_domain_blocks
     show_domain_blocks_rationale
     noindex
@@ -56,6 +57,7 @@ class Form::AdminSettings
     show_replies_in_public_timelines
     spam_check_enabled
     trends
+    trendable_by_default
     noindex
   ).freeze
 
diff --git a/app/models/home_feed.rb b/app/models/home_feed.rb
index ba7564983..1fd506138 100644
--- a/app/models/home_feed.rb
+++ b/app/models/home_feed.rb
@@ -7,19 +7,7 @@ class HomeFeed < Feed
     @account = account
   end
 
-  def get(limit, max_id = nil, since_id = nil, min_id = nil)
-    if redis.exists("account:#{@account.id}:regeneration")
-      from_database(limit, max_id, since_id, min_id)
-    else
-      super
-    end
-  end
-
-  private
-
-  def from_database(limit, max_id, since_id, min_id)
-    Status.as_home_timeline(@account)
-          .paginate_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
-          .reject { |status| FeedManager.instance.filter?(:home, status, @account.id) }
+  def regenerating?
+    redis.exists("account:#{@id}:regeneration")
   end
 end
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index 4f06a40cf..056ed816e 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -57,6 +57,7 @@ class MediaAttachment < ApplicationRecord
     small: {
       convert_options: {
         output: {
+          'loglevel' => 'fatal',
           vf: 'scale=\'min(400\, iw):min(400\, ih)\':force_original_aspect_ratio=decrease',
         },
       },
@@ -70,6 +71,7 @@ class MediaAttachment < ApplicationRecord
       keep_same_format: true,
       convert_options: {
         output: {
+          'loglevel' => 'fatal',
           'map_metadata' => '-1',
           'c:v' => 'copy',
           'c:a' => 'copy',
@@ -84,6 +86,7 @@ class MediaAttachment < ApplicationRecord
       content_type: 'audio/mpeg',
       convert_options: {
         output: {
+          'loglevel' => 'fatal',
           'q:a' => 2,
         },
       },
diff --git a/app/models/status.rb b/app/models/status.rb
index 7ac0fb5bd..c189d19bf 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -291,10 +291,6 @@ class Status < ApplicationRecord
       where(language: nil).or where(language: account.chosen_languages)
     end
 
-    def as_home_timeline(account)
-      where(account: [account] + account.following).where(visibility: [:public, :unlisted, :private])
-    end
-
     def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil, cache_ids = false)
       # direct timeline is mix of direct message from_me and to_me.
       # 2 queries are executed with pagination.
diff --git a/app/models/tag.rb b/app/models/tag.rb
index 82786daa8..d3a7e1e6d 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -37,6 +37,7 @@ class Tag < ApplicationRecord
   scope :pending_review, -> { unreviewed.where.not(requested_review_at: nil) }
   scope :usable, -> { where(usable: [true, nil]) }
   scope :listable, -> { where(listable: [true, nil]) }
+  scope :trendable, -> { Setting.trendable_by_default ? where(trendable: [true, nil]) : where(trendable: true) }
   scope :discoverable, -> { listable.joins(:account_tag_stat).where(AccountTagStat.arel_table[:accounts_count].gt(0)).order(Arel.sql('account_tag_stats.accounts_count desc')) }
   scope :most_used, ->(account) { joins(:statuses).where(statuses: { account: account }).group(:id).order(Arel.sql('count(*) desc')) }
   scope :matches_name, ->(value) { where(arel_table[:name].matches("#{value}%")) }
@@ -76,7 +77,7 @@ class Tag < ApplicationRecord
   alias listable? listable
 
   def trendable
-    boolean_with_default('trendable', false)
+    boolean_with_default('trendable', Setting.trendable_by_default)
   end
 
   alias trendable? trendable
diff --git a/app/models/trending_tags.rb b/app/models/trending_tags.rb
index 8cdade42d..c69f6d3c3 100644
--- a/app/models/trending_tags.rb
+++ b/app/models/trending_tags.rb
@@ -90,7 +90,7 @@ class TrendingTags
       tag_ids = redis.zrevrange(KEY, 0, LIMIT - 1).map(&:to_i)
 
       tags = Tag.where(id: tag_ids)
-      tags = tags.where(trendable: true) if filtered
+      tags = tags.trendable if filtered
       tags = tags.each_with_object({}) { |tag, h| h[tag.id] = tag }
 
       tag_ids.map { |tag_id| tags[tag_id] }.compact.take(limit)
diff --git a/app/services/hashtag_query_service.rb b/app/services/hashtag_query_service.rb
index 282821710..196de0639 100644
--- a/app/services/hashtag_query_service.rb
+++ b/app/services/hashtag_query_service.rb
@@ -1,6 +1,8 @@
 # frozen_string_literal: true
 
 class HashtagQueryService < BaseService
+  LIMIT_PER_MODE = 4
+
   def call(tag, params, account = nil, local = false)
     tags = tags_for(Array(tag.name) | Array(params[:any])).pluck(:id)
     all  = tags_for(params[:all])
@@ -15,6 +17,6 @@ class HashtagQueryService < BaseService
   private
 
   def tags_for(names)
-    Tag.matching_name(names) if names.presence
+    Tag.matching_name(Array(names).take(LIMIT_PER_MODE)) if names.present?
   end
 end
diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml
index f24f4e195..80f4cd828 100644
--- a/app/views/about/show.html.haml
+++ b/app/views/about/show.html.haml
@@ -52,13 +52,12 @@
         .hero-widget__img
           = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('media/images/preview.jpg'), alt: @instance_presenter.site_title
 
-        - if @instance_presenter.site_short_description.present?
-          .hero-widget__text
-            %p
-              = @instance_presenter.site_short_description.html_safe.presence
-              = link_to about_more_path do
-                = t('about.learn_more')
-                = fa_icon 'angle-double-right'
+        .hero-widget__text
+          %p
+            = @instance_presenter.site_short_description.html_safe.presence || t('about.about_mastodon_html')
+            = link_to about_more_path do
+              = t('about.learn_more')
+              = fa_icon 'angle-double-right'
 
         .hero-widget__footer
           .hero-widget__footer__column
diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml
index 2103b0fa7..526c844e9 100644
--- a/app/views/admin/custom_emojis/_custom_emoji.html.haml
+++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml
@@ -17,6 +17,10 @@
       - else
         = custom_emoji.domain
 
+        - if custom_emoji.local_counterpart.present?
+          &bull;
+          = t('admin.accounts.location.local')
+
       %br/
 
       - if custom_emoji.disabled?
diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml
index e96ea0b03..ba66aeff8 100644
--- a/app/views/admin/settings/edit.html.haml
+++ b/app/views/admin/settings/edit.html.haml
@@ -20,10 +20,10 @@
       = f.input :site_contact_email, wrapper: :with_label, label: t('admin.settings.contact_information.email')
 
   .fields-group
-    = f.input :site_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description.title'), hint: t('admin.settings.site_description.desc_html'), input_html: { rows: 4 }
+    = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 }
 
   .fields-group
-    = f.input :site_short_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_short_description.title'), hint: t('admin.settings.site_short_description.desc_html'), input_html: { rows: 2 }
+    = f.input :site_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description.title'), hint: t('admin.settings.site_description.desc_html'), input_html: { rows: 2 }
 
   .fields-row
     .fields-row__column.fields-row__column-6.fields-group
@@ -72,6 +72,9 @@
       = f.input :trends, as: :boolean, wrapper: :with_label, label: t('admin.settings.trends.title'), hint: t('admin.settings.trends.desc_html')
 
     .fields-group
+      = f.input :trendable_by_default, as: :boolean, wrapper: :with_label, label: t('admin.settings.trendable_by_default.title'), hint: t('admin.settings.trendable_by_default.desc_html')
+
+    .fields-group
       = f.input :noindex, as: :boolean, wrapper: :with_label, label: t('admin.settings.default_noindex.title'), hint: t('admin.settings.default_noindex.desc_html')
 
   .fields-group
@@ -101,8 +104,8 @@
       = f.input :show_domain_blocks_rationale, wrapper: :with_label, collection: %i(disabled users all), label: t('admin.settings.domain_blocks_rationale.title'), label_method: lambda { |value| t("admin.settings.domain_blocks.#{value}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
 
   .fields-group
-    = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 }
     = f.input :site_extended_description, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_description_extended.title'), hint: t('admin.settings.site_description_extended.desc_html'), input_html: { rows: 8 } unless whitelist_mode?
+    = f.input :closed_registrations_message, as: :text, wrapper: :with_block_label, label: t('admin.settings.registrations.closed_message.title'), hint: t('admin.settings.registrations.closed_message.desc_html'), input_html: { rows: 8 }
     = f.input :site_terms, wrapper: :with_block_label, as: :text, label: t('admin.settings.site_terms.title'), hint: t('admin.settings.site_terms.desc_html'), input_html: { rows: 8 }
     = f.input :custom_css, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }, label: t('admin.settings.custom_css.title'), hint: t('admin.settings.custom_css.desc_html')
 
diff --git a/app/views/application/_sidebar.html.haml b/app/views/application/_sidebar.html.haml
index 33e7c96fe..7ec91c06a 100644
--- a/app/views/application/_sidebar.html.haml
+++ b/app/views/application/_sidebar.html.haml
@@ -3,7 +3,7 @@
     = image_tag @instance_presenter.hero&.file&.url || @instance_presenter.thumbnail&.file&.url || asset_pack_path('media/images/preview.jpg'), alt: @instance_presenter.site_title
 
   .hero-widget__text
-    %p= @instance_presenter.site_short_description.html_safe.presence || @instance_presenter.site_description.html_safe.presence || t('about.generic_description', domain: site_hostname)
+    %p= @instance_presenter.site_short_description.html_safe.presence || t('about.about_mastodon_html')
 
 - if Setting.trends && !(user_signed_in? && !current_user.setting_trends)
   - trends = TrendingTags.get(3)
diff --git a/app/views/shared/_og.html.haml b/app/views/shared/_og.html.haml
index 576f47a67..c8f12974e 100644
--- a/app/views/shared/_og.html.haml
+++ b/app/views/shared/_og.html.haml
@@ -1,5 +1,5 @@
 - thumbnail     = @instance_presenter.thumbnail
-- description ||= strip_tags(@instance_presenter.site_short_description.presence || @instance_presenter.site_description.presence || t('about.about_mastodon_html'))
+- description ||= strip_tags(@instance_presenter.site_short_description.presence || t('about.about_mastodon_html'))
 
 %meta{ name: 'description', content: description }/
 
diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml
index 12f03ccdd..5cee84ada 100644
--- a/app/views/statuses/_detailed_status.html.haml
+++ b/app/views/statuses/_detailed_status.html.haml
@@ -20,7 +20,7 @@
       %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
         %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
         %button.status__content__spoiler-link= t('statuses.show_more')
-    .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }
+    .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }
       = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
       - if status.preloadable_poll
         = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
diff --git a/app/views/statuses/_simple_status.html.haml b/app/views/statuses/_simple_status.html.haml
index fe1591bf9..a0e77ac6d 100644
--- a/app/views/statuses/_simple_status.html.haml
+++ b/app/views/statuses/_simple_status.html.haml
@@ -24,7 +24,7 @@
       %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
         %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
         %button.status__content__spoiler-link= t('statuses.show_more')
-    .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
+    .e-content{ style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
       = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
       - if status.preloadable_poll
         = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
diff --git a/config/deploy.rb b/config/deploy.rb
index c4133e794..4dc36c65c 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-lock '3.11.1'
+lock '3.11.2'
 
 set :repo_url, ENV.fetch('REPO', 'https://github.com/tootsuite/mastodon.git')
 set :branch, ENV.fetch('BRANCH', 'master')
diff --git a/config/initializers/json_ld.rb b/config/initializers/json_ld.rb
index d5575d135..3ed3c4b31 100644
--- a/config/initializers/json_ld.rb
+++ b/config/initializers/json_ld.rb
@@ -1,3 +1,4 @@
 # frozen_string_literal: true
 
 require_relative '../../lib/json_ld/security'
+require_relative '../../lib/json_ld/identity'
diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index f308c2841..d3602e655 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -1,10 +1,11 @@
 # frozen_string_literal: true
 
-Paperclip.options[:read_timeout] = 60
-
 Paperclip.interpolates :filename do |attachment, style|
-  return attachment.original_filename if style == :original
-  [basename(attachment, style), extension(attachment, style)].delete_if(&:blank?).join('.')
+  if style == :original
+    attachment.original_filename
+  else
+    [basename(attachment, style), extension(attachment, style)].delete_if(&:blank?).join('.')
+  end
 end
 
 Paperclip::Attachment.default_options.merge!(
@@ -24,22 +25,27 @@ if ENV['S3_ENABLED'] == 'true'
     storage: :s3,
     s3_protocol: s3_protocol,
     s3_host_name: s3_hostname,
+
     s3_headers: {
       'X-Amz-Multipart-Threshold' => ENV.fetch('S3_MULTIPART_THRESHOLD') { 15.megabytes }.to_i,
       'Cache-Control' => 'public, max-age=315576000, immutable',
     },
+
     s3_permissions: ENV.fetch('S3_PERMISSION') { 'public-read' },
     s3_region: s3_region,
+
     s3_credentials: {
       bucket: ENV['S3_BUCKET'],
       access_key_id: ENV['AWS_ACCESS_KEY_ID'],
       secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
     },
+
     s3_options: {
       signature_version: ENV.fetch('S3_SIGNATURE_VERSION') { 'v4' },
       http_open_timeout: 5,
       http_read_timeout: 5,
       http_idle_timeout: 5,
+      retry_limit: 0,
     }
   )
 
@@ -48,6 +54,7 @@ if ENV['S3_ENABLED'] == 'true'
       endpoint: ENV['S3_ENDPOINT'],
       force_path_style: true
     )
+
     Paperclip::Attachment.default_options[:url] = ':s3_path_url'
   end
 
@@ -73,6 +80,7 @@ elsif ENV['SWIFT_ENABLED'] == 'true'
       openstack_region: ENV['SWIFT_REGION'],
       openstack_cache_ttl: ENV.fetch('SWIFT_CACHE_TTL') { 60 },
     },
+
     fog_directory: ENV['SWIFT_CONTAINER'],
     fog_host: ENV['SWIFT_OBJECT_URL'],
     fog_public: true
@@ -81,7 +89,7 @@ else
   Paperclip::Attachment.default_options.merge!(
     storage: :filesystem,
     use_timestamp: true,
-    path: (ENV['PAPERCLIP_ROOT_PATH'] || ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename',
-    url: (ENV['PAPERCLIP_ROOT_URL'] || '/system') + '/:class/:attachment/:id_partition/:style/:filename',
+    path: ENV.fetch('PAPERCLIP_ROOT_PATH', ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename',
+    url: ENV.fetch('PAPERCLIP_ROOT_URL', '/system') + '/:class/:attachment/:id_partition/:style/:filename',
   )
 end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 2665d0b28..6f742fdf6 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -2,7 +2,7 @@
 en:
   about:
     about_hashtag_html: These are public toots tagged with <strong>#%{hashtag}</strong>. You can interact with them if you have an account anywhere in the fediverse.
-    about_mastodon_html: Mastodon is a social network based on open web protocols and free, open-source software. It is decentralized like e-mail.
+    about_mastodon_html: 'The social network of the future: No ads, no corporate surveillance, ethical design, and decentralization! Own your data with Mastodon!'
     about_this: About
     active_count_after: active
     active_footnote: Monthly Active Users (MAU)
@@ -18,7 +18,6 @@ en:
     discover_users: Discover users
     documentation: Documentation
     federation_hint_html: With an account on %{instance} you'll be able to follow people on any Mastodon server and beyond.
-    generic_description: "%{domain} is one server in the network"
     get_apps: Try a mobile app
     hosted_on: Mastodon hosted on %{domain}
     instance_actor_flash: |
@@ -486,8 +485,8 @@ en:
           open: Anyone can sign up
         title: Registrations mode
       show_known_fediverse_at_about_page:
-        desc_html: When toggled, it will show toots from all the known fediverse on preview. Otherwise it will only show local toots.
-        title: Show known fediverse on timeline preview
+        desc_html: When disabled, restricts the public timeline linked from the landing page to showing only local content
+        title: Include federated content on unauthenticated public timeline page
       show_reblogs_in_public_timelines:
         desc_html: Show public boosts of public toots in local and public timelines.
         title: Show boosts in public timelines
@@ -511,15 +510,18 @@ en:
         title: Custom terms of service
       site_title: Server name
       spam_check_enabled:
-        desc_html: Mastodon can auto-silence and auto-report accounts that send repeated unsolicited messages. There may be false positives.
+        desc_html: Mastodon can auto-report accounts that send repeated unsolicited messages. There may be false positives.
         title: Anti-spam automation
       thumbnail:
         desc_html: Used for previews via OpenGraph and API. 1200x630px recommended
         title: Server thumbnail
       timeline_preview:
-        desc_html: Display public timeline on landing page
-        title: Timeline preview
+        desc_html: Display link to public timeline on landing page and allow API access to the public timeline without authentication
+        title: Allow unauthenticated access to public timeline
       title: Site settings
+      trendable_by_default:
+        desc_html: Affects hashtags that have not been previously disallowed
+        title: Allow hashtags to trend without prior review
       trends:
         desc_html: Publicly display previously reviewed hashtags that are currently trending
         title: Trending hashtags
diff --git a/config/settings.yml b/config/settings.yml
index 0e615f8e8..d13e99831 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -40,6 +40,7 @@ defaults: &defaults
   use_blurhash: true
   use_pending_items: false
   trends: true
+  trendable_by_default: false
   notification_emails:
     follow: false
     reblog: false
diff --git a/db/migrate/20181024224956_migrate_account_conversations.rb b/db/migrate/20181024224956_migrate_account_conversations.rb
index b718f9e1d..d4bc3b91d 100644
--- a/db/migrate/20181024224956_migrate_account_conversations.rb
+++ b/db/migrate/20181024224956_migrate_account_conversations.rb
@@ -52,6 +52,6 @@ class MigrateAccountConversations < ActiveRecord::Migration[5.2]
   end
 
   def notifications_about_direct_statuses
-    Notification.joins(mention: :status).where(activity_type: 'Mention', statuses: { visibility: :direct })
+    Notification.joins('INNER JOIN mentions ON mentions.id = notifications.activity_id INNER JOIN statuses ON statuses.id = mentions.status_id').where(activity_type: 'Mention', statuses: { visibility: :direct })
   end
 end
diff --git a/db/migrate/20191007013357_update_pt_locales.rb b/db/migrate/20191007013357_update_pt_locales.rb
new file mode 100644
index 000000000..b7288d38a
--- /dev/null
+++ b/db/migrate/20191007013357_update_pt_locales.rb
@@ -0,0 +1,11 @@
+class UpdatePtLocales < ActiveRecord::Migration[5.2]
+  disable_ddl_transaction!
+
+  def up
+    User.where(locale: 'pt').in_batches.update_all(locale: 'pt-PT')
+  end
+
+  def down
+    User.where(locale: 'pt-PT').in_batches.update_all(locale: 'pt')
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d9c4f7a37..eb86a9f68 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2019_10_01_213028) do
+ActiveRecord::Schema.define(version: 2019_10_07_013357) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -706,6 +706,30 @@ ActiveRecord::Schema.define(version: 2019_10_01_213028) do
     t.index ["tag_id", "status_id"], name: "index_statuses_tags_on_tag_id_and_status_id", unique: true
   end
 
+  create_table "stream_entries", force: :cascade do |t|
+    t.bigint "activity_id"
+    t.string "activity_type"
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.boolean "hidden", default: false, null: false
+    t.bigint "account_id"
+    t.index ["account_id", "activity_type", "id"], name: "index_stream_entries_on_account_id_and_activity_type_and_id"
+    t.index ["activity_id", "activity_type"], name: "index_stream_entries_on_activity_id_and_activity_type"
+  end
+
+  create_table "subscriptions", force: :cascade do |t|
+    t.string "callback_url", default: "", null: false
+    t.string "secret"
+    t.datetime "expires_at"
+    t.boolean "confirmed", default: false, null: false
+    t.datetime "created_at", null: false
+    t.datetime "updated_at", null: false
+    t.datetime "last_successful_delivery_at"
+    t.string "domain"
+    t.bigint "account_id", null: false
+    t.index ["account_id", "callback_url"], name: "index_subscriptions_on_account_id_and_callback_url", unique: true
+  end
+
   create_table "tags", force: :cascade do |t|
     t.string "name", default: "", null: false
     t.datetime "created_at", null: false
diff --git a/lib/json_ld/identity.rb b/lib/json_ld/identity.rb
new file mode 100644
index 000000000..4fb3f8e9d
--- /dev/null
+++ b/lib/json_ld/identity.rb
@@ -0,0 +1,87 @@
+# -*- encoding: utf-8 -*-
+# frozen_string_literal: true
+# This file generated automatically from http://w3id.org/identity/v1
+require 'json/ld'
+class JSON::LD::Context
+  add_preloaded("http://w3id.org/identity/v1") do
+    new(term_definitions: {
+      "Credential" => TermDefinition.new("Credential", id: "https://w3id.org/credentials#Credential", simple: true),
+      "CryptographicKey" => TermDefinition.new("CryptographicKey", id: "https://w3id.org/security#Key", simple: true),
+      "CryptographicKeyCredential" => TermDefinition.new("CryptographicKeyCredential", id: "https://w3id.org/credentials#CryptographicKeyCredential", simple: true),
+      "EncryptedMessage" => TermDefinition.new("EncryptedMessage", id: "https://w3id.org/security#EncryptedMessage", simple: true),
+      "GraphSignature2012" => TermDefinition.new("GraphSignature2012", id: "https://w3id.org/security#GraphSignature2012", simple: true),
+      "Group" => TermDefinition.new("Group", id: "https://www.w3.org/ns/activitystreams#Group", simple: true),
+      "Identity" => TermDefinition.new("Identity", id: "https://w3id.org/identity#Identity", simple: true),
+      "LinkedDataSignature2015" => TermDefinition.new("LinkedDataSignature2015", id: "https://w3id.org/security#LinkedDataSignature2015", simple: true),
+      "Organization" => TermDefinition.new("Organization", id: "http://schema.org/Organization", simple: true),
+      "Person" => TermDefinition.new("Person", id: "http://schema.org/Person", simple: true),
+      "PostalAddress" => TermDefinition.new("PostalAddress", id: "http://schema.org/PostalAddress", simple: true),
+      "about" => TermDefinition.new("about", id: "http://schema.org/about", type_mapping: "@id"),
+      "accessControl" => TermDefinition.new("accessControl", id: "https://w3id.org/permissions#accessControl", type_mapping: "@id"),
+      "address" => TermDefinition.new("address", id: "http://schema.org/address", type_mapping: "@id"),
+      "addressCountry" => TermDefinition.new("addressCountry", id: "http://schema.org/addressCountry", simple: true),
+      "addressLocality" => TermDefinition.new("addressLocality", id: "http://schema.org/addressLocality", simple: true),
+      "addressRegion" => TermDefinition.new("addressRegion", id: "http://schema.org/addressRegion", simple: true),
+      "cipherAlgorithm" => TermDefinition.new("cipherAlgorithm", id: "https://w3id.org/security#cipherAlgorithm", simple: true),
+      "cipherData" => TermDefinition.new("cipherData", id: "https://w3id.org/security#cipherData", simple: true),
+      "cipherKey" => TermDefinition.new("cipherKey", id: "https://w3id.org/security#cipherKey", simple: true),
+      "claim" => TermDefinition.new("claim", id: "https://w3id.org/credentials#claim", type_mapping: "@id"),
+      "comment" => TermDefinition.new("comment", id: "http://www.w3.org/2000/01/rdf-schema#comment", simple: true),
+      "created" => TermDefinition.new("created", id: "http://purl.org/dc/terms/created", type_mapping: "http://www.w3.org/2001/XMLSchema#dateTime"),
+      "creator" => TermDefinition.new("creator", id: "http://purl.org/dc/terms/creator", type_mapping: "@id"),
+      "cred" => TermDefinition.new("cred", id: "https://w3id.org/credentials#", simple: true, prefix: true),
+      "credential" => TermDefinition.new("credential", id: "https://w3id.org/credentials#credential", type_mapping: "@id"),
+      "dc" => TermDefinition.new("dc", id: "http://purl.org/dc/terms/", simple: true, prefix: true),
+      "description" => TermDefinition.new("description", id: "http://schema.org/description", simple: true),
+      "digestAlgorithm" => TermDefinition.new("digestAlgorithm", id: "https://w3id.org/security#digestAlgorithm", simple: true),
+      "digestValue" => TermDefinition.new("digestValue", id: "https://w3id.org/security#digestValue", simple: true),
+      "domain" => TermDefinition.new("domain", id: "https://w3id.org/security#domain", simple: true),
+      "email" => TermDefinition.new("email", id: "http://schema.org/email", simple: true),
+      "expires" => TermDefinition.new("expires", id: "https://w3id.org/security#expiration", type_mapping: "http://www.w3.org/2001/XMLSchema#dateTime"),
+      "familyName" => TermDefinition.new("familyName", id: "http://schema.org/familyName", simple: true),
+      "givenName" => TermDefinition.new("givenName", id: "http://schema.org/givenName", simple: true),
+      "id" => TermDefinition.new("id", id: "@id", simple: true),
+      "identity" => TermDefinition.new("identity", id: "https://w3id.org/identity#", simple: true, prefix: true),
+      "identityService" => TermDefinition.new("identityService", id: "https://w3id.org/identity#identityService", type_mapping: "@id"),
+      "idp" => TermDefinition.new("idp", id: "https://w3id.org/identity#idp", type_mapping: "@id"),
+      "image" => TermDefinition.new("image", id: "http://schema.org/image", type_mapping: "@id"),
+      "initializationVector" => TermDefinition.new("initializationVector", id: "https://w3id.org/security#initializationVector", simple: true),
+      "issued" => TermDefinition.new("issued", id: "https://w3id.org/credentials#issued", type_mapping: "http://www.w3.org/2001/XMLSchema#dateTime"),
+      "issuer" => TermDefinition.new("issuer", id: "https://w3id.org/credentials#issuer", type_mapping: "@id"),
+      "label" => TermDefinition.new("label", id: "http://www.w3.org/2000/01/rdf-schema#label", simple: true),
+      "member" => TermDefinition.new("member", id: "http://schema.org/member", type_mapping: "@id"),
+      "memberOf" => TermDefinition.new("memberOf", id: "http://schema.org/memberOf", type_mapping: "@id"),
+      "name" => TermDefinition.new("name", id: "http://schema.org/name", simple: true),
+      "nonce" => TermDefinition.new("nonce", id: "https://w3id.org/security#nonce", simple: true),
+      "normalizationAlgorithm" => TermDefinition.new("normalizationAlgorithm", id: "https://w3id.org/security#normalizationAlgorithm", simple: true),
+      "owner" => TermDefinition.new("owner", id: "https://w3id.org/security#owner", type_mapping: "@id"),
+      "password" => TermDefinition.new("password", id: "https://w3id.org/security#password", simple: true),
+      "paymentProcessor" => TermDefinition.new("paymentProcessor", id: "https://w3id.org/payswarm#processor", simple: true),
+      "perm" => TermDefinition.new("perm", id: "https://w3id.org/permissions#", simple: true, prefix: true),
+      "postalCode" => TermDefinition.new("postalCode", id: "http://schema.org/postalCode", simple: true),
+      "preferences" => TermDefinition.new("preferences", id: "https://w3id.org/payswarm#preferences", type_mapping: "@vocab"),
+      "privateKey" => TermDefinition.new("privateKey", id: "https://w3id.org/security#privateKey", type_mapping: "@id"),
+      "privateKeyPem" => TermDefinition.new("privateKeyPem", id: "https://w3id.org/security#privateKeyPem", simple: true),
+      "ps" => TermDefinition.new("ps", id: "https://w3id.org/payswarm#", simple: true, prefix: true),
+      "publicKey" => TermDefinition.new("publicKey", id: "https://w3id.org/security#publicKey", type_mapping: "@id"),
+      "publicKeyPem" => TermDefinition.new("publicKeyPem", id: "https://w3id.org/security#publicKeyPem", simple: true),
+      "publicKeyService" => TermDefinition.new("publicKeyService", id: "https://w3id.org/security#publicKeyService", type_mapping: "@id"),
+      "rdf" => TermDefinition.new("rdf", id: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", simple: true, prefix: true),
+      "rdfs" => TermDefinition.new("rdfs", id: "http://www.w3.org/2000/01/rdf-schema#", simple: true, prefix: true),
+      "recipient" => TermDefinition.new("recipient", id: "https://w3id.org/credentials#recipient", type_mapping: "@id"),
+      "revoked" => TermDefinition.new("revoked", id: "https://w3id.org/security#revoked", type_mapping: "http://www.w3.org/2001/XMLSchema#dateTime"),
+      "schema" => TermDefinition.new("schema", id: "http://schema.org/", simple: true, prefix: true),
+      "sec" => TermDefinition.new("sec", id: "https://w3id.org/security#", simple: true, prefix: true),
+      "signature" => TermDefinition.new("signature", id: "https://w3id.org/security#signature", simple: true),
+      "signatureAlgorithm" => TermDefinition.new("signatureAlgorithm", id: "https://w3id.org/security#signatureAlgorithm", simple: true),
+      "signatureValue" => TermDefinition.new("signatureValue", id: "https://w3id.org/security#signatureValue", simple: true),
+      "streetAddress" => TermDefinition.new("streetAddress", id: "http://schema.org/streetAddress", simple: true),
+      "title" => TermDefinition.new("title", id: "http://purl.org/dc/terms/title", simple: true),
+      "type" => TermDefinition.new("type", id: "@type", simple: true),
+      "url" => TermDefinition.new("url", id: "http://schema.org/url", type_mapping: "@id"),
+      "writePermission" => TermDefinition.new("writePermission", id: "https://w3id.org/permissions#writePermission", type_mapping: "@id"),
+      "xsd" => TermDefinition.new("xsd", id: "http://www.w3.org/2001/XMLSchema#", simple: true, prefix: true)
+    })
+  end
+  alias_preloaded("https://w3id.org/identity/v1", "http://w3id.org/identity/v1")
+end
diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/accounts_cli.rb
index a09a6ab04..6dbb75689 100644
--- a/lib/mastodon/accounts_cli.rb
+++ b/lib/mastodon/accounts_cli.rb
@@ -211,7 +211,6 @@ module Mastodon
     end
 
     option :concurrency, type: :numeric, default: 5, aliases: [:c]
-    option :verbose, type: :boolean, aliases: [:v]
     option :dry_run, type: :boolean
     desc 'cull', 'Remove remote accounts that no longer exist'
     long_desc <<-LONG_DESC
diff --git a/lib/mastodon/cli_helper.rb b/lib/mastodon/cli_helper.rb
index da7348349..ec4d9a81e 100644
--- a/lib/mastodon/cli_helper.rb
+++ b/lib/mastodon/cli_helper.rb
@@ -15,7 +15,12 @@ module Mastodon
     end
 
     def parallelize_with_progress(scope)
-      ActiveRecord::Base.configurations[Rails.env]['pool'] = options[:concurrency]
+      if options[:concurrency] < 1
+        say('Cannot run with this concurrency setting, must be at least 1', :red)
+        exit(1)
+      end
+
+      ActiveRecord::Base.configurations[Rails.env]['pool'] = options[:concurrency] + 1
 
       progress  = create_progress_bar(scope.count)
       pool      = Concurrent::FixedThreadPool.new(options[:concurrency])
@@ -27,17 +32,26 @@ module Mastodon
 
         items.each do |item|
           futures << Concurrent::Future.execute(executor: pool) do
-            ActiveRecord::Base.connection_pool.with_connection do
-              begin
-                progress.log("Processing #{item.id}") if options[:verbose]
-
-                result = yield(item)
-                aggregate.increment(result) if result.is_a?(Integer)
-              rescue => e
-                progress.log pastel.red("Error processing #{item.id}: #{e}")
-              ensure
-                progress.increment
+            begin
+              if !progress.total.nil? && progress.progress + 1 > progress.total
+                # The number of items has changed between start and now,
+                # since there is no good way to predict the final count from
+                # here, just change the progress bar to an indeterminate one
+
+                progress.total = nil
               end
+
+              progress.log("Processing #{item.id}") if options[:verbose]
+
+              result = ActiveRecord::Base.connection_pool.with_connection do
+                yield(item)
+              end
+
+              aggregate.increment(result) if result.is_a?(Integer)
+            rescue => e
+              progress.log pastel.red("Error processing #{item.id}: #{e}")
+            ensure
+              progress.increment
             end
           end
         end
@@ -46,7 +60,7 @@ module Mastodon
         futures.map(&:value)
       end
 
-      progress.finish
+      progress.stop
 
       [total.value, aggregate.value]
     end
diff --git a/lib/mastodon/feeds_cli.rb b/lib/mastodon/feeds_cli.rb
index ea7c90dff..578ea15c5 100644
--- a/lib/mastodon/feeds_cli.rb
+++ b/lib/mastodon/feeds_cli.rb
@@ -27,7 +27,6 @@ module Mastodon
       dry_run = options[:dry_run] ? '(DRY RUN)' : ''
 
       if options[:all] || username.nil?
-
         processed, = parallelize_with_progress(Account.joins(:user).merge(User.active)) do |account|
           PrecomputeFeedService.new.call(account) unless options[:dry_run]
         end
diff --git a/lib/mastodon/media_cli.rb b/lib/mastodon/media_cli.rb
index ec2f36c30..e48175134 100644
--- a/lib/mastodon/media_cli.rb
+++ b/lib/mastodon/media_cli.rb
@@ -50,6 +50,7 @@ module Mastodon
     option :concurrency, type: :numeric, default: 5, aliases: [:c]
     option :verbose, type: :boolean, default: false, aliases: [:v]
     option :dry_run, type: :boolean, default: false
+    option :force, type: :boolean, default: false
     desc 'refresh', 'Fetch remote media files'
     long_desc <<-DESC
       Re-downloads media attachments from other servers. You must specify the
@@ -62,6 +63,9 @@ module Mastodon
       using username@domain handle of the account.
 
       Use the --domain option to download attachments from a specific domain.
+
+      By default, attachments that are believed to be already downloaded will
+      not be re-downloaded. To force re-download of every URL, use --force.
     DESC
     def refresh
       dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
@@ -85,7 +89,7 @@ module Mastodon
       end
 
       processed, aggregate = parallelize_with_progress(scope) do |media_attachment|
-        next if media_attachment.remote_url.blank?
+        next if media_attachment.remote_url.blank? || (!options[:force] && media_attachment.file_file_name.present?)
 
         unless options[:dry_run]
           media_attachment.reset_file!
@@ -97,5 +101,17 @@ module Mastodon
 
       say("Downloaded #{processed} media attachments (approx. #{number_to_human_size(aggregate)})#{dry_run}", :green, true)
     end
+
+    desc 'usage', 'Calculate disk space consumed by Mastodon'
+    def usage
+      say("Attachments:\t#{number_to_human_size(MediaAttachment.sum(:file_file_size))} (#{number_to_human_size(MediaAttachment.where(account: Account.local).sum(:file_file_size))} local)")
+      say("Custom emoji:\t#{number_to_human_size(CustomEmoji.sum(:image_file_size))} (#{number_to_human_size(CustomEmoji.local.sum(:image_file_size))} local)")
+      say("Preview cards:\t#{number_to_human_size(PreviewCard.sum(:image_file_size))}")
+      say("Avatars:\t#{number_to_human_size(Account.sum(:avatar_file_size))} (#{number_to_human_size(Account.local.sum(:avatar_file_size))} local)")
+      say("Headers:\t#{number_to_human_size(Account.sum(:header_file_size))} (#{number_to_human_size(Account.local.sum(:header_file_size))} local)")
+      say("Backups:\t#{number_to_human_size(Backup.sum(:dump_file_size))}")
+      say("Imports:\t#{number_to_human_size(Import.sum(:data_file_size))}")
+      say("Settings:\t#{number_to_human_size(SiteUpload.sum(:file_file_size))}")
+    end
   end
 end
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index 8010e526b..a8e5f0b79 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -13,7 +13,7 @@ module Mastodon
     end
 
     def patch
-      0
+      1
     end
 
     def flags
diff --git a/package.json b/package.json
index 6282f1439..6baaeae58 100644
--- a/package.json
+++ b/package.json
@@ -160,7 +160,7 @@
     "stringz": "^2.0.0",
     "substring-trie": "^1.0.2",
     "terser-webpack-plugin": "^1.4.1",
-    "tesseract.js": "^2.0.0-alpha.15",
+    "tesseract.js": "^2.0.0-alpha.16",
     "throng": "^4.0.0",
     "tiny-queue": "^0.2.1",
     "uuid": "^3.1.0",
@@ -177,7 +177,7 @@
     "babel-jest": "^24.9.0",
     "enzyme": "^3.10.0",
     "enzyme-adapter-react-16": "^1.14.0",
-    "eslint": "^6.4.0",
+    "eslint": "^6.5.0",
     "eslint-plugin-import": "~2.18.2",
     "eslint-plugin-jsx-a11y": "~6.2.3",
     "eslint-plugin-promise": "~4.2.1",
diff --git a/spec/lib/spam_check_spec.rb b/spec/lib/spam_check_spec.rb
index 4cae46111..d4d66a499 100644
--- a/spec/lib/spam_check_spec.rb
+++ b/spec/lib/spam_check_spec.rb
@@ -181,10 +181,6 @@ RSpec.describe SpamCheck do
       described_class.new(status2).flag!
     end
 
-    it 'silences the account' do
-      expect(sender.silenced?).to be true
-    end
-
     it 'creates a report about the account' do
       expect(sender.targeted_reports.unresolved.count).to eq 1
     end
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 3eec464bd..b2f6234cb 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -126,8 +126,8 @@ RSpec.describe Account, type: :model do
       end
 
       it 'sets default avatar, header, avatar_remote_url, and header_remote_url' do
-        expect(account.avatar_remote_url).to eq ''
-        expect(account.header_remote_url).to eq ''
+        expect(account.avatar_remote_url).to eq 'https://remote.test/invalid_avatar'
+        expect(account.header_remote_url).to eq expectation.header_remote_url
         expect(account.avatar_file_name).to  eq nil
         expect(account.header_file_name).to  eq nil
       end
diff --git a/spec/models/concerns/remotable_spec.rb b/spec/models/concerns/remotable_spec.rb
index a4289cc45..99a60cbf6 100644
--- a/spec/models/concerns/remotable_spec.rb
+++ b/spec/models/concerns/remotable_spec.rb
@@ -18,6 +18,8 @@ RSpec.describe Remotable do
 
     def hoge=(arg); end
 
+    def hoge_file_name; end
+
     def hoge_file_name=(arg); end
 
     def has_attribute?(arg); end
@@ -109,12 +111,21 @@ RSpec.describe Remotable do
       end
 
       context 'foo[attribute_name] == url' do
-        it 'makes no request' do
+        it 'makes no request if file is saved' do
           allow(foo).to receive(:[]).with(attribute_name).and_return(url)
+          allow(foo).to receive(:hoge_file_name).and_return('foo.jpg')
 
           foo.hoge_remote_url = url
           expect(request).not_to have_been_requested
         end
+
+        it 'makes request if file is not saved' do
+          allow(foo).to receive(:[]).with(attribute_name).and_return(url)
+          allow(foo).to receive(:hoge_file_name).and_return(nil)
+
+          foo.hoge_remote_url = url
+          expect(request).to have_been_requested
+        end
       end
 
       context "scheme is https, parsed_url.host isn't empty, and foo[attribute_name] != url" do
diff --git a/spec/models/home_feed_spec.rb b/spec/models/home_feed_spec.rb
index 3acb997f1..ee7a83960 100644
--- a/spec/models/home_feed_spec.rb
+++ b/spec/models/home_feed_spec.rb
@@ -34,11 +34,10 @@ RSpec.describe HomeFeed, type: :model do
         Redis.current.set("account:#{account.id}:regeneration", true)
       end
 
-      it 'gets statuses with ids in the range from database' do
+      it 'returns nothing' do
         results = subject.get(3)
 
-        expect(results.map(&:id)).to eq [10, 3, 2]
-        expect(results.first.attributes.keys).to include('id', 'updated_at')
+        expect(results.map(&:id)).to eq []
       end
     end
   end
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index 8e90b92d0..02f533287 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -333,49 +333,6 @@ RSpec.describe Status, type: :model do
     end
   end
 
-  describe '.as_home_timeline' do
-    let(:account) { Fabricate(:account) }
-    let(:followed) { Fabricate(:account) }
-    let(:not_followed) { Fabricate(:account) }
-
-    before do
-      Fabricate(:follow, account: account, target_account: followed)
-
-      @self_status = Fabricate(:status, account: account, visibility: :public)
-      @self_direct_status = Fabricate(:status, account: account, visibility: :direct)
-      @followed_status = Fabricate(:status, account: followed, visibility: :public)
-      @followed_direct_status = Fabricate(:status, account: followed, visibility: :direct)
-      @not_followed_status = Fabricate(:status, account: not_followed, visibility: :public)
-
-      @results = Status.as_home_timeline(account)
-    end
-
-    it 'includes statuses from self' do
-      expect(@results).to include(@self_status)
-    end
-
-    it 'does not include direct statuses from self' do
-      expect(@results).to_not include(@self_direct_status)
-    end
-
-    it 'includes statuses from followed' do
-      expect(@results).to include(@followed_status)
-    end
-
-    it 'does not include direct statuses mentioning recipient from followed' do
-      Fabricate(:mention, account: account, status: @followed_direct_status)
-      expect(@results).to_not include(@followed_direct_status)
-    end
-
-    it 'does not include direct statuses not mentioning recipient from followed' do
-      expect(@results).not_to include(@followed_direct_status)
-    end
-
-    it 'does not include statuses from non-followed' do
-      expect(@results).not_to include(@not_followed_status)
-    end
-  end
-
   describe '.as_direct_timeline' do
     let(:account) { Fabricate(:account) }
     let(:followed) { Fabricate(:account) }
diff --git a/yarn.lock b/yarn.lock
index 32019a6b5..86c72dbef 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3921,10 +3921,10 @@ eslint@^2.7.0:
     text-table "~0.2.0"
     user-home "^2.0.0"
 
-eslint@^6.4.0:
-  version "6.4.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.4.0.tgz#5aa9227c3fbe921982b2eda94ba0d7fae858611a"
-  integrity sha512-WTVEzK3lSFoXUovDHEbkJqCVPEPwbhCq4trDktNI6ygs7aO41d4cDT0JFAT5MivzZeVLWlg7vHL+bgrQv/t3vA==
+eslint@^6.5.0:
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.0.tgz#304623eec903969dd5c9f2d61c6ce3d6ecec8750"
+  integrity sha512-IIbSW+vKOqMatPmS9ayyku4tvWxHY2iricSRtOz6+ZA5IPRlgXzEL0u/j6dr4eha0ugmhMwDTqxtmNu3kj9O4w==
   dependencies:
     "@babel/code-frame" "^7.0.0"
     ajv "^6.10.0"
@@ -4005,12 +4005,7 @@ esrecurse@^4.1.0:
   dependencies:
     estraverse "^4.1.0"
 
-estraverse@^4.0.0, estraverse@^4.2.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
-  integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=
-
-estraverse@^4.1.0, estraverse@^4.1.1:
+estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
   integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
@@ -7009,11 +7004,6 @@ node-fetch@^1.0.1:
     encoding "^0.1.11"
     is-stream "^1.0.1"
 
-node-fetch@^2.3.0:
-  version "2.6.0"
-  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
-  integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
-
 node-forge@0.8.2:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.2.tgz#b4bcc59fb12ce77a8825fc6a783dfe3182499c5a"
@@ -9398,21 +9388,16 @@ selfsigned@^1.10.6:
   dependencies:
     node-forge "0.8.2"
 
-"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.1, semver@^5.7.0:
-  version "5.7.0"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
-  integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
 
 semver@4.3.2:
   version "4.3.2"
   resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7"
   integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=
 
-semver@^5.3.0, semver@^5.5.0, semver@^5.6.0:
-  version "5.7.1"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
-  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
-
 semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
   version "6.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@@ -10103,10 +10088,10 @@ terser@^4.1.2:
     source-map "~0.6.1"
     source-map-support "~0.5.12"
 
-tesseract.js-core@^2.0.0-beta.11:
-  version "2.0.0-beta.11"
-  resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-2.0.0-beta.11.tgz#c35e3e689efad30138603977ad7eaaac44c7fd37"
-  integrity sha512-07haKH2JYYo0OfIJoioMS9dDiI5Hrl7+r1MqjeNAAT5WpKO0ATe4cpncC8s1kz0e3s1kaC5WOwL3YJcjbJE+hg==
+tesseract.js-core@^2.0.0-beta.12:
+  version "2.0.0-beta.13"
+  resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-2.0.0-beta.13.tgz#a21d798e88098898a9bdd935d0553215e03274f8"
+  integrity sha512-GboWV/aV5h+Whito6L6Q3WCFZ2+lgxZGgjY84wSpWbTLEkkZgHsU+dz1or+3rWSABH/nuzHDco1bZRk5+f94mw==
 
 tesseract.js-utils@^1.0.0-beta.8:
   version "1.0.0-beta.8"
@@ -10120,18 +10105,17 @@ tesseract.js-utils@^1.0.0-beta.8:
     is-url "^1.2.4"
     zlibjs "^0.3.1"
 
-tesseract.js@^2.0.0-alpha.15:
-  version "2.0.0-alpha.15"
-  resolved "https://registry.yarnpkg.com/tesseract.js/-/tesseract.js-2.0.0-alpha.15.tgz#9887f4d1c10e25bb098fde7a10580c865c362fad"
-  integrity sha512-qM1XUFVlTO+tx6oVRpd9QQ8PwQLxo3qhbfIHByUlUVIqWx6y/U9xlHIaG033/Tjfs2EQ0NAehPTOJ+eNElsXEg==
+tesseract.js@^2.0.0-alpha.16:
+  version "2.0.0-alpha.16"
+  resolved "https://registry.yarnpkg.com/tesseract.js/-/tesseract.js-2.0.0-alpha.16.tgz#1e17717234a1464481abe12283f2c3ac79603d2e"
+  integrity sha512-8g3je2Kl8rkAFtpmwilGGj+8rCiPClNQaCjW6IafOPNn7hzFnVdL6fU6rG1Xsrc4Twv0HOa75kbpx5u70/WbTA==
   dependencies:
     axios "^0.18.0"
     check-types "^7.4.0"
     is-url "1.2.2"
-    node-fetch "^2.3.0"
     opencollective-postinstall "^2.0.2"
     resolve-url "^0.2.1"
-    tesseract.js-core "^2.0.0-beta.11"
+    tesseract.js-core "^2.0.0-beta.12"
     tesseract.js-utils "^1.0.0-beta.8"
 
 test-exclude@^5.0.0: