feat(views): skip owner views, 30-minute device dedupe
- Insert is skipped entirely when the authenticated viewer owns the video. Owners re-watching, previewing an edit, or impersonating no longer inflate their own view counts. - Dedupe window drops 1h -> 30min for both auth and guest paths. - Auth-path dedupe now also matches on device fingerprint / device_id cookie, so signing out and back in during the same session doesn't double-count a single viewer. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
48c947d171
commit
c7d2f25ef6
1 file changed
+24
-6
@@ -691,11 +691,28 @@ class VideoController extends Controller
|
|||||||
$viewFp = $request->cookie('_fp');
|
$viewFp = $request->cookie('_fp');
|
||||||
$viewFp = ($viewFp && preg_match('/^[a-f0-9]{64}$/', $viewFp)) ? $viewFp : null;
|
$viewFp = ($viewFp && preg_match('/^[a-f0-9]{64}$/', $viewFp)) ? $viewFp : null;
|
||||||
|
|
||||||
if (Auth::check()) {
|
// Owners don't count toward their own view totals. Previews, re-watches
|
||||||
|
// from the owner's own account, and even owner-impersonation sessions
|
||||||
|
// would otherwise inflate the number. The share/analytics screens
|
||||||
|
// already filter owner_id separately, so skipping the insert is safe.
|
||||||
|
$isOwnerViewing = Auth::check() && Auth::id() === (int) $video->user_id;
|
||||||
|
|
||||||
|
if (Auth::check() && ! $isOwnerViewing) {
|
||||||
|
// 30-minute user-level dedupe. Also dedupe against the strongest
|
||||||
|
// device signal available (fingerprint > device_id cookie) so a
|
||||||
|
// user signing out and back in during the same session doesn't
|
||||||
|
// double-count.
|
||||||
$exists = \DB::table('video_views')
|
$exists = \DB::table('video_views')
|
||||||
->where('user_id', Auth::id())
|
|
||||||
->where('video_id', $video->id)
|
->where('video_id', $video->id)
|
||||||
->where('watched_at', '>', now()->subHour())
|
->where('watched_at', '>', now()->subMinutes(30))
|
||||||
|
->where(function ($q) use ($viewFp, $viewDid) {
|
||||||
|
$q->where('user_id', Auth::id());
|
||||||
|
if ($viewFp) {
|
||||||
|
$q->orWhere('device_hash', $viewFp);
|
||||||
|
} elseif ($viewDid) {
|
||||||
|
$q->orWhere('device_id', $viewDid);
|
||||||
|
}
|
||||||
|
})
|
||||||
->exists();
|
->exists();
|
||||||
|
|
||||||
if (! $exists) {
|
if (! $exists) {
|
||||||
@@ -711,12 +728,13 @@ class VideoController extends Controller
|
|||||||
'watched_at' => now(),
|
'watched_at' => now(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
} elseif (! $isOwnerViewing) {
|
||||||
// Guest: prefer the fingerprint hash for dedup (strongest signal); fall back to device_id cookie
|
// Guest: 30-minute dedupe keyed on the strongest device signal
|
||||||
|
// (fingerprint hash > device_id cookie).
|
||||||
$exists = \DB::table('video_views')
|
$exists = \DB::table('video_views')
|
||||||
->whereNull('user_id')
|
->whereNull('user_id')
|
||||||
->where('video_id', $video->id)
|
->where('video_id', $video->id)
|
||||||
->where('watched_at', '>', now()->subHour())
|
->where('watched_at', '>', now()->subMinutes(30))
|
||||||
->where(function ($q) use ($viewFp, $viewDid) {
|
->where(function ($q) use ($viewFp, $viewDid) {
|
||||||
if ($viewFp) $q->where('device_hash', $viewFp);
|
if ($viewFp) $q->where('device_hash', $viewFp);
|
||||||
else $q->where('device_id', $viewDid);
|
else $q->where('device_id', $viewDid);
|
||||||
|
|||||||
Reference in new issue
Block a user