StatusRemoteUpdatePipeline.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <?php
  2. namespace App\Jobs\StatusPipeline;
  3. use App\Media;
  4. use App\Models\StatusEdit;
  5. use App\ModLog;
  6. use App\Profile;
  7. use App\Services\StatusService;
  8. use App\Status;
  9. use Illuminate\Bus\Queueable;
  10. use Illuminate\Contracts\Queue\ShouldQueue;
  11. use Illuminate\Foundation\Bus\Dispatchable;
  12. use Illuminate\Queue\InteractsWithQueue;
  13. use Illuminate\Queue\SerializesModels;
  14. use Illuminate\Support\Facades\Http;
  15. use Purify;
  16. class StatusRemoteUpdatePipeline implements ShouldQueue
  17. {
  18. use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
  19. public $activity;
  20. /**
  21. * Create a new job instance.
  22. */
  23. public function __construct($activity)
  24. {
  25. $this->activity = $activity;
  26. }
  27. /**
  28. * Execute the job.
  29. */
  30. public function handle(): void
  31. {
  32. $activity = $this->activity;
  33. $status = Status::with('media')->whereObjectUrl($activity['id'])->first();
  34. if (! $status) {
  35. return;
  36. }
  37. $this->createPreviousEdit($status);
  38. $this->updateMedia($status, $activity);
  39. $this->updateImmediateAttributes($status, $activity);
  40. $this->createEdit($status, $activity);
  41. }
  42. protected function createPreviousEdit($status)
  43. {
  44. if (! $status->edits()->count()) {
  45. StatusEdit::create([
  46. 'status_id' => $status->id,
  47. 'profile_id' => $status->profile_id,
  48. 'caption' => $status->caption,
  49. 'spoiler_text' => $status->cw_summary,
  50. 'is_nsfw' => $status->is_nsfw,
  51. 'ordered_media_attachment_ids' => $status->media()->orderBy('order')->pluck('id')->toArray(),
  52. 'created_at' => $status->created_at,
  53. ]);
  54. }
  55. }
  56. protected function updateMedia($status, $activity)
  57. {
  58. if (! isset($activity['attachment'])) {
  59. return;
  60. }
  61. $ogm = $status->media->count() ? $status->media()->orderBy('order')->get() : collect([]);
  62. $nm = collect($activity['attachment'])->filter(function ($nm) {
  63. return isset(
  64. $nm['type'],
  65. $nm['mediaType'],
  66. $nm['url']
  67. ) &&
  68. in_array($nm['type'], ['Document', 'Image', 'Video']) &&
  69. in_array($nm['mediaType'], explode(',', config_cache('pixelfed.media_types')));
  70. });
  71. // Skip when no media
  72. if (! $ogm->count() && ! $nm->count()) {
  73. return;
  74. }
  75. Media::whereProfileId($status->profile_id)
  76. ->whereStatusId($status->id)
  77. ->update([
  78. 'status_id' => null,
  79. ]);
  80. $nm->each(function ($n, $key) use ($status) {
  81. $res = Http::withOptions(['allow_redirects' => false])->retry(3, 100, throw: false)->head($n['url']);
  82. if (! $res->successful()) {
  83. return;
  84. }
  85. if (! in_array($res->header('content-type'), explode(',', config_cache('pixelfed.media_types')))) {
  86. return;
  87. }
  88. $m = new Media;
  89. $m->status_id = $status->id;
  90. $m->profile_id = $status->profile_id;
  91. $m->remote_media = true;
  92. $m->media_path = $n['url'];
  93. $m->mime = $res->header('content-type');
  94. $m->size = $res->hasHeader('content-length') ? $res->header('content-length') : null;
  95. $m->caption = isset($n['name']) && ! empty($n['name']) ? Purify::clean($n['name']) : null;
  96. $m->remote_url = $n['url'];
  97. $m->blurhash = isset($n['blurhash']) && (strlen($n['blurhash']) < 50) ? $n['blurhash'] : null;
  98. $m->width = isset($n['width']) && ! empty($n['width']) ? $n['width'] : null;
  99. $m->height = isset($n['height']) && ! empty($n['height']) ? $n['height'] : null;
  100. $m->skip_optimize = true;
  101. $m->order = $key + 1;
  102. $m->save();
  103. });
  104. }
  105. protected function updateImmediateAttributes($status, $activity)
  106. {
  107. if (isset($activity['content'])) {
  108. $status->caption = strip_tags($activity['content']);
  109. $status->rendered = Purify::clean($activity['content']);
  110. }
  111. if (isset($activity['sensitive'])) {
  112. if ((bool) $activity['sensitive'] == false) {
  113. $status->is_nsfw = false;
  114. $exists = ModLog::whereObjectType('App\Status::class')
  115. ->whereObjectId($status->id)
  116. ->whereAction('admin.status.moderate')
  117. ->exists();
  118. if ($exists == true) {
  119. $status->is_nsfw = true;
  120. }
  121. $profile = Profile::find($status->profile_id);
  122. if (! $profile || $profile->cw == true) {
  123. $status->is_nsfw = true;
  124. }
  125. } else {
  126. $status->is_nsfw = true;
  127. }
  128. }
  129. if (isset($activity['summary'])) {
  130. $status->cw_summary = Purify::clean($activity['summary']);
  131. } else {
  132. $status->cw_summary = null;
  133. }
  134. $status->edited_at = now();
  135. $status->save();
  136. StatusService::del($status->id);
  137. }
  138. protected function createEdit($status, $activity)
  139. {
  140. $cleaned = isset($activity['content']) ? Purify::clean($activity['content']) : null;
  141. $spoiler_text = isset($activity['summary']) ? Purify::clean($activity['summary']) : null;
  142. $sensitive = isset($activity['sensitive']) ? $activity['sensitive'] : null;
  143. $mids = $status->media()->count() ? $status->media()->orderBy('order')->pluck('id')->toArray() : null;
  144. StatusEdit::create([
  145. 'status_id' => $status->id,
  146. 'profile_id' => $status->profile_id,
  147. 'caption' => $cleaned,
  148. 'spoiler_text' => $spoiler_text,
  149. 'is_nsfw' => $sensitive,
  150. 'ordered_media_attachment_ids' => $mids,
  151. ]);
  152. }
  153. }