springboot-ffmpeg-m3u8-convertor nplayer视频播放弹幕 artplayer视频弹幕

学习链接

ffmpeg-cli-wrapper - 内部封装了操作ffmpeg命令的java类库,它提供了一些类和方法,可以方便地构建和执行 ffmpeg 命令,而不需要直接操作字符串或进程。并且支持异步执行和进度监听

springboot-ffmpeg-m3u8-convertor - gitee代码 - springboot+ffmpeg,将视频转换为 m3u8 格式。支持 .mp4 | .flv | .avi | .mov | .wmv | .wav 格式视频转换。转换方式有:指定文件路径 、文件上传转换两种转换方式。

java-ffmpeg-convert-wav-to-mp3-demo

在 java 中使用 ffmpeg 的四个阶段

nplayer官网文档

artplayer官网文档

播放效果

NPlayer
在这里插入图片描述
ArtPlayer

代码

package.json

{"name": "nplayer-demo","version": "0.1.0","private": true,"scripts": {"serve": "vue-cli-service serve","build": "vue-cli-service build"},"dependencies": {"@nplayer/danmaku": "^1.0.12","artplayer": "^5.2.2","artplayer-plugin-danmuku": "^5.1.5","core-js": "^3.8.3","hls.js": "^1.5.20","nplayer": "^1.0.15","vue": "^2.6.14","vue-router": "^3.6.5"},"devDependencies": {"@vue/cli-plugin-babel": "~5.0.0","@vue/cli-service": "~5.0.0","sass": "^1.32.7","sass-loader": "^12.0.0","vue-template-compiler": "^2.6.14"},"browserslist": ["> 1%","last 2 versions","not dead"]
}

NPlayer.vue

