This commit is contained in:
Harvey Zack committed 2024-08-31 11:47:28 +08:00
commit c7299b926e
62 files changed
+10765 -705

No files matched your search

@@ -495,6 +495,7 @@ export default class Danmuku {
// 获取准备好发送的弹幕,可能包含ready和wait状态的弹幕
const readys = this.readys;
for (let index = 0; index < readys.length; index++) {
const danmu = readys[index];
+25 -22
View File
@@ -1,24 +1,27 @@
{
"license": "MIT",
"version": "1.0.0",
"name": "artplayer-plugin-vast",
"types": "types/artplayer-plugin-vast.d.ts",
"description": "vast plugin for ArtPlayer",
"main": "dist/artplayer-plugin-vast.js",
"legacy": "dist/artplayer-plugin-vast.legacy.js",
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
"browserslist": "last 1 Chrome version",
"homepage": "https://artplayer.org",
"repository": {
"type": "git",
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
},
"bugs": {
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
},
"keywords": [
"html5",
"video",
"player"
]
"license": "MIT",
"version": "1.0.0",
"name": "artplayer-plugin-vast",
"types": "types/artplayer-plugin-vast.d.ts",
"description": "vast plugin for ArtPlayer",
"main": "dist/artplayer-plugin-vast.js",
"legacy": "dist/artplayer-plugin-vast.legacy.js",
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
"browserslist": "last 1 Chrome version",
"homepage": "https://artplayer.org",
"repository": {
"type": "git",
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
},
"bugs": {
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
},
"keywords": [
"html5",
"video",
"player"
],
"dependencies": {
"@glomex/vast-ima-player": "^1.21.0"
}
}
+45 -16
View File
@@ -1,26 +1,55 @@
import style from 'bundle-text:./style.less';
import { Player, PlayerOptions, loadImaSdk } from '@glomex/vast-ima-player';
export default function artplayerPluginVast(callback) {
return async (art) => {
const { template, constructor } = art;
const { createElement, setStyles, append } = constructor.utils;
const { $video, $player } = template;
await loadImaSdk();
const google = window.google;
const ima = google.ima;
const adsRenderingSettings = new ima.AdsRenderingSettings();
const playerOptions = new PlayerOptions();
const id = `art-${Date.now()}`;
const $container = createElement('div');
append($player, $container);
$container.id = id;
setStyles($container, {
position: 'absolute',
inset: '0',
width: '100%',
height: '100%',
zIndex: '150',
display: 'none',
});
const imaPlayer = new Player(ima, $video, $container, adsRenderingSettings, playerOptions);
function playUrl(url) {
const playAdsRequest = new ima.AdsRequest();
playAdsRequest.adTagUrl = url;
imaPlayer.playAds(playAdsRequest);
}
function playRes(res) {
const playAdsRequest = new ima.AdsRequest();
playAdsRequest.adsResponse = res;
imaPlayer.playAds(playAdsRequest);
}
if (typeof callback === 'function') {
await callback({ art, id, ima, imaPlayer, $container, playUrl, playRes });
}
export default function artplayerPluginVast(option) {
return (art) => {
return {
name: 'artplayerPluginVast',
};
};
}
if (typeof document !== 'undefined') {
const id = 'artplayer-plugin-vast';
const $style = document.getElementById(id);
if ($style) {
$style.textContent = style;
} else {
const $style = document.createElement('style');
$style.id = id;
$style.textContent = style;
document.head.appendChild($style);
}
}
if (typeof window !== 'undefined') {
window['artplayerPluginVast'] = artplayerPluginVast;
}
@@ -1,3 +0,0 @@
.artplayer-plugin-vast {
//
}
@@ -3,9 +3,15 @@ import type Artplayer from 'artplayer';
export = artplayerPluginVast;
export as namespace artplayerPluginVast;
type Option = {
//
};
type Option = (params: {
art: Artplayer;
id: string;
ima: any;
imaPlayer: any;
$container: HTMLDivElement;
playUrl: (url: string) => void;
playRes: (res: string) => void;
}) => void;
type Result = {
name: 'artplayerPluginVast';
@@ -1,11 +0,0 @@
# artplayer-proxy-libmedia
libmedia proxy for ArtPlayer
## Demo
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-proxy-libmedia/index.js&example=libmedia)
## License
MIT © Harvey Zack
@@ -1,24 +0,0 @@
{
"license": "MIT",
"version": "1.0.0",
"name": "artplayer-proxy-libmedia",
"types": "types/artplayer-proxy-libmedia.d.ts",
"description": "libmedia proxy for ArtPlayer",
"main": "dist/artplayer-proxy-libmedia.js",
"legacy": "dist/artplayer-proxy-libmedia.legacy.js",
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
"browserslist": "last 1 Chrome version",
"homepage": "https://artplayer.org",
"repository": {
"type": "git",
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
},
"bugs": {
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
},
"keywords": [
"html5",
"video",
"player"
]
}
@@ -1,9 +0,0 @@
export default function artplayerProxyLibmedia(option) {
return (art) => {
return document.createElement('video');
};
}
if (typeof window !== 'undefined') {
window['artplayerProxyLibmedia'] = artplayerProxyLibmedia;
}
@@ -1,14 +0,0 @@
import type Artplayer from 'artplayer';
export = artplayerProxyLibmedia;
export as namespace artplayerProxyLibmedia;
type Option = {
//
};
type Result = {
name: 'artplayerProxyLibmedia';
};
declare const artplayerProxyLibmedia: (option: Option) => (art: Artplayer) => Result;
@@ -0,0 +1,8 @@
/*!
* artplayer-proxy-webav.js v1.0.0
* Github: https://github.com/zhw2590582/ArtPlayer
* (c) 2017-2024 Harvey Zack
* Released under the MIT License.
*/
!function(t,e,r,s,n){var a="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},o="function"==typeof a[s]&&a[s],h=o.cache||{},l="undefined"!=typeof module&&"function"==typeof module.require&&module.require.bind(module);function u(e,r){if(!h[e]){if(!t[e]){var n="function"==typeof a[s]&&a[s];if(!r&&n)return n(e,!0);if(o)return o(e,!0);if(l&&"string"==typeof e)return l(e);var d=Error("Cannot find module '"+e+"'");throw d.code="MODULE_NOT_FOUND",d}p.resolve=function(r){var s=t[e][1][r];return null!=s?s:r},p.cache={};var f=h[e]=new u.Module(e);t[e][0].call(f.exports,p,f,f.exports,this)}return h[e].exports;function p(t){var e=p.resolve(t);return!1===e?{}:u(e)}}u.isParcelRequire=!0,u.Module=function(t){this.id=t,this.bundle=u,this.exports={}},u.modules=t,u.cache=h,u.parent=o,u.register=function(e,r){t[e]=[function(t,e){e.exports=r},{}]},Object.defineProperty(u,"root",{get:function(){return a[s]}}),a[s]=u;for(var d=0;d<e.length;d++)u(e[d]);if(r){var f=u(r);"object"==typeof exports&&"undefined"!=typeof module?module.exports=f:"function"==typeof define&&define.amd&&define(function(){return f})}}({jc2x7:[function(t,e,r){var s=t("@parcel/transformer-js/src/esmodule-helpers.js");s.defineInteropFlag(r),s.export(r,"default",()=>a);var n=t("@webav/av-cliper");function a(){return t=>{let e,r;let{option:s,constructor:a}=t,{createElement:o,def:h}=a.utils,l=o("canvas"),u=l.getContext("2d"),d=null,f=null,p=null,c=null,m=0,g={playing:!1,duration:0,videoWidth:0,videoHeight:0,currentTime:0,playbackRate:1,paused:!0,ended:!1,readyState:0,buffered:0,muted:s.muted,volume:s.volume,autoplay:s.autoplay};function y(){p&&(clearInterval(p),p=null),f&&(f.stop(),f=null)}function _(){if(r){let t=g.muted?0:g.volume;r.gain.setValueAtTime(t,e.currentTime)}}async function b(){e||(r=(e=new AudioContext).createGain()).connect(e.destination);let s=1e6*g.currentTime,n=0,a=!0,o=performance.now();async function h(){if(!g.playing)return;let h=performance.now(),l=h-o;o=h,null!==c?(s=1e6*c,c=null,a=!0):s+=1e3*l*g.playbackRate,g.currentTime=s/1e6;let{state:p,video:m,audio:_}=await d.tick(Math.round(s));if(t.emit("video:timeupdate",{type:"timeupdate"}),"done"===p){y(),g.ended=!0,g.playing=!1,g.paused=!0,t.emit("video:ended",{type:"ended"});return}if(m&&"success"===p&&(u.clearRect(0,0,g.videoWidth,g.videoHeight),u.drawImage(m,0,0,g.videoWidth,g.videoHeight),m.close()),a)a=!1;else if(_?.[0]?.length){let t=e.createBuffer(2,_[0].length,48e3);t.copyToChannel(_[0],0),t.copyToChannel(_[1],1),(f=e.createBufferSource()).buffer=t,f.connect(r),f.playbackRate.setValueAtTime(g.playbackRate,e.currentTime),n=Math.max(e.currentTime,n),f.start(n),n+=t.duration/g.playbackRate}}y(),_(),g.playing=!0,g.paused=!1,p=setInterval(h,1e3/60)}async function w(t){let{video:e}=await d.tick(1e6*t);e&&(u.clearRect(0,0,l.width,l.height),u.drawImage(e,0,0,l.width,l.height),e.close())}function v(){let e=t.template?.$player;if(!e||s.autoSize)return;let r=l.videoWidth/l.videoHeight,n=e.clientWidth,a=e.clientHeight,o=0,h=0;n/a>r?o=(n-a*r)/2:h=(a-n/r)/2,Object.assign(l.style,{padding:`${h}px ${o}px`})}async function S(){if(!await (0,n.Combinator).isSupported())throw t.notice.show="WebAV is not supported",Error("WebAV is not supported");y(),Object.assign(g,{playing:!1,duration:0,videoWidth:0,videoHeight:0,currentTime:0,playbackRate:1,paused:!0,ended:!1,readyState:0,buffered:0,muted:s.muted,volume:s.volume,autoplay:s.autoplay}),d&&(d.destroy(),t.emit("video:abort",{type:"abort"}),t.emit("video:emptied",{type:"emptied"}));try{await Promise.resolve(),t.emit("video:loadstart",{type:"loadstart"});let e=await fetch(s.url);if(!e.body)throw Error("No response body");d=new n.MP4Clip(e.body)}catch(e){throw t.emit("video:error",e),e}let e=await d.ready;Object.assign(g,{readyState:4,duration:Math.round(e.duration/1e6),videoWidth:e.width,videoHeight:e.height}),l.width=g.videoWidth,l.height=g.videoHeight,await w(.1),v(),t.emit("video:loadedmetadata",{type:"loadedmetadata"}),t.emit("video:durationchange",{type:"durationchange"}),t.emit("video:loadeddata",{type:"loadeddata"}),t.emit("video:canplay",{type:"canplay"}),t.emit("video:canplaythrough",{type:"canplaythrough"})}return h(l,"textTracks",{get:()=>[]}),h(l,"duration",{get:()=>g.duration}),h(l,"videoWidth",{get:()=>g.videoWidth}),h(l,"videoHeight",{get:()=>g.videoHeight}),h(l,"volume",{get:()=>g.volume,set:e=>{g.volume=Math.max(0,Math.min(1,e)),_(),t.emit("video:volumechange",{type:"volumechange"})}}),h(l,"currentTime",{get:()=>g.currentTime,set:e=>{let r=Math.max(0,Math.min(e,g.duration)),s=performance.now();s-m>16&&(m=s,c=r,g.currentTime=r,g.playing||w(r),t.emit("video:timeupdate",{type:"timeupdate"}))}}),h(l,"autoplay",{get:()=>g.autoplay,set:t=>{g.autoplay=t,t&&g.readyState>=4&&l.play()}}),h(l,"src",{get:()=>s.url,set:t=>{s.url=t,S().then(()=>{s.autoplay&&l.play()})}}),h(l,"playbackRate",{get:()=>g.playbackRate,set:r=>{g.playbackRate=Math.max(.25,Math.min(2,r)),f&&fLine truncated
@@ -0,0 +1,8 @@
/*!
* artplayer-proxy-webav.js v1.0.0
* Github: https://github.com/zhw2590582/ArtPlayer
* (c) 2017-2024 Harvey Zack
* Released under the MIT License.
*/
!function(t,e,r,n,s){var a="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:"undefined"!=typeof global?global:{},o="function"==typeof a[n]&&a[n],u=o.cache||{},l="undefined"!=typeof module&&"function"==typeof module.require&&module.require.bind(module);function c(e,r){if(!u[e]){if(!t[e]){var s="function"==typeof a[n]&&a[n];if(!r&&s)return s(e,!0);if(o)return o(e,!0);if(l&&"string"==typeof e)return l(e);var h=Error("Cannot find module '"+e+"'");throw h.code="MODULE_NOT_FOUND",h}d.resolve=function(r){var n=t[e][1][r];return null!=n?n:r},d.cache={};var f=u[e]=new c.Module(e);t[e][0].call(f.exports,d,f,f.exports,this)}return u[e].exports;function d(t){var e=d.resolve(t);return!1===e?{}:c(e)}}c.isParcelRequire=!0,c.Module=function(t){this.id=t,this.bundle=c,this.exports={}},c.modules=t,c.cache=u,c.parent=o,c.register=function(e,r){t[e]=[function(t,e){e.exports=r},{}]},Object.defineProperty(c,"root",{get:function(){return a[n]}}),a[n]=c;for(var h=0;h<e.length;h++)c(e[h]);if(r){var f=c(r);"object"==typeof exports&&"undefined"!=typeof module?module.exports=f:"function"==typeof define&&define.amd&&define(function(){return f})}}({c4SNP:[function(t,e,r){var n=t("@parcel/transformer-js/src/esmodule-helpers.js");n.defineInteropFlag(r),n.export(r,"default",function(){return u});var s=t("@swc/helpers/_/_async_to_generator"),a=t("@swc/helpers/_/_ts_generator"),o=t("@webav/av-cliper");function u(){return function(t){var e,r,n=t.option,u=t.constructor.utils,l=u.createElement,c=u.def,h=l("canvas"),f=h.getContext("2d"),d=null,p=null,m=null,_=null,y=0,g={playing:!1,duration:0,videoWidth:0,videoHeight:0,currentTime:0,playbackRate:1,paused:!0,ended:!1,readyState:0,buffered:0,muted:n.muted,volume:n.volume,autoplay:n.autoplay};function v(){m&&(clearInterval(m),m=null),p&&(p.stop(),p=null)}function b(){if(r){var t=g.muted?0:g.volume;r.gain.setValueAtTime(t,e.currentTime)}}function w(){return(w=(0,s._)(function(){var n,o,u,l;function c(){return h.apply(this,arguments)}function h(){return(h=(0,s._)(function(){var s,c,h,m,y,b,w,S;return(0,a._)(this,function(a){switch(a.label){case 0:if(!g.playing)return[2];return h=(c=performance.now())-l,l=c,null!==_?(n=1e6*_,_=null,u=!0):n+=1e3*h*g.playbackRate,g.currentTime=n/1e6,[4,d.tick(Math.round(n))];case 1:if(y=(m=a.sent()).state,b=m.video,w=m.audio,t.emit("video:timeupdate",{type:"timeupdate"}),"done"===y)return v(),g.ended=!0,g.playing=!1,g.paused=!0,t.emit("video:ended",{type:"ended"}),[2];return b&&"success"===y&&(f.clearRect(0,0,g.videoWidth,g.videoHeight),f.drawImage(b,0,0,g.videoWidth,g.videoHeight),b.close()),u?u=!1:(null==w?void 0:null===(s=w[0])||void 0===s?void 0:s.length)&&((S=e.createBuffer(2,w[0].length,48e3)).copyToChannel(w[0],0),S.copyToChannel(w[1],1),(p=e.createBufferSource()).buffer=S,p.connect(r),p.playbackRate.setValueAtTime(g.playbackRate,e.currentTime),o=Math.max(e.currentTime,o),p.start(o),o+=S.duration/g.playbackRate),[2]}})})).apply(this,arguments)}return(0,a._)(this,function(t){return e||(r=(e=new AudioContext).createGain()).connect(e.destination),n=1e6*g.currentTime,o=0,u=!0,l=performance.now(),v(),b(),g.playing=!0,g.paused=!1,m=setInterval(c,1e3/60),[2]})})).apply(this,arguments)}function S(t){return x.apply(this,arguments)}function x(){return(x=(0,s._)(function(t){var e;return(0,a._)(this,function(r){switch(r.label){case 0:return[4,d.tick(1e6*t)];case 1:return(e=r.sent().video)&&(f.clearRect(0,0,h.width,h.height),f.drawImage(e,0,0,h.width,h.height),e.close()),[2]}})})).apply(this,arguments)}function E(){var e,r=null===(e=t.template)||void 0===e?void 0:e.$player;if(r&&!n.autoSize){var s=h.videoWidth/h.videoHeight,a=r.clientWidth,o=r.clientHeight,u=0,l=0;a/o>s?u=(a-o*s)/2:l=(o-a/s)/2,Object.assign(h.style,{padding:"".concat(l,"px ").concat(u,"px")})}}function U(){return(U=(0,s._)(function(){var e,r,s;return(0,a._)(this,function(a){switch(a.label){case 0:return[4,(0,o.Combinator).isSupported()];case 1:if(!a.sent())throw t.notice.show="WebAV is not supported",Error("WebAV is not supported");v(),Object.assign(g,{playing:!1,duration:0,videoWidth:0,videoHeight:0,currentTime:0,playbackRate:1,paused:!0,ended:!1,readyState:0,buffered:0,muted:n.muted,volume:n.volume,autoplay:n.autoplay}),d&&(d.destroy(),t.emit("video:abort",{type:"abort"}),t.emit("video:emptied",{type:"emptied"})),a.label=2;case 2:return a.trys.push([2,5,,6]),[4,Promise.resolve()];case 3:return a.sent(),t.emit("video:loadstart",{type:"loadstart"}),[4,fetch(n.url)];case 4:if(!(e=a.sent()).body)throw Error("No response body");return d=new o.MP4Clip(e.body),[3,6];case 5:throw r=a.sent(),t.emit("video:error",r),r;case 6:return[4,d.ready];case 7:return Object.assign(g,{readyState:4,duration:Math.round((s=a.sent()).duration/1e6),videoWidth:s.width,videoHeight:s.height}),h.width=g.videoWidth,h.height=g.videoHeight,[4,S(.1)];case 8:return a.sent(),E(),t.emit("video:loadedmetadata",{type:"loadedmetadata"}),t.emit("video:durationchange",{type:"durationchange"})Line truncated
+26 -23
View File
@@ -1,24 +1,27 @@
{
"license": "MIT",
"version": "1.0.0",
"name": "artplayer-proxy-webav",
"types": "types/artplayer-proxy-webav.d.ts",
"description": "webav proxy for ArtPlayer",
"main": "dist/artplayer-proxy-webav.js",
"legacy": "dist/artplayer-proxy-webav.legacy.js",
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
"browserslist": "last 1 Chrome version",
"homepage": "https://artplayer.org",
"repository": {
"type": "git",
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
},
"bugs": {
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
},
"keywords": [
"html5",
"video",
"player"
]
}
"license": "MIT",
"version": "1.0.0",
"name": "artplayer-proxy-webav",
"types": "types/artplayer-proxy-webav.d.ts",
"description": "webav proxy for ArtPlayer",
"main": "dist/artplayer-proxy-webav.js",
"legacy": "dist/artplayer-proxy-webav.legacy.js",
"author": "Harvey Zack <laozhaochaguan@gmail.com>",
"browserslist": "last 1 Chrome version",
"homepage": "https://artplayer.org",
"repository": {
"type": "git",
"url": "git+https://github.com/zhw2590582/ArtPlayer.git"
},
"bugs": {
"url": "https://github.com/zhw2590582/ArtPlayer/issues"
},
"keywords": [
"html5",
"video",
"player"
],
"dependencies": {
"@webav/av-cliper": "^0.14.14"
}
}
+365 -2
View File
@@ -1,6 +1,369 @@
export default function artplayerProxyWebAV(option) {
import { MP4Clip, Combinator } from '@webav/av-cliper';
export default function artplayerProxyWebAV(opt = {}) {
return (art) => {
return document.createElement('video');
const { option, constructor } = art;
const { createElement, def } = constructor.utils;
const canvas = createElement('canvas');
const ctx = canvas.getContext('2d');
let audioCtx;
let gainNode;
let clip = null;
let audioSource = null;
let intervalId = null;
let seekTarget = null;
let lastSeekTime = 0;
const state = {
playing: false,
duration: 0,
videoWidth: 0,
videoHeight: 0,
currentTime: 0,
playbackRate: 1,
paused: true,
ended: false,
readyState: 0,
buffered: 0,
muted: option.muted,
volume: option.volume,
autoplay: option.autoplay,
};
function reset() {
Object.assign(state, {
playing: false,
duration: 0,
videoWidth: 0,
videoHeight: 0,
currentTime: 0,
playbackRate: 1,
paused: true,
ended: false,
readyState: 0,
buffered: 0,
muted: option.muted,
volume: option.volume,
autoplay: option.autoplay,
});
}
function stop() {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
if (audioSource) {
audioSource.stop();
audioSource = null;
}
}
function updateVolume() {
if (gainNode) {
const effectiveVolume = state.muted ? 0 : state.volume;
gainNode.gain.setValueAtTime(effectiveVolume, audioCtx.currentTime);
}
}
async function play() {
if (!audioCtx) {
audioCtx = new AudioContext();
gainNode = audioCtx.createGain();
gainNode.connect(audioCtx.destination);
}
let curTime = state.currentTime * 1e6;
let startAt = 0;
let first = true;
let lastFrameTime = performance.now();
stop();
updateVolume();
async function frameHandler() {
if (!state.playing) return;
const currentFrameTime = performance.now();
const deltaTime = currentFrameTime - lastFrameTime;
lastFrameTime = currentFrameTime;
if (seekTarget !== null) {
curTime = seekTarget * 1e6;
seekTarget = null;
first = true;
} else {
curTime += deltaTime * 1000 * state.playbackRate;
}
state.currentTime = curTime / 1e6;
const { state: clipState, video, audio } = await clip.tick(Math.round(curTime));
art.emit('video:timeupdate', { type: 'timeupdate' });
if (clipState === 'done') {
stop();
state.ended = true;
state.playing = false;
state.paused = true;
art.emit('video:ended', { type: 'ended' });
return;
}
if (video && clipState === 'success') {
ctx.clearRect(0, 0, state.videoWidth, state.videoHeight);
ctx.drawImage(video, 0, 0, state.videoWidth, state.videoHeight);
video.close();
}
if (first) {
first = false;
} else if (audio?.[0]?.length) {
const buf = audioCtx.createBuffer(2, audio[0].length, 48000);
buf.copyToChannel(audio[0], 0);
buf.copyToChannel(audio[1], 1);
audioSource = audioCtx.createBufferSource();
audioSource.buffer = buf;
audioSource.connect(gainNode);
audioSource.playbackRate.setValueAtTime(state.playbackRate, audioCtx.currentTime);
startAt = Math.max(audioCtx.currentTime, startAt);
audioSource.start(startAt);
startAt += buf.duration / state.playbackRate;
}
}
state.playing = true;
state.paused = false;
intervalId = setInterval(frameHandler, 1000 / 60); // 约60fps
}
async function preview(time) {
const { video } = await clip.tick(time * 1e6);
if (video) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
video.close();
}
}
function resize() {
const player = art.template?.$player;
if (!player || option.autoSize) return;
const aspectRatio = canvas.videoWidth / canvas.videoHeight;
const containerWidth = player.clientWidth;
const containerHeight = player.clientHeight;
const containerRatio = containerWidth / containerHeight;
let paddingLeft = 0;
let paddingTop = 0;
if (containerRatio > aspectRatio) {
const canvasWidth = containerHeight * aspectRatio;
paddingLeft = (containerWidth - canvasWidth) / 2;
} else {
const canvasHeight = containerWidth / aspectRatio;
paddingTop = (containerHeight - canvasHeight) / 2;
}
Object.assign(canvas.style, {
padding: `${paddingTop}px ${paddingLeft}px`,
});
}
async function init() {
const isSupported = await Combinator.isSupported();
if (!isSupported) {
art.notice.show = 'WebAV is not supported';
throw new Error('WebAV is not supported');
}
stop();
reset();
if (clip) {
clip.destroy();
art.emit('video:abort', { type: 'abort' });
art.emit('video:emptied', { type: 'emptied' });
}
try {
await Promise.resolve();
state.readyState = 1;
art.emit('video:loadstart', { type: 'loadstart' });
const response = await fetch(option.url);
if (!response.body) {
throw new Error('No response body');
}
clip = new MP4Clip(response.body, opt);
} catch (error) {
state.readyState = 0;
art.emit('video:error', error);
throw error;
}
const info = await clip.ready;
Object.assign(state, {
readyState: 4,
duration: Math.round(info.duration / 1e6),
videoWidth: info.width,
videoHeight: info.height,
});
canvas.width = state.videoWidth;
canvas.height = state.videoHeight;
await preview(0.1);
resize();
art.emit('video:loadedmetadata', { type: 'loadedmetadata' });
art.emit('video:durationchange', { type: 'durationchange' });
art.emit('video:loadeddata', { type: 'loadeddata' });
art.emit('video:canplay', { type: 'canplay' });
art.emit('video:canplaythrough', { type: 'canplaythrough' });
}
def(canvas, 'duration', {
get: () => state.duration,
});
def(canvas, 'videoWidth', {
get: () => state.videoWidth,
});
def(canvas, 'videoHeight', {
get: () => state.videoHeight,
});
def(canvas, 'volume', {
get: () => state.volume,
set: (val) => {
state.volume = Math.max(0, Math.min(1, val));
updateVolume();
art.emit('video:volumechange', { type: 'volumechange' });
},
});
def(canvas, 'currentTime', {
get: () => state.currentTime,
set: (val) => {
if (state.readyState < 4) return;
const newTime = Math.max(0, Math.min(val, state.duration));
const now = performance.now();
if (now - lastSeekTime > 16) {
lastSeekTime = now;
seekTarget = newTime;
state.currentTime = newTime;
if (!state.playing) {
preview(newTime);
}
art.emit('video:timeupdate', { type: 'timeupdate' });
}
},
});
def(canvas, 'autoplay', {
get: () => state.autoplay,
set: (val) => {
state.autoplay = val;
if (val && state.readyState >= 4) {
canvas.play();
}
},
});
def(canvas, 'src', {
get: () => option.url,
set: (val) => {
option.url = val;
init().then(() => {
if (option.autoplay) {
canvas.play();
}
});
},
});
def(canvas, 'playbackRate', {
get: () => state.playbackRate,
set: (val) => {
state.playbackRate = Math.max(0.25, Math.min(2, val));
if (audioSource) {
audioSource.playbackRate.setValueAtTime(state.playbackRate, audioCtx.currentTime);
}
art.emit('video:ratechange', { type: 'ratechange' });
},
});
def(canvas, 'playing', {
get: () => state.playing,
});
def(canvas, 'paused', {
get: () => state.paused,
});
def(canvas, 'ended', {
get: () => state.ended,
});
def(canvas, 'readyState', {
get: () => state.readyState,
});
def(canvas, 'muted', {
get: () => state.muted,
set: (val) => {
state.muted = val;
updateVolume();
art.emit('video:volumechange', { type: 'volumechange' });
},
});
def(canvas, 'buffered', {
get: () => ({
start: () => 0,
end: () => state.buffered,
length: 1,
}),
});
def(canvas, 'play', {
value: async () => {
if (state.readyState < 4) return false;
await play();
art.emit('video:play', { type: 'play' });
art.emit('video:playing', { type: 'playing' });
return true;
},
});
def(canvas, 'pause', {
value: () => {
stop();
state.playing = false;
state.paused = true;
art.emit('video:pause', { type: 'pause' });
},
});
art.on('destroy', () => {
stop();
if (clip) {
clip.destroy();
}
});
art.on('resize', resize);
return canvas;
};
}
@@ -7,8 +7,6 @@ type Option = {
//
};
type Result = {
name: 'artplayerProxyWebAV';
};
type Result = HTMLCanvasElement;
declare const artplayerProxyWebAV: (option: Option) => (art: Artplayer) => Result;
@@ -53,7 +53,6 @@ console.info(art.template.$video);
- `proxy` 方法用于代理 `DOM` 事件
- `hover` 方法用于代理自定义的 `hover` 事件
- `loadImg` 方法用于监听图片的 `load` 加载事件
<div className="run-code">▶ Run Code</div>
@@ -74,10 +73,6 @@ art.events.hover(container, (event) => {
}, (event) => {
console.info('mouseleave', event);
});
art.events.loadImg('/assets/sample/poster.jpg').then(img => {
console.info('loadImg', img);
});
```
:::warning 提示
@@ -52,7 +52,6 @@ Manages all `DOM` events in the player, which is essentially a proxy for `addEve
- The `proxy` method is used to proxy `DOM` events
- The `hover` method is used to proxy custom `hover` events
- The `loadImg` method is used to listen for the `load` event of images
<div className="run-code">▶ Run Code</div>
@@ -73,10 +72,6 @@ art.events.hover(container, (event) => {
}, (event) => {
console.info('mouseleave', event);
});
art.events.loadImg('/assets/sample/poster.jpg').then(img => {
console.info('loadImg', img);
});
```
:::warning Warning
@@ -412,13 +412,13 @@ var art = new Artplayer({
});
```
| Hotkey | Description |
| ------- | --------------------- |
| `↑` | Increase volume |
| `↓` | Decrease volume |
| `←` | Fast forward video |
| `→` | Rewind video |
| `space` | Toggle play/pause |
| Hotkey | Description |
| ------- | ------------------ |
| `↑` | Increase volume |
| `↓` | Decrease volume |
| `←` | Fast forward video |
| `→` | Rewind video |
| `space` | Toggle play/pause |
:::warning Tip
@@ -734,11 +734,11 @@ var art = new Artplayer({
Whether to show the `quality selection` list in the bottom control bar
| Property | Type | Description |
| ---------- | --------- | -------------- |
| `default` | `Boolean` | Default quality|
| `html` | `String` | Quality name |
| `url` | `String` | Quality URL |
| Property | Type | Description |
| --------- | --------- | --------------- |
| `default` | `Boolean` | Default quality |
| `html` | `String` | Quality name |
| `url` | `String` | Quality URL |
<div className="run-code">▶ Run Code</div>
@@ -766,10 +766,10 @@ var art = new Artplayer({
Show `highlight information` on the progress bar
| Property | Type | Description |
| -------- | -------- | -------------------- |
| `time` | `Number` | Highlight time (in seconds)|
| `text` | `String` | Highlight text |
| Property | Type | Description |
| -------- | -------- | --------------------------- |
| `time` | `Number` | Highlight time (in seconds) |
| `text` | `String` | Highlight text |
<div className="run-code">▶ Run Code</div>
@@ -836,13 +836,14 @@ var art = new Artplayer({
Set `thumbnails` on the progress bar
| Property | Type | Description |
| -------- | -------- | ------------------- |
| `url` | `String` | Thumbnail URL |
| `number` | `Number` | Number of thumbnails|
| `column` | `Number` | Columns of thumbnails|
| `width` | `Number` | Thumbnail width |
| `height` | `Number` | Thumbnail height |
| Property | Type | Description |
| -------- | -------- | --------------------- |
| `url` | `String` | Thumbnail URL |
| `number` | `Number` | Number of thumbnails |
| `column` | `Number` | Columns of thumbnails |
| `width` | `Number` | Thumbnail width |
| `height` | `Number` | Thumbnail height |
| `scale` | `Number` | Thumbnail scale |
<div className="run-code">▶ Run Code</div>
@@ -870,11 +871,11 @@ var art = new Artplayer({
Set the video subtitles, supporting subtitle formats: `vtt`, `srt`, `ass`
| Property | Type | Description |
| ----------- | ---------- | ----------------------------------- |
| `name` | `String` | Subtitle name |
| `url` | `String` | Subtitle URL |
| `type` | `String` | Subtitle type, options: `vtt`, `srt`, `ass` |
| Property | Type | Description |
| -------- | -------- | ------------------------------------------- |
| `name` | `String` | Subtitle name |
| `url` | `String` | Subtitle URL |
| `type` | `String` | Subtitle type, options: `vtt`, `srt`, `ass` |
| `style` | `Object` | Subtitle style |
| `encoding` | `String` | Subtitle encoding, default `utf-8` |
@@ -853,6 +853,7 @@ var art = new Artplayer({
| `column` | `Number` | 预览图列数 |
| `width` | `Number` | 预览图宽度 |
| `height` | `Number` | 预览图高度 |
| `scale` | `Number` | 预览图缩放 |
<div className="run-code">▶ Run Code</div>
@@ -883,7 +884,7 @@ var art = new Artplayer({
| 属性 | 类型 | 描述 |
| ----------- | ---------- | ----------------------------------- |
| `name` | `String` | 字幕名字 |
| `name` | `String` | 字幕名字 |
| `url` | `String` | 字幕地址 |
| `type` | `String` | 字幕类型,可选 `vtt`, `srt`, `ass` |
| `style` | `Object` | 字幕样式 |
-24
View File
@@ -1,4 +1,3 @@
import { ArtPlayerError } from '../utils/error';
import clickInit from './clickInit';
import hoverInit from './hoverInit';
import moveInit from './moveInit';
@@ -13,7 +12,6 @@ export default class Events {
this.destroyEvents = [];
this.proxy = this.proxy.bind(this);
this.hover = this.hover.bind(this);
this.loadImg = this.loadImg.bind(this);
clickInit(art, this);
hoverInit(art, this);
@@ -45,28 +43,6 @@ export default class Events {
}
}
loadImg(img) {
return new Promise((resolve, reject) => {
let image;
if (img instanceof HTMLImageElement) {
image = img;
} else if (typeof img === 'string') {
image = new Image();
image.src = img;
} else {
return reject(new ArtPlayerError('Unable to get Image'));
}
if (image.complete) {
return resolve(image);
}
this.proxy(image, 'load', () => resolve(image));
this.proxy(image, 'error', () => reject(new ArtPlayerError(`Failed to load Image: ${image.src}`)));
});
}
remove(destroyEvent) {
const index = this.destroyEvents.indexOf(destroyEvent);
if (index > -1) {
+1
View File
@@ -170,6 +170,7 @@ export default class Artplayer extends Emitter {
column: 10,
width: 0,
height: 0,
scale: 1,
},
subtitle: {
url: '',
+3 -1
View File
@@ -8,6 +8,8 @@ export default function seekMix(art) {
});
def(art, 'loadedTime', {
get: () => ($video.buffered.length ? $video.buffered.end($video.buffered.length - 1) : 0),
get: () => {
return $video.buffered.length ? $video.buffered.end($video.buffered.length - 1) : 0;
},
});
}
+4 -1
View File
@@ -3,6 +3,9 @@ import { def } from '../utils';
export default function playingMix(art) {
const { $video } = art.template;
def(art, 'playing', {
get: () => !!($video.currentTime > 0 && !$video.paused && !$video.ended && $video.readyState > 2),
get: () => {
if (typeof $video.playing === 'boolean') return $video.playing;
return !!($video.currentTime > 0 && !$video.paused && !$video.ended && $video.readyState > 2);
},
});
}
@@ -1,9 +1,8 @@
import { def, setStyle, isMobile } from '../utils';
import { def, setStyle, isMobile, loadImg } from '../utils';
export default function thumbnailsMix(art) {
const {
option,
events: { loadImg },
template: { $progress, $video },
} = art;
@@ -24,14 +23,14 @@ export default function thumbnailsMix(art) {
const $thumbnails = art.controls?.thumbnails;
if (!$thumbnails) return;
const { url, number, column, width, height } = option.thumbnails;
const width2 = width || image.naturalWidth / column;
const height2 = height || width2 / ($video.videoWidth / $video.videoHeight);
const { number, column, width, height, scale } = option.thumbnails;
const width2 = width * scale || image.naturalWidth / column;
const height2 = height * scale || width2 / ($video.videoWidth / $video.videoHeight);
const perWidth = $progress.clientWidth / number;
const perIndex = Math.floor(posWidth / perWidth);
const yIndex = Math.ceil(perIndex / column) - 1;
const xIndex = perIndex % column || column - 1;
setStyle($thumbnails, 'backgroundImage', `url(${url})`);
setStyle($thumbnails, 'backgroundImage', `url(${image.src})`);
setStyle($thumbnails, 'height', `${height2}px`);
setStyle($thumbnails, 'width', `${width2}px`);
setStyle($thumbnails, 'backgroundPosition', `-${xIndex * width2}px -${yIndex * height2}px`);
@@ -46,7 +45,7 @@ export default function thumbnailsMix(art) {
art.on('setBar', async (type, percentage, event) => {
const $thumbnails = art.controls?.thumbnails;
const { url } = option.thumbnails;
const { url, scale } = option.thumbnails;
if (!$thumbnails || !url) return;
const isMobileDroging = type === 'played' && event && isMobile;
@@ -54,7 +53,7 @@ export default function thumbnailsMix(art) {
if (type === 'hover' || isMobileDroging) {
if (!loading) {
loading = true;
image = await loadImg(url);
image = await loadImg(url, scale);
isLoad = true;
}
+13 -4
View File
@@ -22,6 +22,9 @@ export default function autoPlayback(art) {
const $last = query('.art-auto-playback-last', $autoPlayback);
const $jump = query('.art-auto-playback-jump', $autoPlayback);
const $close = query('.art-auto-playback-close', $autoPlayback);
append($close, icons.close);
let timer = null;
art.on('video:timeupdate', () => {
if (art.playing) {
@@ -35,11 +38,14 @@ export default function autoPlayback(art) {
}
});
art.on('ready', () => {
function init() {
const times = storage.get('times') || {};
const currentTime = times[art.option.id || art.option.url];
clearTimeout(timer);
setStyle($autoPlayback, 'display', 'none');
if (currentTime && currentTime >= constructor.AUTO_PLAYBACK_MIN) {
append($close, icons.close);
setStyle($autoPlayback, 'display', 'flex');
$last.innerText = `${i18n.get('Last Seen')} ${secondToTime(currentTime)}`;
@@ -57,12 +63,15 @@ export default function autoPlayback(art) {
});
art.once('video:timeupdate', () => {
setTimeout(() => {
timer = setTimeout(() => {
setStyle($autoPlayback, 'display', 'none');
}, constructor.AUTO_PLAYBACK_TIMEOUT);
});
}
});
}
art.on('ready', init);
art.on('restart', init);
return {
name: 'auto-playback',
+1
View File
@@ -104,6 +104,7 @@ export default {
column: n,
width: n,
height: n,
scale: n,
},
subtitle: {
url: s,
+3 -1
View File
@@ -42,10 +42,11 @@ export default class Subtitle extends Component {
}
get textTrack() {
return this.art.template.$video.textTracks[0];
return this.art.template.$video?.textTracks?.[0];
}
get activeCue() {
if (!this.textTrack) return null;
return this.textTrack.activeCues[0];
}
@@ -108,6 +109,7 @@ export default class Subtitle extends Component {
template: { $subtitle },
} = this.art;
if (!this.textTrack) return null;
validator(subtitleOption, scheme.subtitle);
if (!subtitleOption.url) return;
this.style(subtitleOption.style);
+41
View File
@@ -117,3 +117,44 @@ export function supportsFlex() {
export function getRect(el) {
return el.getBoundingClientRect();
}
export function loadImg(url, scale) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function () {
if (!scale || scale === 1) {
resolve(img);
} else {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width * scale;
canvas.height = img.height * scale;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob((blob) => {
const blobUrl = URL.createObjectURL(blob);
const scaledImg = new Image();
scaledImg.onload = function () {
resolve(scaledImg);
};
scaledImg.onerror = function () {
URL.revokeObjectURL(blobUrl);
reject(new Error(`Image load failed: ${url}`));
};
scaledImg.src = blobUrl;
});
}
};
img.onerror = function () {
reject(new Error(`Image load failed: ${url}`));
};
img.src = url;
});
}
-1
View File
@@ -118,7 +118,6 @@ declare class Artplayer extends Player {
options?: boolean | AddEventListenerOptions,
): () => void;
hover(element: HTMLElement, mouseenter?: (event: Event) => any, mouseleave?: (event: Event) => any): void;
loadImg(element: HTMLImageElement | string): Promise<HTMLImageElement>;
remove(event: Event): void;
};
+6 -1
View File
@@ -34,6 +34,11 @@ export type Thumbnails = {
* The thumbnail height
*/
height?: number;
/**
* The thumbnail scale
*/
scale?: number;
};
export type Option = {
@@ -210,7 +215,7 @@ export type Option = {
/**
* Custom video proxy
*/
proxy?: null | ((this: Artplayer, art: Artplayer) => HTMLCanvasElement | HTMLVideoElement);
proxy?: (this: Artplayer, art: Artplayer) => HTMLCanvasElement | HTMLVideoElement;
/**
* Custom plugin list
+1
View File
@@ -54,4 +54,5 @@ export type Utils = {
supportsFlex(): boolean;
setStyleText(element: HTMLElement, text: string): void;
getRect(el: HTMLElement): { top: number; left: number; width: number; height: number };
loadImg(url: string, scale?: Number): Promise<HTMLImageElement>;
};