Browse Source

Update ApiV1Controller, add experimental home timeline support to v1/timelines/home

Daniel Supernault 1 year ago
parent
commit
24c370ee22
1 changed files with 105 additions and 35 deletions
  1. 105 35
      app/Http/Controllers/Api/ApiV1Controller.php

+ 105 - 35
app/Http/Controllers/Api/ApiV1Controller.php

@@ -71,6 +71,7 @@ use App\Services\{
 	CollectionService,
 	FollowerService,
 	HashtagService,
+	HomeTimelineService,
 	InstanceService,
 	LikeService,
 	NetworkTimelineService,
@@ -102,6 +103,7 @@ use Illuminate\Support\Facades\RateLimiter;
 use Purify;
 use Carbon\Carbon;
 use App\Http\Resources\MastoApi\FollowedTagResource;
+use App\Jobs\HomeFeedPipeline\FeedWarmCachePipeline;
 
 class ApiV1Controller extends Controller
 {
@@ -2129,11 +2131,11 @@ class ApiV1Controller extends Controller
 	public function timelineHome(Request $request)
 	{
 		$this->validate($request,[
-		  'page'        => 'sometimes|integer|max:40',
-		  'min_id'      => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
-		  'max_id'      => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
-		  'limit'       => 'sometimes|integer|min:1|max:100',
-          'include_reblogs' => 'sometimes',
+			'page'        => 'sometimes|integer|max:40',
+			'min_id'      => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
+			'max_id'      => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
+			'limit'       => 'sometimes|integer|min:1|max:40',
+			'include_reblogs' => 'sometimes',
 		]);
 
 		$napi = $request->has(self::PF_API_ENTITY_KEY);
@@ -2142,13 +2144,77 @@ class ApiV1Controller extends Controller
 		$max = $request->input('max_id');
 		$limit = $request->input('limit') ?? 20;
 		$pid = $request->user()->profile_id;
-        $includeReblogs = $request->filled('include_reblogs');
-        $nullFields = $includeReblogs ?
-            ['in_reply_to_id'] :
-            ['in_reply_to_id', 'reblog_of_id'];
-        $inTypes = $includeReblogs ?
-            ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album', 'share'] :
-            ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'];
+		$includeReblogs = $request->filled('include_reblogs');
+		$nullFields = $includeReblogs ?
+		['in_reply_to_id'] :
+		['in_reply_to_id', 'reblog_of_id'];
+		$inTypes = $includeReblogs ?
+		['photo', 'photo:album', 'video', 'video:album', 'photo:video:album', 'share'] :
+		['photo', 'photo:album', 'video', 'video:album', 'photo:video:album'];
+
+		if(config('exp.cached_home_timeline')) {
+			if($min || $max) {
+				if($request->has('min_id')) {
+					$res = HomeTimelineService::getRankedMinId($pid, $min ?? 0, $limit + 10);
+				} else {
+					$res = HomeTimelineService::getRankedMaxId($pid, $max ?? 0, $limit + 10);
+				}
+			} else {
+				$res = HomeTimelineService::get($pid, 0, $limit + 10);
+			}
+
+			if(!$res) {
+				$res = Cache::has('pf:services:apiv1:home:cached:coldbootcheck:' . $pid);
+				if(!$res) {
+					Cache::set('pf:services:apiv1:home:cached:coldbootcheck:' . $pid, 1, 86400);
+					FeedWarmCachePipeline::dispatchSync($pid);
+					return response()->json([], 206);
+				} else {
+					Cache::set('pf:services:apiv1:home:cached:coldbootcheck:' . $pid, 1, 86400);
+					return response()->json([], 206);
+				}
+			}
+
+			$res = collect($res)->take($limit)->map(function($id) use($napi) {
+				return $napi ? StatusService::get($id, false) : StatusService::getMastodon($id, false);
+			})->filter(function($res) {
+				return $res && isset($res['account']);
+			})->map(function($status) use($pid) {
+				if($pid) {
+					$status['favourited'] = (bool) LikeService::liked($pid, $status['id']);
+					$status['reblogged'] = (bool) ReblogService::get($pid, $status['id']);
+					$status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
+				}
+				return $status;
+			});
+
+			$baseUrl = config('app.url') . '/api/v1/timelines/home?limit=' . $limit . '&';
+			$minId = $res->map(function($s) {
+				return ['id' => $s['id']];
+			})->min('id');
+			$maxId = $res->map(function($s) {
+				return ['id' => $s['id']];
+			})->max('id');
+
+			if($minId == $maxId) {
+				$minId = null;
+			}
+
+			if($maxId) {
+				$link = '<'.$baseUrl.'max_id='.$minId.'>; rel="next"';
+			}
+
+			if($minId) {
+				$link = '<'.$baseUrl.'min_id='.$maxId.'>; rel="prev"';
+			}
+
+			if($maxId && $minId) {
+				$link = '<'.$baseUrl.'max_id='.$minId.'>; rel="next",<'.$baseUrl.'min_id='.$maxId.'>; rel="prev"';
+			}
+
+			$headers = isset($link) ? ['Link' => $link] : [];
+			return $this->json($res->toArray(), 200, $headers);
+		}
 
 		$following = Cache::remember('profile:following:'.$pid, 1209600, function() use($pid) {
 			$following = Follower::whereProfileId($pid)->pluck('following_id');
@@ -2161,6 +2227,7 @@ class ApiV1Controller extends Controller
 			$following = array_diff($following, $muted);
 		}
 
+
 		if($min || $max) {
 			$dir = $min ? '>' : '<';
 			$id = $min ?? $max;
@@ -2199,22 +2266,22 @@ class ApiV1Controller extends Controller
 				if($pid) {
 					$status['favourited'] = (bool) LikeService::liked($pid, $s['id']);
 					$status['reblogged'] = (bool) ReblogService::get($pid, $status['id']);
-                    $status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
+					$status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
 				}
 				return $status;
 			})
 			->filter(function($status) {
 				return $status && isset($status['account']);
 			})
-            ->map(function($status) use($pid) {
-                if(!empty($status['reblog'])) {
-                    $status['reblog']['favourited'] = (bool) LikeService::liked($pid, $status['reblog']['id']);
-                    $status['reblog']['reblogged'] = (bool) ReblogService::get($pid, $status['reblog']['id']);
-                    $status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
-                }
-
-                return $status;
-            })
+			->map(function($status) use($pid) {
+				if(!empty($status['reblog'])) {
+					$status['reblog']['favourited'] = (bool) LikeService::liked($pid, $status['reblog']['id']);
+					$status['reblog']['reblogged'] = (bool) ReblogService::get($pid, $status['reblog']['id']);
+					$status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
+				}
+
+				return $status;
+			})
 			->take($limit)
 			->values();
 		} else {
@@ -2252,22 +2319,22 @@ class ApiV1Controller extends Controller
 				if($pid) {
 					$status['favourited'] = (bool) LikeService::liked($pid, $s['id']);
 					$status['reblogged'] = (bool) ReblogService::get($pid, $status['id']);
-                    $status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
+					$status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
 				}
 				return $status;
 			})
 			->filter(function($status) {
 				return $status && isset($status['account']);
 			})
-            ->map(function($status) use($pid) {
-                if(!empty($status['reblog'])) {
-                    $status['reblog']['favourited'] = (bool) LikeService::liked($pid, $status['reblog']['id']);
-                    $status['reblog']['reblogged'] = (bool) ReblogService::get($pid, $status['reblog']['id']);
-                    $status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
-                }
-
-                return $status;
-            })
+			->map(function($status) use($pid) {
+				if(!empty($status['reblog'])) {
+					$status['reblog']['favourited'] = (bool) LikeService::liked($pid, $status['reblog']['id']);
+					$status['reblog']['reblogged'] = (bool) ReblogService::get($pid, $status['reblog']['id']);
+					$status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
+				}
+
+				return $status;
+			})
 			->take($limit)
 			->values();
 		}
