multi_embed_player 3.0.1 → 3.1.0
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/.github/workflows/build-and-deploy.yml +44 -0
- package/.gitmodules +3 -0
- package/CLAUDE.md +92 -0
- package/README.md +0 -24
- package/add_types.sh +61 -0
- package/browserExtention/chrome/background.js +55 -0
- package/browserExtention/chrome/extention.json +1 -0
- package/browserExtention/chrome/liteplayer.js +26439 -0
- package/browserExtention/chrome/manifest.json +31 -0
- package/browserExtention/chrome/player-selector.js +1854 -0
- package/browserExtention/firefox/background.js +27 -0
- package/browserExtention/firefox/extention.json +1 -0
- package/browserExtention/firefox/liteplayer.js +26439 -0
- package/browserExtention/firefox/manifest.json +19 -0
- package/browserExtention/firefox/player-selector.js +1854 -0
- package/documents/.hugo_build.lock +0 -0
- package/documents/archetypes/default.md +5 -0
- package/documents/assets/jsconfig.json +11 -0
- package/documents/content/docs/install.md +103 -0
- package/documents/content/docs/quickstart.md +51 -0
- package/documents/content/docs/reference/HTML.md +31 -0
- package/documents/content/docs/reference/_index.md +10 -0
- package/documents/content/docs/reference/error_code.md +23 -0
- package/documents/content/docs/reference/iframe_api.md +737 -0
- package/documents/content/docs/reference/iframe_class.md +230 -0
- package/documents/content/docs/reference/multi_embed_player_class.md +113 -0
- package/documents/content/docs/reference/reserved_words.md +71 -0
- package/documents/content/docs/usage/GDPR_mode.md +77 -0
- package/documents/content/docs/usage/_index.md +10 -0
- package/documents/content/docs/usage/custom_playlist.md +239 -0
- package/documents/content/docs/usage/embed_api.md +163 -0
- package/documents/content/docs/usage/embed_various_service.md +81 -0
- package/documents/content/docs/usage/thumbnail_click.md +57 -0
- package/documents/go.mod +8 -0
- package/documents/go.sum +14 -0
- package/documents/hugo.toml +18 -0
- package/documents/layouts/partials/docs/sidebar.html +117 -0
- package/documents/layouts/partials/landing/features.html +47 -0
- package/documents/layouts/robots.txt +4 -0
- package/documents/static/_headers +7 -0
- package/documents/static/localStorageCheck.html +27 -0
- package/documents/static/no_extention.json +1 -0
- package/example.html +27 -0
- package/extention.json +1 -0
- package/icon/video_not_found.odg +0 -0
- package/icon/video_not_found.svgz +0 -0
- package/iframe_api/bilibili.ts +1095 -0
- package/iframe_api/niconico.ts +429 -0
- package/iframe_api/soundcloud.ts +450 -0
- package/iframe_api/youtube.ts +311 -0
- package/multi_embed_player.ts +989 -0
- package/package.json +10 -41
- package/player_api_gate/bilibili-api-gate/cgi/cpp/bilibili-api-gate-cgi.cpp +281 -0
- package/player_api_gate/bilibili-api-gate/cgi/go/src.go +46 -0
- package/player_api_gate/bilibili-api-gate/cloudflare_workers/package-lock.json +1356 -0
- package/player_api_gate/bilibili-api-gate/cloudflare_workers/package.json +12 -0
- package/player_api_gate/bilibili-api-gate/cloudflare_workers/src/index.js +50 -0
- package/player_api_gate/bilibili-api-gate/cloudflare_workers/wrangler.toml +3 -0
- package/player_api_gate/iframe-api-ts/.editorconfig +12 -0
- package/player_api_gate/iframe-api-ts/.prettierrc +6 -0
- package/player_api_gate/iframe-api-ts/package-lock.json +3054 -0
- package/player_api_gate/iframe-api-ts/package.json +18 -0
- package/player_api_gate/iframe-api-ts/src/bilibili.ts +49 -0
- package/player_api_gate/iframe-api-ts/src/index.ts +35 -0
- package/player_api_gate/iframe-api-ts/src/niconico.ts +95 -0
- package/player_api_gate/iframe-api-ts/src/soundcloud.ts +38 -0
- package/player_api_gate/iframe-api-ts/src/types.ts +115 -0
- package/player_api_gate/iframe-api-ts/src/url-proxy.ts +29 -0
- package/player_api_gate/iframe-api-ts/src/utils.ts +82 -0
- package/player_api_gate/iframe-api-ts/src/youtube.ts +41 -0
- package/player_api_gate/iframe-api-ts/test/bilibili.spec.ts +47 -0
- package/player_api_gate/iframe-api-ts/test/env.d.ts +3 -0
- package/player_api_gate/iframe-api-ts/test/index.spec.ts +59 -0
- package/player_api_gate/iframe-api-ts/test/niconico.spec.ts +55 -0
- package/player_api_gate/iframe-api-ts/test/soundcloud.spec.ts +55 -0
- package/player_api_gate/iframe-api-ts/test/tsconfig.json +8 -0
- package/player_api_gate/iframe-api-ts/test/url-proxy.spec.ts +46 -0
- package/player_api_gate/iframe-api-ts/test/youtube.spec.ts +45 -0
- package/player_api_gate/iframe-api-ts/tsconfig.json +45 -0
- package/player_api_gate/iframe-api-ts/vitest.config.mts +11 -0
- package/player_api_gate/iframe-api-ts/worker-configuration.d.ts +5768 -0
- package/player_api_gate/iframe-api-ts/wrangler.jsonc +47 -0
- package/player_api_gate/iframe_api/.editorconfig +13 -0
- package/player_api_gate/iframe_api/.prettierrc +6 -0
- package/player_api_gate/iframe_api/package-lock.json +1307 -0
- package/player_api_gate/iframe_api/package.json +12 -0
- package/player_api_gate/iframe_api/src/bilibili_api.js +60 -0
- package/player_api_gate/iframe_api/src/index.js +47 -0
- package/player_api_gate/iframe_api/src/niconico_api.js +112 -0
- package/player_api_gate/iframe_api/src/soundcloud_api.js +57 -0
- package/player_api_gate/iframe_api/src/url_proxy.js +28 -0
- package/player_api_gate/iframe_api/src/youtube_api.js +44 -0
- package/player_api_gate/iframe_api/wrangler.toml +51 -0
- package/player_api_gate/niconico-imager/cgi/go/src.go +74 -0
- package/player_api_gate/niconico-imager/cloudflare_workers/package-lock.json +2175 -0
- package/player_api_gate/niconico-imager/cloudflare_workers/package.json +12 -0
- package/player_api_gate/niconico-imager/cloudflare_workers/src/index.js +78 -0
- package/player_api_gate/niconico-imager/cloudflare_workers/wrangler.toml +3 -0
- package/test_script.html +172 -0
- package/tsconfig.json +36 -0
- package/dist/iframe_api/bilibili.d.ts +0 -91
- package/dist/iframe_api/bilibili.d.ts.map +0 -1
- package/dist/iframe_api/bilibili.js +0 -451
- package/dist/iframe_api/bilibili.js.map +0 -1
- package/dist/iframe_api/index.d.ts +0 -6
- package/dist/iframe_api/index.d.ts.map +0 -1
- package/dist/iframe_api/index.js +0 -8
- package/dist/iframe_api/index.js.map +0 -1
- package/dist/iframe_api/niconico.d.ts +0 -42
- package/dist/iframe_api/niconico.d.ts.map +0 -1
- package/dist/iframe_api/niconico.js +0 -181
- package/dist/iframe_api/niconico.js.map +0 -1
- package/dist/iframe_api/soundcloud.d.ts +0 -80
- package/dist/iframe_api/soundcloud.d.ts.map +0 -1
- package/dist/iframe_api/soundcloud.js +0 -188
- package/dist/iframe_api/soundcloud.js.map +0 -1
- package/dist/iframe_api/youtube.d.ts +0 -133
- package/dist/iframe_api/youtube.d.ts.map +0 -1
- package/dist/iframe_api/youtube.js +0 -278
- package/dist/iframe_api/youtube.js.map +0 -1
- package/dist/multi_embed_player.d.ts +0 -48
- package/dist/multi_embed_player.d.ts.map +0 -1
- package/dist/multi_embed_player.js +0 -318
- package/dist/multi_embed_player.js.map +0 -1
- package/dist/types.d.ts +0 -126
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -22
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,1095 @@
|
|
|
1
|
+
// Notice! This file JSDOC is generated by gitHub copilot.
|
|
2
|
+
// So some of this JSDOC is not correct.
|
|
3
|
+
// Please refer to documents https://multi-embed-player.pages.dev/docs/reference/iframe_class/#mep_bilibili-class
|
|
4
|
+
|
|
5
|
+
interface mep_bilibili_playerVars {
|
|
6
|
+
startSeconds?: number;
|
|
7
|
+
endSeconds?: number;
|
|
8
|
+
autoplay?: number;
|
|
9
|
+
displayComment?: number;
|
|
10
|
+
fastLoad?: number;
|
|
11
|
+
play_control_wrap?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface mep_bilibili_content {
|
|
15
|
+
videoId: string;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
playerVars: mep_bilibili_playerVars;
|
|
19
|
+
overwrite?: boolean;
|
|
20
|
+
displayComment?: number;
|
|
21
|
+
play_control_wrap?: number;
|
|
22
|
+
startSeconds?: number;
|
|
23
|
+
endSeconds?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface BilibiliApiResponse {
|
|
27
|
+
code: number;
|
|
28
|
+
data?: {
|
|
29
|
+
title: string;
|
|
30
|
+
duration: number;
|
|
31
|
+
pic?: string;
|
|
32
|
+
};
|
|
33
|
+
image_base64?: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface BilibiliPlayerState {
|
|
37
|
+
getPlayerState: string;
|
|
38
|
+
currentTime?: number;
|
|
39
|
+
dulation?: number;
|
|
40
|
+
volumeValue?: number;
|
|
41
|
+
getTitle?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface BilibiliQuery {
|
|
45
|
+
bvid: string;
|
|
46
|
+
t?: number;
|
|
47
|
+
autoplay: 0 | 1;
|
|
48
|
+
danmaku: 0 | 1;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Class representing a Bilibili player.
|
|
53
|
+
* @class
|
|
54
|
+
* @classdesc This class provides methods to control a Bilibili player and handle errors.
|
|
55
|
+
* @property {boolean} localStorageCheck - A flag indicating whether the local storage is accessible.
|
|
56
|
+
* @property {boolean} mep_extension_bilibili - A flag indicating whether the extension is installed.
|
|
57
|
+
* @property {string} api_endpoint - The API endpoint for the player.
|
|
58
|
+
* @property {string} no_extention_error - The error message to display when the extension is not installed.
|
|
59
|
+
* @property {string} player_base_url - The base URL for the player.
|
|
60
|
+
* @property {Object} bilibili_api_cache - The cache for the Bilibili API.
|
|
61
|
+
* @property {string} cors_proxy - The CORS proxy for the player.
|
|
62
|
+
* @constructor
|
|
63
|
+
* @param {HTMLElement|String} replacing_element - The element to replace with the player or the ID of the element to replace with the player.
|
|
64
|
+
* @param {Object} content - The content to display in the player.
|
|
65
|
+
* @param {Function} player_set_event_function - The function to set the player event.
|
|
66
|
+
*/
|
|
67
|
+
class mep_bilibili{
|
|
68
|
+
player: HTMLIFrameElement | HTMLElement = document.createElement("iframe");
|
|
69
|
+
play_control_wrap: boolean = false;
|
|
70
|
+
front_error_code: number | undefined;
|
|
71
|
+
loading: boolean = false;
|
|
72
|
+
before_mute_volume: number = 100;
|
|
73
|
+
content_width: number = 0;
|
|
74
|
+
content_height: number = 0;
|
|
75
|
+
videoid: string = "";
|
|
76
|
+
original_replacing_element: HTMLElement = document.createElement("div");
|
|
77
|
+
player_set_event: ((player: HTMLIFrameElement) => void) | undefined;
|
|
78
|
+
seek_time: number = 0;
|
|
79
|
+
seek_time_used: boolean = false;
|
|
80
|
+
noextention_count_stop: number = 0;
|
|
81
|
+
state: BilibiliPlayerState = { getPlayerState: "0" };
|
|
82
|
+
apicache: Record<string, any> = {};
|
|
83
|
+
no_extention_pause: boolean = false;
|
|
84
|
+
startSeconds: number = 0;
|
|
85
|
+
innerStartSeconds: number = 0;
|
|
86
|
+
autoplay_flag: boolean = false;
|
|
87
|
+
displayCommentMode: boolean = false;
|
|
88
|
+
fastload: boolean = false;
|
|
89
|
+
no_extention_estimate_stop: boolean = false;
|
|
90
|
+
play_start_time: number = 0;
|
|
91
|
+
play_start_count_interval: number | undefined;
|
|
92
|
+
endSeconds: number = -1;
|
|
93
|
+
end_point_observe: number | undefined;
|
|
94
|
+
custom_state: number | undefined;
|
|
95
|
+
estimate_time: number | undefined;
|
|
96
|
+
start_event_count: number = 0;
|
|
97
|
+
end_event_count: number = 0;
|
|
98
|
+
|
|
99
|
+
static error_description = {0:"unknown error occurred",1:"data api endpoint invalid or throw error",2:"can't access local storage",3:"data api throw error",4:"player throw error direct"};
|
|
100
|
+
static localStorageCheck: boolean | null = null;//ニコニコと同じくlocalstorageにアクセスできないと死ぬため
|
|
101
|
+
static mep_extension_bilibili = false;//拡張機能ないとまともに動かん
|
|
102
|
+
static api_endpoint = "https://iframe_api.ryokuryu.workers.dev";//please change this if you use
|
|
103
|
+
static no_extention_error = "you seems not to install mep_extention yet.if it not installed in your browser,you can't exac some function(mute unMute setVolume etc) and some function(getDulation,getPlayerState etc) will return incorrect data which is not reflect real data";
|
|
104
|
+
static player_base_url = "";//"https://www.bilibili.com/blackboard/webplayer/embed-old.html?"
|
|
105
|
+
static bilibili_api_cache: Record<string, BilibiliApiResponse> = {};
|
|
106
|
+
static cors_proxy = "";
|
|
107
|
+
static currentTime_delay = 2;
|
|
108
|
+
static bilibili_api_promise: Record<string, {res: ((value: BilibiliApiResponse) => void)[], rej: ((reason?: any) => void)[]}> = {};
|
|
109
|
+
constructor(replacing_element: HTMLElement | string, content: mep_bilibili_content, player_set_event_function?: (player: HTMLIFrameElement) => void){
|
|
110
|
+
if(mep_bilibili.player_base_url==""){
|
|
111
|
+
const ua = navigator.userAgent;
|
|
112
|
+
if(ua.indexOf("Firefox")!=-1||ua.indexOf("Edg")!=-1){
|
|
113
|
+
mep_bilibili.player_base_url = "https://www.bilibili.com/blackboard/webplayer/embed-old.html?";//fast
|
|
114
|
+
}
|
|
115
|
+
else{
|
|
116
|
+
mep_bilibili.player_base_url = "https://player.bilibili.com/player.html?";//load often lazy in japan but this can mute auto play
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
(async()=>{
|
|
120
|
+
await this.#element_constructor(replacing_element,content,player_set_event_function);
|
|
121
|
+
})();
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* @private
|
|
125
|
+
* This function is used for display when player is paused.
|
|
126
|
+
* When this img clicked the player will be played.
|
|
127
|
+
*/
|
|
128
|
+
async #image_player(first_load = false){
|
|
129
|
+
try{
|
|
130
|
+
let exist_img_children = false;
|
|
131
|
+
this.player.parentElement?.childNodes.forEach((node)=>{if(node.nodeName==="IMG"){exist_img_children = true}if(node.nodeName==="DIV"&&(node as Element).classList.contains("mep_bilibili_transparent")){(node as Element).remove()}});
|
|
132
|
+
if(!exist_img_children&&this.play_control_wrap){
|
|
133
|
+
const img_element = document.createElement("img");
|
|
134
|
+
img_element.src = (await this.#getVideodataApi())["image_base64"] || "";
|
|
135
|
+
img_element.width = Number((this.player as HTMLIFrameElement).width);
|
|
136
|
+
img_element.height = Number((this.player as HTMLIFrameElement).height);
|
|
137
|
+
img_element.style.width = "100%";
|
|
138
|
+
img_element.style.height = "100%";
|
|
139
|
+
img_element.style.objectFit = "cover";
|
|
140
|
+
img_element.style.cursor = "pointer";
|
|
141
|
+
img_element.addEventListener("click",()=>{this.playVideo()});
|
|
142
|
+
this.player.parentElement?.prepend(img_element);
|
|
143
|
+
}
|
|
144
|
+
this.player.hidden = true;
|
|
145
|
+
(this.player as HTMLIFrameElement).src = "";
|
|
146
|
+
if(first_load){
|
|
147
|
+
this.player.dispatchEvent(new Event("onReady"));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch(e){
|
|
151
|
+
console.error(e);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* @private
|
|
156
|
+
* This function is used for display when player is playing.
|
|
157
|
+
* When this div clicked the player will be paused.
|
|
158
|
+
*/
|
|
159
|
+
#set_pause_transparent(){
|
|
160
|
+
let exist_div_children = false;
|
|
161
|
+
this.player.parentElement?.childNodes.forEach((node)=>{if(node.nodeName==="DIV"&&(node as Element).classList.contains("mep_bilibili_transparent")){exist_div_children = true}});
|
|
162
|
+
if(!exist_div_children&&this.play_control_wrap){
|
|
163
|
+
const div_element = document.createElement("div");
|
|
164
|
+
div_element.classList.add("mep_bilibili_transparent");
|
|
165
|
+
div_element.style.width = "100%";
|
|
166
|
+
div_element.style.height = "100%";
|
|
167
|
+
div_element.style.zIndex = "1";
|
|
168
|
+
div_element.style.position = "absolute";
|
|
169
|
+
div_element.style.cursor = "pointer";
|
|
170
|
+
div_element.addEventListener("click",()=>{this.pauseVideo()});
|
|
171
|
+
this.player.parentElement?.prepend(div_element);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* @private
|
|
176
|
+
* This function is used for display when player is loading.
|
|
177
|
+
*/
|
|
178
|
+
#add_loading_animation(){
|
|
179
|
+
//https://qiita.com/yoshio-the-end/items/8cec41bad0e817928893
|
|
180
|
+
let exist_mep_load_animation = false;
|
|
181
|
+
Array.from(document.head.getElementsByClassName("mep_load_animation")).forEach((element)=>{if(element.nodeName==="STYLE"){exist_mep_load_animation = true}});
|
|
182
|
+
if(!exist_mep_load_animation){
|
|
183
|
+
const style_element = document.createElement("style");
|
|
184
|
+
style_element.classList.add("mep_load_animation");
|
|
185
|
+
style_element.innerHTML = `
|
|
186
|
+
.mep_loading_animation{
|
|
187
|
+
border: 12px solid #fafafa;
|
|
188
|
+
border-radius: 50%;
|
|
189
|
+
border-top: 12px solid #3498db;
|
|
190
|
+
width: 100px;
|
|
191
|
+
height: 100px;
|
|
192
|
+
animation: spin 1s linear infinite;
|
|
193
|
+
}
|
|
194
|
+
@keyframes spin{
|
|
195
|
+
0%{
|
|
196
|
+
transform: rotate(0deg);
|
|
197
|
+
}
|
|
198
|
+
100%{
|
|
199
|
+
transform: rotate(360deg);
|
|
200
|
+
}
|
|
201
|
+
}`;
|
|
202
|
+
document.head.appendChild(style_element);
|
|
203
|
+
}
|
|
204
|
+
let exist_animation_div = false;
|
|
205
|
+
this.player.parentElement?.childNodes.forEach((node)=>{if(node.nodeName==="DIV"&&(node as Element).classList.contains("mep_loading_animation")){exist_animation_div = true}});
|
|
206
|
+
if(!exist_animation_div){
|
|
207
|
+
const div_element = document.createElement("div");
|
|
208
|
+
div_element.classList.add("mep_loading_animation");
|
|
209
|
+
div_element.style.zIndex = "2";
|
|
210
|
+
div_element.style.top = "calc(50% - 50px)";
|
|
211
|
+
div_element.style.left = "calc(50% - 50px)";
|
|
212
|
+
div_element.style.position = "absolute";
|
|
213
|
+
this.player.parentElement?.prepend(div_element);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* @private
|
|
218
|
+
* This function will be called when player is loaded.
|
|
219
|
+
*/
|
|
220
|
+
#remove_loading_animation(){
|
|
221
|
+
this.player.parentElement?.childNodes.forEach((node)=>{if(node.nodeName==="DIV"&&(node as Element).classList.contains("mep_loading_animation")){(node as Element).remove()}});
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* @private
|
|
225
|
+
* This function is used for display when player calld error.
|
|
226
|
+
*/
|
|
227
|
+
#add_error_description(){
|
|
228
|
+
const error_description_document = document.createElement("div");
|
|
229
|
+
error_description_document.style.width = "100%";
|
|
230
|
+
error_description_document.style.height = "100%";
|
|
231
|
+
let error_message = "unknown error occurred";
|
|
232
|
+
if(this.front_error_code!=undefined&&(window as any).mep_bilibili.error_description[this.front_error_code]!=undefined){
|
|
233
|
+
error_message = (window as any).mep_bilibili.error_description[this.front_error_code] + "\n front end error code:" + String(this.front_error_code);
|
|
234
|
+
}
|
|
235
|
+
error_description_document.innerText = error_message;
|
|
236
|
+
this.player.replaceWith(error_description_document);
|
|
237
|
+
this.player = error_description_document;
|
|
238
|
+
try{
|
|
239
|
+
this.player.parentElement!.style.backgroundImage = "";
|
|
240
|
+
}
|
|
241
|
+
catch{}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* @private
|
|
245
|
+
* This functiuon add css style for player when not exist.
|
|
246
|
+
*/
|
|
247
|
+
#add_player_css_style(){
|
|
248
|
+
let exist_mep_bilibili_player_css = false;
|
|
249
|
+
Array.from(document.head.getElementsByClassName("mep_bilibili_player_css")).forEach((element)=>{if(element.nodeName==="STYLE"){exist_mep_bilibili_player_css = true}});
|
|
250
|
+
if(!exist_mep_bilibili_player_css){
|
|
251
|
+
const style_element = document.createElement("style");
|
|
252
|
+
style_element.classList.add("mep_bilibili_player_css");
|
|
253
|
+
style_element.innerHTML = `
|
|
254
|
+
.mep_bilibili_player{
|
|
255
|
+
border: none;
|
|
256
|
+
width: 100%;
|
|
257
|
+
height: 100%;
|
|
258
|
+
}`;
|
|
259
|
+
document.head.appendChild(style_element);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* @private
|
|
264
|
+
* This function is used for display when player can't access local storage.
|
|
265
|
+
*/
|
|
266
|
+
#add_local_storage_error_description(){
|
|
267
|
+
const error_description_document = document.createElement("div");
|
|
268
|
+
error_description_document.style.width = "100%";
|
|
269
|
+
error_description_document.style.height = "100%";
|
|
270
|
+
error_description_document.innerText = "Due to not to access localstorage,can't play bilibili video\nyou should turn on third party cookie for this site and then reload this page";
|
|
271
|
+
this.player.replaceWith(error_description_document);
|
|
272
|
+
this.player = error_description_document;
|
|
273
|
+
try{
|
|
274
|
+
this.player.parentElement!.style.backgroundImage = "";
|
|
275
|
+
}
|
|
276
|
+
catch{}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Constructor function for the Bilibili player.
|
|
280
|
+
* @param {string|HTMLElement} replacing_element - The element to replace with the Bilibili player.
|
|
281
|
+
* @param {Object} content - The content object containing the video ID and player variables.
|
|
282
|
+
* @param {Function} player_set_event_function - The function to set event listeners on the player.
|
|
283
|
+
*/
|
|
284
|
+
async #element_constructor(replacing_element: HTMLElement | string, content: mep_bilibili_content, player_set_event_function?: (player: HTMLIFrameElement) => void): Promise<void> {
|
|
285
|
+
this.loading = true;
|
|
286
|
+
this.before_mute_volume = 100;
|
|
287
|
+
this.content_width = content.width;
|
|
288
|
+
this.content_height = content.height;
|
|
289
|
+
if(content?.play_control_wrap == 0){
|
|
290
|
+
this.play_control_wrap = false;
|
|
291
|
+
}
|
|
292
|
+
else{
|
|
293
|
+
this.play_control_wrap = true;
|
|
294
|
+
}
|
|
295
|
+
this.videoid = content["videoId"];
|
|
296
|
+
if(typeof replacing_element === "string"){
|
|
297
|
+
const element = document.getElementById(replacing_element);
|
|
298
|
+
if (!element) throw new Error(`Element with id '${replacing_element}' not found`);
|
|
299
|
+
replacing_element = element;
|
|
300
|
+
}
|
|
301
|
+
this.original_replacing_element = replacing_element;
|
|
302
|
+
let bilibili_doc = document.createElement("iframe");
|
|
303
|
+
replacing_element.replaceWith(bilibili_doc);
|
|
304
|
+
this.player = bilibili_doc;
|
|
305
|
+
this.#add_loading_animation();
|
|
306
|
+
this.player.addEventListener("onReady",()=>{this.#remove_loading_animation();this.loading = false},{once:true});
|
|
307
|
+
this.player.addEventListener("onError",()=>{this.#remove_loading_animation();this.#add_error_description()},{once:true});
|
|
308
|
+
if(typeof player_set_event_function == "function"){
|
|
309
|
+
player_set_event_function(this.player as HTMLIFrameElement);
|
|
310
|
+
this.player_set_event = player_set_event_function;
|
|
311
|
+
}
|
|
312
|
+
const api_response = await this.#getVideodataApi();
|
|
313
|
+
if((api_response as any)?.code!==0){//video can play or not if code not 0 such as 69002 the video maybe delete.
|
|
314
|
+
console.error("error occured when get bilibili api. Are you sure you overwrite iframe_api endpoint? or cors proxy is not working? or videoid is invalid?");
|
|
315
|
+
this.front_error_code = 1;
|
|
316
|
+
this.player.dispatchEvent(new CustomEvent("onError",{detail:{code:1100}}));
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.seek_time = -1;
|
|
320
|
+
this.seek_time_used = true;
|
|
321
|
+
this.noextention_count_stop = 0;
|
|
322
|
+
if(typeof content?.videoId !== "string"){
|
|
323
|
+
console.log("videoId = undefined is not valid")
|
|
324
|
+
}
|
|
325
|
+
this.state = {
|
|
326
|
+
getPlayerState: "PAUSE"
|
|
327
|
+
};
|
|
328
|
+
this.apicache = {};
|
|
329
|
+
if((window as any).mep_bilibili.localStorageCheck!=true){
|
|
330
|
+
await this.#checkLocalstorage();
|
|
331
|
+
}
|
|
332
|
+
if((window as any).mep_bilibili.localStorageCheck===false){
|
|
333
|
+
this.#add_local_storage_error_description();
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
this.no_extention_pause = false;
|
|
337
|
+
this.#messageListener();
|
|
338
|
+
this.startSeconds = 0;
|
|
339
|
+
this.innerStartSeconds = 0;
|
|
340
|
+
if(content?.playerVars?.startSeconds!=undefined){
|
|
341
|
+
this.startSeconds = content?.playerVars?.startSeconds;
|
|
342
|
+
this.innerStartSeconds = parseInt(content?.playerVars?.startSeconds.toString());
|
|
343
|
+
}
|
|
344
|
+
this.autoplay_flag = false;
|
|
345
|
+
if(content?.playerVars?.autoplay==1){//終わり次第再生
|
|
346
|
+
this.autoplay_flag = true;
|
|
347
|
+
}
|
|
348
|
+
if(content?.playerVars?.displayComment!=undefined){
|
|
349
|
+
if(content?.playerVars?.displayComment==0){
|
|
350
|
+
this.displayCommentMode = false;
|
|
351
|
+
}
|
|
352
|
+
else if(content?.playerVars?.displayComment==1){
|
|
353
|
+
this.displayCommentMode = true;
|
|
354
|
+
}
|
|
355
|
+
else{
|
|
356
|
+
this.displayCommentMode = false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
let bilibili_query: BilibiliQuery = {
|
|
360
|
+
bvid: "",
|
|
361
|
+
autoplay: 0,
|
|
362
|
+
danmaku: 0
|
|
363
|
+
};
|
|
364
|
+
if(content["videoId"]==undefined){
|
|
365
|
+
console.log("invalid videoid:" + content["videoId"] + "so stop loading");
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
bilibili_query.bvid = content["videoId"];
|
|
369
|
+
if(this.startSeconds>0){
|
|
370
|
+
bilibili_query.t = this.startSeconds;
|
|
371
|
+
}
|
|
372
|
+
if(this.autoplay_flag){
|
|
373
|
+
bilibili_query.autoplay = 1;
|
|
374
|
+
}
|
|
375
|
+
else{
|
|
376
|
+
bilibili_query.autoplay = 0;
|
|
377
|
+
}
|
|
378
|
+
if(!this.autoplay_flag&&!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
379
|
+
this.no_extention_pause = true;
|
|
380
|
+
}
|
|
381
|
+
if(this.displayCommentMode){
|
|
382
|
+
bilibili_query.danmaku = 1;
|
|
383
|
+
}
|
|
384
|
+
else{
|
|
385
|
+
bilibili_query.danmaku = 0;
|
|
386
|
+
}
|
|
387
|
+
this.fastload = false;
|
|
388
|
+
if(content?.playerVars?.fastLoad!=undefined){
|
|
389
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
390
|
+
console.log("fast load ignored because of mep extention not installed in your browser")
|
|
391
|
+
}
|
|
392
|
+
else{
|
|
393
|
+
if(content?.playerVars?.fastLoad==1){
|
|
394
|
+
this.fastload = true;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
let query_string = "";
|
|
399
|
+
let bilibili_query_keys = Object.keys(bilibili_query) as (keyof BilibiliQuery)[];
|
|
400
|
+
for(let x=0;x<bilibili_query_keys.length;x++){
|
|
401
|
+
const key = bilibili_query_keys[x];
|
|
402
|
+
if(key !== undefined) {
|
|
403
|
+
const value = bilibili_query[key];
|
|
404
|
+
if(value !== undefined){
|
|
405
|
+
query_string += key + "=" + String(value) + "&";
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
query_string = query_string.slice(0,-1);
|
|
410
|
+
if(this.autoplay_flag){
|
|
411
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){//時間カウント用プログラムの追加
|
|
412
|
+
this.no_extention_estimate_stop = true;
|
|
413
|
+
this.#set_pause_transparent();
|
|
414
|
+
this.player.addEventListener("load",()=>{this.play_start_time = new Date().getTime();this.no_extention_estimate_stop = false;this.play_start_count_interval = setInterval(this.#observe_load_time.bind(this),500);this.player.dispatchEvent(new Event("onReady"))},{once:true});
|
|
415
|
+
}
|
|
416
|
+
else{
|
|
417
|
+
this.player.addEventListener("onReady",()=>{if(this.fastload&&this.startSeconds!=0){this.seekTo(this.startSeconds)};if(this.fastload&&this.autoplay_flag){this.playVideo()}})
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if(this.autoplay_flag||(window as any).mep_bilibili.mep_extension_bilibili===true){
|
|
421
|
+
bilibili_doc.src = (window as any).mep_bilibili.player_base_url + query_string;
|
|
422
|
+
}
|
|
423
|
+
else{
|
|
424
|
+
this.#image_player(true);
|
|
425
|
+
}
|
|
426
|
+
this.#add_player_css_style();
|
|
427
|
+
bilibili_doc.classList.add("mep_bilibili_player");
|
|
428
|
+
bilibili_doc.width = String(content.width);
|
|
429
|
+
bilibili_doc.height = String(content.height);
|
|
430
|
+
bilibili_doc.allow = "autoplay";//fix bug not autoplay on chrome
|
|
431
|
+
bilibili_doc.allowFullscreen = true;//fix bug can't watch on full screen(all browser)
|
|
432
|
+
bilibili_doc.style.border = "none";//fix bug display border on outer frame
|
|
433
|
+
try{(bilibili_doc.parentElement as any).setEvent()}catch{}
|
|
434
|
+
//bilibili_doc.sandbox = "allow-scripts";
|
|
435
|
+
this.endSeconds = -1;
|
|
436
|
+
if(content?.playerVars?.endSeconds!=undefined){
|
|
437
|
+
this.endSeconds = content?.playerVars?.endSeconds;
|
|
438
|
+
}
|
|
439
|
+
if(this.endSeconds!=-1){
|
|
440
|
+
this.end_point_observe = setInterval(this.#observe_end_time.bind(this),500);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Checks whether cross domain iframe can use local storage.
|
|
445
|
+
* If it can't, it will not load bilibili embed.
|
|
446
|
+
* @returns {Promise<void>}
|
|
447
|
+
*/
|
|
448
|
+
async #checkLocalstorage(){
|
|
449
|
+
//check whether cross domain iframe can use local storage
|
|
450
|
+
//if can't ,it will not load bilibili embed
|
|
451
|
+
if((window as any).mep_bilibili.localStorageCheck==null){
|
|
452
|
+
let selected_localStorageCheck_url = "";
|
|
453
|
+
if(!location.origin.includes("pages.dev")){
|
|
454
|
+
selected_localStorageCheck_url = "https://multi-embed-player.pages.dev/localStorageCheck";
|
|
455
|
+
}
|
|
456
|
+
else{
|
|
457
|
+
selected_localStorageCheck_url = "https://multi-embed-player.netlify.app/localstoragecheck";
|
|
458
|
+
}
|
|
459
|
+
const cdls = document.createElement("iframe");
|
|
460
|
+
cdls.width = "0";
|
|
461
|
+
cdls.height = "0";
|
|
462
|
+
cdls.src = selected_localStorageCheck_url;//if you don't prefer you can change this file.But you must change origin.If you this embed example.com,you must not this otherdomain.example.com
|
|
463
|
+
//and if extention exists,it will redirect to send information about exist browser extention
|
|
464
|
+
cdls.style.cssText = "border:none;"
|
|
465
|
+
const origin = new URL(selected_localStorageCheck_url).origin;
|
|
466
|
+
document.body.appendChild(cdls);
|
|
467
|
+
const return_localstorage_status = await new Promise<boolean>(function(resolve: (value: boolean) => void, reject: (reason?: any) => void){
|
|
468
|
+
window.addEventListener("message",function(ms){
|
|
469
|
+
if(ms.origin==origin){
|
|
470
|
+
try{
|
|
471
|
+
if(ms.data.extention){
|
|
472
|
+
(window as any).mep_bilibili.mep_extension_bilibili = true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch{}
|
|
476
|
+
resolve(ms.data.localStorageUsable);
|
|
477
|
+
}
|
|
478
|
+
})
|
|
479
|
+
}.bind(origin));
|
|
480
|
+
cdls.remove();
|
|
481
|
+
if(!return_localstorage_status){
|
|
482
|
+
(window as any).mep_bilibili.localStorageCheck = false;
|
|
483
|
+
this.front_error_code = 2;
|
|
484
|
+
this.player.dispatchEvent(new CustomEvent("onError",{detail:{code:1200}}));//can't play bilibili video
|
|
485
|
+
}
|
|
486
|
+
else{
|
|
487
|
+
(window as any).mep_bilibili.localStorageCheck = true;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
else if((window as any).mep_bilibili.localStorageCheck==false){
|
|
491
|
+
this.front_error_code = 2;
|
|
492
|
+
this.player.dispatchEvent(new CustomEvent("onError",{detail:{code:1200}}));//can't play bilibili video
|
|
493
|
+
console.log("error")
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Observes the load time of the video player and estimates the current time of the video.
|
|
498
|
+
* @private
|
|
499
|
+
*/
|
|
500
|
+
async #observe_load_time(){
|
|
501
|
+
if(this.noextention_count_stop==0||this.noextention_count_stop==1){
|
|
502
|
+
let now_time = new Date().getTime();
|
|
503
|
+
if(!this.no_extention_estimate_stop){
|
|
504
|
+
if(this.innerStartSeconds!=undefined&&this.innerStartSeconds!=0){
|
|
505
|
+
this.estimate_time = (now_time - this.play_start_time)/1000 + this.innerStartSeconds - (window as any).mep_bilibili.currentTime_delay;
|
|
506
|
+
}
|
|
507
|
+
else if(this.seek_time!=undefined&&this.seek_time!=0){
|
|
508
|
+
this.estimate_time = (now_time - this.play_start_time)/1000 + this.seek_time - (window as any).mep_bilibili.currentTime_delay;
|
|
509
|
+
}
|
|
510
|
+
else{
|
|
511
|
+
this.estimate_time = (now_time - this.play_start_time)/1000 + this.startSeconds - (window as any).mep_bilibili.currentTime_delay;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if(this.endSeconds!=-1&&this.estimate_time!==undefined&&this.estimate_time>this.endSeconds){
|
|
515
|
+
this.custom_state = 4;
|
|
516
|
+
this.player.dispatchEvent(new Event("onEndVideo"));//再生を終了したことにする
|
|
517
|
+
clearInterval(this.play_start_count_interval);//確認を消去
|
|
518
|
+
this.pauseVideo();
|
|
519
|
+
}
|
|
520
|
+
if(this.endSeconds===-1&&this.estimate_time!==undefined&&this.estimate_time>=(await this.getDuration())){
|
|
521
|
+
this.player.dispatchEvent(new Event("onEndVideo"));//再生を終了したことにする
|
|
522
|
+
clearInterval(this.play_start_count_interval);//確認を消去
|
|
523
|
+
this.pauseVideo();
|
|
524
|
+
}
|
|
525
|
+
if(this.noextention_count_stop==1){
|
|
526
|
+
this.noextention_count_stop = 2;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Observes the end time of the video player and dispatches the onEndVideo event.
|
|
533
|
+
* @private
|
|
534
|
+
*/
|
|
535
|
+
#observe_end_time(){
|
|
536
|
+
let current_time = this.getCurrentTime();
|
|
537
|
+
if(this.endSeconds!=-1&&this.endSeconds<=current_time){//時間が来た
|
|
538
|
+
this.custom_state = 4;
|
|
539
|
+
clearInterval(this.end_point_observe);
|
|
540
|
+
this.pauseVideo();
|
|
541
|
+
this.player.dispatchEvent(new Event("onEndVideo"));
|
|
542
|
+
}
|
|
543
|
+
else if(this.endSeconds==-1){
|
|
544
|
+
clearInterval(this.end_point_observe);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Loads the video player with the specified content in cue mode.
|
|
549
|
+
* @param {Object} content - The content to display in the player.
|
|
550
|
+
*/
|
|
551
|
+
cueVideoById(content: Partial<mep_bilibili_content>): void {
|
|
552
|
+
if(content["overwrite"]==undefined){
|
|
553
|
+
content["overwrite"] = true;
|
|
554
|
+
}
|
|
555
|
+
if(content["startSeconds"]!=undefined&&content["overwrite"]==true){
|
|
556
|
+
this.startSeconds = content["startSeconds"];
|
|
557
|
+
}
|
|
558
|
+
if(typeof content.startSeconds !== "undefined"){
|
|
559
|
+
this.innerStartSeconds = parseInt(content.startSeconds.toString());
|
|
560
|
+
}
|
|
561
|
+
else{
|
|
562
|
+
this.innerStartSeconds = 0;
|
|
563
|
+
}
|
|
564
|
+
if(content["endSeconds"]!=undefined&&content["overwrite"]==true){
|
|
565
|
+
this.endSeconds = content["endSeconds"];
|
|
566
|
+
}
|
|
567
|
+
this.autoplay_flag = false;
|
|
568
|
+
this.no_extention_pause = true;
|
|
569
|
+
if(content["overwrite"]==true){
|
|
570
|
+
this.videoid = content["videoId"] || "";
|
|
571
|
+
}
|
|
572
|
+
this.#image_player();
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Loads the video player with the specified content in play mode.
|
|
576
|
+
* @param {Object} content - The content to display in the player.
|
|
577
|
+
*/
|
|
578
|
+
loadVideoById(content: Partial<mep_bilibili_content>): void {
|
|
579
|
+
if(content["overwrite"]==undefined){
|
|
580
|
+
content["overwrite"] = true;
|
|
581
|
+
}
|
|
582
|
+
if(content["startSeconds"]!=undefined&&content["overwrite"]==true){
|
|
583
|
+
this.startSeconds = content["startSeconds"];
|
|
584
|
+
}
|
|
585
|
+
if(typeof content.startSeconds !== "undefined"){
|
|
586
|
+
this.innerStartSeconds = parseInt(content.startSeconds.toString());
|
|
587
|
+
}
|
|
588
|
+
else{
|
|
589
|
+
this.innerStartSeconds = 0;
|
|
590
|
+
}
|
|
591
|
+
if(content["endSeconds"]!=undefined&&content["overwrite"]==true){
|
|
592
|
+
this.endSeconds = content["endSeconds"];
|
|
593
|
+
}
|
|
594
|
+
this.autoplay_flag = true;
|
|
595
|
+
this.no_extention_pause = false;
|
|
596
|
+
this.#video_loader(content);
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Asynchronously loads a Bilibili video player with the given content.
|
|
600
|
+
* @param {Object} content - The content of the video player to be loaded.
|
|
601
|
+
* @returns {Promise<void>} - A Promise that resolves when the video player is loaded.
|
|
602
|
+
* @private
|
|
603
|
+
*/
|
|
604
|
+
async #video_loader(content: Partial<mep_bilibili_content>): Promise<void> {
|
|
605
|
+
if(this.player===undefined){
|
|
606
|
+
this.player = this.original_replacing_element;
|
|
607
|
+
}
|
|
608
|
+
this.loading = true;
|
|
609
|
+
this.#add_loading_animation();
|
|
610
|
+
if((window as any).mep_bilibili.localStorageCheck===false){
|
|
611
|
+
this.#add_local_storage_error_description();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
this.player.parentElement?.childNodes.forEach((node)=>{if((node as Element).tagName==="IMG"){(node as Element).remove()}});
|
|
615
|
+
let bilibili_query: BilibiliQuery = {
|
|
616
|
+
bvid: "",
|
|
617
|
+
autoplay: 0,
|
|
618
|
+
danmaku: 0
|
|
619
|
+
};
|
|
620
|
+
if(this.videoid!=content?.videoId){//when load other video
|
|
621
|
+
this.seek_time = -1;
|
|
622
|
+
this.seek_time_used = true;
|
|
623
|
+
this.estimate_time = undefined;
|
|
624
|
+
this.noextention_count_stop = 0;
|
|
625
|
+
}
|
|
626
|
+
this.videoid = content?.videoId || "";
|
|
627
|
+
if(((await this.#getVideodataApi()) as any)?.code!=0){//video can play or not if code not 0 such as 69002 the video maybe delete.
|
|
628
|
+
this.front_error_code = 3;
|
|
629
|
+
this.player.dispatchEvent(new CustomEvent("onError",{detail:{code:404}}));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
bilibili_query.bvid = content?.videoId || "";
|
|
633
|
+
if(content?.startSeconds && content.startSeconds>0){
|
|
634
|
+
bilibili_query.t = content.startSeconds;
|
|
635
|
+
}
|
|
636
|
+
if(this.autoplay_flag){
|
|
637
|
+
bilibili_query.autoplay = 1;
|
|
638
|
+
}
|
|
639
|
+
else{
|
|
640
|
+
bilibili_query.autoplay = 0;
|
|
641
|
+
}
|
|
642
|
+
if(content?.displayComment!=undefined){
|
|
643
|
+
if(content?.displayComment==0){
|
|
644
|
+
this.displayCommentMode = false;
|
|
645
|
+
}
|
|
646
|
+
else if(content?.displayComment==1){
|
|
647
|
+
this.displayCommentMode = true;
|
|
648
|
+
}
|
|
649
|
+
else{
|
|
650
|
+
this.displayCommentMode = false;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
if(this.displayCommentMode){
|
|
654
|
+
bilibili_query.danmaku = 1;
|
|
655
|
+
}
|
|
656
|
+
else{
|
|
657
|
+
bilibili_query.danmaku = 0;
|
|
658
|
+
}
|
|
659
|
+
let query_string = "";
|
|
660
|
+
let bilibili_query_keys = Object.keys(bilibili_query) as (keyof BilibiliQuery)[];
|
|
661
|
+
for(let x=0;x<bilibili_query_keys.length;x++){
|
|
662
|
+
const key = bilibili_query_keys[x];
|
|
663
|
+
if(key !== undefined) {
|
|
664
|
+
const value = bilibili_query[key];
|
|
665
|
+
if(value !== undefined){
|
|
666
|
+
query_string += key + "=" + String(value) + "&";
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
query_string = query_string.slice(0,-1);
|
|
671
|
+
const new_player = document.createElement("iframe");
|
|
672
|
+
this.player.replaceWith(new_player);
|
|
673
|
+
this.player = new_player;
|
|
674
|
+
if(typeof this.player_set_event == "function"){
|
|
675
|
+
this.player_set_event(this.player as HTMLIFrameElement);
|
|
676
|
+
}
|
|
677
|
+
else{
|
|
678
|
+
try{
|
|
679
|
+
(this.player.parentElement as any).setEvent();
|
|
680
|
+
}
|
|
681
|
+
catch{}
|
|
682
|
+
}
|
|
683
|
+
this.player.addEventListener("onReady",()=>{this.#remove_loading_animation();this.loading = false},{once:true});
|
|
684
|
+
this.player.addEventListener("onError",()=>{this.#remove_loading_animation();this.#add_error_description()},{once:true});
|
|
685
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){//時間カウント用プログラムの追加
|
|
686
|
+
this.no_extention_estimate_stop = true;
|
|
687
|
+
if(this.autoplay_flag){
|
|
688
|
+
this.#set_pause_transparent();
|
|
689
|
+
}
|
|
690
|
+
else{
|
|
691
|
+
this.#image_player(true);
|
|
692
|
+
}
|
|
693
|
+
new_player.addEventListener("load",()=>{this.play_start_time = new Date().getTime();this.no_extention_estimate_stop = false;this.play_start_count_interval = setInterval(this.#observe_load_time.bind(this),500);this.player.dispatchEvent(new Event("onReady"))},{once:true});
|
|
694
|
+
}
|
|
695
|
+
(this.player as HTMLIFrameElement).src = (window as any).mep_bilibili.player_base_url + query_string;
|
|
696
|
+
(this.player as HTMLIFrameElement).allow = "autoplay";//fix bug not autoplay on chrome
|
|
697
|
+
(this.player as HTMLIFrameElement).allowFullscreen = true;//fix bug can't watch on full screen(all browser)
|
|
698
|
+
this.player.style.border = "none";//fix bug display border on outer frame
|
|
699
|
+
(this.player as HTMLIFrameElement).width = String(this.content_width);
|
|
700
|
+
(this.player as HTMLIFrameElement).height = String(this.content_height);
|
|
701
|
+
this.#add_player_css_style();
|
|
702
|
+
this.player.classList.add("mep_bilibili_player");
|
|
703
|
+
this.player.hidden = false;
|
|
704
|
+
//this.player.sandbox = "allow-scripts";
|
|
705
|
+
if(this.endSeconds!=-1){
|
|
706
|
+
this.end_point_observe = setInterval(this.#observe_end_time.bind(this),500);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Returns the current time of the video.
|
|
711
|
+
* @returns {Promise<number>} - The current time of the video.
|
|
712
|
+
*/
|
|
713
|
+
getCurrentTime(): number {
|
|
714
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
715
|
+
if(this.loading){
|
|
716
|
+
return this.startSeconds;
|
|
717
|
+
}
|
|
718
|
+
else if(!this.seek_time_used){
|
|
719
|
+
return this.seek_time;
|
|
720
|
+
}
|
|
721
|
+
else if(this.estimate_time!=undefined){
|
|
722
|
+
return this.estimate_time;
|
|
723
|
+
}
|
|
724
|
+
else{
|
|
725
|
+
return this.startSeconds;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
else{
|
|
729
|
+
return this.state.currentTime || 0;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Plays the video. If the Bilibili extension is not installed, loads the video with the specified parameters.
|
|
734
|
+
* If the extension is installed, sends a message to the extension to play the video.
|
|
735
|
+
*/
|
|
736
|
+
playVideo(): void {
|
|
737
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
738
|
+
this.#set_pause_transparent();
|
|
739
|
+
this.no_extention_pause = false;
|
|
740
|
+
this.noextention_count_stop = 0;
|
|
741
|
+
let generate_sorce: Partial<mep_bilibili_content> = {"videoId":this.videoid,"overwrite":false};
|
|
742
|
+
if(this.endSeconds!=-1){
|
|
743
|
+
generate_sorce.endSeconds = this.endSeconds;
|
|
744
|
+
}
|
|
745
|
+
if(this.seek_time!=-1&&!this.seek_time_used){
|
|
746
|
+
this.seek_time_used = true;
|
|
747
|
+
generate_sorce.startSeconds = this.seek_time;
|
|
748
|
+
}
|
|
749
|
+
else if(this.estimate_time!=0){
|
|
750
|
+
generate_sorce.startSeconds = this.estimate_time;
|
|
751
|
+
}
|
|
752
|
+
else if(this.startSeconds!=0){
|
|
753
|
+
generate_sorce.startSeconds = this.startSeconds;
|
|
754
|
+
}
|
|
755
|
+
this.loadVideoById(generate_sorce);
|
|
756
|
+
}
|
|
757
|
+
else{
|
|
758
|
+
(this.player as HTMLIFrameElement).contentWindow?.postMessage({eventName:"play"},"*");
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Pauses the video playback. If the Bilibili extension is not detected, it replaces the player with an image and stops the play start count interval.
|
|
763
|
+
* @function
|
|
764
|
+
*/
|
|
765
|
+
pauseVideo(): void {
|
|
766
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
767
|
+
clearInterval(this.play_start_count_interval);
|
|
768
|
+
this.no_extention_pause = true;
|
|
769
|
+
this.noextention_count_stop = 1;
|
|
770
|
+
this.#image_player();
|
|
771
|
+
this.seek_time = this.estimate_time || 0;
|
|
772
|
+
try{(this.player.parentElement as any).deleteEvent()}catch{}
|
|
773
|
+
}
|
|
774
|
+
else{
|
|
775
|
+
(this.player as HTMLIFrameElement).contentWindow?.postMessage({eventName:"pause"},"*");
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Seeks to the specified time in the video.
|
|
780
|
+
* @async
|
|
781
|
+
* @param {number} seektime - The time to seek to, in seconds.
|
|
782
|
+
*/
|
|
783
|
+
async seekTo(seektime: number): Promise<void> {
|
|
784
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
785
|
+
let generate_sorce: Partial<mep_bilibili_content> = {"videoId":this.videoid,"overwrite":false};
|
|
786
|
+
if(this.endSeconds!=-1){
|
|
787
|
+
generate_sorce.endSeconds = this.endSeconds;
|
|
788
|
+
}
|
|
789
|
+
generate_sorce.startSeconds = seektime;
|
|
790
|
+
this.seek_time = seektime;
|
|
791
|
+
this.seek_time_used = false;
|
|
792
|
+
let player_state = await this.getPlayerState();
|
|
793
|
+
if(player_state==2){
|
|
794
|
+
this.seek_time_used = true;
|
|
795
|
+
this.loadVideoById(generate_sorce);
|
|
796
|
+
}
|
|
797
|
+
else{
|
|
798
|
+
this.estimate_time = seektime;
|
|
799
|
+
this.cueVideoById(generate_sorce);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
else{
|
|
803
|
+
(this.player as HTMLIFrameElement).contentWindow?.postMessage({eventName:"seek",seekTime:Number(seektime)},"*");
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Calculates the real duration of the video based on the start and end seconds.
|
|
808
|
+
* @async
|
|
809
|
+
* @function
|
|
810
|
+
* @returns {Promise<number>} The real duration of the video.
|
|
811
|
+
*/
|
|
812
|
+
async getRealDulation(): Promise<number> {//original function
|
|
813
|
+
if(this.endSeconds==-1){
|
|
814
|
+
return await this.getDuration() - this.startSeconds;
|
|
815
|
+
}
|
|
816
|
+
else{
|
|
817
|
+
return this.endSeconds - this.startSeconds;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Asynchronously retrieves video data API for Bilibili.
|
|
822
|
+
* @returns {Promise<Object>} Promise object representing the video data API for Bilibili.
|
|
823
|
+
*/
|
|
824
|
+
async #getVideodataApi(): Promise<BilibiliApiResponse> {
|
|
825
|
+
return new Promise(async(resolve,reject)=>{
|
|
826
|
+
let multi_embed_player_class_usable = false;
|
|
827
|
+
try{
|
|
828
|
+
if(typeof multi_embed_player !== 'undefined' && multi_embed_player.cors_proxy){};
|
|
829
|
+
multi_embed_player_class_usable = true;
|
|
830
|
+
}
|
|
831
|
+
catch{
|
|
832
|
+
multi_embed_player_class_usable = false;
|
|
833
|
+
}
|
|
834
|
+
let url = "";
|
|
835
|
+
if(multi_embed_player_class_usable){
|
|
836
|
+
url = (typeof multi_embed_player !== 'undefined') ? multi_embed_player.cors_proxy : '';
|
|
837
|
+
}
|
|
838
|
+
else{
|
|
839
|
+
url = (window as any).mep_bilibili.cors_proxy;
|
|
840
|
+
}
|
|
841
|
+
if(multi_embed_player_class_usable){
|
|
842
|
+
if(typeof multi_embed_player !== 'undefined' && !(this.videoid in multi_embed_player.api_cache.bilibili)){
|
|
843
|
+
if(typeof multi_embed_player_fetch_iframe_api !== 'undefined'){
|
|
844
|
+
await multi_embed_player_fetch_iframe_api("bilibili",this.videoid,multi_embed_player.cors_proxy!=="",true,false,true,this.player);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if(typeof multi_embed_player !== 'undefined'){
|
|
848
|
+
resolve(multi_embed_player.api_cache.bilibili[this.videoid]);
|
|
849
|
+
} else {
|
|
850
|
+
reject('multi_embed_player not available');
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
else{
|
|
854
|
+
if(!(this.videoid in (window as any).mep_bilibili.bilibili_api_cache)){
|
|
855
|
+
if((window as any).mep_bilibili.bilibili_api_promise[this.videoid]==undefined){
|
|
856
|
+
(window as any).mep_bilibili.bilibili_api_promise[this.videoid] = {res:[],rej:[]};
|
|
857
|
+
try{
|
|
858
|
+
if((window as any).mep_bilibili.cors_proxy===""){
|
|
859
|
+
(window as any).mep_bilibili.bilibili_api_cache[this.videoid] = await(await fetch(`${(window as any).mep_bilibili.api_endpoint}?route=bilibili&videoid=${this.videoid}&image_base64=1`)).json();
|
|
860
|
+
}
|
|
861
|
+
else{
|
|
862
|
+
let json_response_bilibili = await(await fetch(url + `https://api.bilibili.com/x/web-interface/view?bvid=${this.videoid}`)).json();
|
|
863
|
+
if(json_response_bilibili?.data?.pic===undefined){
|
|
864
|
+
json_response_bilibili["image_base64"] = null;
|
|
865
|
+
}
|
|
866
|
+
else{
|
|
867
|
+
json_response_bilibili["image_base64"] = url + json_response_bilibili.data.pic;
|
|
868
|
+
}
|
|
869
|
+
(window as any).mep_bilibili.bilibili_api_cache[this.videoid] = json_response_bilibili;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
catch{
|
|
873
|
+
console.error("error occured when get bilibili api. Are you sure you overwrite iframe_api endpoint? or cors proxy is not working?");
|
|
874
|
+
this.front_error_code = 1;
|
|
875
|
+
this.player.dispatchEvent(new CustomEvent("onError",{detail:{code:1100}}));
|
|
876
|
+
}
|
|
877
|
+
(window as any).mep_bilibili.bilibili_api_promise[this.videoid].res.forEach((resolve: (value: BilibiliApiResponse) => void)=>{resolve((window as any).mep_bilibili.bilibili_api_cache[this.videoid])});
|
|
878
|
+
resolve((window as any).mep_bilibili.bilibili_api_cache[this.videoid]);
|
|
879
|
+
}
|
|
880
|
+
else{
|
|
881
|
+
(window as any).mep_bilibili.bilibili_api_promise[this.videoid].res.push(resolve);
|
|
882
|
+
(window as any).mep_bilibili.bilibili_api_promise[this.videoid].rej.push(reject);
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
else{
|
|
887
|
+
resolve((window as any).mep_bilibili.bilibili_api_cache[this.videoid]);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Get the duration of the video.
|
|
895
|
+
* @async
|
|
896
|
+
* @returns {Promise<number>} The duration of the video in seconds.
|
|
897
|
+
*/
|
|
898
|
+
async getDuration(): Promise<number> {
|
|
899
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
900
|
+
let videodata_api = await this.#getVideodataApi();
|
|
901
|
+
return videodata_api["data"]?.["duration"] || 0;
|
|
902
|
+
}
|
|
903
|
+
else{
|
|
904
|
+
return this.state.dulation || 0
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Gets the title of the video.
|
|
910
|
+
* @returns {Promise<string>} The title of the video.
|
|
911
|
+
*/
|
|
912
|
+
async getTitle(): Promise<string> {
|
|
913
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
914
|
+
let videodata_api = await this.#getVideodataApi();
|
|
915
|
+
return videodata_api["data"]?.["title"] || "";
|
|
916
|
+
}
|
|
917
|
+
else{
|
|
918
|
+
return this.state.getTitle || ""
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Returns the current state of the player.
|
|
923
|
+
* @async
|
|
924
|
+
* @returns {number} The state of the player. Possible values are:
|
|
925
|
+
* 0 - Player is not ready or cache is not available.
|
|
926
|
+
* 1 - Player is ready and not playing.
|
|
927
|
+
* 2 - Player is playing.
|
|
928
|
+
* 3 - Player is paused.
|
|
929
|
+
* 4 - Player was ended.
|
|
930
|
+
*/
|
|
931
|
+
async getPlayerState(): Promise<number> {
|
|
932
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
933
|
+
const realDulationCache = await this.getRealDulation();
|
|
934
|
+
const currentTimeCahce = await this.getCurrentTime();
|
|
935
|
+
if(this.loading||currentTimeCahce==undefined||realDulationCache==undefined||Number.isNaN(realDulationCache)){
|
|
936
|
+
return 0//1のほうが適切かもしれない
|
|
937
|
+
}
|
|
938
|
+
else if(this.innerStartSeconds==currentTimeCahce){
|
|
939
|
+
return 1
|
|
940
|
+
}
|
|
941
|
+
else if(((currentTimeCahce - this.startSeconds)/realDulationCache)>0.99){
|
|
942
|
+
return 4
|
|
943
|
+
}
|
|
944
|
+
else if(this.no_extention_pause){
|
|
945
|
+
return 3
|
|
946
|
+
}
|
|
947
|
+
else if(((currentTimeCahce - this.startSeconds)/realDulationCache)<0.99){//再生中の可能性大
|
|
948
|
+
return 2
|
|
949
|
+
}
|
|
950
|
+
else{
|
|
951
|
+
return -2
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
else{
|
|
955
|
+
if(this.state.getPlayerState!=undefined){
|
|
956
|
+
if(this.state.getPlayerState=="READY"){
|
|
957
|
+
return 1
|
|
958
|
+
}
|
|
959
|
+
else if(this.state.getPlayerState=="PLAYING"){
|
|
960
|
+
return 2
|
|
961
|
+
}
|
|
962
|
+
else if(this.state.getPlayerState=="PAUSED"){
|
|
963
|
+
let current_duration = ((this.getCurrentTime()) / ((await this.getDuration()) - this.endSeconds))*100//%で出す
|
|
964
|
+
if(current_duration>99){
|
|
965
|
+
return 4
|
|
966
|
+
}
|
|
967
|
+
else{
|
|
968
|
+
return 3
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return 0; // Default return value
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
/**
|
|
976
|
+
* Sets the volume of the Bilibili player.
|
|
977
|
+
* When not install extention, this function will not work.
|
|
978
|
+
* @param {number} volume - The volume level to set, between 0 and 100.
|
|
979
|
+
*/
|
|
980
|
+
setVolume(volume: number): void {
|
|
981
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
982
|
+
console.warn((window as any).mep_bilibili.no_extention_error);
|
|
983
|
+
}
|
|
984
|
+
else{
|
|
985
|
+
(this.player as HTMLIFrameElement).contentWindow?.postMessage({eventName:"setVolume",volume:Number(volume/100)},"*");//100で割って差をなくす
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Gets the current volume of the Bilibili player.
|
|
990
|
+
* When not install extention, this function will not work.
|
|
991
|
+
* @returns {number} The current volume value.
|
|
992
|
+
*/
|
|
993
|
+
getVolume(): number | undefined {
|
|
994
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
995
|
+
console.warn((window as any).mep_bilibili.no_extention_error);
|
|
996
|
+
return undefined;
|
|
997
|
+
}
|
|
998
|
+
else{
|
|
999
|
+
return this.state.volumeValue
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Checks if the player is currently muted.
|
|
1004
|
+
* When not install extention, this function will not work.
|
|
1005
|
+
* @returns {boolean} True if the player is muted, false otherwise.
|
|
1006
|
+
*/
|
|
1007
|
+
isMuted(): boolean | undefined {
|
|
1008
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
1009
|
+
console.warn((window as any).mep_bilibili.no_extention_error);
|
|
1010
|
+
return undefined;
|
|
1011
|
+
}
|
|
1012
|
+
else{
|
|
1013
|
+
if(this.getVolume()!=0){
|
|
1014
|
+
return false
|
|
1015
|
+
}
|
|
1016
|
+
else{
|
|
1017
|
+
return true
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Mutes the player by setting the volume to 0.
|
|
1023
|
+
* When not install extention, this function will not work.
|
|
1024
|
+
* @function
|
|
1025
|
+
* @returns {void}
|
|
1026
|
+
*/
|
|
1027
|
+
mute(): void {
|
|
1028
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
1029
|
+
console.warn((window as any).mep_bilibili.no_extention_error);
|
|
1030
|
+
}
|
|
1031
|
+
else{
|
|
1032
|
+
this.before_mute_volume = this.getVolume() || 0;
|
|
1033
|
+
this.setVolume(0);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Unmutes the player by setting the volume to the previous volume.
|
|
1038
|
+
* When not install extention, this function will not work.
|
|
1039
|
+
* @function
|
|
1040
|
+
* @returns {void}
|
|
1041
|
+
*/
|
|
1042
|
+
unMute(): void {
|
|
1043
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
1044
|
+
console.warn((window as any).mep_bilibili.no_extention_error);
|
|
1045
|
+
}
|
|
1046
|
+
else{
|
|
1047
|
+
this.setVolume(this.before_mute_volume);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
/**
|
|
1051
|
+
* Sends a message to the player's content window to display or hide comments.
|
|
1052
|
+
* When not install extention, this function will not work.
|
|
1053
|
+
* @param {string} mode - The visibility mode of the comments. Possible values are "visible" or "hidden".
|
|
1054
|
+
* @returns {void}
|
|
1055
|
+
*/
|
|
1056
|
+
displayComment(mode: string): void {
|
|
1057
|
+
if(!(window as any).mep_bilibili.mep_extension_bilibili){
|
|
1058
|
+
console.warn((window as any).mep_bilibili.no_extention_error);
|
|
1059
|
+
}
|
|
1060
|
+
else{
|
|
1061
|
+
(this.player as HTMLIFrameElement).contentWindow?.postMessage({eventName:"displayComment",commentVisibility:mode},"*");
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Listens to messages from the iframe and updates the player state accordingly.
|
|
1066
|
+
* @private
|
|
1067
|
+
* This function only run multi embed player extension installed.
|
|
1068
|
+
*/
|
|
1069
|
+
#messageListener(): void {
|
|
1070
|
+
this.start_event_count = 0;
|
|
1071
|
+
this.end_event_count = 0;
|
|
1072
|
+
window.addEventListener("message",(data)=>{
|
|
1073
|
+
if(data.data.type=="data_change"){
|
|
1074
|
+
this.state = Object.assign(this.state,data.data);
|
|
1075
|
+
if(this.start_event_count==0&&data.data.dulation>0){
|
|
1076
|
+
this.start_event_count = 1;
|
|
1077
|
+
this.player.dispatchEvent(new Event("onReady"));
|
|
1078
|
+
}
|
|
1079
|
+
if(this.end_event_count==0&&data.data.dulation>data.data.currentTime-1&&data.data.getPlayerState=="PAUSED"){
|
|
1080
|
+
this.player.dispatchEvent(new Event("onEndVideo"));
|
|
1081
|
+
this.end_event_count = 1;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
else if(data.data.type=="error"){
|
|
1085
|
+
this.front_error_code = 4;
|
|
1086
|
+
this.player.dispatchEvent(new CustomEvent("onError",{detail:{code:500}}));
|
|
1087
|
+
}
|
|
1088
|
+
})
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
// after loading a class, call the function to set the variable before use this class.
|
|
1092
|
+
if(typeof multi_embed_player_set_variable !== 'undefined' && typeof multi_embed_player_set_variable === "function"){
|
|
1093
|
+
multi_embed_player_set_variable(mep_bilibili);
|
|
1094
|
+
}
|
|
1095
|
+
(window as any).mep_bilibili = mep_bilibili;
|