diff --git a/app/Http/Controllers/ImportPostController.php b/app/Http/Controllers/ImportPostController.php new file mode 100644 index 000000000..5acd42ae7 --- /dev/null +++ b/app/Http/Controllers/ImportPostController.php @@ -0,0 +1,298 @@ +middleware('auth'); + } + + public function getConfig(Request $request) + { + return [ + 'enabled' => config('import.instagram.enabled'), + + 'limits' => [ + 'max_posts' => config('import.instagram.limits.max_posts'), + 'max_attempts' => config('import.instagram.limits.max_attempts'), + ], + + 'allow_video_posts' => config('import.instagram.allow_video_posts'), + + 'permissions' => [ + 'admins_only' => config('import.instagram.permissions.admins_only'), + 'admin_follows_only' => config('import.instagram.permissions.admin_follows_only'), + 'min_account_age' => config('import.instagram.permissions.min_account_age'), + 'min_follower_count' => config('import.instagram.permissions.min_follower_count'), + ], + + 'allowed' => $this->checkPermissions($request, false) + ]; + } + + public function getProcessingCount(Request $request) + { + abort_unless(config('import.instagram.enabled'), 404); + + $processing = ImportPost::whereProfileId($request->user()->profile_id) + ->whereNull('status_id') + ->whereSkipMissingMedia(false) + ->count(); + + $finished = ImportPost::whereProfileId($request->user()->profile_id) + ->whereNotNull('status_id') + ->whereSkipMissingMedia(false) + ->count(); + + return response()->json([ + 'processing_count' => $processing, + 'finished_count' => $finished, + ]); + } + + public function getImportedFiles(Request $request) + { + abort_unless(config('import.instagram.enabled'), 404); + + return response()->json( + ImportService::getImportedFiles($request->user()->profile_id), + 200, + [], + JSON_UNESCAPED_SLASHES + ); + } + + public function getImportedPosts(Request $request) + { + abort_unless(config('import.instagram.enabled'), 404); + + return ImportStatus::collection( + ImportPost::whereProfileId($request->user()->profile_id) + ->whereNotNull('status_id') + ->cursorPaginate(9) + ); + } + + public function store(Request $request) + { + abort_unless(config('import.instagram.enabled'), 404); + $this->checkPermissions($request); + + $uid = $request->user()->id; + $pid = $request->user()->profile_id; + foreach($request->input('files') as $file) { + $media = $file['media']; + $c = collect($media); + $postHash = hash('sha256', $c->toJson()); + $exts = $c->map(function($m) { + $fn = last(explode('/', $m['uri'])); + return last(explode('.', $fn)); + }); + $postType = 'photo'; + + if($exts->count() > 1) { + if($exts->contains('mp4')) { + if($exts->contains('jpg', 'png')) { + $postType = 'photo:video:album'; + } else { + $postType = 'video:album'; + } + } else { + $postType = 'photo:album'; + } + } else { + if(in_array($exts[0], ['jpg', 'png'])) { + $postType = 'photo'; + } else if(in_array($exts[0], ['mp4'])) { + $postType = 'video'; + } + } + + $ip = new ImportPost; + $ip->user_id = $uid; + $ip->profile_id = $pid; + $ip->post_hash = $postHash; + $ip->service = 'instagram'; + $ip->post_type = $postType; + $ip->media_count = $c->count(); + $ip->media = $c->map(function($m) { + return [ + 'uri' => $m['uri'], + 'title' => $m['title'], + 'creation_timestamp' => $m['creation_timestamp'] + ]; + })->toArray(); + $ip->caption = $c->count() > 1 ? $file['title'] : $ip->media[0]['title']; + $ip->filename = last(explode('/', $ip->media[0]['uri'])); + $ip->metadata = $c->map(function($m) { + return [ + 'uri' => $m['uri'], + 'media_metadata' => isset($m['media_metadata']) ? $m['media_metadata'] : null + ]; + })->toArray(); + $ip->creation_date = $c->count() > 1 ? now()->parse($file['creation_timestamp']) : now()->parse($media[0]['creation_timestamp']); + $ip->creation_year = now()->parse($ip->creation_date)->format('y'); + $ip->creation_month = now()->parse($ip->creation_date)->format('m'); + $ip->creation_day = now()->parse($ip->creation_date)->format('d'); + $ip->save(); + + ImportService::getImportedFiles($pid, true); + ImportService::getPostCount($pid, true); + } + return [ + 'msg' => 'Success' + ]; + } + + public function storeMedia(Request $request) + { + abort_unless(config('import.instagram.enabled'), 404); + + $this->checkPermissions($request); + + $mimes = config('import.allow_video_posts') ? 'mimetypes:image/png,image/jpeg,video/mp4' : 'mimetypes:image/png,image/jpeg'; + + $this->validate($request, [ + 'file' => 'required|array|max:10', + 'file.*' => [ + 'required', + 'file', + $mimes, + 'max:' . config('pixelfed.max_photo_size') + ] + ]); + + foreach($request->file('file') as $file) { + $fileName = $file->getClientOriginalName(); + $file->storeAs('imports/' . $request->user()->id . '/', $fileName); + } + + ImportService::getImportedFiles($request->user()->profile_id, true); + + return [ + 'msg' => 'Success' + ]; + } + + protected function checkPermissions($request, $abortOnFail = true) + { + $user = $request->user(); + + if($abortOnFail) { + abort_unless(config('import.instagram.enabled'), 404); + } + + if($user->is_admin) { + if(!$abortOnFail) { + return true; + } else { + return; + } + } + + $admin = User::whereIsAdmin(true)->first(); + + if(config('import.instagram.permissions.admins_only')) { + if($abortOnFail) { + abort_unless($user->is_admin, 404, 'Only admins can use this feature.'); + } else { + if(!$user->is_admin) { + return false; + } + } + } + + if(config('import.instagram.permissions.admin_follows_only')) { + $exists = Follower::whereProfileId($admin->profile_id) + ->whereFollowingId($user->profile_id) + ->exists(); + if($abortOnFail) { + abort_unless( + $exists, + 404, + 'Only admins, and accounts they follow can use this feature' + ); + } else { + if(!$exists) { + return false; + } + } + } + + if(config('import.instagram.permissions.min_account_age')) { + $res = $user->created_at->lt( + now()->subDays(config('import.instagram.permissions.min_account_age')) + ); + if($abortOnFail) { + abort_unless( + $res, + 404, + 'Your account is too new to use this feature' + ); + } else { + if(!$res) { + return false; + } + } + } + + if(config('import.instagram.permissions.min_follower_count')) { + $res = Follower::whereFollowingId($user->profile_id)->count() >= config('import.instagram.permissions.min_follower_count'); + if($abortOnFail) { + abort_unless( + $res, + 404, + 'You don\'t have enough followers to use this feature' + ); + } else { + if(!$res) { + return false; + } + } + } + + if(intval(config('import.instagram.limits.max_posts')) > 0) { + $res = ImportService::getPostCount($user->profile_id) >= intval(config('import.instagram.limits.max_posts')); + if($abortOnFail) { + abort_if( + $res, + 404, + 'You have reached the limit of post imports and cannot import any more posts' + ); + } else { + if($res) { + return false; + } + } + } + + if(intval(config('import.instagram.limits.max_attempts')) > 0) { + $res = ImportService::getAttempts($user->profile_id) >= intval(config('import.instagram.limits.max_attempts')); + if($abortOnFail) { + abort_if( + $res, + 404, + 'You have reached the limit of post import attempts and cannot import any more posts' + ); + } else { + if($res) { + return false; + } + } + } + + if(!$abortOnFail) { + return true; + } + } +}