<template><div id="app"><div class="video-area"><div class="video-wrapper" ref="videoWrapperRef"></div><div class="video-select-wrapper"><ul><li v-for="(m3u8, index) in m3u8List" :key="index" @click="switchVideo(m3u8)">{{ m3u8.videoName }}</li></ul></div></div><div><button @click="sendADanmu">发送1个弹幕</button><button @click="pauseDanmu">暂停弹幕</button><button @click="resumeDanmu">恢复弹幕</button><button @click="getDanmu">获取弹幕列表</button><!-- 添加一个弹幕到弹幕列表,并返回该弹幕插入下标。(大量弹幕请不要循环调用该方法,请使用其他批量方法) --><button @click="addDanmuToDanmuList">添加一个弹幕到弹幕列表</button><!-- 在现有弹幕列表末尾添加弹幕列表。需要保证添加的弹幕列表是有序的,而且其第一个弹幕的时间比现有的最后一个时间大。 --><button @click="addDanmuListToDanmuList">在现有弹幕列表末尾添加弹幕列表</button><!-- 重置弹幕列表。如果你又有一堆无序弹幕列表需要加入。可以通过 getItems() 获取现有弹幕,然后拼接两个列表,做排序,再调用该方法。 --><button @click="resetDanmuList">重置弹幕列表</button></div><div><button @click="startVideo">播放视频</button><button @click="pauseVideo">暂停视频</button><button @click="toggleVideo">切换视频播放状态</button><button @click="jumpTo">跳到指定时间开始播放</button><button @click="updatePlayerOptions">更新播放器参数</button><button @click="destroyPlayer">销毁</button></div></div>
</template><script>
import Hls from 'hls.js'
import Player, { EVENT } from 'nplayer';
import Danmaku from '@nplayer/danmaku'
console.log(EVENT)export default {name: 'App',components: {},data() {return {hls: null,player: null,video: null,danmaku: null,m3u8List: [{ videoId: '1', videoUrl: 'http://127.0.0.1/test3.m3u8', poster: 'http://127.0.0.1/20250219/13/41/poster.jpg', videoName: '第1集' },{ videoId: '2', videoUrl: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8', poster: '', videoName: '联网视频' },{ videoId: '3', videoUrl: 'http://127.0.0.1/test1.m3u8', poster: 'http://127.0.0.1/20250219/13/37/poster.jpg', videoName: '第2集' },{ videoId: '4', videoUrl: 'http://127.0.0.1/test5.m3u8', poster: '', videoName: '第3集' },{ videoId: '5', videoUrl: 'http://127.0.0.1/test6.m3u8', poster: '', videoName: '第4集' },// {videoUrl: 'http://127.0.0.1/20250219/13/49/test7.m3u8',poster:'http://127.0.0.1/poster.jpg',videoName: '第5集'},]}},mounted() {this.initPlayer(/* this.m3u8List[0] */)},methods: {isNativeHlsSupported() {const video = document.createElement("video");return video.canPlayType("application/vnd.apple.mpegurl") !== "";},destroyPlayer() {if (this.player) {if (this.player.playing) {this.player.pause()}this.player.danmaku.resetItems([])this.player.dispose();}if (this.hls) {this.hls.destroy()}},switchVideo(m3u8) {console.log('播放: ', m3u8);this.destroyPlayer()this.initPlayer(m3u8)},sendADanmu() {/* 这里不需要设置time时间,默认直接取视频时间 */this.danmaku.send({ text: 'sendADanmu~~~' })},pauseDanmu() {console.log(this.danmaku.paused);this.danmaku.pause()console.log(this.danmaku.paused);},resumeDanmu() {console.log(this.danmaku.paused);this.danmaku.resume()console.log(this.danmaku.paused);},getDanmu() {console.log(this.danmaku.getItems());},addDanmuToDanmuList() {// 添加一个弹幕到弹幕列表,并返回该弹幕插入下标。(大量弹幕请不要循环调用该方法,请使用其他批量方法)// 这里需要设置time时间,否则不会添加到弹幕列表console.log(this.danmaku.addItem({ text: 'addDanmuToDanmuList~~~', time: 8 }));},addDanmuListToDanmuList() {/* 它会在末尾添加大量弹幕,这里可以不指定time */console.log(this.danmaku.appendItems([{ text: 'addDanmuListToDanmuList~~~' },{ text: 'addDanmuListToDanmuList~~~' },{ text: 'addDanmuListToDanmuList~~~' },{ text: 'addDanmuListToDanmuList~~~' },{ text: 'addDanmuListToDanmuList~~~' },]));},resetDanmuList() {const oldItems = this.player.danmaku.getItems()const newUnsortItems = [{ time: 3, text: '重置弹幕1' },{ time: 4, text: '重置弹幕2' },{ time: 10, text: '重置弹幕3' },]const sortedItems = oldItems.concat(newUnsortItems).sort((a, b) => a.time - b.time)this.player.danmaku.resetItems(sortedItems)},startVideo() {this.player.play()},pauseVideo() {this.player.pause()},toggleVideo() {this.player.toggle()},jumpTo() {this.player.seek(30)},updatePlayerOptions() {this.player.updateOptions({})},initPlayer(m3u8) {console.log('initPlayer' + m3u8);this.video = document.createElement('video')const videoWrapper = this.$refs.videoWrapperReflet _this = thisconst danmakuOptions = {// 是否开启无限弹幕模式。unlimited: true,// 1、弹幕列表必须按照 time 从小到大排序。//    如果获取的弹幕是无序的,那么在传入之前需要自己 .sort((a, b) => a.time - b.time) 一下。// 2、你还可以通过 danmaku 对象的 appendItems 和 resetItems 等方法,添加和重置弹幕。items: [],// 发送弹幕之前会调用该回调,用来判断是否丢弃当前弹幕。discard(bullet) {console.log('discard bullet? 内容是:', bullet.text, bullet);if (bullet.text.indexOf('2B') > -1) {// 不显示该弹幕return true} else {return false}}}this.player = new Player({isTouch: false, // 默认会自动检测poster: m3u8?.poster,// 开启快捷键功能shortcut: true,// volumeStep 参数控制volumeStep: 0.2,// 前进或后退 时长参数控制seekStep: 2,// videoProps: { autoplay: false },volumeVertical: true,i18n: 'zh',video: this.video,plugins: [new Danmaku(danmakuOptions)]})window.player = this.player// 弹幕插件还会在 player 对象上注册一个 danmaku 对象。可以通过 player.danmaku 访问该对象。// 可以通过 danmaku 对象的 appendItems 和 resetItems 等方法,添加和重置弹幕console.log('danmaku 对象', this.player.danmaku)this.danmaku = this.player.danmakuwindow.danmaku = this.player.danmaku// 用户发送弹幕之前触发。this.player.on('DanmakuSend', (opts) => {console.log('DanmakuSend', opts);})this.player.on("DanmakuUpdateOptions", (opts) => {console.log('DanmakuUpdateOptions', opts);})this.player.mount(videoWrapper)if (this.isNativeHlsSupported()) {this.player.src = url; // 直接使用原生播放} else {if (m3u8 && m3u8.videoUrl) {this.hls = new Hls()this.hls.attachMedia(this.player.video)this.hls.on(Hls.Events.MEDIA_ATTACHED, function () {console.log('Hls.Events.MEDIA_ATTACHED监听');// _this.hls.loadSource('https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8')// _this.hls.loadSource('http://127.0.0.1/20250219/13/41/test3.m3u8')// _this.hls.loadSource('http://127.0.0.1/test/test.m3u8')})this.hls.on(Hls.Events.MANIFEST_PARSED, function () {console.log('Hls.Events.MANIFEST_PARSED监听');})this.hls.loadSource(m3u8.videoUrl)// 模拟加载弹幕new Promise((resolve, reject) => {console.log('加载弹幕');this.player.danmaku.appendItems([{ time: 1, text: m3u8.videoId + '- 弹幕1~' },{ time: 2, text: m3u8.videoId + '- 弹幕2~' },{ time: 2.5, text: m3u8.videoId + '- 2B~' },{ time: 3, text: m3u8.videoId + '- 弹幕3~' },{ time: 4, text: m3u8.videoId + '- 弹幕4~' },{ time: 5, text: m3u8.videoId + '- 弹幕5~' },{ time: 6, text: m3u8.videoId + '- 自定义弹幕哦~', color: '#f00', type: 'scroll', isMe: false, force: true }])})}}window.hls = this.hls},},}
</script><style lang="scss">
body {margin: 0;
}
.nplayer_video {object-fit: cover;
}
.video-area {width: 1200px; // 80 * 45   16 9 margin: 20px auto;display: flex;height: 448px;
}.video-wrapper {width: 800px;border: 1px solid #ccc;border-radius: 6px;overflow: hidden;box-shadow: 0 5px 10px 2px rgba(0,0,0,.08);
}ul,
li {list-style-type: none;padding: 0;margin: 0;
}.video-select-wrapper {border: 1px solid #ccc;border-radius: 4px;margin-left: 15px;flex: 1;ul {width: 100%;height: 100%;li {border: 1px solid #eee;margin: 5px;height: 30px;text-align: center;line-height: 30px;color: #333;&:hover {background-color: #8cc6f2;border-radius: 4px;cursor: pointer;color: #fff;}}}
}button {margin: 5px;
}
</style>

ArtPlauer

<template><div id="app"><div class="video-area"><div class="art-wrapper"><div ref="artRef" class="artplayer-app"></div></div><div class="video-select-wrapper"><ul><li :class="{active: currVideoId == videoItem.videoId}" v-for="(videoItem, index) in videoList" :key="index" @click="switchVideo(videoItem)">{{videoItem.videoName }}</li></ul></div></div><div class="btn-wrapper"><button @click="playVideo()">播放视频</button><button @click="pauseVideo()">暂停视频</button><button @click="jumpTo()">跳转到10s</button><button @click="showDanmu()">显示弹幕</button><button @click="hideDanmu()">隐藏弹幕</button><button @click="emitDanmu()">发送1条实时弹幕</button><!-- 通过方法 config 实时改变弹幕配置 --><button @click="configDanmu()">修改弹幕配置</button><button @click="reloadDanmu()">重新加载当前弹幕库</button><button @click="toggleDanmu()">切换当前弹幕库</button><button @click="appendDanmu()">追加弹幕</button><button @click="resetDanmu()">用于清空当前显示的弹幕</button></div></div>
</template><script>
import Hls from 'hls.js'
import Artplayer from "artplayer";
import artplayerPluginDanmuku from 'artplayer-plugin-danmuku';export default {name: 'App',data() {return {instance: null,currVideoId: null,videoList: [{ videoId: 1, videoUrl: 'http://127.0.0.1/test1.mp4', videoName: 'jvm', poster: 'http://127.0.0.1/test1.jpg' },{ videoId: 2, videoUrl: 'http://127.0.0.1/test2.mp4', videoName: 'seata', poster: 'http://127.0.0.1/test2.jpg' },{ videoId: 3, videoUrl: 'http://127.0.0.1/unknown.mp4', videoName: 'unknown', poster: 'http://127.0.0.1/test3.jpg' },]}},mounted() {this.initPlayer(null)/* setTimeout(()=>{this.switchVideo(this.videoList[0])}, 2000) */},methods: {playVideo() {this.instance.play()},pauseVideo() {this.instance.pause()},jumpTo() {this.instance.seek = 10},emitDanmu() {this.instance.plugins.artplayerPluginDanmuku.emit({text: '发送1条实时弹幕',color: '#ff0',border: true,});},showDanmu() {console.log(art.plugins.artplayerPluginDanmuku.isHide);this.instance.plugins.artplayerPluginDanmuku.show()},hideDanmu() {console.log(art.plugins.artplayerPluginDanmuku.isHide);this.instance.plugins.artplayerPluginDanmuku.hide()},configDanmu() {this.instance.plugins.artplayerPluginDanmuku.config({fontSize: Number(40),});},reloadDanmu() {// 当danmuku的配置是路径的时候,会重新发起请求this.instance.plugins.artplayerPluginDanmuku.load();},toggleDanmu() {art.plugins.artplayerPluginDanmuku.config({danmuku: [{text: '1-toggleDanmu(⊙o⊙)?', // 弹幕文本time: 10, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象},{text: '3-toggleDanmu(⊙o⊙)?', // 弹幕文本time: 11, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象}],});this.instance.plugins.artplayerPluginDanmuku.load();},appendDanmu() {// 追加新的弹幕库,参数类型和option.danmuku相同this.instance.plugins.artplayerPluginDanmuku.load([{text: '666-appendDanmu(⊙o⊙)?', // 弹幕文本time: 12, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象},{text: '666-appendDanmu(⊙o⊙)?', // 弹幕文本time: 12, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象}])},resetDanmu() {// 并不是真的删除弹幕,只是当前显示的弹幕变成不显示了,再次拖动进度条,仍然会显示this.instance.plugins.artplayerPluginDanmuku.reset();},switchVideo(videoItem) {/* // 可以直接修改url,但是比如一些设置无法动态修改,比如highlight this.currVideoId = videoItem.videoIdthis.instance.poster = videoItem.posterthis.instance.switchUrl(videoItem.videoUrl) */if(this.instance) {this.instance.destroy()}this.initPlayer(videoItem)this.currVideoId = videoItem.videoIdthis.instance.poster = videoItem.posterthis.instance.switchUrl(videoItem.videoUrl)},initPlayer(videoItem) {let _this = thisthis.instance = new Artplayer({// 语言lang: 'zh-cn', // or 'en'// 播放器的唯一标识,目前只用于记忆播放 autoplaybackid: 'your-url-id',// container: this.$refs.artRef,container: '.artplayer-app',// 默认支持三种视频文件格式:.mp4, .ogg, .webm// url: 'http://127.0.0.1/test1.mp4',// 视频的海报,只会出现在播放器初始化且未播放的状态下// poster: 'http://127.0.0.1/test2.jpg',// 播放器主题颜色,目前用于 进度条 和 高亮元素 上theme: '#00aeec',// 播放器的默认音量 Default: 0.7volume: 0.5,// 使用直播模式,会隐藏进度条和播放时间// isLive: true,// 是否默认静音// muted: true,// 是否自动播放(假如希望默认进入页面就能自动播放视频,muted 必需为 true)// autoplay: true,// 播放器的尺寸默认会填充整个 container 容器尺寸,所以经常出现黑边,该值能自动调整播放器尺寸以隐藏黑边,类似 css 的 object-fit: cover;autoSize: true,// 当播放器滚动到浏览器视口以外时,自动进入 迷你播放 模式autoMini: true,// 是否显示视频翻转功能 (会出现在 设置面板 和 右键菜单 里)// flip: true,// 是否显示视频播放速度功能,会出现在 设置面板 和 右键菜单 里playbackRate: true,// 是否显示视频长宽比功能,会出现在 设置面板 和 右键菜单 里aspectRatio: true,// 是否在底部控制栏里显示 视频截图 功能(由于浏览器安全机制,假如视频源地址和网站是跨域的,可能会出现截图失败)// screenshot: true,// 是否在底部控制栏里显示 设置面板 的开关按钮setting: true,// 是否使用快捷键(只在播放器获得焦点后(如点击了播放器后),这些快捷键才会生效)hotkey: true,// 是否在底部控制栏里显示 画中画 的开关按钮pip: true,// 假如页面里同时存在多个播放器,是否只能让一个播放器播放mutex: true,// 是否在底部控制栏里显示播放器 窗口全屏 按钮fullscreen: true,// 是否在底部控制栏里显示播放器 网页全屏 按钮fullscreenWeb: true,// 迷你进度条,只在播放器失去焦点后且正在播放时出现miniProgressBar: true,// 在移动端是否使用 playsInline 模式playsInline: true,// 是否在移动端显示一个 锁定按钮 ,用于隐藏底部 控制栏lock: true,// 是否在移动端添加长按视频快进功能fastForward: true,// 初始化自定义的 层/* layers: [{name: 'potser',html: `<img style="width: 50px" src="http://127.0.0.1/naughty.gif">`,style: {position: 'absolute',top: '20px',right: '20px',opacity: '.9',},click: function (...args) {console.info('click', args);// 点击后,该层消时_this.instance.layers.show = false;},mounted: function (...args) {console.info('mounted', args);},},], */// 初始化自定义的 设置面板/* settings: [{html: 'setting01',selector: [{html: 'setting01-01',},{html: 'setting01-02',},],onSelect: function (...args) {console.info(args);},},{html: 'setting02',selector: [{html: 'setting02-01',},{html: 'setting02-02',},],onSelect: function (...args) {console.info(args);},},], */// 初始化自定义的 右键菜单contextmenu: [{html: 'zzhua的个人主页',click: function (...args) {console.info('click', args);_this.instance.contextmenu.show = false;},},],// 初始化自定义的底部 控制栏/* controls: [{position: 'right',html: '测试',tooltip: 'Your Control',style: {color: 'red',},click: function (...args) {console.info('click', args);},},], *//* controls: [{position: 'right',html: '隐藏弹幕',click: function () {_this.instance.plugins.artplayerPluginDanmuku.hide();},},{position: 'right',html: '显示弹幕',click: function () {_this.instance.plugins.artplayerPluginDanmuku.show();},},], */// 是否在底部控制栏里显示 画质选择 列表/* quality: [{default: true,html: '480P',url: '/assets/sample/video.mp4',},{html: '720P',url: '/assets/sample/video.mp4',},], */highlight: [{time: 60,text: 'One more chance',},{time: 120,text: '谁でもいいはずなのに',}],// 在进度条上设置 预览图/* thumbnails: {url: '/assets/sample/thumbnails.png',number: 60,column: 10,}, */// 设置视频的字幕,支持字幕格式:vtt, srt, ass/* subtitle: {url: '/assets/sample/subtitle.srt',type: 'srt',encoding: 'utf-8',escape: true,style: {color: '#03A9F4','font-size': '30px',},}, */// 用于替换默认图标,支持 Html 字符串和 HTMLElement/* icons: {loading: '<img src="/assets/img/ploading.gif">',state: '<img src="/assets/img/state.png">',}, */plugins: [artplayerPluginDanmuku({// 使用弹幕数组/* danmuku: [{text: '1-初始弹幕数据(⊙o⊙)?' + videoItem?.videoId, // 弹幕文本time: 1, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象},{text: '3-初始弹幕数据(⊙o⊙)?' + videoItem?.videoId, // 弹幕文本time: 3, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象}], */// 使用异步返回danmuku: function () {return new Promise((resovle) => {return resovle([{text: '1-初始弹幕数据(⊙o⊙)?' + videoItem?.videoId, // 弹幕文本time: 3, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象},{text: '3-初始弹幕数据(⊙o⊙)?' + videoItem?.videoId, // 弹幕文本time: 4, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象}]);});},// danmuku: '/assets/sample/danmuku.xml',// 以下为非必填speed: 5, // 弹幕持续时间,范围在[1 ~ 10]margin: [10, '25%'], // 弹幕上下边距,支持像素数字和百分比opacity: 1, // 弹幕透明度,范围在[0 ~ 1]color: '#FFFFFF', // 默认弹幕颜色,可以被单独弹幕项覆盖mode: 0, // 默认弹幕模式: 0: 滚动,1: 顶部,2: 底部modes: [0, 1, 2], // 弹幕可见的模式fontSize: 25, // 弹幕字体大小,支持像素数字和百分比antiOverlap: true, // 弹幕是否防重叠synchronousPlayback: false, // 是否同步播放速度mount: undefined, // 弹幕发射器挂载点, 默认为播放器控制栏中部heatmap: true, // 是否开启热力图width: 512, // 当播放器宽度小于此值时,弹幕发射器置于播放器底部points: [], // 热力图数据visible: true, // 弹幕层是否可见emitter: true, // 是否开启弹幕发射器maxLength: 200, // 弹幕输入框最大长度, 范围在[1 ~ 1000]lockTime: 5, // 输入框锁定时间,范围在[1 ~ 60]theme: 'dark', // 弹幕主题,支持 dark 和 light,只在自定义挂载时生效OPACITY: {}, // 不透明度配置项FONT_SIZE: {}, // 弹幕字号配置项MARGIN: {}, // 显示区域配置项SPEED: {}, // 弹幕速度配置项COLOR: [], // 颜色列表配置项// 手动发送弹幕前的过滤器,返回 true 则可以发送,可以做存库处理beforeEmit(danmu) {return new Promise((resolve) => {console.log('beforeEmit弹幕',danmu);setTimeout(() => {resolve(true);}, 1000);});},filter: (danmu) => {console.log('filter弹幕',danmu);return danmu.text.length <= 100}, // 弹幕载入前的过滤器beforeVisible: (e) => {console.log('beforeVisible弹幕',e);return true}, // 弹幕显示前的过滤器,返回 true 则可以发送}),],},// 构造函数接受一个函数作为第二个参数,播放器初始化成功且视频可以播放时触发,和ready事件一样// 等同于:this.instance.on('ready', function() {}function onReady(art) {console.log('播放器初始化成功且视频可以播放时触发1', art == _this.instance);},);// 有时候 url 地址没那么快知道,这时候你可以异步设置 url/* setTimeout(() => {this.instance.url = 'http://127.0.0.1/test1.mp4';}, 5000); */// 播放器初始化成功且视频可以播放时触发this.instance.on('ready', function () {console.log('播放器初始化成功且视频可以播放时触发2');// _this.instance.play();})window.art = this.instancethis.$nextTick(() => {this.$emit("get-instance", this.instance);});}},beforeDestroy() {if (this.instance && this.instance.destroy) {this.instance.destroy(false);}},
}</script><style lang="scss">
body {margin: 0;
}.artplayer-app {/* 您可能需要初始化容器元素的大小,如: *//* width: 400px;height: 300px; *//* 或者使用 aspect-ratio: */width: 800px;aspect-ratio: 16/9
}ul,
li {list-style-type: none;padding: 0;margin: 0;
}
.btn-wrapper {width: 1200px;margin: auto;
}
.video-area {width: 1200px;margin: 20px auto;display: flex;.art-wrapper {border: 1px solid #ccc;}.video-select-wrapper {border: 1px solid #ccc;margin-left: 5px;flex: 1;border-radius: 6px;ul {width: 100%;height: 100%;li {border: 1px solid #eee;margin: 5px;height: 30px;text-align: center;line-height: 30px;color: #333;border-radius: 6px;&:hover {background-color: #8cc6f2;border-radius: 4px;cursor: pointer;color: #fff;}&.active {background-color: #8cc6f2;color: #fff;}}}}
}
</style>

Aplayer2

加载m3u8

<template><div id="app"><div class="video-area"><div class="art-wrapper"><div ref="artRef" class="artplayer-app"></div></div><div class="video-select-wrapper"><ul><li :class="{active: currVideoId == videoItem.videoId}" v-for="(videoItem, index) in videoList" :key="index" @click="switchVideo(videoItem)">{{videoItem.videoName }}</li></ul></div></div><div class="btn-wrapper"><button @click="playVideo()">播放视频</button><button @click="pauseVideo()">暂停视频</button><button @click="jumpTo()">跳转到10s</button><button @click="showDanmu()">显示弹幕</button><button @click="hideDanmu()">隐藏弹幕</button><button @click="emitDanmu()">发送1条实时弹幕</button><!-- 通过方法 config 实时改变弹幕配置 --><button @click="configDanmu()">修改弹幕配置</button><button @click="reloadDanmu()">重新加载当前弹幕库</button><button @click="toggleDanmu()">切换当前弹幕库</button><button @click="appendDanmu()">追加弹幕</button><button @click="resetDanmu()">用于清空当前显示的弹幕</button></div></div>
</template><script>
import Hls from 'hls.js'
import Artplayer from "artplayer";
import artplayerPluginDanmuku from 'artplayer-plugin-danmuku';export default {name: 'App',data() {return {instance: null,currVideoId: null,videoList: [{ videoId: '1', videoUrl: 'http://127.0.0.1/20250219/13/41/test3.m3u8', poster: 'http://127.0.0.1/20250219/13/41/poster.jpg', videoName: '第1集' },{ videoId: '2', videoUrl: 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8', poster: '', videoName: '联网视频' },{ videoId: '3', videoUrl: 'http://127.0.0.1/20250219/13/37/test1.m3u8', poster: 'http://127.0.0.1/20250219/13/37/poster.jpg', videoName: '第2集' },{ videoId: '4', videoUrl: 'http://127.0.0.1/20250219/13/47/test5.m3u8', poster: '', videoName: '第3集' },{ videoId: '5', videoUrl: 'http://127.0.0.1/20250219/13/49/test6.m3u8', poster: '', videoName: '第4集' },                ]}},mounted() {this.initPlayer(null)/* setTimeout(()=>{this.switchVideo(this.videoList[0])}, 2000) */},methods: {playVideo() {this.instance.play()},pauseVideo() {this.instance.pause()},jumpTo() {this.instance.seek = 10},emitDanmu() {this.instance.plugins.artplayerPluginDanmuku.emit({text: '发送1条实时弹幕',color: '#ff0',border: true,});},showDanmu() {console.log(art.plugins.artplayerPluginDanmuku.isHide);this.instance.plugins.artplayerPluginDanmuku.show()},hideDanmu() {console.log(art.plugins.artplayerPluginDanmuku.isHide);this.instance.plugins.artplayerPluginDanmuku.hide()},configDanmu() {this.instance.plugins.artplayerPluginDanmuku.config({fontSize: Number(40),});},reloadDanmu() {// 当danmuku的配置是路径的时候,会重新发起请求this.instance.plugins.artplayerPluginDanmuku.load();},toggleDanmu() {art.plugins.artplayerPluginDanmuku.config({danmuku: [{text: '1-toggleDanmu(⊙o⊙)?', // 弹幕文本time: 10, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象},{text: '3-toggleDanmu(⊙o⊙)?', // 弹幕文本time: 11, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象}],});this.instance.plugins.artplayerPluginDanmuku.load();},appendDanmu() {// 追加新的弹幕库,参数类型和option.danmuku相同this.instance.plugins.artplayerPluginDanmuku.load([{text: '666-appendDanmu(⊙o⊙)?', // 弹幕文本time: 12, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象},{text: '666-appendDanmu(⊙o⊙)?', // 弹幕文本time: 12, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象}])},resetDanmu() {// 并不是真的删除弹幕,只是当前显示的弹幕变成不显示了,再次拖动进度条,仍然会显示this.instance.plugins.artplayerPluginDanmuku.reset();},switchVideo(videoItem) {/* // 可以直接修改url,但是比如一些设置无法动态修改,比如highlight this.currVideoId = videoItem.videoIdthis.instance.poster = videoItem.posterthis.instance.switchUrl(videoItem.videoUrl) */if(this.instance) {this.instance.destroy()}this.initPlayer(videoItem)this.currVideoId = videoItem.videoIdthis.instance.poster = videoItem.posterthis.instance.switchUrl(videoItem.videoUrl)},playM3u8(video, url, art) {if (Hls.isSupported()) {if (this.instance.hls) this.instance.hls.destroy();const hls = new Hls();hls.loadSource(url);hls.attachMedia(video);this.instance.hls = hls;this.instance.on('destroy', () => hls.destroy());hls.on('hlsError', (e)=>{console.log('hlsError', video, url, art);})window.hls = hls} else if (video.canPlayType('application/vnd.apple.mpegurl')) {video.src = url;} else {this.instance.notice.show = 'Unsupported playback format: m3u8';}},initPlayer(videoItem) {let _this = thisthis.instance = new Artplayer({// 播放m3u8视频type: 'm3u8',customType: {m3u8: this.playM3u8,},// 语言lang: 'zh-cn', // or 'en'// 播放器的唯一标识,目前只用于记忆播放 autoplaybackid: 'your-url-id',// container: this.$refs.artRef,container: '.artplayer-app',// 默认支持三种视频文件格式:.mp4, .ogg, .webm// url: 'http://127.0.0.1/test1.mp4',// 视频的海报,只会出现在播放器初始化且未播放的状态下// poster: 'http://127.0.0.1/test2.jpg',// 播放器主题颜色,目前用于 进度条 和 高亮元素 上theme: '#00aeec',// 播放器的默认音量 Default: 0.7volume: 0.5,// 使用直播模式,会隐藏进度条和播放时间// isLive: true,// 是否默认静音// muted: true,// 是否自动播放(假如希望默认进入页面就能自动播放视频,muted 必需为 true)// autoplay: true,// 播放器的尺寸默认会填充整个 container 容器尺寸,所以经常出现黑边,该值能自动调整播放器尺寸以隐藏黑边,类似 css 的 object-fit: cover;autoSize: true,// 当播放器滚动到浏览器视口以外时,自动进入 迷你播放 模式autoMini: true,// 是否显示视频翻转功能 (会出现在 设置面板 和 右键菜单 里)// flip: true,// 是否显示视频播放速度功能,会出现在 设置面板 和 右键菜单 里playbackRate: true,// 是否显示视频长宽比功能,会出现在 设置面板 和 右键菜单 里aspectRatio: true,// 是否在底部控制栏里显示 视频截图 功能(由于浏览器安全机制,假如视频源地址和网站是跨域的,可能会出现截图失败)// screenshot: true,// 是否在底部控制栏里显示 设置面板 的开关按钮setting: true,// 是否使用快捷键(只在播放器获得焦点后(如点击了播放器后),这些快捷键才会生效)hotkey: true,// 是否在底部控制栏里显示 画中画 的开关按钮pip: true,// 假如页面里同时存在多个播放器,是否只能让一个播放器播放mutex: true,// 是否在底部控制栏里显示播放器 窗口全屏 按钮fullscreen: true,// 是否在底部控制栏里显示播放器 网页全屏 按钮fullscreenWeb: true,// 迷你进度条,只在播放器失去焦点后且正在播放时出现miniProgressBar: true,// 在移动端是否使用 playsInline 模式playsInline: true,// 是否在移动端显示一个 锁定按钮 ,用于隐藏底部 控制栏lock: true,// 是否在移动端添加长按视频快进功能fastForward: true,// 初始化自定义的 层/* layers: [{name: 'potser',html: `<img style="width: 50px" src="http://127.0.0.1/naughty.gif">`,style: {position: 'absolute',top: '20px',right: '20px',opacity: '.9',},click: function (...args) {console.info('click', args);// 点击后,该层消时_this.instance.layers.show = false;},mounted: function (...args) {console.info('mounted', args);},},], */// 初始化自定义的 设置面板/* settings: [{html: 'setting01',selector: [{html: 'setting01-01',},{html: 'setting01-02',},],onSelect: function (...args) {console.info(args);},},{html: 'setting02',selector: [{html: 'setting02-01',},{html: 'setting02-02',},],onSelect: function (...args) {console.info(args);},},], */// 初始化自定义的 右键菜单contextmenu: [{html: 'zzhua的个人主页',click: function (...args) {console.info('click', args);_this.instance.contextmenu.show = false;},},],// 初始化自定义的底部 控制栏/* controls: [{position: 'right',html: '测试',tooltip: 'Your Control',style: {color: 'red',},click: function (...args) {console.info('click', args);},},], *//* controls: [{position: 'right',html: '隐藏弹幕',click: function () {_this.instance.plugins.artplayerPluginDanmuku.hide();},},{position: 'right',html: '显示弹幕',click: function () {_this.instance.plugins.artplayerPluginDanmuku.show();},},], */// 是否在底部控制栏里显示 画质选择 列表/* quality: [{default: true,html: '480P',url: '/assets/sample/video.mp4',},{html: '720P',url: '/assets/sample/video.mp4',},], */highlight: [{time: 60,text: 'One more chance',},{time: 120,text: '谁でもいいはずなのに',}],// 在进度条上设置 预览图/* thumbnails: {url: '/assets/sample/thumbnails.png',number: 60,column: 10,}, */// 设置视频的字幕,支持字幕格式:vtt, srt, ass/* subtitle: {url: '/assets/sample/subtitle.srt',type: 'srt',encoding: 'utf-8',escape: true,style: {color: '#03A9F4','font-size': '30px',},}, */// 用于替换默认图标,支持 Html 字符串和 HTMLElement/* icons: {loading: '<img src="/assets/img/ploading.gif">',state: '<img src="/assets/img/state.png">',}, */plugins: [artplayerPluginDanmuku({// 使用弹幕数组/* danmuku: [{text: '1-初始弹幕数据(⊙o⊙)?' + videoItem?.videoId, // 弹幕文本time: 1, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象},{text: '3-初始弹幕数据(⊙o⊙)?' + videoItem?.videoId, // 弹幕文本time: 3, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象}], */// 使用异步返回danmuku: function () {return new Promise((resovle) => {return resovle([{text: '1-初始弹幕数据(⊙o⊙)?' + videoItem?.videoId, // 弹幕文本time: 3, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象},{text: '3-初始弹幕数据(⊙o⊙)?' + videoItem?.videoId, // 弹幕文本time: 4, // 弹幕时间, 默认为当前播放器时间mode: 0, // 弹幕模式: 0: 滚动(默认),1: 顶部,2: 底部color: '#FFFFFF', // 弹幕颜色,默认为白色border: false, // 弹幕是否有描边, 默认为 falsestyle: {}, // 弹幕自定义样式, 默认为空对象}]);});},// danmuku: '/assets/sample/danmuku.xml',// 以下为非必填speed: 5, // 弹幕持续时间,范围在[1 ~ 10]margin: [10, '25%'], // 弹幕上下边距,支持像素数字和百分比opacity: 1, // 弹幕透明度,范围在[0 ~ 1]color: '#FFFFFF', // 默认弹幕颜色,可以被单独弹幕项覆盖mode: 0, // 默认弹幕模式: 0: 滚动,1: 顶部,2: 底部modes: [0, 1, 2], // 弹幕可见的模式fontSize: 25, // 弹幕字体大小,支持像素数字和百分比antiOverlap: true, // 弹幕是否防重叠synchronousPlayback: false, // 是否同步播放速度mount: undefined, // 弹幕发射器挂载点, 默认为播放器控制栏中部heatmap: true, // 是否开启热力图width: 512, // 当播放器宽度小于此值时,弹幕发射器置于播放器底部points: [], // 热力图数据visible: true, // 弹幕层是否可见emitter: true, // 是否开启弹幕发射器maxLength: 200, // 弹幕输入框最大长度, 范围在[1 ~ 1000]lockTime: 5, // 输入框锁定时间,范围在[1 ~ 60]theme: 'dark', // 弹幕主题,支持 dark 和 light,只在自定义挂载时生效OPACITY: {}, // 不透明度配置项FONT_SIZE: {}, // 弹幕字号配置项MARGIN: {}, // 显示区域配置项SPEED: {}, // 弹幕速度配置项COLOR: [], // 颜色列表配置项// 手动发送弹幕前的过滤器,返回 true 则可以发送,可以做存库处理beforeEmit(danmu) {return new Promise((resolve) => {console.log('beforeEmit弹幕',danmu);setTimeout(() => {resolve(true);}, 1000);});},filter: (danmu) => {console.log('filter弹幕',danmu);return danmu.text.length <= 100}, // 弹幕载入前的过滤器beforeVisible: (e) => {console.log('beforeVisible弹幕',e);return true}, // 弹幕显示前的过滤器,返回 true 则可以发送}),],},// 构造函数接受一个函数作为第二个参数,播放器初始化成功且视频可以播放时触发,和ready事件一样// 等同于:this.instance.on('ready', function() {}function onReady(art) {console.log('播放器初始化成功且视频可以播放时触发1', art == _this.instance);},);// 有时候 url 地址没那么快知道,这时候你可以异步设置 url/* setTimeout(() => {this.instance.url = 'http://127.0.0.1/test1.mp4';}, 5000); */// 播放器初始化成功且视频可以播放时触发this.instance.on('ready', function () {console.log('播放器初始化成功且视频可以播放时触发2');// _this.instance.play();})window.art = this.instancethis.$nextTick(() => {this.$emit("get-instance", this.instance);});}},beforeDestroy() {if (this.instance && this.instance.destroy) {this.instance.destroy(false);}},
}</script><style lang="scss">
body {margin: 0;
}.artplayer-app {/* 您可能需要初始化容器元素的大小,如: *//* width: 400px;height: 300px; *//* 或者使用 aspect-ratio: */width: 800px;aspect-ratio: 16/9
}ul,
li {list-style-type: none;padding: 0;margin: 0;
}
.btn-wrapper {width: 1200px;margin: auto;
}
.video-area {width: 1200px;margin: 20px auto;display: flex;.art-wrapper {border: 1px solid #ccc;}.video-select-wrapper {border: 1px solid #ccc;margin-left: 5px;flex: 1;border-radius: 6px;ul {width: 100%;height: 100%;li {border: 1px solid #eee;margin: 5px;height: 30px;text-align: center;line-height: 30px;color: #333;border-radius: 6px;&:hover {background-color: #8cc6f2;border-radius: 4px;cursor: pointer;color: #fff;}&.active {background-color: #8cc6f2;color: #fff;}}}}
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/20927.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下centos系统中使用docker容器中的ollama下载deepseek速度太慢解决办法

以下是使用shell脚本实现的一个示例&#xff0c;该脚本会尝试下载一个名为"deepseek-r1:32b"的模型。通过每隔60秒中断一次下载操作&#xff0c;从何恢复下载速度。亲测有效,其中需要将模型改为你自己要下载的模型 #!/bin/bashwhile true; do# 检查模型是否已下载完…

自动创建spring boot应用(eclipse版本)

使用spring starter project创建项目 设置Service URL 把Service URL设置为 https://start.aliyun.com/ 如下图&#xff1a; 使用这个网址&#xff0c;创建项目更快。 选择Spring Web依赖 项目结构 mvnw和mvnw.cmd:这是maven包装器&#xff08;wrapper&#xff09;脚本&…

基于flask+vue的租房信息可视化系统

✔️本项目利用 python 网络爬虫抓取某租房网站的租房信息&#xff0c;完成数据清洗和结构化&#xff0c;存储到数据库中&#xff0c;搭建web系统对各个市区的租金、房源信息进行展示&#xff0c;根据各种条件对租金进行预测。 1、数据概览 ​ 将爬取到的数据进行展示&#xff…

uniapp 滚动尺

scale组件代码&#xff08;部分class样式使用到了uview1.0的样式&#xff09; <template><view><view class"scale"><view class"pointer u-flex-col u-col-center"><u-icon name"arrow-down-fill" size"26&qu…

分布式大语言模型服务引擎vLLM论文解读

论文地址&#xff1a;Efficient Memory Management for Large Language Model Serving with PagedAttention 摘要 大语言模型&#xff08;LLMs&#xff09;的高吞吐量服务需要一次对足够多的请求进行批处理。然而&#xff0c;现有系统面临困境&#xff0c;因为每个请求的键值…

【HeadFirst系列之HeadFirst设计模式】第5天之工厂模式:比萨店的秘密武器,轻松搞定对象创建!

工厂模式&#xff1a;比萨店的秘密武器&#xff0c;轻松搞定对象创建&#xff01; 大家好&#xff0c;今天我们来聊聊设计模式中的工厂模式。如果你曾经为对象的创建感到头疼&#xff0c;或者觉得代码中到处都是 new 关键字&#xff0c;那么工厂模式就是你的救星&#xff01;本…

CSS基本选择器

1. 通配选择器 作用&#xff1a;可以选中所有的 HTML 元素。 语法&#xff1a; * { 属性名: 属性值; } 举例&#xff1a; <!DOCTYPE html> <html lang"zh-cn"> <head><meta charset"UTF-8"><meta name"viewport" …

Idea24.3 如何设置Git忽略某一个文件

文章目录 左上角找到commit选中你要忽略的文件 右键New Changelist给这个文件夹名称和描述 点击ok将要忽略的文件添加到这个文件夹 左上角找到commit 选中你要忽略的文件 右键New Changelist 给这个文件夹名称和描述 点击ok 将要忽略的文件添加到这个文件夹

ctfshow web入门 web11-web24

web11 web12 进来浏览网站&#xff0c;底部有一串数字&#xff0c;根据提示可能有用&#xff0c;访问robots.txt&#xff0c;发现禁止访问/admin/&#xff0c;进去看看发现需要输入用户名和密码&#xff0c;刚想爆破就猜对了&#xff0c;用户名是admin&#xff0c;密码是页面下…

MySQL笔记-对max_allowed_packet的进一步理解(2024-10-28)

背景 最近不仅仅在做开发&#xff0c;还在不停的做实施&#xff0c;运维。以前都不太喜欢做实施&#xff0c;运维&#xff0c;但是工作6年后&#xff0c;对这些还是比较感兴趣了&#xff0c;毕竟计算机这块不仅仅是开发&#xff0c;还有很多岗位&#xff0c;并且实施和运维会从…

Elasticsearch:探索 CLIP 替代方案

作者&#xff1a;来自 Elastic Jeffrey Rengifo 及 Toms Mura 分析图像到图像和文本到图像搜索的 CLIP 模型的替代方案。 在本文中&#xff0c;我们将通过一个模拟房地产网站的实际示例介绍 CLIP 多模态模型&#xff0c;探索替代方案&#xff0c;并分析它们的优缺点&#xff0c…

Spring中的日志

日志 了解一下 (有个印象) 门面模式 (外观模式) 含有两种角色&#xff1a; Facade (外观角色 / 门面角色): 系统对外的统一接口。SubSystem (子系统角色): 可以含有多个子系统&#xff0c;每个子系统都不是单独的类&#xff0c;而是一个类的集合。 Facade 对 SubSystem 是…

uniapp邪门事件

很久之前在这篇《THREEJS 在 uni-app 中使用&#xff08;微信小程序&#xff09;》&#xff1a;THREEJS 在 uni-app 中使用&#xff08;微信小程序&#xff09;_uni-app_帶刺的小葡萄-华为开发者空间 中学到了如何在uniapp的微信小程序里接入three.js的3d模型 由于小程序自身很…

C#项目04——递归求和

实现逻辑 利用递归&#xff0c;求取1~N以内的和 知识点 正常情况下&#xff0c;C#每条线程都会分配1MB的地址空间&#xff0c;因此执行递归的层次不能太深&#xff0c;否则就会出现溢出的风险&#xff0c; 业务设计 程序代码 private void button1_Click(object sender, E…

SQLMesh 系列教程6- 详解 Python 模型

本文将介绍 SQLMesh 的 Python 模型&#xff0c;探讨其定义、优势及在企业业务场景中的应用。SQLMesh 不仅支持 SQL 模型&#xff0c;还允许通过 Python 编写数据模型&#xff0c;提供更高的灵活性和可编程性。我们将通过一个电商平台的实例&#xff0c;展示如何使用 Python 模…

docker修改镜像默认存储路径(基于 WSL2 的迁移方法)

文章目录 打开powershell窗口1、停止 WSL2、导出数据3、取消注册4、导入数据到新位置5、确认转移情况6、重新启动 Docker Desktop7、查看 打开powershell窗口 任意地方shift右键 1、停止 WSL wsl --shutdown2、导出数据 wsl --export docker-desktop-data E:\docker\Docke…

Java开发实习面试笔试题(含答案)

在广州一家中大公司面试&#xff08;BOSS标注是1000-9999人&#xff0c;薪资2-3k&#xff09;&#xff0c;招聘上写着Java开发&#xff0c;基本没有标注前端要求&#xff0c;但是到场知道是前后端分离人不分离。开始先让你做笔试&#xff08;12道问答4道SQL题&#xff09;&…

火语言RPA--Excel读取内容

【组件功能】&#xff1a;读取Excel内指定位置的内容或读取整篇Sheet页内容 配置预览 配置说明 读取位置 单元格&#xff1a;读取指定单元格中的内容。 行&#xff1a;读取指定行内容。 列&#xff1a;读取指定列内容。 区域&#xff1a;读取指定区域内容。 整篇sheet页&…

基于Flask的第七次人口普查数据分析系统的设计与实现

【Flask】基于Flask的第七次人口普查数据分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 基于Flask的人口普查可视化分析系统 二、项目界面展示 登录/注册 首页/详情 …

国产编辑器EverEdit -告别东找西找!一键打开当前文件所在目录!

1 文件操作 2 应用场景 在文件编辑过程中&#xff0c;有时需要对文件进行一些操作&#xff0c;比如&#xff1a;在命令窗口输入文件路径、文件名&#xff0c;进入到文件目录&#xff0c;对文件进行压缩等&#xff0c;如果没有直达命令&#xff0c;用户需要通过文件管理器找到目…