tracking-lib-ott 1.0.14 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,6 +46,7 @@ tracking.init({
46
46
  deviceModel: "Samsung TV 2024",
47
47
  deviceBrand: "Samsung",
48
48
  events: [],
49
+ userId: -1, // User ID, -1 nếu chưa đăng nhập
49
50
 
50
51
  // Optional fields
51
52
  adId: "gaid-or-idfa",
package/dist/index.cjs CHANGED
@@ -70,6 +70,7 @@ var defaultConfig = {
70
70
  deviceId: "",
71
71
  deviceModel: "",
72
72
  deviceBrand: "",
73
+ userId: -1,
73
74
  // Optional properties
74
75
  adId: "",
75
76
  sdkVersion: "",
@@ -158,7 +159,7 @@ var clearSessionCookie = (options = {}) => {
158
159
 
159
160
  // src/tracking.ts
160
161
  var config = { ...defaultConfig };
161
- var userId = -1;
162
+ var userId = config.userId ?? -1;
162
163
  var previousPageId = null;
163
164
  var pageMap = { ...defaultPageMap };
164
165
  var initTracking = (options = {}) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/cookie-utils.ts","../src/tracking.ts","../src/session.ts"],"sourcesContent":["/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n UserInfo,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\nexport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Re-export cookie utilities\r\nexport {\r\n generateSessionId,\r\n getCookie,\r\n setCookie,\r\n getOrCreateSessionId,\r\n renewSessionCookie,\r\n clearSessionCookie,\r\n} from \"./cookie-utils\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n setUserInfo,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n","/**\r\n * Constants for Tracking Library\r\n */\r\n\r\nimport type { TrackingConfig, PageMap } from \"./types\";\r\n\r\nexport const defaultConfig: TrackingConfig = {\r\n baseURL: \"\",\r\n appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"WebPC\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n\r\n // Optional properties\r\n adId: \"\",\r\n sdkVersion: \"\",\r\n\r\n events: [],\r\n};\r\n\r\nexport const defaultPageMap: PageMap = {\r\n \"\": { name: \"Home\", id: \"home\" },\r\n home: { name: \"Home\", id: \"home\" },\r\n search: { name: \"Search\", id: \"search\" },\r\n detail: { name: \"Detail\", id: \"detail\" },\r\n player: { name: \"Player\", id: \"player\" },\r\n video: { name: \"Video\", id: \"video\" },\r\n live: { name: \"Live\", id: \"live\" },\r\n category: { name: \"Category\", id: \"category\" },\r\n profile: { name: \"Profile\", id: \"profile\" },\r\n login: { name: \"Login\", id: \"login\" },\r\n};\r\n","/**\r\n * Cookie Utilities for Session Management\r\n */\r\n\r\nimport { v4 as uuidv4 } from \"uuid\";\r\n\r\nexport type SessionCookieConfig = {\r\n /** Tên cookie lưu session_id. Default: '_tracking_session_id' */\r\n cookieName?: string;\r\n /** Thời gian hết hạn cookie (phút). Default: 30 */\r\n expirationMinutes?: number;\r\n /** Domain cho cookie (hỗ trợ group domain như .example.com) */\r\n domain?: string;\r\n /** Path cho cookie. Default: '/' */\r\n path?: string;\r\n /** SameSite attribute. Default: 'Lax' */\r\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\r\n};\r\n\r\nconst defaultCookieConfig: Required<SessionCookieConfig> = {\r\n cookieName: \"_tracking_session_id\",\r\n expirationMinutes: 30,\r\n domain: \"\",\r\n path: \"/\",\r\n sameSite: \"Lax\",\r\n};\r\n\r\n/**\r\n * Tạo session ID theo format UUID v4\r\n */\r\nexport const generateSessionId = (): string => {\r\n return uuidv4();\r\n};\r\n\r\n/**\r\n * Lấy giá trị cookie theo tên\r\n */\r\nexport const getCookie = (name: string): string | null => {\r\n if (typeof document === \"undefined\") return null;\r\n\r\n const nameEQ = name + \"=\";\r\n const cookies = document.cookie.split(\";\");\r\n\r\n for (let i = 0; i < cookies.length; i++) {\r\n let cookie = cookies[i];\r\n while (cookie.charAt(0) === \" \") {\r\n cookie = cookie.substring(1);\r\n }\r\n if (cookie.indexOf(nameEQ) === 0) {\r\n return cookie.substring(nameEQ.length);\r\n }\r\n }\r\n return null;\r\n};\r\n\r\n/**\r\n * Set cookie với các options\r\n */\r\nexport const setCookie = (\r\n name: string,\r\n value: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n const expirationMs = config.expirationMinutes * 60 * 1000;\r\n const expires = new Date(Date.now() + expirationMs);\r\n\r\n let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n if (config.sameSite) {\r\n cookieString += `; SameSite=${config.sameSite}`;\r\n }\r\n\r\n // Nếu SameSite=None thì phải có Secure\r\n if (config.sameSite === \"None\") {\r\n cookieString += \"; Secure\";\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n\r\n/**\r\n * Lấy hoặc tạo mới session_id từ cookie\r\n * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ\r\n * Nếu không -> tạo mới và lưu vào cookie\r\n */\r\nexport const getOrCreateSessionId = (\r\n options: SessionCookieConfig = {},\r\n): string => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n const existingSessionId = getCookie(config.cookieName);\r\n\r\n if (existingSessionId) {\r\n return existingSessionId;\r\n }\r\n\r\n // Tạo mới session_id\r\n const newSessionId = generateSessionId();\r\n setCookie(config.cookieName, newSessionId, options);\r\n\r\n return newSessionId;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi hàm này khi có event session_start để extend thời gian hết hạn\r\n */\r\nexport const renewSessionCookie = (\r\n sessionId: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n setCookie(config.cookieName, sessionId, options);\r\n};\r\n\r\n/**\r\n * Xóa session cookie\r\n */\r\nexport const clearSessionCookie = (options: SessionCookieConfig = {}): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n let cookieString = `${config.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n","/**\r\n * Core Tracking Functions\r\n */\r\n\r\nimport type {\r\n TrackingConfig,\r\n PageMap,\r\n PageInfo,\r\n EventData,\r\n NextRouterLike,\r\n UserInfo,\r\n} from \"./types\";\r\nimport { defaultConfig, defaultPageMap } from \"./constants\";\r\nimport { getOrCreateSessionId } from \"./cookie-utils\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\n\r\n// Config runtime\r\nlet config: TrackingConfig = { ...defaultConfig };\r\n\r\n// User info state\r\nlet userId: number = -1;\r\n\r\n// State\r\nlet previousPageId: string | null = null;\r\n\r\n// Page mapping\r\nlet pageMap: any = { ...defaultPageMap };\r\n\r\n/**\r\n * Khởi tạo tracking với config\r\n */\r\nexport const initTracking = (\r\n options: Partial<TrackingConfig> & { pageMap?: Partial<PageMap> } = {},\r\n) => {\r\n config = { ...config, ...options };\r\n\r\n // Auto-generate sessionId from cookie if not provided\r\n if (!config.sessionId) {\r\n config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);\r\n if (config.debug) {\r\n console.log(\r\n \"[Tracking] Auto-generated session_id from cookie:\",\r\n config.sessionId,\r\n );\r\n }\r\n } else if (config.debug) {\r\n console.log(\"[Tracking] Using user-provided session_id:\", config.sessionId);\r\n }\r\n\r\n if (options.pageMap) {\r\n pageMap = { ...pageMap, ...options.pageMap };\r\n }\r\n\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] baseURL is required.\");\r\n }\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Initialized with config:\", config);\r\n }\r\n};\r\n\r\n/**\r\n * Cập nhật thông tin người dùng (userId)\r\n * userId sẽ được tự động thêm vào mỗi event trong events array\r\n */\r\nexport const setUserInfo = (userInfo: UserInfo) => {\r\n userId = userInfo.userId;\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] User info updated, userId:\", userId);\r\n }\r\n};\r\n\r\n/**\r\n * Lấy thông tin page từ URL\r\n */\r\nexport const getPageInfo = (url: string): PageInfo => {\r\n const path = (url || \"\").split(\"?\")[0];\r\n const pathSegment = path.split(\"/\").filter(Boolean)[0] || \"home\";\r\n\r\n return (\r\n pageMap[pathSegment] || {\r\n name: pathSegment.charAt(0).toUpperCase() + pathSegment.slice(1),\r\n id: pathSegment,\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * Send Event -> POST JSON\r\n */\r\nexport const sendEvent = async (\r\n eventData: EventData,\r\n): Promise<Response | void> => {\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] Not initialized. Call initTracking() first.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Tách event_name ra khỏi eventData để tạo event object\r\n const { event_name, ...eventParams } = eventData;\r\n\r\n // Tạo event object với format chuẩn theo API spec\r\n const eventObject = {\r\n // Required fields\r\n user_id: userId, // userId được set từ setUserInfo, -1 nếu chưa set\r\n event_name: event_name || \"unknown\",\r\n event_time: Math.floor(Date.now() / 1000),\r\n // Optional fields\r\n event_params: JSON.stringify(eventParams), // JSON string ≤ 1024\r\n };\r\n\r\n const payload = {\r\n // Required properties\r\n app_id: config.appId,\r\n package_id: config.packageId,\r\n platform: config.platform,\r\n\r\n session_id: config.sessionId,\r\n session_sign: config.sessionSign,\r\n\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n device_os_version: config.deviceOsVersion,\r\n sdk_version: config.sdkVersion,\r\n\r\n // Note: ip_address và carrier_net - Server tự lấy từ header\r\n\r\n // Events batch - tự động thêm event vào mảng\r\n events: [...(config.events ?? []), eventObject],\r\n };\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Sending event:\", payload);\r\n }\r\n\r\n const res = await fetch(config.baseURL, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!res.ok && config.debug) {\r\n const text = await res.text().catch(() => \"\");\r\n console.error(\"[Tracking] HTTP Error:\", res.status, text);\r\n }\r\n\r\n return res;\r\n } catch (error) {\r\n if (config.debug) {\r\n console.error(\"[Tracking] Error:\", error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Track page view event\r\n */\r\nexport const trackPageView = (url: string) => {\r\n const pageInfo = getPageInfo(url);\r\n\r\n sendEvent({\r\n event_name: \"page_view\",\r\n page_name: pageInfo.name,\r\n page_id: pageInfo.id,\r\n page_path: url,\r\n referrer_page_id: previousPageId || \"\",\r\n });\r\n\r\n previousPageId = pageInfo.id;\r\n};\r\n\r\n/**\r\n * Track custom event\r\n */\r\nexport const trackEvent = (eventName: string, params: EventData = {}) => {\r\n sendEvent({\r\n event_name: eventName,\r\n ...params,\r\n });\r\n};\r\n\r\n/**\r\n * Track view content details event\r\n * @param contentId - ID của content (required)\r\n * @param contentTitle - Tiêu đề content (required)\r\n * @param contentType - Loại content: 0=VOD, 1=Event, 2=Channel (required)\r\n * @param channelId - Mã kênh EPG (optional)\r\n * @param eventTime - Timestamp Unix (optional, mặc định lấy time hiện tại)\r\n */\r\nexport const trackViewContentDetails = (\r\n contentId: string,\r\n contentTitle: string,\r\n contentType: 0 | 1 | 2,\r\n channelId?: string,\r\n eventTime?: number,\r\n) => {\r\n sendEvent({\r\n event_name: \"view_content_details\",\r\n content_id: contentId,\r\n content_title: contentTitle,\r\n content_type: contentType,\r\n channel_id: channelId,\r\n event_time: eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback started event\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID: MD5(content_id + start_timestamp) (required)\r\n * @param params.videoQuality - Chất lượng video: 360p, 480p, 720p, 1080p, 2k, 4k (required)\r\n * @param params.startTimestamp - Unix timestamp khi bắt đầu phát (required)\r\n * @param params.startPosition - Vị trí bắt đầu: Live=0, VOD: 0 nếu mới, >0 nếu xem lại (required)\r\n * @param params.duration - Thời lượng: Live=-1, VOD=số giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n startTimestamp: number;\r\n startPosition: number;\r\n duration: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n start_timestamp: params.startTimestamp,\r\n start_position: params.startPosition,\r\n duration: params.duration,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback progress event\r\n * Gửi định kỳ: loop 1 = 5s, loop 2 = 25s để track thời lượng user xem nội dung\r\n * Lưu ý: total_watch_time là tổng thời gian xem được (không tính pause)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackProgress = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_progress\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback completed event\r\n * Tương tự playback_progress nhưng là event cuối cùng khi kết thúc xem\r\n * Với Live có thể thoát chủ động thì tracking event này\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackCompleted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_completed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback error event\r\n * Trigger khi:\r\n * - Video không load/play được (network timeout, source 404)\r\n * - Player error (format không hỗ trợ, DRM error)\r\n * - Source error (media source and manifest-related errors)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.errorCode - Mã lỗi (optional)\r\n * @param params.errorMessage - Thông báo lỗi (optional)\r\n * @param params.errorType - Loại lỗi: video_playback_error, network_error... (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackError = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1;\r\n playbackSession: string;\r\n channelId?: string;\r\n errorCode?: number;\r\n errorMessage?: string;\r\n errorType?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_error\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n channel_id: params.channelId,\r\n error_code: params.errorCode,\r\n error_message: params.errorMessage,\r\n error_type: params.errorType,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track quality changed event\r\n * Trigger khi:\r\n * - Tự động thay đổi do thuật toán ABR\r\n * - Thay đổi chủ động bởi người dùng\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.fromQuality - Chất lượng trước khi đổi (required)\r\n * @param params.toQuality - Chất lượng sau khi đổi (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: automatic_abr hoặc user_selected (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackQualityChanged = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n fromQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n toQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"automatic_abr\" | \"user_selected\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"quality_changed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n from_quality: params.fromQuality,\r\n to_quality: params.toQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer started event\r\n * Buffering xảy ra khi video không còn segment kế tiếp để phát, video sẽ pause để tải thêm segment\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer ended event\r\n * Khi buffering kết thúc và video tiếp tục phát\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.bufferDurationSec - Tổng số giây mà buffer xảy ra (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferEnded = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n bufferDurationSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_ended\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n buffer_duration_sec: params.bufferDurationSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback paused event\r\n * Khi user pause video\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackPaused = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_paused\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback resumed event\r\n * Khi user resume video sau khi pause\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackResumed = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_resumed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Hook cho Next.js - auto track page views\r\n * Dùng cho Next.js Pages Router (next/router)\r\n */\r\nexport const usePageViewTracking = (router: NextRouterLike) => {\r\n if (typeof window === \"undefined\") return;\r\n if (!router) return;\r\n\r\n // Track initial page\r\n trackPageView(router.asPath);\r\n\r\n const handleRouteChange = (url: string) => {\r\n trackPageView(url);\r\n };\r\n\r\n router.events.on(\"routeChangeComplete\", handleRouteChange);\r\n\r\n return () => {\r\n router.events.off(\"routeChangeComplete\", handleRouteChange);\r\n };\r\n};\r\n","/**\r\n * Session Lifecycle Tracking\r\n * - session_start: Khi user mở app\r\n * - session_progress: Định kỳ khi user đang active (foreground)\r\n * - session_end: Khi user thoát app\r\n */\r\n\r\nimport { sendEvent } from \"./tracking\";\r\nimport { renewSessionCookie } from \"./cookie-utils\";\r\nimport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\nexport type SessionConfig = {\r\n /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */\r\n progressInterval?: number;\r\n /** Có tự động start session khi init không. Default: true */\r\n autoStart?: boolean;\r\n /** Debug mode */\r\n debug?: boolean;\r\n /** User ID */\r\n userId?: string;\r\n /** Session cookie configuration */\r\n sessionCookieConfig?: SessionCookieConfig;\r\n};\r\n\r\nconst defaultSessionConfig: SessionConfig = {\r\n progressInterval: 5 * 60 * 1000, // 5 phút\r\n autoStart: true,\r\n debug: false,\r\n};\r\n\r\n// State\r\nlet sessionConfig: SessionConfig = { ...defaultSessionConfig };\r\nlet progressTimer: ReturnType<typeof setInterval> | null = null;\r\nlet isSessionActive = false;\r\nlet sessionStartTime: number = 0;\r\n\r\n/**\r\n * Log helper\r\n */\r\nconst log = (...args: any[]) => {\r\n if (sessionConfig.debug) {\r\n console.log(\"[Session]\", ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Gửi event session_start\r\n */\r\nexport const sendSessionStart = () => {\r\n if (isSessionActive) {\r\n log(\"Session already started, skipping...\");\r\n return;\r\n }\r\n\r\n sessionStartTime = Date.now();\r\n isSessionActive = true;\r\n\r\n sendEvent({\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Gia hạn cookie session_id (extend thêm 30 phút)\r\n renewSessionIdCookie();\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n\r\n // Gia hạn cookie session_id mỗi khi user còn active\r\n renewSessionIdCookie();\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi khi có event session_start để extend thời gian hết hạn\r\n */\r\nconst renewSessionIdCookie = () => {\r\n // Lấy sessionId và config từ tracking module\r\n // Note: Cần export getter function từ tracking.ts để lấy config\r\n if (sessionConfig.sessionCookieConfig) {\r\n // Tạm thời sử dụng cookie name mặc định để lấy sessionId\r\n const cookieName =\r\n sessionConfig.sessionCookieConfig.cookieName || \"_tracking_session_id\";\r\n const sessionId = document.cookie\r\n .split(\"; \")\r\n .find((row) => row.startsWith(cookieName + \"=\"))\r\n ?.split(\"=\")[1];\r\n\r\n if (sessionId) {\r\n renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);\r\n log(\"Session cookie renewed for:\", sessionId);\r\n }\r\n }\r\n};\r\n"],"mappings":"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","names":["uuidv4","config"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/cookie-utils.ts","../src/tracking.ts","../src/session.ts"],"sourcesContent":["/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n UserInfo,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\nexport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Re-export cookie utilities\r\nexport {\r\n generateSessionId,\r\n getCookie,\r\n setCookie,\r\n getOrCreateSessionId,\r\n renewSessionCookie,\r\n clearSessionCookie,\r\n} from \"./cookie-utils\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n setUserInfo,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n","/**\r\n * Constants for Tracking Library\r\n */\r\n\r\nimport type { TrackingConfig, PageMap } from \"./types\";\r\n\r\nexport const defaultConfig: TrackingConfig = {\r\n baseURL: \"\",\r\n appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"WebPC\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n userId: -1,\r\n\r\n // Optional properties\r\n adId: \"\",\r\n sdkVersion: \"\",\r\n\r\n events: [],\r\n};\r\n\r\nexport const defaultPageMap: PageMap = {\r\n \"\": { name: \"Home\", id: \"home\" },\r\n home: { name: \"Home\", id: \"home\" },\r\n search: { name: \"Search\", id: \"search\" },\r\n detail: { name: \"Detail\", id: \"detail\" },\r\n player: { name: \"Player\", id: \"player\" },\r\n video: { name: \"Video\", id: \"video\" },\r\n live: { name: \"Live\", id: \"live\" },\r\n category: { name: \"Category\", id: \"category\" },\r\n profile: { name: \"Profile\", id: \"profile\" },\r\n login: { name: \"Login\", id: \"login\" },\r\n};\r\n","/**\r\n * Cookie Utilities for Session Management\r\n */\r\n\r\nimport { v4 as uuidv4 } from \"uuid\";\r\n\r\nexport type SessionCookieConfig = {\r\n /** Tên cookie lưu session_id. Default: '_tracking_session_id' */\r\n cookieName?: string;\r\n /** Thời gian hết hạn cookie (phút). Default: 30 */\r\n expirationMinutes?: number;\r\n /** Domain cho cookie (hỗ trợ group domain như .example.com) */\r\n domain?: string;\r\n /** Path cho cookie. Default: '/' */\r\n path?: string;\r\n /** SameSite attribute. Default: 'Lax' */\r\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\r\n};\r\n\r\nconst defaultCookieConfig: Required<SessionCookieConfig> = {\r\n cookieName: \"_tracking_session_id\",\r\n expirationMinutes: 30,\r\n domain: \"\",\r\n path: \"/\",\r\n sameSite: \"Lax\",\r\n};\r\n\r\n/**\r\n * Tạo session ID theo format UUID v4\r\n */\r\nexport const generateSessionId = (): string => {\r\n return uuidv4();\r\n};\r\n\r\n/**\r\n * Lấy giá trị cookie theo tên\r\n */\r\nexport const getCookie = (name: string): string | null => {\r\n if (typeof document === \"undefined\") return null;\r\n\r\n const nameEQ = name + \"=\";\r\n const cookies = document.cookie.split(\";\");\r\n\r\n for (let i = 0; i < cookies.length; i++) {\r\n let cookie = cookies[i];\r\n while (cookie.charAt(0) === \" \") {\r\n cookie = cookie.substring(1);\r\n }\r\n if (cookie.indexOf(nameEQ) === 0) {\r\n return cookie.substring(nameEQ.length);\r\n }\r\n }\r\n return null;\r\n};\r\n\r\n/**\r\n * Set cookie với các options\r\n */\r\nexport const setCookie = (\r\n name: string,\r\n value: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n const expirationMs = config.expirationMinutes * 60 * 1000;\r\n const expires = new Date(Date.now() + expirationMs);\r\n\r\n let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n if (config.sameSite) {\r\n cookieString += `; SameSite=${config.sameSite}`;\r\n }\r\n\r\n // Nếu SameSite=None thì phải có Secure\r\n if (config.sameSite === \"None\") {\r\n cookieString += \"; Secure\";\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n\r\n/**\r\n * Lấy hoặc tạo mới session_id từ cookie\r\n * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ\r\n * Nếu không -> tạo mới và lưu vào cookie\r\n */\r\nexport const getOrCreateSessionId = (\r\n options: SessionCookieConfig = {},\r\n): string => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n const existingSessionId = getCookie(config.cookieName);\r\n\r\n if (existingSessionId) {\r\n return existingSessionId;\r\n }\r\n\r\n // Tạo mới session_id\r\n const newSessionId = generateSessionId();\r\n setCookie(config.cookieName, newSessionId, options);\r\n\r\n return newSessionId;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi hàm này khi có event session_start để extend thời gian hết hạn\r\n */\r\nexport const renewSessionCookie = (\r\n sessionId: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n setCookie(config.cookieName, sessionId, options);\r\n};\r\n\r\n/**\r\n * Xóa session cookie\r\n */\r\nexport const clearSessionCookie = (options: SessionCookieConfig = {}): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n let cookieString = `${config.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n","/**\r\n * Core Tracking Functions\r\n */\r\n\r\nimport type {\r\n TrackingConfig,\r\n PageMap,\r\n PageInfo,\r\n EventData,\r\n NextRouterLike,\r\n UserInfo,\r\n} from \"./types\";\r\nimport { defaultConfig, defaultPageMap } from \"./constants\";\r\nimport { getOrCreateSessionId } from \"./cookie-utils\";\r\n\r\n// Config runtime\r\nlet config: TrackingConfig = { ...defaultConfig };\r\n\r\n// User info state\r\nlet userId: number = config.userId ?? -1;\r\n\r\n// State\r\nlet previousPageId: string | null = null;\r\n\r\n// Page mapping\r\nlet pageMap: any = { ...defaultPageMap };\r\n\r\n/**\r\n * Khởi tạo tracking với config\r\n */\r\nexport const initTracking = (\r\n options: Partial<TrackingConfig> & { pageMap?: Partial<PageMap> } = {},\r\n) => {\r\n config = { ...config, ...options };\r\n\r\n // Auto-generate sessionId from cookie if not provided\r\n if (!config.sessionId) {\r\n config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);\r\n if (config.debug) {\r\n console.log(\r\n \"[Tracking] Auto-generated session_id from cookie:\",\r\n config.sessionId,\r\n );\r\n }\r\n } else if (config.debug) {\r\n console.log(\"[Tracking] Using user-provided session_id:\", config.sessionId);\r\n }\r\n\r\n if (options.pageMap) {\r\n pageMap = { ...pageMap, ...options.pageMap };\r\n }\r\n\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] baseURL is required.\");\r\n }\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Initialized with config:\", config);\r\n }\r\n};\r\n\r\n/**\r\n * Cập nhật thông tin người dùng (userId)\r\n * userId sẽ được tự động thêm vào mỗi event trong events array\r\n */\r\nexport const setUserInfo = (userInfo: UserInfo) => {\r\n userId = userInfo.userId;\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] User info updated, userId:\", userId);\r\n }\r\n};\r\n\r\n/**\r\n * Lấy thông tin page từ URL\r\n */\r\nexport const getPageInfo = (url: string): PageInfo => {\r\n const path = (url || \"\").split(\"?\")[0];\r\n const pathSegment = path.split(\"/\").filter(Boolean)[0] || \"home\";\r\n\r\n return (\r\n pageMap[pathSegment] || {\r\n name: pathSegment.charAt(0).toUpperCase() + pathSegment.slice(1),\r\n id: pathSegment,\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * Send Event -> POST JSON\r\n */\r\nexport const sendEvent = async (\r\n eventData: EventData,\r\n): Promise<Response | void> => {\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] Not initialized. Call initTracking() first.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Tách event_name ra khỏi eventData để tạo event object\r\n const { event_name, ...eventParams } = eventData;\r\n\r\n // Tạo event object với format chuẩn theo API spec\r\n const eventObject = {\r\n // Required fields\r\n user_id: userId, // userId được set từ setUserInfo, -1 nếu chưa set\r\n event_name: event_name || \"unknown\",\r\n event_time: Math.floor(Date.now() / 1000),\r\n // Optional fields\r\n event_params: JSON.stringify(eventParams), // JSON string ≤ 1024\r\n };\r\n\r\n const payload = {\r\n // Required properties\r\n app_id: config.appId,\r\n package_id: config.packageId,\r\n platform: config.platform,\r\n\r\n session_id: config.sessionId,\r\n session_sign: config.sessionSign,\r\n\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n device_os_version: config.deviceOsVersion,\r\n sdk_version: config.sdkVersion,\r\n\r\n // Note: ip_address và carrier_net - Server tự lấy từ header\r\n\r\n // Events batch - tự động thêm event vào mảng\r\n events: [...(config.events ?? []), eventObject],\r\n };\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Sending event:\", payload);\r\n }\r\n\r\n const res = await fetch(config.baseURL, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!res.ok && config.debug) {\r\n const text = await res.text().catch(() => \"\");\r\n console.error(\"[Tracking] HTTP Error:\", res.status, text);\r\n }\r\n\r\n return res;\r\n } catch (error) {\r\n if (config.debug) {\r\n console.error(\"[Tracking] Error:\", error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Track page view event\r\n */\r\nexport const trackPageView = (url: string) => {\r\n const pageInfo = getPageInfo(url);\r\n\r\n sendEvent({\r\n event_name: \"page_view\",\r\n page_name: pageInfo.name,\r\n page_id: pageInfo.id,\r\n page_path: url,\r\n referrer_page_id: previousPageId || \"\",\r\n });\r\n\r\n previousPageId = pageInfo.id;\r\n};\r\n\r\n/**\r\n * Track custom event\r\n */\r\nexport const trackEvent = (eventName: string, params: EventData = {}) => {\r\n sendEvent({\r\n event_name: eventName,\r\n ...params,\r\n });\r\n};\r\n\r\n/**\r\n * Track view content details event\r\n * @param contentId - ID của content (required)\r\n * @param contentTitle - Tiêu đề content (required)\r\n * @param contentType - Loại content: 0=VOD, 1=Event, 2=Channel (required)\r\n * @param channelId - Mã kênh EPG (optional)\r\n * @param eventTime - Timestamp Unix (optional, mặc định lấy time hiện tại)\r\n */\r\nexport const trackViewContentDetails = (\r\n contentId: string,\r\n contentTitle: string,\r\n contentType: 0 | 1 | 2,\r\n channelId?: string,\r\n eventTime?: number,\r\n) => {\r\n sendEvent({\r\n event_name: \"view_content_details\",\r\n content_id: contentId,\r\n content_title: contentTitle,\r\n content_type: contentType,\r\n channel_id: channelId,\r\n event_time: eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback started event\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID: MD5(content_id + start_timestamp) (required)\r\n * @param params.videoQuality - Chất lượng video: 360p, 480p, 720p, 1080p, 2k, 4k (required)\r\n * @param params.startTimestamp - Unix timestamp khi bắt đầu phát (required)\r\n * @param params.startPosition - Vị trí bắt đầu: Live=0, VOD: 0 nếu mới, >0 nếu xem lại (required)\r\n * @param params.duration - Thời lượng: Live=-1, VOD=số giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n startTimestamp: number;\r\n startPosition: number;\r\n duration: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n start_timestamp: params.startTimestamp,\r\n start_position: params.startPosition,\r\n duration: params.duration,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback progress event\r\n * Gửi định kỳ: loop 1 = 5s, loop 2 = 25s để track thời lượng user xem nội dung\r\n * Lưu ý: total_watch_time là tổng thời gian xem được (không tính pause)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackProgress = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_progress\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback completed event\r\n * Tương tự playback_progress nhưng là event cuối cùng khi kết thúc xem\r\n * Với Live có thể thoát chủ động thì tracking event này\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackCompleted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_completed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback error event\r\n * Trigger khi:\r\n * - Video không load/play được (network timeout, source 404)\r\n * - Player error (format không hỗ trợ, DRM error)\r\n * - Source error (media source and manifest-related errors)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.errorCode - Mã lỗi (optional)\r\n * @param params.errorMessage - Thông báo lỗi (optional)\r\n * @param params.errorType - Loại lỗi: video_playback_error, network_error... (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackError = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1;\r\n playbackSession: string;\r\n channelId?: string;\r\n errorCode?: number;\r\n errorMessage?: string;\r\n errorType?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_error\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n channel_id: params.channelId,\r\n error_code: params.errorCode,\r\n error_message: params.errorMessage,\r\n error_type: params.errorType,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track quality changed event\r\n * Trigger khi:\r\n * - Tự động thay đổi do thuật toán ABR\r\n * - Thay đổi chủ động bởi người dùng\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.fromQuality - Chất lượng trước khi đổi (required)\r\n * @param params.toQuality - Chất lượng sau khi đổi (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: automatic_abr hoặc user_selected (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackQualityChanged = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n fromQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n toQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"automatic_abr\" | \"user_selected\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"quality_changed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n from_quality: params.fromQuality,\r\n to_quality: params.toQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer started event\r\n * Buffering xảy ra khi video không còn segment kế tiếp để phát, video sẽ pause để tải thêm segment\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer ended event\r\n * Khi buffering kết thúc và video tiếp tục phát\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.bufferDurationSec - Tổng số giây mà buffer xảy ra (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferEnded = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n bufferDurationSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_ended\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n buffer_duration_sec: params.bufferDurationSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback paused event\r\n * Khi user pause video\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackPaused = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_paused\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback resumed event\r\n * Khi user resume video sau khi pause\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackResumed = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_resumed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Hook cho Next.js - auto track page views\r\n * Dùng cho Next.js Pages Router (next/router)\r\n */\r\nexport const usePageViewTracking = (router: NextRouterLike) => {\r\n if (typeof window === \"undefined\") return;\r\n if (!router) return;\r\n\r\n // Track initial page\r\n trackPageView(router.asPath);\r\n\r\n const handleRouteChange = (url: string) => {\r\n trackPageView(url);\r\n };\r\n\r\n router.events.on(\"routeChangeComplete\", handleRouteChange);\r\n\r\n return () => {\r\n router.events.off(\"routeChangeComplete\", handleRouteChange);\r\n };\r\n};\r\n","/**\r\n * Session Lifecycle Tracking\r\n * - session_start: Khi user mở app\r\n * - session_progress: Định kỳ khi user đang active (foreground)\r\n * - session_end: Khi user thoát app\r\n */\r\n\r\nimport { sendEvent } from \"./tracking\";\r\nimport { renewSessionCookie } from \"./cookie-utils\";\r\nimport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\nexport type SessionConfig = {\r\n /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */\r\n progressInterval?: number;\r\n /** Có tự động start session khi init không. Default: true */\r\n autoStart?: boolean;\r\n /** Debug mode */\r\n debug?: boolean;\r\n /** User ID */\r\n userId?: string;\r\n /** Session cookie configuration */\r\n sessionCookieConfig?: SessionCookieConfig;\r\n};\r\n\r\nconst defaultSessionConfig: SessionConfig = {\r\n progressInterval: 5 * 60 * 1000, // 5 phút\r\n autoStart: true,\r\n debug: false,\r\n};\r\n\r\n// State\r\nlet sessionConfig: SessionConfig = { ...defaultSessionConfig };\r\nlet progressTimer: ReturnType<typeof setInterval> | null = null;\r\nlet isSessionActive = false;\r\nlet sessionStartTime: number = 0;\r\n\r\n/**\r\n * Log helper\r\n */\r\nconst log = (...args: any[]) => {\r\n if (sessionConfig.debug) {\r\n console.log(\"[Session]\", ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Gửi event session_start\r\n */\r\nexport const sendSessionStart = () => {\r\n if (isSessionActive) {\r\n log(\"Session already started, skipping...\");\r\n return;\r\n }\r\n\r\n sessionStartTime = Date.now();\r\n isSessionActive = true;\r\n\r\n sendEvent({\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Gia hạn cookie session_id (extend thêm 30 phút)\r\n renewSessionIdCookie();\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n\r\n // Gia hạn cookie session_id mỗi khi user còn active\r\n renewSessionIdCookie();\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi khi có event session_start để extend thời gian hết hạn\r\n */\r\nconst renewSessionIdCookie = () => {\r\n // Lấy sessionId và config từ tracking module\r\n // Note: Cần export getter function từ tracking.ts để lấy config\r\n if (sessionConfig.sessionCookieConfig) {\r\n // Tạm thời sử dụng cookie name mặc định để lấy sessionId\r\n const cookieName =\r\n sessionConfig.sessionCookieConfig.cookieName || \"_tracking_session_id\";\r\n const sessionId = document.cookie\r\n .split(\"; \")\r\n .find((row) => row.startsWith(cookieName + \"=\"))\r\n ?.split(\"=\")[1];\r\n\r\n if (sessionId) {\r\n renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);\r\n log(\"Session cookie renewed for:\", sessionId);\r\n }\r\n }\r\n};\r\n"],"mappings":"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","names":["uuidv4","config"]}
package/dist/index.d.cts CHANGED
@@ -110,6 +110,7 @@ type TrackingConfig = {
110
110
  deviceModel: string;
111
111
  deviceBrand: string;
112
112
  events: any[];
113
+ userId?: number;
113
114
  adId?: string;
114
115
  deviceOsVersion?: string;
115
116
  sdkVersion?: string;
package/dist/index.d.ts CHANGED
@@ -110,6 +110,7 @@ type TrackingConfig = {
110
110
  deviceModel: string;
111
111
  deviceBrand: string;
112
112
  events: any[];
113
+ userId?: number;
113
114
  adId?: string;
114
115
  deviceOsVersion?: string;
115
116
  sdkVersion?: string;
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ var defaultConfig = {
11
11
  deviceId: "",
12
12
  deviceModel: "",
13
13
  deviceBrand: "",
14
+ userId: -1,
14
15
  // Optional properties
15
16
  adId: "",
16
17
  sdkVersion: "",
@@ -99,7 +100,7 @@ var clearSessionCookie = (options = {}) => {
99
100
 
100
101
  // src/tracking.ts
101
102
  var config = { ...defaultConfig };
102
- var userId = -1;
103
+ var userId = config.userId ?? -1;
103
104
  var previousPageId = null;
104
105
  var pageMap = { ...defaultPageMap };
105
106
  var initTracking = (options = {}) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/cookie-utils.ts","../src/tracking.ts","../src/session.ts","../src/index.ts"],"sourcesContent":["/**\r\n * Constants for Tracking Library\r\n */\r\n\r\nimport type { TrackingConfig, PageMap } from \"./types\";\r\n\r\nexport const defaultConfig: TrackingConfig = {\r\n baseURL: \"\",\r\n appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"WebPC\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n\r\n // Optional properties\r\n adId: \"\",\r\n sdkVersion: \"\",\r\n\r\n events: [],\r\n};\r\n\r\nexport const defaultPageMap: PageMap = {\r\n \"\": { name: \"Home\", id: \"home\" },\r\n home: { name: \"Home\", id: \"home\" },\r\n search: { name: \"Search\", id: \"search\" },\r\n detail: { name: \"Detail\", id: \"detail\" },\r\n player: { name: \"Player\", id: \"player\" },\r\n video: { name: \"Video\", id: \"video\" },\r\n live: { name: \"Live\", id: \"live\" },\r\n category: { name: \"Category\", id: \"category\" },\r\n profile: { name: \"Profile\", id: \"profile\" },\r\n login: { name: \"Login\", id: \"login\" },\r\n};\r\n","/**\r\n * Cookie Utilities for Session Management\r\n */\r\n\r\nimport { v4 as uuidv4 } from \"uuid\";\r\n\r\nexport type SessionCookieConfig = {\r\n /** Tên cookie lưu session_id. Default: '_tracking_session_id' */\r\n cookieName?: string;\r\n /** Thời gian hết hạn cookie (phút). Default: 30 */\r\n expirationMinutes?: number;\r\n /** Domain cho cookie (hỗ trợ group domain như .example.com) */\r\n domain?: string;\r\n /** Path cho cookie. Default: '/' */\r\n path?: string;\r\n /** SameSite attribute. Default: 'Lax' */\r\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\r\n};\r\n\r\nconst defaultCookieConfig: Required<SessionCookieConfig> = {\r\n cookieName: \"_tracking_session_id\",\r\n expirationMinutes: 30,\r\n domain: \"\",\r\n path: \"/\",\r\n sameSite: \"Lax\",\r\n};\r\n\r\n/**\r\n * Tạo session ID theo format UUID v4\r\n */\r\nexport const generateSessionId = (): string => {\r\n return uuidv4();\r\n};\r\n\r\n/**\r\n * Lấy giá trị cookie theo tên\r\n */\r\nexport const getCookie = (name: string): string | null => {\r\n if (typeof document === \"undefined\") return null;\r\n\r\n const nameEQ = name + \"=\";\r\n const cookies = document.cookie.split(\";\");\r\n\r\n for (let i = 0; i < cookies.length; i++) {\r\n let cookie = cookies[i];\r\n while (cookie.charAt(0) === \" \") {\r\n cookie = cookie.substring(1);\r\n }\r\n if (cookie.indexOf(nameEQ) === 0) {\r\n return cookie.substring(nameEQ.length);\r\n }\r\n }\r\n return null;\r\n};\r\n\r\n/**\r\n * Set cookie với các options\r\n */\r\nexport const setCookie = (\r\n name: string,\r\n value: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n const expirationMs = config.expirationMinutes * 60 * 1000;\r\n const expires = new Date(Date.now() + expirationMs);\r\n\r\n let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n if (config.sameSite) {\r\n cookieString += `; SameSite=${config.sameSite}`;\r\n }\r\n\r\n // Nếu SameSite=None thì phải có Secure\r\n if (config.sameSite === \"None\") {\r\n cookieString += \"; Secure\";\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n\r\n/**\r\n * Lấy hoặc tạo mới session_id từ cookie\r\n * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ\r\n * Nếu không -> tạo mới và lưu vào cookie\r\n */\r\nexport const getOrCreateSessionId = (\r\n options: SessionCookieConfig = {},\r\n): string => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n const existingSessionId = getCookie(config.cookieName);\r\n\r\n if (existingSessionId) {\r\n return existingSessionId;\r\n }\r\n\r\n // Tạo mới session_id\r\n const newSessionId = generateSessionId();\r\n setCookie(config.cookieName, newSessionId, options);\r\n\r\n return newSessionId;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi hàm này khi có event session_start để extend thời gian hết hạn\r\n */\r\nexport const renewSessionCookie = (\r\n sessionId: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n setCookie(config.cookieName, sessionId, options);\r\n};\r\n\r\n/**\r\n * Xóa session cookie\r\n */\r\nexport const clearSessionCookie = (options: SessionCookieConfig = {}): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n let cookieString = `${config.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n","/**\r\n * Core Tracking Functions\r\n */\r\n\r\nimport type {\r\n TrackingConfig,\r\n PageMap,\r\n PageInfo,\r\n EventData,\r\n NextRouterLike,\r\n UserInfo,\r\n} from \"./types\";\r\nimport { defaultConfig, defaultPageMap } from \"./constants\";\r\nimport { getOrCreateSessionId } from \"./cookie-utils\";\r\nimport { v4 as uuidv4 } from \"uuid\";\r\n\r\n// Config runtime\r\nlet config: TrackingConfig = { ...defaultConfig };\r\n\r\n// User info state\r\nlet userId: number = -1;\r\n\r\n// State\r\nlet previousPageId: string | null = null;\r\n\r\n// Page mapping\r\nlet pageMap: any = { ...defaultPageMap };\r\n\r\n/**\r\n * Khởi tạo tracking với config\r\n */\r\nexport const initTracking = (\r\n options: Partial<TrackingConfig> & { pageMap?: Partial<PageMap> } = {},\r\n) => {\r\n config = { ...config, ...options };\r\n\r\n // Auto-generate sessionId from cookie if not provided\r\n if (!config.sessionId) {\r\n config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);\r\n if (config.debug) {\r\n console.log(\r\n \"[Tracking] Auto-generated session_id from cookie:\",\r\n config.sessionId,\r\n );\r\n }\r\n } else if (config.debug) {\r\n console.log(\"[Tracking] Using user-provided session_id:\", config.sessionId);\r\n }\r\n\r\n if (options.pageMap) {\r\n pageMap = { ...pageMap, ...options.pageMap };\r\n }\r\n\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] baseURL is required.\");\r\n }\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Initialized with config:\", config);\r\n }\r\n};\r\n\r\n/**\r\n * Cập nhật thông tin người dùng (userId)\r\n * userId sẽ được tự động thêm vào mỗi event trong events array\r\n */\r\nexport const setUserInfo = (userInfo: UserInfo) => {\r\n userId = userInfo.userId;\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] User info updated, userId:\", userId);\r\n }\r\n};\r\n\r\n/**\r\n * Lấy thông tin page từ URL\r\n */\r\nexport const getPageInfo = (url: string): PageInfo => {\r\n const path = (url || \"\").split(\"?\")[0];\r\n const pathSegment = path.split(\"/\").filter(Boolean)[0] || \"home\";\r\n\r\n return (\r\n pageMap[pathSegment] || {\r\n name: pathSegment.charAt(0).toUpperCase() + pathSegment.slice(1),\r\n id: pathSegment,\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * Send Event -> POST JSON\r\n */\r\nexport const sendEvent = async (\r\n eventData: EventData,\r\n): Promise<Response | void> => {\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] Not initialized. Call initTracking() first.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Tách event_name ra khỏi eventData để tạo event object\r\n const { event_name, ...eventParams } = eventData;\r\n\r\n // Tạo event object với format chuẩn theo API spec\r\n const eventObject = {\r\n // Required fields\r\n user_id: userId, // userId được set từ setUserInfo, -1 nếu chưa set\r\n event_name: event_name || \"unknown\",\r\n event_time: Math.floor(Date.now() / 1000),\r\n // Optional fields\r\n event_params: JSON.stringify(eventParams), // JSON string ≤ 1024\r\n };\r\n\r\n const payload = {\r\n // Required properties\r\n app_id: config.appId,\r\n package_id: config.packageId,\r\n platform: config.platform,\r\n\r\n session_id: config.sessionId,\r\n session_sign: config.sessionSign,\r\n\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n device_os_version: config.deviceOsVersion,\r\n sdk_version: config.sdkVersion,\r\n\r\n // Note: ip_address và carrier_net - Server tự lấy từ header\r\n\r\n // Events batch - tự động thêm event vào mảng\r\n events: [...(config.events ?? []), eventObject],\r\n };\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Sending event:\", payload);\r\n }\r\n\r\n const res = await fetch(config.baseURL, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!res.ok && config.debug) {\r\n const text = await res.text().catch(() => \"\");\r\n console.error(\"[Tracking] HTTP Error:\", res.status, text);\r\n }\r\n\r\n return res;\r\n } catch (error) {\r\n if (config.debug) {\r\n console.error(\"[Tracking] Error:\", error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Track page view event\r\n */\r\nexport const trackPageView = (url: string) => {\r\n const pageInfo = getPageInfo(url);\r\n\r\n sendEvent({\r\n event_name: \"page_view\",\r\n page_name: pageInfo.name,\r\n page_id: pageInfo.id,\r\n page_path: url,\r\n referrer_page_id: previousPageId || \"\",\r\n });\r\n\r\n previousPageId = pageInfo.id;\r\n};\r\n\r\n/**\r\n * Track custom event\r\n */\r\nexport const trackEvent = (eventName: string, params: EventData = {}) => {\r\n sendEvent({\r\n event_name: eventName,\r\n ...params,\r\n });\r\n};\r\n\r\n/**\r\n * Track view content details event\r\n * @param contentId - ID của content (required)\r\n * @param contentTitle - Tiêu đề content (required)\r\n * @param contentType - Loại content: 0=VOD, 1=Event, 2=Channel (required)\r\n * @param channelId - Mã kênh EPG (optional)\r\n * @param eventTime - Timestamp Unix (optional, mặc định lấy time hiện tại)\r\n */\r\nexport const trackViewContentDetails = (\r\n contentId: string,\r\n contentTitle: string,\r\n contentType: 0 | 1 | 2,\r\n channelId?: string,\r\n eventTime?: number,\r\n) => {\r\n sendEvent({\r\n event_name: \"view_content_details\",\r\n content_id: contentId,\r\n content_title: contentTitle,\r\n content_type: contentType,\r\n channel_id: channelId,\r\n event_time: eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback started event\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID: MD5(content_id + start_timestamp) (required)\r\n * @param params.videoQuality - Chất lượng video: 360p, 480p, 720p, 1080p, 2k, 4k (required)\r\n * @param params.startTimestamp - Unix timestamp khi bắt đầu phát (required)\r\n * @param params.startPosition - Vị trí bắt đầu: Live=0, VOD: 0 nếu mới, >0 nếu xem lại (required)\r\n * @param params.duration - Thời lượng: Live=-1, VOD=số giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n startTimestamp: number;\r\n startPosition: number;\r\n duration: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n start_timestamp: params.startTimestamp,\r\n start_position: params.startPosition,\r\n duration: params.duration,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback progress event\r\n * Gửi định kỳ: loop 1 = 5s, loop 2 = 25s để track thời lượng user xem nội dung\r\n * Lưu ý: total_watch_time là tổng thời gian xem được (không tính pause)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackProgress = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_progress\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback completed event\r\n * Tương tự playback_progress nhưng là event cuối cùng khi kết thúc xem\r\n * Với Live có thể thoát chủ động thì tracking event này\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackCompleted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_completed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback error event\r\n * Trigger khi:\r\n * - Video không load/play được (network timeout, source 404)\r\n * - Player error (format không hỗ trợ, DRM error)\r\n * - Source error (media source and manifest-related errors)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.errorCode - Mã lỗi (optional)\r\n * @param params.errorMessage - Thông báo lỗi (optional)\r\n * @param params.errorType - Loại lỗi: video_playback_error, network_error... (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackError = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1;\r\n playbackSession: string;\r\n channelId?: string;\r\n errorCode?: number;\r\n errorMessage?: string;\r\n errorType?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_error\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n channel_id: params.channelId,\r\n error_code: params.errorCode,\r\n error_message: params.errorMessage,\r\n error_type: params.errorType,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track quality changed event\r\n * Trigger khi:\r\n * - Tự động thay đổi do thuật toán ABR\r\n * - Thay đổi chủ động bởi người dùng\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.fromQuality - Chất lượng trước khi đổi (required)\r\n * @param params.toQuality - Chất lượng sau khi đổi (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: automatic_abr hoặc user_selected (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackQualityChanged = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n fromQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n toQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"automatic_abr\" | \"user_selected\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"quality_changed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n from_quality: params.fromQuality,\r\n to_quality: params.toQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer started event\r\n * Buffering xảy ra khi video không còn segment kế tiếp để phát, video sẽ pause để tải thêm segment\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer ended event\r\n * Khi buffering kết thúc và video tiếp tục phát\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.bufferDurationSec - Tổng số giây mà buffer xảy ra (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferEnded = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n bufferDurationSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_ended\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n buffer_duration_sec: params.bufferDurationSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback paused event\r\n * Khi user pause video\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackPaused = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_paused\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback resumed event\r\n * Khi user resume video sau khi pause\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackResumed = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_resumed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Hook cho Next.js - auto track page views\r\n * Dùng cho Next.js Pages Router (next/router)\r\n */\r\nexport const usePageViewTracking = (router: NextRouterLike) => {\r\n if (typeof window === \"undefined\") return;\r\n if (!router) return;\r\n\r\n // Track initial page\r\n trackPageView(router.asPath);\r\n\r\n const handleRouteChange = (url: string) => {\r\n trackPageView(url);\r\n };\r\n\r\n router.events.on(\"routeChangeComplete\", handleRouteChange);\r\n\r\n return () => {\r\n router.events.off(\"routeChangeComplete\", handleRouteChange);\r\n };\r\n};\r\n","/**\r\n * Session Lifecycle Tracking\r\n * - session_start: Khi user mở app\r\n * - session_progress: Định kỳ khi user đang active (foreground)\r\n * - session_end: Khi user thoát app\r\n */\r\n\r\nimport { sendEvent } from \"./tracking\";\r\nimport { renewSessionCookie } from \"./cookie-utils\";\r\nimport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\nexport type SessionConfig = {\r\n /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */\r\n progressInterval?: number;\r\n /** Có tự động start session khi init không. Default: true */\r\n autoStart?: boolean;\r\n /** Debug mode */\r\n debug?: boolean;\r\n /** User ID */\r\n userId?: string;\r\n /** Session cookie configuration */\r\n sessionCookieConfig?: SessionCookieConfig;\r\n};\r\n\r\nconst defaultSessionConfig: SessionConfig = {\r\n progressInterval: 5 * 60 * 1000, // 5 phút\r\n autoStart: true,\r\n debug: false,\r\n};\r\n\r\n// State\r\nlet sessionConfig: SessionConfig = { ...defaultSessionConfig };\r\nlet progressTimer: ReturnType<typeof setInterval> | null = null;\r\nlet isSessionActive = false;\r\nlet sessionStartTime: number = 0;\r\n\r\n/**\r\n * Log helper\r\n */\r\nconst log = (...args: any[]) => {\r\n if (sessionConfig.debug) {\r\n console.log(\"[Session]\", ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Gửi event session_start\r\n */\r\nexport const sendSessionStart = () => {\r\n if (isSessionActive) {\r\n log(\"Session already started, skipping...\");\r\n return;\r\n }\r\n\r\n sessionStartTime = Date.now();\r\n isSessionActive = true;\r\n\r\n sendEvent({\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Gia hạn cookie session_id (extend thêm 30 phút)\r\n renewSessionIdCookie();\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n\r\n // Gia hạn cookie session_id mỗi khi user còn active\r\n renewSessionIdCookie();\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi khi có event session_start để extend thời gian hết hạn\r\n */\r\nconst renewSessionIdCookie = () => {\r\n // Lấy sessionId và config từ tracking module\r\n // Note: Cần export getter function từ tracking.ts để lấy config\r\n if (sessionConfig.sessionCookieConfig) {\r\n // Tạm thời sử dụng cookie name mặc định để lấy sessionId\r\n const cookieName =\r\n sessionConfig.sessionCookieConfig.cookieName || \"_tracking_session_id\";\r\n const sessionId = document.cookie\r\n .split(\"; \")\r\n .find((row) => row.startsWith(cookieName + \"=\"))\r\n ?.split(\"=\")[1];\r\n\r\n if (sessionId) {\r\n renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);\r\n log(\"Session cookie renewed for:\", sessionId);\r\n }\r\n }\r\n};\r\n","/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n UserInfo,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\nexport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Re-export cookie utilities\r\nexport {\r\n generateSessionId,\r\n getCookie,\r\n setCookie,\r\n getOrCreateSessionId,\r\n renewSessionCookie,\r\n clearSessionCookie,\r\n} from \"./cookie-utils\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n setUserInfo,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n"],"mappings":"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","names":["config"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/cookie-utils.ts","../src/tracking.ts","../src/session.ts","../src/index.ts"],"sourcesContent":["/**\r\n * Constants for Tracking Library\r\n */\r\n\r\nimport type { TrackingConfig, PageMap } from \"./types\";\r\n\r\nexport const defaultConfig: TrackingConfig = {\r\n baseURL: \"\",\r\n appId: \"\",\r\n packageId: \"com.vtvcab.onsportstv\",\r\n platform: \"WebPC\",\r\n debug: false,\r\n\r\n // Required properties\r\n sessionId: \"\",\r\n sessionSign: \"\",\r\n deviceId: \"\",\r\n deviceModel: \"\",\r\n deviceBrand: \"\",\r\n userId: -1,\r\n\r\n // Optional properties\r\n adId: \"\",\r\n sdkVersion: \"\",\r\n\r\n events: [],\r\n};\r\n\r\nexport const defaultPageMap: PageMap = {\r\n \"\": { name: \"Home\", id: \"home\" },\r\n home: { name: \"Home\", id: \"home\" },\r\n search: { name: \"Search\", id: \"search\" },\r\n detail: { name: \"Detail\", id: \"detail\" },\r\n player: { name: \"Player\", id: \"player\" },\r\n video: { name: \"Video\", id: \"video\" },\r\n live: { name: \"Live\", id: \"live\" },\r\n category: { name: \"Category\", id: \"category\" },\r\n profile: { name: \"Profile\", id: \"profile\" },\r\n login: { name: \"Login\", id: \"login\" },\r\n};\r\n","/**\r\n * Cookie Utilities for Session Management\r\n */\r\n\r\nimport { v4 as uuidv4 } from \"uuid\";\r\n\r\nexport type SessionCookieConfig = {\r\n /** Tên cookie lưu session_id. Default: '_tracking_session_id' */\r\n cookieName?: string;\r\n /** Thời gian hết hạn cookie (phút). Default: 30 */\r\n expirationMinutes?: number;\r\n /** Domain cho cookie (hỗ trợ group domain như .example.com) */\r\n domain?: string;\r\n /** Path cho cookie. Default: '/' */\r\n path?: string;\r\n /** SameSite attribute. Default: 'Lax' */\r\n sameSite?: \"Strict\" | \"Lax\" | \"None\";\r\n};\r\n\r\nconst defaultCookieConfig: Required<SessionCookieConfig> = {\r\n cookieName: \"_tracking_session_id\",\r\n expirationMinutes: 30,\r\n domain: \"\",\r\n path: \"/\",\r\n sameSite: \"Lax\",\r\n};\r\n\r\n/**\r\n * Tạo session ID theo format UUID v4\r\n */\r\nexport const generateSessionId = (): string => {\r\n return uuidv4();\r\n};\r\n\r\n/**\r\n * Lấy giá trị cookie theo tên\r\n */\r\nexport const getCookie = (name: string): string | null => {\r\n if (typeof document === \"undefined\") return null;\r\n\r\n const nameEQ = name + \"=\";\r\n const cookies = document.cookie.split(\";\");\r\n\r\n for (let i = 0; i < cookies.length; i++) {\r\n let cookie = cookies[i];\r\n while (cookie.charAt(0) === \" \") {\r\n cookie = cookie.substring(1);\r\n }\r\n if (cookie.indexOf(nameEQ) === 0) {\r\n return cookie.substring(nameEQ.length);\r\n }\r\n }\r\n return null;\r\n};\r\n\r\n/**\r\n * Set cookie với các options\r\n */\r\nexport const setCookie = (\r\n name: string,\r\n value: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n const expirationMs = config.expirationMinutes * 60 * 1000;\r\n const expires = new Date(Date.now() + expirationMs);\r\n\r\n let cookieString = `${name}=${value}; expires=${expires.toUTCString()}; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n if (config.sameSite) {\r\n cookieString += `; SameSite=${config.sameSite}`;\r\n }\r\n\r\n // Nếu SameSite=None thì phải có Secure\r\n if (config.sameSite === \"None\") {\r\n cookieString += \"; Secure\";\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n\r\n/**\r\n * Lấy hoặc tạo mới session_id từ cookie\r\n * Nếu cookie tồn tại và chưa hết hạn -> trả về giá trị cũ\r\n * Nếu không -> tạo mới và lưu vào cookie\r\n */\r\nexport const getOrCreateSessionId = (\r\n options: SessionCookieConfig = {},\r\n): string => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n const existingSessionId = getCookie(config.cookieName);\r\n\r\n if (existingSessionId) {\r\n return existingSessionId;\r\n }\r\n\r\n // Tạo mới session_id\r\n const newSessionId = generateSessionId();\r\n setCookie(config.cookieName, newSessionId, options);\r\n\r\n return newSessionId;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi hàm này khi có event session_start để extend thời gian hết hạn\r\n */\r\nexport const renewSessionCookie = (\r\n sessionId: string,\r\n options: SessionCookieConfig = {},\r\n): void => {\r\n const config = { ...defaultCookieConfig, ...options };\r\n setCookie(config.cookieName, sessionId, options);\r\n};\r\n\r\n/**\r\n * Xóa session cookie\r\n */\r\nexport const clearSessionCookie = (options: SessionCookieConfig = {}): void => {\r\n if (typeof document === \"undefined\") return;\r\n\r\n const config = { ...defaultCookieConfig, ...options };\r\n let cookieString = `${config.cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${config.path}`;\r\n\r\n if (config.domain) {\r\n cookieString += `; domain=${config.domain}`;\r\n }\r\n\r\n document.cookie = cookieString;\r\n};\r\n","/**\r\n * Core Tracking Functions\r\n */\r\n\r\nimport type {\r\n TrackingConfig,\r\n PageMap,\r\n PageInfo,\r\n EventData,\r\n NextRouterLike,\r\n UserInfo,\r\n} from \"./types\";\r\nimport { defaultConfig, defaultPageMap } from \"./constants\";\r\nimport { getOrCreateSessionId } from \"./cookie-utils\";\r\n\r\n// Config runtime\r\nlet config: TrackingConfig = { ...defaultConfig };\r\n\r\n// User info state\r\nlet userId: number = config.userId ?? -1;\r\n\r\n// State\r\nlet previousPageId: string | null = null;\r\n\r\n// Page mapping\r\nlet pageMap: any = { ...defaultPageMap };\r\n\r\n/**\r\n * Khởi tạo tracking với config\r\n */\r\nexport const initTracking = (\r\n options: Partial<TrackingConfig> & { pageMap?: Partial<PageMap> } = {},\r\n) => {\r\n config = { ...config, ...options };\r\n\r\n // Auto-generate sessionId from cookie if not provided\r\n if (!config.sessionId) {\r\n config.sessionId = getOrCreateSessionId(config.sessionCookieConfig);\r\n if (config.debug) {\r\n console.log(\r\n \"[Tracking] Auto-generated session_id from cookie:\",\r\n config.sessionId,\r\n );\r\n }\r\n } else if (config.debug) {\r\n console.log(\"[Tracking] Using user-provided session_id:\", config.sessionId);\r\n }\r\n\r\n if (options.pageMap) {\r\n pageMap = { ...pageMap, ...options.pageMap };\r\n }\r\n\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] baseURL is required.\");\r\n }\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Initialized with config:\", config);\r\n }\r\n};\r\n\r\n/**\r\n * Cập nhật thông tin người dùng (userId)\r\n * userId sẽ được tự động thêm vào mỗi event trong events array\r\n */\r\nexport const setUserInfo = (userInfo: UserInfo) => {\r\n userId = userInfo.userId;\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] User info updated, userId:\", userId);\r\n }\r\n};\r\n\r\n/**\r\n * Lấy thông tin page từ URL\r\n */\r\nexport const getPageInfo = (url: string): PageInfo => {\r\n const path = (url || \"\").split(\"?\")[0];\r\n const pathSegment = path.split(\"/\").filter(Boolean)[0] || \"home\";\r\n\r\n return (\r\n pageMap[pathSegment] || {\r\n name: pathSegment.charAt(0).toUpperCase() + pathSegment.slice(1),\r\n id: pathSegment,\r\n }\r\n );\r\n};\r\n\r\n/**\r\n * Send Event -> POST JSON\r\n */\r\nexport const sendEvent = async (\r\n eventData: EventData,\r\n): Promise<Response | void> => {\r\n if (!config.baseURL) {\r\n console.warn(\"[Tracking] Not initialized. Call initTracking() first.\");\r\n return;\r\n }\r\n\r\n try {\r\n // Tách event_name ra khỏi eventData để tạo event object\r\n const { event_name, ...eventParams } = eventData;\r\n\r\n // Tạo event object với format chuẩn theo API spec\r\n const eventObject = {\r\n // Required fields\r\n user_id: userId, // userId được set từ setUserInfo, -1 nếu chưa set\r\n event_name: event_name || \"unknown\",\r\n event_time: Math.floor(Date.now() / 1000),\r\n // Optional fields\r\n event_params: JSON.stringify(eventParams), // JSON string ≤ 1024\r\n };\r\n\r\n const payload = {\r\n // Required properties\r\n app_id: config.appId,\r\n package_id: config.packageId,\r\n platform: config.platform,\r\n\r\n session_id: config.sessionId,\r\n session_sign: config.sessionSign,\r\n\r\n device_id: config.deviceId,\r\n device_model: config.deviceModel,\r\n device_brand: config.deviceBrand,\r\n\r\n // Optional properties\r\n ad_id: config.adId,\r\n device_os_version: config.deviceOsVersion,\r\n sdk_version: config.sdkVersion,\r\n\r\n // Note: ip_address và carrier_net - Server tự lấy từ header\r\n\r\n // Events batch - tự động thêm event vào mảng\r\n events: [...(config.events ?? []), eventObject],\r\n };\r\n\r\n if (config.debug) {\r\n console.log(\"[Tracking] Sending event:\", payload);\r\n }\r\n\r\n const res = await fetch(config.baseURL, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\" },\r\n body: JSON.stringify(payload),\r\n });\r\n\r\n if (!res.ok && config.debug) {\r\n const text = await res.text().catch(() => \"\");\r\n console.error(\"[Tracking] HTTP Error:\", res.status, text);\r\n }\r\n\r\n return res;\r\n } catch (error) {\r\n if (config.debug) {\r\n console.error(\"[Tracking] Error:\", error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Track page view event\r\n */\r\nexport const trackPageView = (url: string) => {\r\n const pageInfo = getPageInfo(url);\r\n\r\n sendEvent({\r\n event_name: \"page_view\",\r\n page_name: pageInfo.name,\r\n page_id: pageInfo.id,\r\n page_path: url,\r\n referrer_page_id: previousPageId || \"\",\r\n });\r\n\r\n previousPageId = pageInfo.id;\r\n};\r\n\r\n/**\r\n * Track custom event\r\n */\r\nexport const trackEvent = (eventName: string, params: EventData = {}) => {\r\n sendEvent({\r\n event_name: eventName,\r\n ...params,\r\n });\r\n};\r\n\r\n/**\r\n * Track view content details event\r\n * @param contentId - ID của content (required)\r\n * @param contentTitle - Tiêu đề content (required)\r\n * @param contentType - Loại content: 0=VOD, 1=Event, 2=Channel (required)\r\n * @param channelId - Mã kênh EPG (optional)\r\n * @param eventTime - Timestamp Unix (optional, mặc định lấy time hiện tại)\r\n */\r\nexport const trackViewContentDetails = (\r\n contentId: string,\r\n contentTitle: string,\r\n contentType: 0 | 1 | 2,\r\n channelId?: string,\r\n eventTime?: number,\r\n) => {\r\n sendEvent({\r\n event_name: \"view_content_details\",\r\n content_id: contentId,\r\n content_title: contentTitle,\r\n content_type: contentType,\r\n channel_id: channelId,\r\n event_time: eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback started event\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID: MD5(content_id + start_timestamp) (required)\r\n * @param params.videoQuality - Chất lượng video: 360p, 480p, 720p, 1080p, 2k, 4k (required)\r\n * @param params.startTimestamp - Unix timestamp khi bắt đầu phát (required)\r\n * @param params.startPosition - Vị trí bắt đầu: Live=0, VOD: 0 nếu mới, >0 nếu xem lại (required)\r\n * @param params.duration - Thời lượng: Live=-1, VOD=số giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n startTimestamp: number;\r\n startPosition: number;\r\n duration: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n start_timestamp: params.startTimestamp,\r\n start_position: params.startPosition,\r\n duration: params.duration,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback progress event\r\n * Gửi định kỳ: loop 1 = 5s, loop 2 = 25s để track thời lượng user xem nội dung\r\n * Lưu ý: total_watch_time là tổng thời gian xem được (không tính pause)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackProgress = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_progress\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback completed event\r\n * Tương tự playback_progress nhưng là event cuối cùng khi kết thúc xem\r\n * Với Live có thể thoát chủ động thì tracking event này\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.totalWatchTime - Tổng thời gian đã xem tính bằng giây (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.subContentId - ID chương trình trong kênh live (optional)\r\n * @param params.subContentTitle - Title chương trình trong kênh live (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackCompleted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n totalWatchTime: number;\r\n channelId?: string;\r\n subContentId?: string;\r\n subContentTitle?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_completed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n total_watch_time: params.totalWatchTime,\r\n channel_id: params.channelId,\r\n sub_content_id: params.subContentId,\r\n sub_content_title: params.subContentTitle,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback error event\r\n * Trigger khi:\r\n * - Video không load/play được (network timeout, source 404)\r\n * - Player error (format không hỗ trợ, DRM error)\r\n * - Source error (media source and manifest-related errors)\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.errorCode - Mã lỗi (optional)\r\n * @param params.errorMessage - Thông báo lỗi (optional)\r\n * @param params.errorType - Loại lỗi: video_playback_error, network_error... (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackError = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1;\r\n playbackSession: string;\r\n channelId?: string;\r\n errorCode?: number;\r\n errorMessage?: string;\r\n errorType?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_error\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n channel_id: params.channelId,\r\n error_code: params.errorCode,\r\n error_message: params.errorMessage,\r\n error_type: params.errorType,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track quality changed event\r\n * Trigger khi:\r\n * - Tự động thay đổi do thuật toán ABR\r\n * - Thay đổi chủ động bởi người dùng\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.fromQuality - Chất lượng trước khi đổi (required)\r\n * @param params.toQuality - Chất lượng sau khi đổi (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: automatic_abr hoặc user_selected (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackQualityChanged = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n fromQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n toQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"automatic_abr\" | \"user_selected\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"quality_changed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n from_quality: params.fromQuality,\r\n to_quality: params.toQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer started event\r\n * Buffering xảy ra khi video không còn segment kế tiếp để phát, video sẽ pause để tải thêm segment\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferStarted = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_started\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track buffer ended event\r\n * Khi buffering kết thúc và video tiếp tục phát\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.videoQuality - Chất lượng video hiện tại (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.bufferDurationSec - Tổng số giây mà buffer xảy ra (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.reason - Lý do: seek_operation, network_congestion (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackBufferEnded = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n videoQuality: \"360p\" | \"480p\" | \"720p\" | \"1080p\" | \"2k\" | \"4k\";\r\n positionSec: number;\r\n bufferDurationSec: number;\r\n channelId?: string;\r\n reason?: \"seek_operation\" | \"network_congestion\";\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"buffer_ended\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n video_quality: params.videoQuality,\r\n position_sec: params.positionSec,\r\n buffer_duration_sec: params.bufferDurationSec,\r\n channel_id: params.channelId,\r\n reason: params.reason,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback paused event\r\n * Khi user pause video\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackPaused = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_paused\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Track playback resumed event\r\n * Khi user resume video sau khi pause\r\n *\r\n * @param params.contentId - ID của content (required)\r\n * @param params.contentTitle - Tiêu đề content (required)\r\n * @param params.contentType - Loại content: 0=VOD, 1=Live, 2=Channel (required)\r\n * @param params.playbackSession - Session ID từ playback_started (required)\r\n * @param params.positionSec - VOD: vị trí hiện tại, Live: số giây từ lúc bắt đầu xem (required)\r\n * @param params.channelId - Mã kênh EPG (optional)\r\n * @param params.eventTime - Unix timestamp (optional)\r\n */\r\nexport const trackPlaybackResumed = (params: {\r\n contentId: string;\r\n contentTitle: string;\r\n contentType: 0 | 1 | 2;\r\n playbackSession: string;\r\n positionSec: number;\r\n channelId?: string;\r\n eventTime?: number;\r\n}) => {\r\n sendEvent({\r\n event_name: \"playback_resumed\",\r\n content_id: params.contentId,\r\n content_title: params.contentTitle,\r\n content_type: params.contentType,\r\n playback_session: params.playbackSession,\r\n position_sec: params.positionSec,\r\n channel_id: params.channelId,\r\n event_time: params.eventTime,\r\n });\r\n};\r\n\r\n/**\r\n * Hook cho Next.js - auto track page views\r\n * Dùng cho Next.js Pages Router (next/router)\r\n */\r\nexport const usePageViewTracking = (router: NextRouterLike) => {\r\n if (typeof window === \"undefined\") return;\r\n if (!router) return;\r\n\r\n // Track initial page\r\n trackPageView(router.asPath);\r\n\r\n const handleRouteChange = (url: string) => {\r\n trackPageView(url);\r\n };\r\n\r\n router.events.on(\"routeChangeComplete\", handleRouteChange);\r\n\r\n return () => {\r\n router.events.off(\"routeChangeComplete\", handleRouteChange);\r\n };\r\n};\r\n","/**\r\n * Session Lifecycle Tracking\r\n * - session_start: Khi user mở app\r\n * - session_progress: Định kỳ khi user đang active (foreground)\r\n * - session_end: Khi user thoát app\r\n */\r\n\r\nimport { sendEvent } from \"./tracking\";\r\nimport { renewSessionCookie } from \"./cookie-utils\";\r\nimport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\nexport type SessionConfig = {\r\n /** Khoảng thời gian gửi session_progress (ms). Default: 5 phút */\r\n progressInterval?: number;\r\n /** Có tự động start session khi init không. Default: true */\r\n autoStart?: boolean;\r\n /** Debug mode */\r\n debug?: boolean;\r\n /** User ID */\r\n userId?: string;\r\n /** Session cookie configuration */\r\n sessionCookieConfig?: SessionCookieConfig;\r\n};\r\n\r\nconst defaultSessionConfig: SessionConfig = {\r\n progressInterval: 5 * 60 * 1000, // 5 phút\r\n autoStart: true,\r\n debug: false,\r\n};\r\n\r\n// State\r\nlet sessionConfig: SessionConfig = { ...defaultSessionConfig };\r\nlet progressTimer: ReturnType<typeof setInterval> | null = null;\r\nlet isSessionActive = false;\r\nlet sessionStartTime: number = 0;\r\n\r\n/**\r\n * Log helper\r\n */\r\nconst log = (...args: any[]) => {\r\n if (sessionConfig.debug) {\r\n console.log(\"[Session]\", ...args);\r\n }\r\n};\r\n\r\n/**\r\n * Gửi event session_start\r\n */\r\nexport const sendSessionStart = () => {\r\n if (isSessionActive) {\r\n log(\"Session already started, skipping...\");\r\n return;\r\n }\r\n\r\n sessionStartTime = Date.now();\r\n isSessionActive = true;\r\n\r\n sendEvent({\r\n event_name: \"session_start\",\r\n event_time: sessionStartTime,\r\n });\r\n\r\n log(\"Session started at:\", new Date(sessionStartTime).toISOString());\r\n\r\n // Gia hạn cookie session_id (extend thêm 30 phút)\r\n renewSessionIdCookie();\r\n\r\n // Bắt đầu timer cho session_progress\r\n startProgressTimer();\r\n};\r\n\r\n/**\r\n * Gửi event session_progress\r\n */\r\nexport const sendSessionProgress = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n sendEvent({\r\n event_name: \"session_progress\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\"Session progress - Duration:\", Math.round(duration / 1000), \"seconds\");\r\n\r\n // Gia hạn cookie session_id mỗi khi user còn active\r\n renewSessionIdCookie();\r\n};\r\n\r\n/**\r\n * Gửi event session_end\r\n */\r\nexport const sendSessionEnd = () => {\r\n if (!isSessionActive) return;\r\n\r\n const now = Math.floor(Date.now() / 1000);\r\n const duration = now - sessionStartTime;\r\n\r\n // Dùng sendBeacon để đảm bảo gửi được khi page unload\r\n // Fallback về sendEvent nếu không support\r\n sendEvent({\r\n event_name: \"session_end\",\r\n session_duration: duration,\r\n });\r\n\r\n log(\r\n \"Session ended - Total duration:\",\r\n Math.round(duration / 1000),\r\n \"seconds\",\r\n );\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Start progress timer\r\n */\r\nconst startProgressTimer = () => {\r\n stopProgressTimer(); // Clear existing timer\r\n\r\n if (sessionConfig.progressInterval && sessionConfig.progressInterval > 0) {\r\n progressTimer = setInterval(() => {\r\n if (document.visibilityState === \"visible\") {\r\n sendSessionProgress();\r\n }\r\n }, sessionConfig.progressInterval);\r\n\r\n log(\r\n \"Progress timer started, interval:\",\r\n sessionConfig.progressInterval,\r\n \"ms\",\r\n );\r\n }\r\n};\r\n\r\n/**\r\n * Stop progress timer\r\n */\r\nconst stopProgressTimer = () => {\r\n if (progressTimer) {\r\n clearInterval(progressTimer);\r\n progressTimer = null;\r\n log(\"Progress timer stopped\");\r\n }\r\n};\r\n\r\n/**\r\n * Handle visibility change\r\n * Pause/resume tracking khi user switch tab hoặc minimize\r\n */\r\nconst handleVisibilityChange = () => {\r\n if (document.visibilityState === \"hidden\") {\r\n // User rời khỏi tab - có thể gửi session_progress cuối\r\n log(\"Tab hidden - pausing progress\");\r\n stopProgressTimer();\r\n } else if (document.visibilityState === \"visible\") {\r\n // User quay lại tab - resume timer\r\n log(\"Tab visible - resuming progress\");\r\n if (isSessionActive) {\r\n startProgressTimer();\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Handle page unload (user đóng tab/browser)\r\n */\r\nconst handleBeforeUnload = () => {\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Handle page hide (mobile: switch app, close tab)\r\n * Dùng pagehide thay vì beforeunload cho mobile\r\n */\r\nconst handlePageHide = (event: PageTransitionEvent) => {\r\n if (event.persisted) {\r\n // Page được cache (bfcache) - không phải thực sự đóng\r\n return;\r\n }\r\n sendSessionEnd();\r\n};\r\n\r\n/**\r\n * Khởi tạo session tracking\r\n */\r\nexport const initSessionTracking = (options: SessionConfig = {}) => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n sessionConfig = { ...defaultSessionConfig, ...options };\r\n\r\n log(\"Initializing with config:\", sessionConfig);\r\n\r\n // Đăng ký event listeners\r\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.addEventListener(\"pagehide\", handlePageHide);\r\n\r\n // Auto start session\r\n if (sessionConfig.autoStart) {\r\n sendSessionStart();\r\n }\r\n};\r\n\r\n/**\r\n * Cleanup - gọi khi unmount component\r\n */\r\nexport const cleanupSessionTracking = () => {\r\n if (typeof window === \"undefined\") return;\r\n\r\n log(\"Cleaning up...\");\r\n\r\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\r\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\r\n window.removeEventListener(\"pagehide\", handlePageHide);\r\n\r\n stopProgressTimer();\r\n isSessionActive = false;\r\n};\r\n\r\n/**\r\n * Cập nhật config (ví dụ: thay đổi interval)\r\n */\r\nexport const updateSessionConfig = (options: Partial<SessionConfig>) => {\r\n sessionConfig = { ...sessionConfig, ...options };\r\n\r\n // Restart timer nếu interval thay đổi\r\n if (options.progressInterval !== undefined && isSessionActive) {\r\n startProgressTimer();\r\n }\r\n\r\n log(\"Config updated:\", sessionConfig);\r\n};\r\n\r\n/**\r\n * Kiểm tra session đang active không\r\n */\r\nexport const isSessionStarted = () => isSessionActive;\r\n\r\n/**\r\n * Lấy thời gian session hiện tại (ms)\r\n */\r\nexport const getSessionDuration = () => {\r\n if (!isSessionActive) return 0;\r\n return Date.now() - sessionStartTime;\r\n};\r\n\r\n/**\r\n * Gia hạn cookie session_id\r\n * Gọi khi có event session_start để extend thời gian hết hạn\r\n */\r\nconst renewSessionIdCookie = () => {\r\n // Lấy sessionId và config từ tracking module\r\n // Note: Cần export getter function từ tracking.ts để lấy config\r\n if (sessionConfig.sessionCookieConfig) {\r\n // Tạm thời sử dụng cookie name mặc định để lấy sessionId\r\n const cookieName =\r\n sessionConfig.sessionCookieConfig.cookieName || \"_tracking_session_id\";\r\n const sessionId = document.cookie\r\n .split(\"; \")\r\n .find((row) => row.startsWith(cookieName + \"=\"))\r\n ?.split(\"=\")[1];\r\n\r\n if (sessionId) {\r\n renewSessionCookie(sessionId, sessionConfig.sessionCookieConfig);\r\n log(\"Session cookie renewed for:\", sessionId);\r\n }\r\n }\r\n};\r\n","/**\r\n * Tracking Library v2 (Fetch + TypeScript)\r\n * Thư viện tracking events có thể dùng cho nhiều project\r\n */\r\n\r\n// Re-export types\r\nexport type {\r\n TrackingConfig,\r\n PageInfo,\r\n PageMap,\r\n EventData,\r\n NextRouterLike,\r\n UserInfo,\r\n} from \"./types\";\r\n\r\nexport type { SessionConfig } from \"./session\";\r\nexport type { SessionCookieConfig } from \"./cookie-utils\";\r\n\r\n// Re-export constants\r\nexport { defaultConfig, defaultPageMap } from \"./constants\";\r\n\r\n// Re-export functions\r\nexport {\r\n initTracking,\r\n setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\n// Re-export session functions\r\nexport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n updateSessionConfig,\r\n isSessionStarted,\r\n getSessionDuration,\r\n} from \"./session\";\r\n\r\n// Re-export cookie utilities\r\nexport {\r\n generateSessionId,\r\n getCookie,\r\n setCookie,\r\n getOrCreateSessionId,\r\n renewSessionCookie,\r\n clearSessionCookie,\r\n} from \"./cookie-utils\";\r\n\r\n// Import for default export\r\nimport {\r\n initTracking,\r\n setUserInfo,\r\n getPageInfo,\r\n sendEvent,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n usePageViewTracking,\r\n} from \"./tracking\";\r\n\r\nimport {\r\n initSessionTracking,\r\n cleanupSessionTracking,\r\n sendSessionStart,\r\n sendSessionProgress,\r\n sendSessionEnd,\r\n} from \"./session\";\r\n\r\n// Export default object\r\nconst tracking = {\r\n init: initTracking,\r\n setUserInfo,\r\n trackPageView,\r\n trackEvent,\r\n trackViewContentDetails,\r\n trackPlaybackStarted,\r\n trackPlaybackProgress,\r\n trackPlaybackCompleted,\r\n trackPlaybackError,\r\n trackQualityChanged,\r\n trackBufferStarted,\r\n trackBufferEnded,\r\n trackPlaybackPaused,\r\n trackPlaybackResumed,\r\n sendEvent,\r\n getPageInfo,\r\n usePageViewTracking,\r\n // Session lifecycle\r\n initSession: initSessionTracking,\r\n cleanupSession: cleanupSessionTracking,\r\n sessionStart: sendSessionStart,\r\n sessionProgress: sendSessionProgress,\r\n sessionEnd: sendSessionEnd,\r\n};\r\n\r\nexport default tracking;\r\n"],"mappings":"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","names":["config"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracking-lib-ott",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",