StatusRemoteUpdatePipeline.php 4.9 KB

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