|
@@ -92,6 +92,94 @@ const maxRetries = 3;
|
|
|
const videoContainer = ref<HTMLDivElement>();
|
|
const videoContainer = ref<HTMLDivElement>();
|
|
|
const player = ref<any>(null);
|
|
const player = ref<any>(null);
|
|
|
|
|
|
|
|
|
|
+// 保存原始的 XMLHttpRequest.open 和 setRequestHeader 方法
|
|
|
|
|
+let originalXHROpen: any = null;
|
|
|
|
|
+let originalXHRSetRequestHeader: any = null;
|
|
|
|
|
+let originalFetch: any = null;
|
|
|
|
|
+let xhrInterceptorActive = false;
|
|
|
|
|
+
|
|
|
|
|
+// 设置 XMLHttpRequest 和 Fetch 拦截器以移除 Range 请求头
|
|
|
|
|
+const setupXHRInterceptor = (): void => {
|
|
|
|
|
+ if (xhrInterceptorActive) return;
|
|
|
|
|
+
|
|
|
|
|
+ // 保存原始方法
|
|
|
|
|
+ originalXHROpen = XMLHttpRequest.prototype.open;
|
|
|
|
|
+ originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
|
|
|
+ originalFetch = window.fetch;
|
|
|
|
|
+
|
|
|
|
|
+ // 拦截 XMLHttpRequest.open 方法
|
|
|
|
|
+ XMLHttpRequest.prototype.open = function (
|
|
|
|
|
+ method: string,
|
|
|
|
|
+ url: string | URL,
|
|
|
|
|
+ ...rest: any[]
|
|
|
|
|
+ ) {
|
|
|
|
|
+ // 保存 URL 以便后续检查
|
|
|
|
|
+ (this as any)._url = url.toString();
|
|
|
|
|
+ return originalXHROpen.apply(this, [method, url, ...rest]);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 拦截 XMLHttpRequest.setRequestHeader 方法
|
|
|
|
|
+ XMLHttpRequest.prototype.setRequestHeader = function (
|
|
|
|
|
+ header: string,
|
|
|
|
|
+ value: string
|
|
|
|
|
+ ) {
|
|
|
|
|
+ // 如果是 .m3u8 文件的 Range 请求头,则跳过
|
|
|
|
|
+ const url = (this as any)._url || "";
|
|
|
|
|
+ if (
|
|
|
|
|
+ url &&
|
|
|
|
|
+ typeof url === "string" &&
|
|
|
|
|
+ url.endsWith(".m3u8") &&
|
|
|
|
|
+ header.toLowerCase() === "range"
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return; // 不设置 Range 请求头
|
|
|
|
|
+ }
|
|
|
|
|
+ return originalXHRSetRequestHeader.apply(this, [header, value]);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 拦截 fetch 方法
|
|
|
|
|
+ window.fetch = function (
|
|
|
|
|
+ input: RequestInfo | URL,
|
|
|
|
|
+ init?: RequestInit
|
|
|
|
|
+ ): Promise<Response> {
|
|
|
|
|
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果是 .m3u8 文件,移除 Range 请求头
|
|
|
|
|
+ if (url && url.endsWith(".m3u8")) {
|
|
|
|
|
+ const modifiedInit = { ...init };
|
|
|
|
|
+ if (modifiedInit.headers) {
|
|
|
|
|
+ const headers = new Headers(modifiedInit.headers);
|
|
|
|
|
+ headers.delete("Range");
|
|
|
|
|
+ headers.delete("range");
|
|
|
|
|
+ modifiedInit.headers = headers;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ modifiedInit.headers = new Headers();
|
|
|
|
|
+ }
|
|
|
|
|
+ return originalFetch.apply(this, [input, modifiedInit]);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return originalFetch.apply(this, [input, init]);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ xhrInterceptorActive = true;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 恢复原始的 XMLHttpRequest 和 Fetch 方法
|
|
|
|
|
+const restoreXHRInterceptor = (): void => {
|
|
|
|
|
+ if (!xhrInterceptorActive) return;
|
|
|
|
|
+
|
|
|
|
|
+ if (originalXHROpen) {
|
|
|
|
|
+ XMLHttpRequest.prototype.open = originalXHROpen;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (originalXHRSetRequestHeader) {
|
|
|
|
|
+ XMLHttpRequest.prototype.setRequestHeader = originalXHRSetRequestHeader;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (originalFetch) {
|
|
|
|
|
+ window.fetch = originalFetch;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ xhrInterceptorActive = false;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 计算属性
|
|
// 计算属性
|
|
|
const hasVideoSource = computed(
|
|
const hasVideoSource = computed(
|
|
|
() => !!props.m3u8Url && props.m3u8Url.trim() !== ""
|
|
() => !!props.m3u8Url && props.m3u8Url.trim() !== ""
|
|
@@ -307,6 +395,9 @@ const initVideoJSPlayer = async (): Promise<void> => {
|
|
|
poster: posterUrl, // 使用处理后的封面URL
|
|
poster: posterUrl, // 使用处理后的封面URL
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // 设置 XMLHttpRequest 拦截器以禁用 .m3u8 文件的 Range 请求
|
|
|
|
|
+ setupXHRInterceptor();
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
// 注册快进按钮组件
|
|
// 注册快进按钮组件
|
|
|
const registerSeekButtons = () => {
|
|
const registerSeekButtons = () => {
|
|
@@ -483,6 +574,8 @@ const destroyPlayer = (): void => {
|
|
|
player.value.dispose();
|
|
player.value.dispose();
|
|
|
player.value = null;
|
|
player.value = null;
|
|
|
}
|
|
}
|
|
|
|
|
+ // 恢复 XMLHttpRequest 拦截器
|
|
|
|
|
+ restoreXHRInterceptor();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 重试功能
|
|
// 重试功能
|