123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- <?php
- namespace App\Http\Controllers;
- use Illuminate\Http\Request;
- use App\Models\ImportPost;
- use App\Services\ImportService;
- use App\Services\StatusService;
- use App\Http\Resources\ImportStatus;
- use App\Follower;
- use App\User;
- class ImportPostController extends Controller
- {
- public function __construct()
- {
- $this->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'),
- 'allow_image_webp' => config('import.instagram.allow_image_webp') && str_contains(config_cache('pixelfed.media_types'), 'image/webp'),
- '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)
- ->has('status')
- ->cursorPaginate(9)
- );
- }
- public function formatHashtags($val = false)
- {
- if(!$val || !strlen($val)) {
- return null;
- }
- $groupedHashtagRegex = '/#\w+(?=#)/';
- return preg_replace($groupedHashtagRegex, '$0 ', $val);
- }
- public function store(Request $request)
- {
- abort_unless(config('import.instagram.enabled'), 404);
- $this->checkPermissions($request);
- $uid = $request->user()->id;
- $pid = $request->user()->profile_id;
- $successCount = 0;
- $errors = [];
- foreach($request->input('files') as $file) {
- try {
- $media = $file['media'];
- $c = collect($media);
- $firstUri = isset($media[0]['uri']) ? $media[0]['uri'] : '';
- $postHash = hash('sha256', $c->toJson() . $firstUri);
- $exists = ImportPost::where('user_id', $uid)
- ->where('post_hash', $postHash)
- ->exists();
- if ($exists) {
- $errors[] = "Duplicate post detected. Skipping...";
- continue;
- }
- $exts = $c->map(function($m) {
- $fn = last(explode('/', $m['uri']));
- return last(explode('.', $fn));
- });
- $postType = $this->determinePostType($exts);
- $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' => $this->formatHashtags($m['title'] ?? ''),
- 'creation_timestamp' => $m['creation_timestamp'] ?? null
- ];
- })->toArray();
- $ip->caption = $c->count() > 1 ?
- $this->formatHashtags($file['title'] ?? '') :
- $this->formatHashtags($ip->media[0]['title'] ?? '');
- $originalFilename = last(explode('/', $ip->media[0]['uri'] ?? ''));
- $ip->filename = $this->sanitizeFilename($originalFilename);
- $ip->metadata = $c->map(function($m) {
- return [
- 'uri' => $m['uri'],
- 'media_metadata' => isset($m['media_metadata']) ? $m['media_metadata'] : null
- ];
- })->toArray();
- $creationTimestamp = $c->count() > 1 ?
- ($file['creation_timestamp'] ?? null) :
- ($media[0]['creation_timestamp'] ?? null);
- if ($creationTimestamp) {
- $ip->creation_date = now()->parse($creationTimestamp);
- $ip->creation_year = $ip->creation_date->format('y');
- $ip->creation_month = $ip->creation_date->format('m');
- $ip->creation_day = $ip->creation_date->format('d');
- } else {
- $ip->creation_date = now();
- $ip->creation_year = now()->format('y');
- $ip->creation_month = now()->format('m');
- $ip->creation_day = now()->format('d');
- }
- $ip->save();
- $successCount++;
- ImportService::getImportedFiles($pid, true);
- ImportService::getPostCount($pid, true);
- } catch (\Exception $e) {
- $errors[] = $e->getMessage();
- \Log::error('Import error: ' . $e->getMessage());
- continue;
- }
- }
- return [
- 'success' => true,
- 'msg' => 'Successfully imported ' . $successCount . ' posts',
- 'errors' => $errors
- ];
- }
- public function storeMedia(Request $request)
- {
- abort_unless(config('import.instagram.enabled'), 404);
- $this->checkPermissions($request);
- $allowedMimeTypes = ['image/png', 'image/jpeg'];
- if (config('import.instagram.allow_image_webp') && str_contains(config_cache('pixelfed.media_types'), 'image/webp')) {
- $allowedMimeTypes[] = 'image/webp';
- }
- if (config('import.instagram.allow_video_posts')) {
- $allowedMimeTypes[] = 'video/mp4';
- }
- $mimes = 'mimetypes:' . implode(',', $allowedMimeTypes);
- $this->validate($request, [
- 'file' => 'required|array|max:10',
- 'file.*' => [
- 'required',
- 'file',
- $mimes,
- 'max:' . config_cache('pixelfed.max_photo_size')
- ]
- ]);
- foreach($request->file('file') as $file) {
- $extension = $file->getClientOriginalExtension();
- $originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
- $safeFilename = preg_replace('/[^a-zA-Z0-9_.-]/', '_', $originalName);
- $fileName = $safeFilename . '.' . $extension;
- $file->storeAs('imports/' . $request->user()->id . '/', $fileName);
- }
- ImportService::getImportedFiles($request->user()->profile_id, true);
- return [
- 'msg' => 'Success'
- ];
- }
- private function determinePostType($exts)
- {
- if ($exts->count() > 1) {
- if ($exts->contains('mp4')) {
- if ($exts->contains('jpg', 'png', 'webp')) {
- return 'photo:video:album';
- } else {
- return 'video:album';
- }
- } else {
- return 'photo:album';
- }
- } else {
- if ($exts->isEmpty()) {
- return 'photo';
- }
- $ext = $exts[0];
- if (in_array($ext, ['jpg', 'jpeg', 'png', 'webp'])) {
- return 'photo';
- } else if (in_array($ext, ['mp4'])) {
- return 'video';
- } else {
- return 'photo';
- }
- }
- }
- private function sanitizeFilename($filename)
- {
- $parts = explode('.', $filename);
- $extension = array_pop($parts);
- $originalName = implode('.', $parts);
- $safeFilename = preg_replace('/[^a-zA-Z0-9_.-]/', '_', $originalName);
- return $safeFilename . '.' . $extension;
- }
- 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;
- }
- }
- }
|