StatusRemoteUpdatePipeline.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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(Purify::clean($activity['content']));
  109. }
  110. if (isset($activity['sensitive'])) {
  111. if ((bool) $activity['sensitive'] == false) {
  112. $status->is_nsfw = false;
  113. $exists = ModLog::whereObjectType('App\Status::class')
  114. ->whereObjectId($status->id)
  115. ->whereAction('admin.status.moderate')
  116. ->exists();
  117. if ($exists == true) {
  118. $status->is_nsfw = true;
  119. }
  120. $profile = Profile::find($status->profile_id);
  121. if (! $profile || $profile->cw == true) {
  122. $status->is_nsfw = true;
  123. }
  124. } else {
  125. $status->is_nsfw = true;
  126. }
  127. }
  128. if (isset($activity['summary'])) {
  129. $status->cw_summary = Purify::clean($activity['summary']);
  130. } else {
  131. $status->cw_summary = null;
  132. }
  133. $status->edited_at = now();
  134. $status->save();
  135. StatusService::del($status->id);
  136. }
  137. protected function createEdit($status, $activity)
  138. {
  139. $cleaned = isset($activity['content']) ? Purify::clean($activity['content']) : null;
  140. $spoiler_text = isset($activity['summary']) ? Purify::clean($activity['summary']) : null;
  141. $sensitive = isset($activity['sensitive']) ? $activity['sensitive'] : null;
  142. $mids = $status->media()->count() ? $status->media()->orderBy('order')->pluck('id')->toArray() : null;
  143. StatusEdit::create([
  144. 'status_id' => $status->id,
  145. 'profile_id' => $status->profile_id,
  146. 'caption' => $cleaned,
  147. 'spoiler_text' => $spoiler_text,
  148. 'is_nsfw' => $sensitive,
  149. 'ordered_media_attachment_ids' => $mids,
  150. ]);
  151. }
  152. }