@@ -2321,10 +2388,11 @@ class ApiV1Controller extends Controller
 		$max = $request->input('max_id');
 		$limit = $request->input('limit') ?? 20;
 		$user = $request->user();
-		$remote = ($request->has('remote') && $request->input('remote') == true) || ($request->filled('local') && $request->input('local') != true);
+		$remote = $request->has('remote');
+		$local = $request->has('local');
         $filtered = $user ? UserFilterService::filters($user->profile_id) : [];
 
-        if((!$request->has('local') || $remote) && config('instance.timeline.network.cached')) {
+        if($remote && config('instance.timeline.network.cached')) {
 			Cache::remember('api:v1:timelines:network:cache_check', 10368000, function() {
 				if(NetworkTimelineService::count() == 0) {
 					NetworkTimelineService::warmCache(true, config('instance.timeline.network.cache_dropoff'));
@@ -2338,7 +2406,9 @@ class ApiV1Controller extends Controller
 			} else {
 				$feed = NetworkTimelineService::get(0, $limit + 5);
 			}
-        } else {
+        }
+
+        if($local || !$remote && !$local) {
 			Cache::remember('api:v1:timelines:public:cache_check', 10368000, function() {
 				if(PublicTimelineService::count() == 0) {
 					PublicTimelineService::warmCache(true, 400);