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

This commit is contained in:
Daniel Supernault 2023-11-12 21:13:08 -07:00
parent 2a8a299058
commit 24c370ee22
No known key found for this signature in database
GPG key ID: 23740873EE6F76A1

View file

@ -71,6 +71,7 @@ use App\Services\{
CollectionService, CollectionService,
FollowerService, FollowerService,
HashtagService, HashtagService,
HomeTimelineService,
InstanceService, InstanceService,
LikeService, LikeService,
NetworkTimelineService, NetworkTimelineService,
@ -102,6 +103,7 @@ use Illuminate\Support\Facades\RateLimiter;
use Purify; use Purify;
use Carbon\Carbon; use Carbon\Carbon;
use App\Http\Resources\MastoApi\FollowedTagResource; use App\Http\Resources\MastoApi\FollowedTagResource;
use App\Jobs\HomeFeedPipeline\FeedWarmCachePipeline;
class ApiV1Controller extends Controller class ApiV1Controller extends Controller
{ {
@ -2129,11 +2131,11 @@ class ApiV1Controller extends Controller
public function timelineHome(Request $request) public function timelineHome(Request $request)
{ {
$this->validate($request,[ $this->validate($request,[
'page' => 'sometimes|integer|max:40', 'page' => 'sometimes|integer|max:40',
'min_id' => 'sometimes|integer|min:0|max:' . PHP_INT_MAX, 'min_id' => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
'max_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', 'limit' => 'sometimes|integer|min:1|max:40',
'include_reblogs' => 'sometimes', 'include_reblogs' => 'sometimes',
]); ]);
$napi = $request->has(self::PF_API_ENTITY_KEY); $napi = $request->has(self::PF_API_ENTITY_KEY);
@ -2142,13 +2144,77 @@ class ApiV1Controller extends Controller
$max = $request->input('max_id'); $max = $request->input('max_id');
$limit = $request->input('limit') ?? 20; $limit = $request->input('limit') ?? 20;
$pid = $request->user()->profile_id; $pid = $request->user()->profile_id;
$includeReblogs = $request->filled('include_reblogs'); $includeReblogs = $request->filled('include_reblogs');
$nullFields = $includeReblogs ? $nullFields = $includeReblogs ?
['in_reply_to_id'] : ['in_reply_to_id'] :
['in_reply_to_id', 'reblog_of_id']; ['in_reply_to_id', 'reblog_of_id'];
$inTypes = $includeReblogs ? $inTypes = $includeReblogs ?
['photo', 'photo:album', 'video', 'video:album', 'photo:video:album', 'share'] : ['photo', 'photo:album', 'video', 'video:album', 'photo:video:album', 'share'] :
['photo', 'photo:album', 'video', 'video:album', 'photo:video:album']; ['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 = Cache::remember('profile:following:'.$pid, 1209600, function() use($pid) {
$following = Follower::whereProfileId($pid)->pluck('following_id'); $following = Follower::whereProfileId($pid)->pluck('following_id');
@ -2161,6 +2227,7 @@ class ApiV1Controller extends Controller
$following = array_diff($following, $muted); $following = array_diff($following, $muted);
} }
if($min || $max) { if($min || $max) {
$dir = $min ? '>' : '<'; $dir = $min ? '>' : '<';
$id = $min ?? $max; $id = $min ?? $max;
@ -2199,22 +2266,22 @@ class ApiV1Controller extends Controller
if($pid) { if($pid) {
$status['favourited'] = (bool) LikeService::liked($pid, $s['id']); $status['favourited'] = (bool) LikeService::liked($pid, $s['id']);
$status['reblogged'] = (bool) ReblogService::get($pid, $status['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; return $status;
}) })
->filter(function($status) { ->filter(function($status) {
return $status && isset($status['account']); return $status && isset($status['account']);
}) })
->map(function($status) use($pid) { ->map(function($status) use($pid) {
if(!empty($status['reblog'])) { if(!empty($status['reblog'])) {
$status['reblog']['favourited'] = (bool) LikeService::liked($pid, $status['reblog']['id']); $status['reblog']['favourited'] = (bool) LikeService::liked($pid, $status['reblog']['id']);
$status['reblog']['reblogged'] = (bool) ReblogService::get($pid, $status['reblog']['id']); $status['reblog']['reblogged'] = (bool) ReblogService::get($pid, $status['reblog']['id']);
$status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']); $status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
} }
return $status; return $status;
}) })
->take($limit) ->take($limit)
->values(); ->values();
} else { } else {
@ -2252,22 +2319,22 @@ class ApiV1Controller extends Controller
if($pid) { if($pid) {
$status['favourited'] = (bool) LikeService::liked($pid, $s['id']); $status['favourited'] = (bool) LikeService::liked($pid, $s['id']);
$status['reblogged'] = (bool) ReblogService::get($pid, $status['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; return $status;
}) })
->filter(function($status) { ->filter(function($status) {
return $status && isset($status['account']); return $status && isset($status['account']);
}) })
->map(function($status) use($pid) { ->map(function($status) use($pid) {
if(!empty($status['reblog'])) { if(!empty($status['reblog'])) {
$status['reblog']['favourited'] = (bool) LikeService::liked($pid, $status['reblog']['id']); $status['reblog']['favourited'] = (bool) LikeService::liked($pid, $status['reblog']['id']);
$status['reblog']['reblogged'] = (bool) ReblogService::get($pid, $status['reblog']['id']); $status['reblog']['reblogged'] = (bool) ReblogService::get($pid, $status['reblog']['id']);
$status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']); $status['bookmarked'] = (bool) BookmarkService::get($pid, $status['id']);
} }
return $status; return $status;
}) })
->take($limit) ->take($limit)
->values(); ->values();
} }
@ -2321,10 +2388,11 @@ class ApiV1Controller extends Controller
$max = $request->input('max_id'); $max = $request->input('max_id');
$limit = $request->input('limit') ?? 20; $limit = $request->input('limit') ?? 20;
$user = $request->user(); $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) : []; $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() { Cache::remember('api:v1:timelines:network:cache_check', 10368000, function() {
if(NetworkTimelineService::count() == 0) { if(NetworkTimelineService::count() == 0) {
NetworkTimelineService::warmCache(true, config('instance.timeline.network.cache_dropoff')); NetworkTimelineService::warmCache(true, config('instance.timeline.network.cache_dropoff'));
@ -2338,7 +2406,9 @@ class ApiV1Controller extends Controller
} else { } else {
$feed = NetworkTimelineService::get(0, $limit + 5); $feed = NetworkTimelineService::get(0, $limit + 5);
} }
} else { }
if($local || !$remote && !$local) {
Cache::remember('api:v1:timelines:public:cache_check', 10368000, function() { Cache::remember('api:v1:timelines:public:cache_check', 10368000, function() {
if(PublicTimelineService::count() == 0) { if(PublicTimelineService::count() == 0) {
PublicTimelineService::warmCache(true, 400); PublicTimelineService::warmCache(true, 400);