123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- <?php
- namespace App\Jobs\RemoteFollowPipeline;
- use Zttp\Zttp;
- use Log, Storage;
- use Carbon\Carbon;
- use Illuminate\Http\File;
- use App\{Media, Profile, Status};
- use Illuminate\Bus\Queueable;
- use Illuminate\Queue\SerializesModels;
- use Illuminate\Queue\InteractsWithQueue;
- use Illuminate\Contracts\Queue\ShouldQueue;
- use Illuminate\Foundation\Bus\Dispatchable;
- use App\Jobs\StatusPipeline\NewStatusPipeline;
- class RemoteFollowImportRecent implements ShouldQueue
- {
- use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
- protected $actor;
- protected $profile;
- protected $outbox;
- protected $mediaCount;
- protected $cursor;
- protected $nextUrl;
- protected $supported;
- /**
- * Create a new job instance.
- *
- * @return void
- */
- public function __construct($actorObject, $profile)
- {
- $this->actor = $actorObject;
- $this->profile = $profile;
- $this->cursor = 1;
- $this->mediaCount = 0;
- $this->supported = [
- 'image/jpg',
- 'image/jpeg',
- 'image/png',
- 'image/gif'
- ];
- }
- /**
- * Execute the job.
- *
- * @return void
- */
- public function handle()
- {
- $outbox = $this->fetchOutbox();
- }
- public function fetchOutbox($url = false)
- {
- Log::info(json_encode($url));
- $url = ($url == false) ? $this->actor['outbox'] : $url;
- $response = Zttp::withHeaders([
- 'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org'
- ])->get($url);
- $this->outbox = $response->json();
- $this->parseOutbox($this->outbox);
- }
- public function parseOutbox($outbox)
- {
- $types = ['OrderedCollection', 'OrderedCollectionPage'];
- if(isset($outbox['totalItems']) && $outbox['totalItems'] < 1) {
- // Skip remote fetch, not enough posts
- Log::info('not enough items');
- return;
- }
- if(isset($outbox['type']) && in_array($outbox['type'], $types)) {
- Log::info('handle ordered collection');
- $this->handleOrderedCollection();
- }
- }
- public function handleOrderedCollection()
- {
- $outbox = $this->outbox;
- if(!isset($outbox['next']) && !isset($outbox['first']['next']) && $this->cursor !== 1) {
- $this->cursor = 40;
- $outbox['next'] = false;
- }
- if($outbox['type'] == 'OrderedCollectionPage') {
- $this->nextUrl = $outbox['next'];
- }
- if(isset($outbox['first']) && !is_array($outbox['first'])) {
- // Mastodon detected
- Log::info('Mastodon detected...');
- $this->nextUrl = $outbox['first'];
- return $this->fetchOutbox($this->nextUrl);
- } else {
- // Pleroma detected.
- $this->nextUrl = isset($outbox['next']) ? $outbox['next'] : (isset($outbox['first']['next']) ? $outbox['first']['next'] : $outbox['next']);
- Log::info('Checking ordered items...');
- $orderedItems = isset($outbox['orderedItems']) ? $outbox['orderedItems'] : $outbox['first']['orderedItems'];
- }
- foreach($orderedItems as $item) {
- Log::info('Parsing items...');
- $parsed = $this->parseObject($item);
- if($parsed !== 0) {
- Log::info('Found media!');
- $this->importActivity($item);
- }
- }
- if($this->cursor < 40 && $this->mediaCount < 9) {
- $this->cursor++;
- $this->mediaCount++;
- $this->fetchOutbox($this->nextUrl);
- }
- }
- public function parseObject($parsed)
- {
- if($parsed['type'] !== 'Create') {
- return 0;
- }
- $activity = $parsed['object'];
- if(isset($activity['attachment']) && !empty($activity['attachment'])) {
- return $this->detectSupportedMedia($activity['attachment']);
- }
- }
- public function detectSupportedMedia($attachments)
- {
- $supported = $this->supported;
- $count = 0;
- foreach($attachments as $media) {
- $mime = $media['mediaType'];
- $count = in_array($mime, $supported) ? ($count + 1) : $count;
- }
- return $count;
- }
- public function importActivity($activity)
- {
- $profile = $this->profile;
- $supported = $this->supported;
- $attachments = $activity['object']['attachment'];
- $caption = str_limit($activity['object']['content'], 125);
- if(Status::whereUrl($activity['id'])->count() !== 0) {
- return true;
- }
- $status = new Status;
- $status->profile_id = $profile->id;
- $status->url = $activity['id'];
- $status->local = false;
- $status->caption = strip_tags($caption);
- $status->created_at = Carbon::parse($activity['published']);
- $count = 0;
- foreach($attachments as $media) {
- Log::info($media['mediaType'] . ' - ' . $media['url']);
- $url = $media['url'];
- $mime = $media['mediaType'];
- if(!in_array($mime, $supported)) {
- Log::info('Invalid media, skipping. ' . $mime);
- continue;
- }
- $count++;
- if($count === 1) {
- $status->save();
- }
- $this->importMedia($url, $mime, $status);
- }
- Log::info(count($attachments) . ' media found...');
- if($count !== 0) {
- NewStatusPipeline::dispatch($status, $status->media->first());
- }
- }
- public function importMedia($url, $mime, $status)
- {
- $user = $this->profile;
- $monthHash = hash('sha1', date('Y') . date('m'));
- $userHash = hash('sha1', $user->id . (string) $user->created_at);
- $storagePath = "public/m/{$monthHash}/{$userHash}";
- try {
- $info = pathinfo($url);
- $img = file_get_contents($url);
- $file = '/tmp/' . str_random(12) . $info['basename'];
- file_put_contents($file, $img);
- $path = Storage::putFile($storagePath, new File($file), 'public');
-
- $media = new Media;
- $media->status_id = $status->id;
- $media->profile_id = $status->profile_id;
- $media->user_id = null;
- $media->media_path = $path;
- $media->size = 0;
- $media->mime = $mime;
- $media->save();
- return true;
- } catch (Exception $e) {
- return false;
- }
- }
- }
|