ImportPostController.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <?php
  2. namespace App\Http\Controllers;
  3. use Illuminate\Http\Request;
  4. use App\Models\ImportPost;
  5. use App\Services\ImportService;
  6. use App\Services\StatusService;
  7. use App\Http\Resources\ImportStatus;
  8. use App\Follower;
  9. use App\User;
  10. class ImportPostController extends Controller
  11. {
  12. public function __construct()
  13. {
  14. $this->middleware('auth');
  15. }
  16. public function getConfig(Request $request)
  17. {
  18. return [
  19. 'enabled' => config('import.instagram.enabled'),
  20. 'limits' => [
  21. 'max_posts' => config('import.instagram.limits.max_posts'),
  22. 'max_attempts' => config('import.instagram.limits.max_attempts'),
  23. ],
  24. 'allow_video_posts' => config('import.instagram.allow_video_posts'),
  25. 'permissions' => [
  26. 'admins_only' => config('import.instagram.permissions.admins_only'),
  27. 'admin_follows_only' => config('import.instagram.permissions.admin_follows_only'),
  28. 'min_account_age' => config('import.instagram.permissions.min_account_age'),
  29. 'min_follower_count' => config('import.instagram.permissions.min_follower_count'),
  30. ],
  31. 'allowed' => $this->checkPermissions($request, false)
  32. ];
  33. }
  34. public function getProcessingCount(Request $request)
  35. {
  36. abort_unless(config('import.instagram.enabled'), 404);
  37. $processing = ImportPost::whereProfileId($request->user()->profile_id)
  38. ->whereNull('status_id')
  39. ->whereSkipMissingMedia(false)
  40. ->count();
  41. $finished = ImportPost::whereProfileId($request->user()->profile_id)
  42. ->whereNotNull('status_id')
  43. ->whereSkipMissingMedia(false)
  44. ->count();
  45. return response()->json([
  46. 'processing_count' => $processing,
  47. 'finished_count' => $finished,
  48. ]);
  49. }
  50. public function getImportedFiles(Request $request)
  51. {
  52. abort_unless(config('import.instagram.enabled'), 404);
  53. return response()->json(
  54. ImportService::getImportedFiles($request->user()->profile_id),
  55. 200,
  56. [],
  57. JSON_UNESCAPED_SLASHES
  58. );
  59. }
  60. public function getImportedPosts(Request $request)
  61. {
  62. abort_unless(config('import.instagram.enabled'), 404);
  63. return ImportStatus::collection(
  64. ImportPost::whereProfileId($request->user()->profile_id)
  65. ->has('status')
  66. ->cursorPaginate(9)
  67. );
  68. }
  69. public function formatHashtags($val = false)
  70. {
  71. if(!$val || !strlen($val)) {
  72. return null;
  73. }
  74. $groupedHashtagRegex = '/#\w+(?=#)/';
  75. return preg_replace($groupedHashtagRegex, '$0 ', $val);
  76. }
  77. public function store(Request $request)
  78. {
  79. abort_unless(config('import.instagram.enabled'), 404);
  80. $this->checkPermissions($request);
  81. $uid = $request->user()->id;
  82. $pid = $request->user()->profile_id;
  83. foreach($request->input('files') as $file) {
  84. $media = $file['media'];
  85. $c = collect($media);
  86. $postHash = hash('sha256', $c->toJson());
  87. $exts = $c->map(function($m) {
  88. $fn = last(explode('/', $m['uri']));
  89. return last(explode('.', $fn));
  90. });
  91. $postType = 'photo';
  92. if($exts->count() > 1) {
  93. if($exts->contains('mp4')) {
  94. if($exts->contains('jpg', 'png')) {
  95. $postType = 'photo:video:album';
  96. } else {
  97. $postType = 'video:album';
  98. }
  99. } else {
  100. $postType = 'photo:album';
  101. }
  102. } else {
  103. if(in_array($exts[0], ['jpg', 'png'])) {
  104. $postType = 'photo';
  105. } else if(in_array($exts[0], ['mp4'])) {
  106. $postType = 'video';
  107. }
  108. }
  109. $ip = new ImportPost;
  110. $ip->user_id = $uid;
  111. $ip->profile_id = $pid;
  112. $ip->post_hash = $postHash;
  113. $ip->service = 'instagram';
  114. $ip->post_type = $postType;
  115. $ip->media_count = $c->count();
  116. $ip->media = $c->map(function($m) {
  117. return [
  118. 'uri' => $m['uri'],
  119. 'title' => $this->formatHashtags($m['title']),
  120. 'creation_timestamp' => $m['creation_timestamp']
  121. ];
  122. })->toArray();
  123. $ip->caption = $c->count() > 1 ? $this->formatHashtags($file['title']) : $this->formatHashtags($ip->media[0]['title']);
  124. $ip->filename = last(explode('/', $ip->media[0]['uri']));
  125. $ip->metadata = $c->map(function($m) {
  126. return [
  127. 'uri' => $m['uri'],
  128. 'media_metadata' => isset($m['media_metadata']) ? $m['media_metadata'] : null
  129. ];
  130. })->toArray();
  131. $ip->creation_date = $c->count() > 1 ? now()->parse($file['creation_timestamp']) : now()->parse($media[0]['creation_timestamp']);
  132. $ip->creation_year = now()->parse($ip->creation_date)->format('y');
  133. $ip->creation_month = now()->parse($ip->creation_date)->format('m');
  134. $ip->creation_day = now()->parse($ip->creation_date)->format('d');
  135. $ip->save();
  136. ImportService::getImportedFiles($pid, true);
  137. ImportService::getPostCount($pid, true);
  138. }
  139. return [
  140. 'msg' => 'Success'
  141. ];
  142. }
  143. public function storeMedia(Request $request)
  144. {
  145. abort_unless(config('import.instagram.enabled'), 404);
  146. $this->checkPermissions($request);
  147. $mimes = config('import.instagram.allow_video_posts') ? 'mimetypes:image/png,image/jpeg,video/mp4' : 'mimetypes:image/png,image/jpeg';
  148. $this->validate($request, [
  149. 'file' => 'required|array|max:10',
  150. 'file.*' => [
  151. 'required',
  152. 'file',
  153. $mimes,
  154. 'max:' . config('pixelfed.max_photo_size')
  155. ]
  156. ]);
  157. foreach($request->file('file') as $file) {
  158. $fileName = $file->getClientOriginalName();
  159. $file->storeAs('imports/' . $request->user()->id . '/', $fileName);
  160. }
  161. ImportService::getImportedFiles($request->user()->profile_id, true);
  162. return [
  163. 'msg' => 'Success'
  164. ];
  165. }
  166. protected function checkPermissions($request, $abortOnFail = true)
  167. {
  168. $user = $request->user();
  169. if($abortOnFail) {
  170. abort_unless(config('import.instagram.enabled'), 404);
  171. }
  172. if($user->is_admin) {
  173. if(!$abortOnFail) {
  174. return true;
  175. } else {
  176. return;
  177. }
  178. }
  179. $admin = User::whereIsAdmin(true)->first();
  180. if(config('import.instagram.permissions.admins_only')) {
  181. if($abortOnFail) {
  182. abort_unless($user->is_admin, 404, 'Only admins can use this feature.');
  183. } else {
  184. if(!$user->is_admin) {
  185. return false;
  186. }
  187. }
  188. }
  189. if(config('import.instagram.permissions.admin_follows_only')) {
  190. $exists = Follower::whereProfileId($admin->profile_id)
  191. ->whereFollowingId($user->profile_id)
  192. ->exists();
  193. if($abortOnFail) {
  194. abort_unless(
  195. $exists,
  196. 404,
  197. 'Only admins, and accounts they follow can use this feature'
  198. );
  199. } else {
  200. if(!$exists) {
  201. return false;
  202. }
  203. }
  204. }
  205. if(config('import.instagram.permissions.min_account_age')) {
  206. $res = $user->created_at->lt(
  207. now()->subDays(config('import.instagram.permissions.min_account_age'))
  208. );
  209. if($abortOnFail) {
  210. abort_unless(
  211. $res,
  212. 404,
  213. 'Your account is too new to use this feature'
  214. );
  215. } else {
  216. if(!$res) {
  217. return false;
  218. }
  219. }
  220. }
  221. if(config('import.instagram.permissions.min_follower_count')) {
  222. $res = Follower::whereFollowingId($user->profile_id)->count() >= config('import.instagram.permissions.min_follower_count');
  223. if($abortOnFail) {
  224. abort_unless(
  225. $res,
  226. 404,
  227. 'You don\'t have enough followers to use this feature'
  228. );
  229. } else {
  230. if(!$res) {
  231. return false;
  232. }
  233. }
  234. }
  235. if(intval(config('import.instagram.limits.max_posts')) > 0) {
  236. $res = ImportService::getPostCount($user->profile_id) >= intval(config('import.instagram.limits.max_posts'));
  237. if($abortOnFail) {
  238. abort_if(
  239. $res,
  240. 404,
  241. 'You have reached the limit of post imports and cannot import any more posts'
  242. );
  243. } else {
  244. if($res) {
  245. return false;
  246. }
  247. }
  248. }
  249. if(intval(config('import.instagram.limits.max_attempts')) > 0) {
  250. $res = ImportService::getAttempts($user->profile_id) >= intval(config('import.instagram.limits.max_attempts'));
  251. if($abortOnFail) {
  252. abort_if(
  253. $res,
  254. 404,
  255. 'You have reached the limit of post import attempts and cannot import any more posts'
  256. );
  257. } else {
  258. if($res) {
  259. return false;
  260. }
  261. }
  262. }
  263. if(!$abortOnFail) {
  264. return true;
  265. }
  266. }
  267. }