Live Stream
Live Stream watched summary
İzleyici özet endpoint kontratı
Live Stream Watched Summary Endpoint Spec
Endpoint
GET /api/v1/live-stream/my-watched-stream-summariesKullanıcının audience olarak izlediği bitmiş yayınları, yayın başına izleme ve harcama özetleriyle listeler.
Auth
Zorunlu:
Authorization: Bearer <JWT>Backend decorator:
@ApiAuth(Role.USER)Query Parameters
| Param | Type | Default | Min | Max | Description |
|---|---|---|---|---|---|
page | number | 1 | 1 | - | Sayfa numarası |
limit | number | 10 | 1 | 100 | Sayfa başına item sayısı |
Örnek:
GET /api/v1/live-stream/my-watched-stream-summaries?page=1&limit=10Response Shape
Backend standart BaseResponseDto<T> wrapper döner. Asıl payload data içindedir.
BaseResponseDto<PaginatedResponseDto<MyWatchedLiveStreamSummaryResponseDto>>data.list içindeki her item:
type MyWatchedLiveStreamSummaryResponseDto = {
liveStream: LiveStreamResponseDto;
summary: MyWatchedLiveStreamSummaryMetricsDto;
};liveStream alanı mevcut LiveStreamResponseDto formatındadır ve whats-live-now response item formatıyla uyumludur. Bu endpoint bağlamında liveStream.role her zaman audience olarak set edilir.
Summary Metrics
| Field | Type | Description |
|---|---|---|
totalCreditsSpent | number | Bu yayın için kullanıcının toplam harcadığı kredi. watchBillingCreditsSpent + giftCreditsSpent |
watchBillingCreditsSpent | number | Dakikalık izleme ücretlendirmesi için harcanan kredi |
giftsSentCount | number | Kullanıcının bu yayında gönderdiği gift sayısı |
giftCreditsSpent | number | Kullanıcının bu yayında gift için harcadığı kredi |
watchedSeconds | number | Kullanıcının bu yayını izlediği toplam süre, saniye |
chatMessagesSentCount | number | Kullanıcının bu yayında gönderdiği live stream chat mesajı sayısı |
streamDurationSeconds | number | Yayının toplam sürdüğü süre, saniye |
uniqueAudienceViewerCount | number | Yayını izleyen toplam tekil audience kullanıcı sayısı |
Example Response
{
"isSuccess": true,
"statusCode": 200,
"data": {
"list": [
{
"liveStream": {
"id": "65f000000000000000000001",
"title": "Yayin basligi",
"liveStreamType": "solo",
"channelName": "channel-name",
"broadcasters": [],
"guests": [],
"creator": {
"_id": "65f000000000000000000010",
"username": "creator",
"name": "Creator",
"surname": "User",
"profilePhoto": null
},
"thumbnailUrl": null,
"recording": false,
"recordingUrl": null,
"status": "ended",
"accessType": "paid",
"price": 10,
"interest": "music",
"durationGoal": null,
"motivation": null,
"isActiveReplayOnCreatorProfile": null,
"replayCreditPrice": null,
"startedAt": "2026-05-11T10:00:00.000Z",
"plannedStartDate": null,
"endedAt": "2026-05-11T11:00:00.000Z",
"plannedEndDate": null,
"createdAt": "2026-05-11T09:55:00.000Z",
"updatedAt": "2026-05-11T11:00:00.000Z",
"fundingGoal": null,
"collectedFunding": null,
"fundingPercentage": null,
"role": "audience",
"miniCrowdFundings": []
},
"summary": {
"totalCreditsSpent": 45,
"watchBillingCreditsSpent": 30,
"giftsSentCount": 2,
"giftCreditsSpent": 15,
"watchedSeconds": 1200,
"chatMessagesSentCount": 4,
"streamDurationSeconds": 3600,
"uniqueAudienceViewerCount": 18
}
}
],
"pagination": {
"currentPage": 1,
"itemsPerPage": 10,
"totalItems": 1,
"totalPages": 1,
"hasNextPage": false,
"hasPrevPage": false
}
},
"errors": [],
"timestamp": "2026-05-14T12:00:00.000Z"
}Empty Response
Kullanıcının audience olarak izlediği bitmiş yayın yoksa:
{
"isSuccess": true,
"statusCode": 200,
"data": {
"list": [],
"pagination": {
"currentPage": 1,
"itemsPerPage": 10,
"totalItems": 0,
"totalPages": 0,
"hasNextPage": false,
"hasPrevPage": false
}
},
"errors": [],
"timestamp": "2026-05-14T12:00:00.000Z"
}Data Rules
- Sadece current user için çalışır.
- Sadece
Participant.role = audiencekayıtları dikkate alınır. - Sadece
LiveStream.status = endedyayınlar döner. - Soft-deleted live stream kayıtları dönmez.
- Aynı yayına birden fazla
audiencekatılımı varsa liste itemıliveStreamIdbazında tekilleştirilir. recordingUrl,recordingveyaisActiveReplayOnCreatorProfilefiltresi uygulanmaz.- Host/guest olarak katıldığı yayınlar bu endpointte dönmez.
Metric Calculation
watchedSeconds
- Kaynak:
participants - Filtre:
userId = currentUserId,role = audience,liveStreamId in pageStreamIds - Her session için süre:
(leftAt ?? liveStream.endedAt ?? participant.updatedAt) - joinedAt- Süre saniye olarak toplanır.
- Toplam değer, yayın süresi biliniyorsa
streamDurationSecondsdeğerini aşmayacak şekilde sınırlandırılır.
streamDurationSeconds
- Kaynak:
livestreams - Hesap:
endedAt - startedAtwatchBillingCreditsSpent
- Kaynak:
streambillingtransactions - Filtre:
userId = currentUserIdliveStreamId in pageStreamIdstransactionType = chargestatus = completed
- Hesap:
sum(amount)
giftsSentCount ve giftCreditsSpent
- Kaynak:
gifts - Filtre:
metadata.senderUserId = currentUserIdmetadata.liveStreamId in pageStreamIds
- Hesap:
giftsSentCount = count(*)giftCreditsSpent = sum(metadata.creditAmount)
chatMessagesSentCount
- Kaynak:
streamchatmessages - Filtre:
senderId = currentUserIdstreamId in pageStreamIds
- Hesap:
count(*)
uniqueAudienceViewerCount
- Kaynak:
participants - Filtre:
role = audienceliveStreamId in pageStreamIds
- Hesap: yayın bazında distinct
userIdcount.
totalCreditsSpent
watchBillingCreditsSpent + giftCreditsSpentTypeScript Client Types
type Pagination = {
currentPage: number;
itemsPerPage: number;
totalItems: number;
totalPages: number;
hasNextPage: boolean;
hasPrevPage: boolean;
};
type PaginatedResponseDto<T> = {
list: T[];
pagination: Pagination;
};
type BaseResponseDto<T> = {
isSuccess: boolean;
statusCode: number;
data: T;
errors: string[];
timestamp: string;
};
type WatchedStreamSummary = {
totalCreditsSpent: number;
watchBillingCreditsSpent: number;
giftsSentCount: number;
giftCreditsSpent: number;
watchedSeconds: number;
chatMessagesSentCount: number;
streamDurationSeconds: number;
uniqueAudienceViewerCount: number;
};
type MyWatchedLiveStreamSummaryItem = {
liveStream: LiveStreamResponseDto;
summary: WatchedStreamSummary;
};
type MyWatchedLiveStreamSummaryResponse = PaginatedResponseDto<MyWatchedLiveStreamSummaryItem>;RTK Query Example
export const liveStreamApi = baseApi.injectEndpoints({
endpoints: (builder) => ({
getMyWatchedStreamSummaries: builder.query<
MyWatchedLiveStreamSummaryResponse,
{ page?: number; limit?: number } | void
>({
query: (params) => ({
url: '/v1/live-stream/my-watched-stream-summaries',
method: 'GET',
params: {
page: params?.page ?? 1,
limit: params?.limit ?? 10,
},
}),
transformResponse: (
response: BaseResponseDto<MyWatchedLiveStreamSummaryResponse>,
) => response.data,
}),
}),
});Error Cases
| Status | Condition |
|---|---|
401 | JWT yok veya geçersiz |
403 | Kullanıcı rolü endpoint için uygun değil |
400 | Query validation hatası |
500 | Beklenmeyen sunucu hatası |
Backend Implementation Map
- Controller:
src/live-stream/live-stream.controller.ts - DTO:
src/live-stream/dto/my-watched-live-stream-summary-response.dto.ts - Use-case:
src/live-stream/use-cases/get-my-watched-live-stream-summaries.usecase.ts - Swagger decorator:
src/live-stream/decorators/api-get-my-live-stream-history.decorator.ts - Tests:
src/live-stream/use-cases/get-my-watched-live-stream-summaries.usecase.spec.tssrc/live-stream/live-stream.controller.auth.spec.tssrc/live-stream/live-stream.controller.versioning.spec.ts
Indexes
Endpoint metrikleri için kullanılan indexler:
GiftSchema.index({
'metadata.senderUserId': 1,
'metadata.liveStreamId': 1,
timestamp: -1,
});
StreamChatMessageSchema.index({ senderId: 1, streamId: 1, createdAt: -1 });
StreamBillingTransactionSchema.index({
userId: 1,
liveStreamId: 1,
transactionType: 1,
status: 1,
});