544 lines
20 KiB
JavaScript
544 lines
20 KiB
JavaScript
/**
|
|
* TikTok Web Application - Deobfuscated JavaScript Bundle
|
|
* Original file: 4004.ab578596.js
|
|
*
|
|
* This bundle contains modules for:
|
|
* - Video codec support detection (H264/H265)
|
|
* - Search functionality and A/B testing
|
|
* - Video preloading and ML predictions
|
|
* - Comment preloading
|
|
* - Related search features
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
// Initialize loadable chunks array if not exists
|
|
(self.__LOADABLE_LOADED_CHUNKS__ = self.__LOADABLE_LOADED_CHUNKS__ || []).push([["4004"], {
|
|
|
|
/**
|
|
* Module 93036: Video Codec Types
|
|
* Defines supported video codec types for web playback
|
|
*/
|
|
93036: function(exports, module, require) {
|
|
require.d(module, {
|
|
t: function() { return videoCodecTypes; }
|
|
});
|
|
|
|
var codecRegistry = {};
|
|
var videoCodecTypes = (
|
|
codecRegistry.H265 = "web_h265",
|
|
codecRegistry.H264 = "web_h264",
|
|
codecRegistry
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Module 83814: HEVC/H265 Support Detection
|
|
* Detects browser support for H265 video codec
|
|
*/
|
|
83814: function(exports, module, require) {
|
|
require.d(module, {
|
|
$l: function() { return isValidVideoQuality; },
|
|
AF: function() { return detectH265Support; },
|
|
GH: function() { return clearH265Cache; },
|
|
gc: function() { return getCachedH265Support; }
|
|
});
|
|
|
|
var cachedH265Support;
|
|
var deviceUtils = require(32049);
|
|
var storageUtils = require(95794);
|
|
|
|
// H265 codec string for testing
|
|
var h265CodecString = 'video/mp4;codecs="hev1.1.6.L93.B0"';
|
|
|
|
/**
|
|
* Check if video quality level is valid for H265
|
|
*/
|
|
function isValidVideoQuality(qualityLevel) {
|
|
return [3, 4, 31].includes(qualityLevel);
|
|
}
|
|
|
|
/**
|
|
* Detect if browser supports H265 video codec
|
|
*/
|
|
function detectH265Support() {
|
|
if (deviceUtils.fU()) return false; // Skip on certain devices
|
|
|
|
if (typeof MediaSource === "undefined") return false;
|
|
|
|
// Check MediaSource support
|
|
if (!MediaSource.isTypeSupported(h265CodecString)) return false;
|
|
|
|
// Check video element support
|
|
var testVideo = document.createElement("video");
|
|
return testVideo.canPlayType(h265CodecString) === "probably";
|
|
}
|
|
|
|
var h265SupportCacheKey = "hevc_support_key_v4";
|
|
var h265TimeCacheKey = "hevc_support_key_time";
|
|
|
|
/**
|
|
* Get cached H265 support with time-based invalidation
|
|
*/
|
|
function getCachedH265Support() {
|
|
if (deviceUtils.fU()) return false;
|
|
|
|
if (cachedH265Support !== undefined) {
|
|
return cachedH265Support;
|
|
}
|
|
|
|
var cachedSupport = storageUtils._S(h265SupportCacheKey, "");
|
|
var cacheTime = Number(storageUtils._S(h265TimeCacheKey, "0"));
|
|
var currentTime = Date.now();
|
|
|
|
// Cache expires after ~14 days (12096e5 ms)
|
|
var cacheExpired = currentTime - cacheTime > 12096e5;
|
|
|
|
if (cacheExpired || cachedSupport === "") {
|
|
// Refresh cache
|
|
cachedH265Support = detectH265Support();
|
|
storageUtils.AP(h265SupportCacheKey, cachedH265Support ? "1" : "0");
|
|
storageUtils.AP(h265TimeCacheKey, String(currentTime));
|
|
|
|
// Additional capability check for supported browsers
|
|
if (cachedH265Support && navigator.mediaCapabilities) {
|
|
navigator.mediaCapabilities.decodingInfo({
|
|
type: "file",
|
|
video: {
|
|
contentType: h265CodecString,
|
|
width: 1920,
|
|
height: 1080,
|
|
bitrate: 10000,
|
|
framerate: 30
|
|
}
|
|
}).then(function(capabilities) {
|
|
var isSupported = capabilities.supported;
|
|
cachedH265Support = isSupported;
|
|
storageUtils.AP(h265SupportCacheKey, isSupported ? "1" : "0");
|
|
}).catch(function(error) {
|
|
console.error("Media capabilities check failed:", error);
|
|
});
|
|
}
|
|
|
|
return cachedH265Support;
|
|
} else {
|
|
return cachedSupport === "1";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear H265 support cache (force re-detection)
|
|
*/
|
|
function clearH265Cache() {
|
|
storageUtils.AP(h265SupportCacheKey, "0");
|
|
storageUtils.AP(h265TimeCacheKey, String(Date.now()));
|
|
cachedH265Support = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Module 83153: Video Stream Device Type Hook
|
|
* React hook for determining optimal video codec based on device capabilities
|
|
*/
|
|
83153: function(exports, module, require) {
|
|
require.d(module, {
|
|
A: function() { return useVideoStreamType; }
|
|
});
|
|
|
|
var React = require(40099);
|
|
var deviceUtils = require(32049);
|
|
var codecTypes = require(93036);
|
|
var h265Utils = require(83814);
|
|
|
|
function useVideoStreamType() {
|
|
// Memoize H265 support detection
|
|
var h265Supported = React.useMemo(function() {
|
|
return h265Utils.gc();
|
|
}, []);
|
|
|
|
// Determine optimal stream device type
|
|
var streamDeviceType = React.useMemo(function() {
|
|
if (deviceUtils.fU()) {
|
|
return codecTypes.t.H264; // Fallback for unsupported devices
|
|
}
|
|
return h265Supported ? codecTypes.t.H265 : codecTypes.t.H264;
|
|
}, [h265Supported]);
|
|
|
|
return {
|
|
openH265: streamDeviceType === codecTypes.t.H265,
|
|
streamDeviceType: streamDeviceType,
|
|
hevcSupport: h265Supported
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Module 76232: Search A/B Testing Hooks
|
|
* Various React hooks for search-related A/B testing experiments
|
|
*/
|
|
76232: function(exports, module, require) {
|
|
require.d(module, {
|
|
AF: function() { return useSearchKeepSugShow; },
|
|
AP: function() { return useWebappModeration; },
|
|
CA: function() { return useSearchRemoveRelatedSearch; },
|
|
Sf: function() { return useShowSearchLiveHead; },
|
|
a8: function() { return useRecomReduceIconRisk; },
|
|
hA: function() { return useSearchBarStyle; },
|
|
uJ: function() { return usePersonalizedSwitch; }
|
|
});
|
|
|
|
var router = require(10874);
|
|
var reduxUtils = require(23680);
|
|
var stateUtils = require(72961);
|
|
var selectorUtils = require(43264);
|
|
var abTestUtils = require(54520);
|
|
var pathUtils = require(88947);
|
|
var userStore = require(10829);
|
|
|
|
var abTestVersionKey = "abTestVersion";
|
|
|
|
/**
|
|
* Hook for search bar style A/B test
|
|
*/
|
|
function useSearchBarStyle() {
|
|
var abTestVersion = stateUtils.L$(selectorUtils.W(function() {
|
|
return [abTestVersionKey];
|
|
}, [])).abTestVersion;
|
|
|
|
var searchBarStyle = abTestUtils.qt(abTestVersion, "search_bar_style_opt") || "v1";
|
|
var isV2 = searchBarStyle === "v2";
|
|
var isV3 = searchBarStyle === "v3";
|
|
|
|
return {
|
|
isSearchBarStyleV1: searchBarStyle === "v1",
|
|
isSearchBarStyleV2: isV2,
|
|
isSearchBarStyleV3: isV3,
|
|
withNewStyle: isV2 || isV3
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook for related search removal A/B test
|
|
*/
|
|
function useSearchRemoveRelatedSearch() {
|
|
var location = router.useLocation();
|
|
var pathname = location.pathname;
|
|
var abTestVersion = stateUtils.L$(selectorUtils.W(function() {
|
|
return [abTestVersionKey];
|
|
}, [])).abTestVersion;
|
|
|
|
var relatedSearchVersion = abTestUtils.qt(abTestVersion, "search_remove_related_search") || "v0";
|
|
|
|
return {
|
|
hasRelatedSearch: relatedSearchVersion === "v0" || pathUtils.ie(pathname),
|
|
hasSugReport: true,
|
|
isNewSearchLayout: relatedSearchVersion !== "v0"
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook for search suggestion keep show A/B test
|
|
*/
|
|
function useSearchKeepSugShow() {
|
|
var abTestVersion = stateUtils.L$(selectorUtils.W(function() {
|
|
return [abTestVersionKey];
|
|
}, [])).abTestVersion;
|
|
|
|
var keepSugVersion = abTestUtils.qt(abTestVersion, "search_keep_sug_show") || "v1";
|
|
return keepSugVersion === "v2";
|
|
}
|
|
|
|
/**
|
|
* Hook for personalized search switch A/B test
|
|
*/
|
|
function usePersonalizedSwitch() {
|
|
var abTestVersion = stateUtils.L$(selectorUtils.W(function() {
|
|
return [abTestVersionKey];
|
|
}, [])).abTestVersion;
|
|
|
|
var personalizedSwitchVersion = abTestUtils.qt(abTestVersion, "search_add_non_personalized_switch") || "v1";
|
|
|
|
var userState = reduxUtils.P(userStore.L, {
|
|
selector: function(state) {
|
|
var appContext = state.appContext;
|
|
return {
|
|
user: appContext ? appContext.user : undefined
|
|
};
|
|
},
|
|
dependencies: []
|
|
}).user;
|
|
|
|
return {
|
|
hasPersonalizedSwitch: personalizedSwitchVersion === "v2" && !!userState
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook for recommendation icon risk reduction A/B test
|
|
*/
|
|
function useRecomReduceIconRisk() {
|
|
var abTestVersion = stateUtils.L$(selectorUtils.W(function() {
|
|
return [abTestVersionKey];
|
|
}, [])).abTestVersion;
|
|
|
|
var iconRiskVersion = abTestUtils.qt(abTestVersion, "should_recom_reduce_icon_risk") || "v0";
|
|
|
|
return {
|
|
shouldRecomReduceIconRisk: iconRiskVersion === "v1"
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook for webapp moderation A/B test
|
|
*/
|
|
function useWebappModeration() {
|
|
var abTestVersion = stateUtils.L$(selectorUtils.W(function() {
|
|
return [abTestVersionKey];
|
|
}, [])).abTestVersion;
|
|
|
|
var moderationVersion = abTestUtils.qt(abTestVersion, "webapp_moderation") || "v0";
|
|
|
|
return {
|
|
notificationShouldBeClickable: moderationVersion === "v1"
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook for search live head display A/B test
|
|
*/
|
|
function useShowSearchLiveHead() {
|
|
var abTestVersion = stateUtils.L$(selectorUtils.W(function() {
|
|
return [abTestVersionKey];
|
|
}, [])).abTestVersion;
|
|
|
|
var liveHeadVersion = abTestUtils.qt(abTestVersion, "show_search_live_head") || "v0";
|
|
|
|
return {
|
|
showLiveHead: liveHeadVersion === "v1"
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Module 12064: FYP Feeder Management
|
|
* Manages For You Page (FYP) feeder state and item tracking
|
|
*/
|
|
12064: function(exports, module, require) {
|
|
require.d(module, {
|
|
ip: function() { return useFypFeederItemId; },
|
|
_k: function() { return useIncrementSentBatchCount; },
|
|
Ob: function() { return useSetFirstItemId; },
|
|
aL: function() { return useFypFeederCache; },
|
|
Ee: function() { return useInitializeFypFeeder; },
|
|
g1: function() { return useSetFirstItemIdCallback; }
|
|
});
|
|
|
|
var React = require(40099);
|
|
var pageTypes = require(94553);
|
|
var storageUtils = require(95794);
|
|
var selectorUtils = require(43264);
|
|
var routerUtils = require(17505);
|
|
var objectUtils = require(5377);
|
|
var assignUtils = require(45996);
|
|
var atomUtils = require(71111);
|
|
var serviceUtils = require(4676);
|
|
|
|
// Default FYP feeder state
|
|
var defaultFypFeederState = {
|
|
pageName: null,
|
|
itemID: "",
|
|
sentBatchCount: 0
|
|
};
|
|
|
|
// Create atom for FYP feeder state
|
|
var fypFeederAtom = atomUtils.atom(defaultFypFeederState);
|
|
fypFeederAtom.debugLabel = "fypFeederAtom";
|
|
|
|
// Create service for FYP feeder management
|
|
var fypFeederService = serviceUtils.i(fypFeederAtom, function(getState, setState) {
|
|
return {
|
|
setCache: function(newData) {
|
|
if (!getState(fypFeederAtom).itemID) {
|
|
setState(fypFeederAtom, function(currentState) {
|
|
return objectUtils._(objectUtils._({}, currentState), newData);
|
|
});
|
|
}
|
|
},
|
|
clearCache: function() {
|
|
if (getState(fypFeederAtom).pageName !== "ALWAYS_ALLOWED") {
|
|
setState(fypFeederAtom, function(currentState) {
|
|
return assignUtils._(objectUtils._(objectUtils._({}, defaultFypFeederState), {
|
|
sentBatchCount: currentState.sentBatchCount
|
|
}));
|
|
});
|
|
}
|
|
},
|
|
incrementSentBatchCount: function() {
|
|
setState(fypFeederAtom, function(currentState) {
|
|
return assignUtils._(objectUtils._(objectUtils._({}, currentState), {
|
|
sentBatchCount: currentState.sentBatchCount + 1
|
|
}));
|
|
});
|
|
}
|
|
};
|
|
});
|
|
|
|
var useServiceDispatchers = fypFeederService.useServiceDispatchers;
|
|
var useServiceState = fypFeederService.useServiceState;
|
|
|
|
var fypFeederLandingKey = "webapp_fyp_feeder_landing";
|
|
var shouldProcessFeeder = true;
|
|
|
|
/**
|
|
* Mark FYP feeder as processed
|
|
*/
|
|
function markFypFeederProcessed() {
|
|
if (storageUtils.Hd(fypFeederLandingKey)) {
|
|
shouldProcessFeeder = false;
|
|
}
|
|
storageUtils.J2(fypFeederLandingKey, "1");
|
|
}
|
|
|
|
/**
|
|
* Check if should send creator item ID for user pages
|
|
*/
|
|
function shouldSendCreatorItemId(pageType) {
|
|
var routerState = routerUtils.CQv();
|
|
var sendCreatorItemId = routerState.sendCreatorItemId;
|
|
var userState = selectorUtils.W(function() {
|
|
return ["user"];
|
|
}, []) || {};
|
|
var user = userState.user;
|
|
|
|
return sendCreatorItemId && pageType === pageTypes.L.User && shouldProcessFeeder && !user;
|
|
}
|
|
|
|
/**
|
|
* Check if should process FYP for video pages
|
|
*/
|
|
function shouldProcessFypVideo(pageType) {
|
|
var routerState = routerUtils.FTg();
|
|
var isFYP = routerState.isFYP;
|
|
|
|
return isFYP &&
|
|
(pageType === pageTypes.L.Video || pageType === pageTypes.L.PhotoVideo) &&
|
|
shouldProcessFeeder;
|
|
}
|
|
|
|
/**
|
|
* Hook to initialize FYP feeder for a page
|
|
*/
|
|
function useInitializeFypFeeder(pageType) {
|
|
var dispatchers = useServiceDispatchers();
|
|
var shouldSendCreator = shouldSendCreatorItemId(pageType);
|
|
var shouldProcessVideo = shouldProcessFypVideo(pageType);
|
|
|
|
React.useEffect(function() {
|
|
return function cleanup() {
|
|
if (!shouldSendCreator && !shouldProcessVideo) {
|
|
dispatchers.clearCache();
|
|
}
|
|
};
|
|
}, [dispatchers, shouldSendCreator, shouldProcessVideo]);
|
|
|
|
React.useEffect(function() {
|
|
markFypFeederProcessed();
|
|
}, [pageType]);
|
|
}
|
|
|
|
/**
|
|
* Hook to set first item ID for FYP feeder
|
|
*/
|
|
function useSetFirstItemId(pageType, itemId) {
|
|
var dispatchers = useServiceDispatchers();
|
|
var shouldSendCreator = shouldSendCreatorItemId(pageType);
|
|
var shouldProcessVideo = shouldProcessFypVideo(pageType);
|
|
|
|
React.useEffect(function() {
|
|
if (itemId && (shouldSendCreator || shouldProcessVideo)) {
|
|
dispatchers.setCache({
|
|
pageName: pageType,
|
|
itemID: itemId
|
|
});
|
|
}
|
|
}, [itemId, pageType, shouldSendCreator, shouldProcessVideo, dispatchers]);
|
|
}
|
|
|
|
/**
|
|
* Hook to get callback for setting first item ID
|
|
*/
|
|
function useSetFirstItemIdCallback() {
|
|
var dispatchers = useServiceDispatchers();
|
|
|
|
return React.useCallback(function(itemId) {
|
|
dispatchers.setCache({
|
|
pageName: "ALWAYS_ALLOWED",
|
|
itemID: itemId,
|
|
sentBatchCount: 0
|
|
});
|
|
}, [dispatchers]);
|
|
}
|
|
|
|
/**
|
|
* Hook to get FYP feeder item ID
|
|
*/
|
|
function useFypFeederItemId() {
|
|
var routerState = routerUtils.FTg();
|
|
var isFYP = routerState.isFYP;
|
|
|
|
var creatorState = routerUtils.CQv();
|
|
var sendCreatorItemId = creatorState.sendCreatorItemId;
|
|
var batchCount = creatorState.batchCount;
|
|
|
|
var feederState = useServiceState();
|
|
var itemID = feederState.itemID;
|
|
var pageName = feederState.pageName;
|
|
var sentBatchCount = feederState.sentBatchCount;
|
|
|
|
var shouldProcessFypPages = isFYP &&
|
|
(pageName === pageTypes.L.Video ||
|
|
pageName === pageTypes.L.PhotoVideo ||
|
|
pageName === "ALWAYS_ALLOWED");
|
|
|
|
var targetBatchCount = 0;
|
|
if (shouldProcessFypPages) {
|
|
targetBatchCount = 1;
|
|
} else if (sendCreatorItemId && pageName === pageTypes.L.User) {
|
|
targetBatchCount = batchCount;
|
|
}
|
|
|
|
var fypFeederItemId = (itemID && sentBatchCount < targetBatchCount) ? itemID : "";
|
|
var setFirstItemId = shouldProcessFypPages ? fypFeederItemId : "";
|
|
|
|
return {
|
|
fypFeederItemId: fypFeederItemId,
|
|
setFirstItemId: setFirstItemId
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook to increment sent batch count
|
|
*/
|
|
function useIncrementSentBatchCount(batchCount) {
|
|
var dispatchers = useServiceDispatchers();
|
|
|
|
React.useEffect(function() {
|
|
if (batchCount && batchCount > 0) {
|
|
dispatchers.incrementSentBatchCount();
|
|
}
|
|
}, [batchCount, dispatchers]);
|
|
}
|
|
}
|
|
|
|
// Additional modules would continue here...
|
|
// The file contains many more modules for ML predictions, comment preloading,
|
|
// search functionality, etc. Each follows similar patterns of:
|
|
// 1. Module definition with exports
|
|
// 2. Dependency imports
|
|
// 3. Function definitions
|
|
// 4. React hooks and state management
|
|
// 5. A/B testing logic
|
|
// 6. API calls and data processing
|
|
|
|
}]);
|