邃芒官网、邃芒慧影、口播(H5) https://m.metavatar.cc
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 

322 líneas
9.3 KiB

  1. <template>
  2. <div :class="inPage ? 'page active' : 'page'">
  3. <div id="top"></div>
  4. <HeadTop />
  5. <div class="content">
  6. <!-- 视频 -->
  7. <div class="videoBox" v-if="videoSrc">
  8. <div id="player-container" />
  9. <!-- 下载按钮 -->
  10. <!-- <a :href="videoSrc" download class="videoBox-download">
  11. <img src="../../assets/icon/icon-download.png" alt="" />
  12. </a> -->
  13. <!-- <a class="videoBox-download" :href="baseUrl + `/api/userVideo/exportVideo?id=${id}`">
  14. <img src="../../assets/icon/icon-download.png" alt="" />
  15. </a> -->
  16. <div class="videoBox-download" @click="downloadVideo">
  17. <img src="../../assets/icon/icon-download.png" alt="" />
  18. </div>
  19. </div>
  20. </div>
  21. </div>
  22. </template>
  23. <script>
  24. import Vue from 'vue';
  25. import { Toast, Dialog } from 'vant';
  26. import { mapState, mapActions } from 'vuex';
  27. import HeadTop from './../../components/common/head.vue';
  28. // 工具
  29. import { scrollToID, getDeviceType } from '../../utils';
  30. import { saveAs } from 'file-saver';
  31. // 接口
  32. import { getModelDetailById } from '../../api/getPaper';
  33. import { doGetVideoDetialById, exportVideoApi } from '../../api/downloadVideo';
  34. Vue.use(Toast);
  35. export default {
  36. components: {
  37. HeadTop
  38. },
  39. data() {
  40. return {
  41. inPage: false,
  42. id: null,
  43. isAndroid: true,
  44. baseUrl: null,
  45. // videoSrc:
  46. // 'https://wenlian.wenzhou.gov.cn:8001//upload/video/%E3%80%8A%E7%93%AF%E8%B6%8A%E4%B9%8B%E5%8D%8E%E3%80%8B%E7%89%87%E8%8A%B11.mp4',
  47. videoSrc: null, //播放地址路径
  48. videoId: null, //视频ID
  49. title: null, // 视频名称
  50. coverUrl: null, //封面图
  51. height: null
  52. };
  53. },
  54. computed: {
  55. ...mapState({
  56. characters: state => state.publicObj.characters
  57. })
  58. },
  59. created() {
  60. this.id = this.$route.query.id;
  61. this.baseUrl = process.env.VUE_APP_API_ROOT;
  62. },
  63. async mounted() {
  64. this.inPage = true;
  65. scrollToID('top');
  66. //初始化数据
  67. await this.getVideoInfo();
  68. this.open();
  69. },
  70. destroyed() {},
  71. methods: {
  72. // 创建阿里云播放器
  73. open() {
  74. // 检测设备类型
  75. const deviceType = getDeviceType();
  76. let isAndroid = false;
  77. if (!deviceType) {
  78. // 设备类型为pc
  79. isAndroid = false;
  80. this.isAndroid = false;
  81. } else {
  82. if (deviceType[0] == 'Android') {
  83. // 设备类型为安卓
  84. isAndroid = true;
  85. this.isAndroid = true;
  86. } else {
  87. // 其他设备
  88. isAndroid = false;
  89. this.isAndroid = false;
  90. }
  91. }
  92. new Aliplayer(
  93. {
  94. id: 'player-container',
  95. cover: this.coverUrl,
  96. source: this.videoSrc,
  97. // 安卓设备尺寸比其他设备小一半,需适配
  98. // width: isAndroid ? '260px' : '480px',
  99. // height: isAndroid ? '427px' : '854px',
  100. width: '100%',
  101. height: this.height < 1920 ? '35%' : '100%',
  102. // 以下可选设置
  103. playsinline: true, // 禁止内置播放
  104. isLive: false, // 直播
  105. rePlay: false, // 循环播放
  106. useH5Prism: true, // 开启H5播放器
  107. preload: true, //播放器自动加载
  108. autoplay: false, // 自动播放
  109. diagnosisButtonVisible: false, //是否显示检测按钮
  110. keyShortCuts: true, //是否启用快捷键
  111. keyFastForwardStep: 3, //快进快退的时间长度
  112. controlBarVisibility: 'always' // 控制条的显示方式
  113. },
  114. function (player) {
  115. player.play();
  116. player.setCoverScale(9, 16);
  117. }
  118. );
  119. },
  120. // 下载视频
  121. async downloadVideo() {
  122. // 视频地址
  123. let that = this;
  124. const videoUrl = this.videoSrc;
  125. const title = this.sanitizeFilename(this.title);
  126. // ios
  127. const deviceType = getDeviceType();
  128. let isAndroid = false;
  129. if (!deviceType) {
  130. // 设备类型为pc
  131. isAndroid = false;
  132. } else {
  133. if (deviceType[0] == 'Android') {
  134. // 设备类型为安卓
  135. isAndroid = true;
  136. } else {
  137. // 其他设备
  138. isAndroid = false;
  139. }
  140. }
  141. if (!isAndroid) {
  142. console.log('ios');
  143. // let videoData = await exportVideoApi(this.id);
  144. const AccessToken = localStorage.getItem('AccessToken');
  145. that
  146. .$axios({
  147. method: 'get',
  148. url: process.env.VUE_APP_API_ROOT + `/api/userVideo/exportVideo?id=${that.id}`,
  149. headers: {
  150. 'content-type': 'application/octet-stream;charset=UTF-8',
  151. token: AccessToken,
  152. 'Access-Control-Allow-Origin': '*',
  153. 'Access-Control-Allow-Credentials': 'true'
  154. }
  155. })
  156. .then(async res => {
  157. console.log(res, 'AccessTokenAccessToken');
  158. await that.downloadFun(res, that.title);
  159. });
  160. // console.log(videoData);
  161. // await this.downloadFun(this.id);
  162. // await this.downloadFile1(this.id);
  163. } else {
  164. // 下载视频
  165. saveAs(videoUrl, this.title + '.mp4');
  166. }
  167. },
  168. // 视频名转化
  169. sanitizeFilename(filename) {
  170. return filename.replace(/[/\\:?*"<>|]/g, '').trim();
  171. },
  172. // ios下载处理
  173. downloadFile1(id) {
  174. return new Promise((resolve, reject) => {
  175. const xhr = new XMLHttpRequest();
  176. xhr.open('GET', process.env.VUE_APP_API_ROOT + `/api/userVideo/exportVideo?id=${id}`, true);
  177. xhr.responseType = 'arraybuffer';
  178. xhr.onload = () => {
  179. if (xhr.status === 200) {
  180. const contentType = xhr.getResponseHeader('Content-Type');
  181. const blob = new Blob([xhr.response], { type: contentType });
  182. const reader = new FileReader();
  183. reader.readAsDataURL(blob);
  184. reader.onloadend = function () {
  185. const base64Data = reader.result;
  186. const link = document.createElement('a');
  187. link.href = base64Data;
  188. link.download = fileName;
  189. document.body.appendChild(link);
  190. link.click();
  191. document.body.removeChild(link);
  192. };
  193. resolve(blob);
  194. } else {
  195. reject(new Error(`Failed to download file, status code: ${xhr.status}`));
  196. }
  197. };
  198. xhr.onerror = () => {
  199. reject(new Error(`Failed to download file, network error`));
  200. };
  201. xhr.send();
  202. });
  203. },
  204. // 转blob下载
  205. async downloadFun(blobFile, fileName) {
  206. let blob = await new Blob([blobFile], {
  207. type: 'video/mp4'
  208. });
  209. // console.log(blob);
  210. // if (window.navigator.msSaveOrOpenBlob) {
  211. // navigator.msSaveBlob(blob, fileName);
  212. // } else {
  213. const reader = await new FileReader();
  214. await reader.readAsDataURL(blob);
  215. // reader.readAsDataURL(blobFile);
  216. reader.onloadend = await function () {
  217. const base64Data = reader.result;
  218. const link = document.createElement('a');
  219. link.href = base64Data;
  220. link.download = fileName;
  221. document.body.appendChild(link);
  222. link.click();
  223. document.body.removeChild(link);
  224. // };
  225. // const dataUrl = 'data:application/octet-stream;base64,' + blobFile
  226. // const a = document.createElement('a')
  227. // a.style.display = 'none'
  228. // a.href = dataUrl
  229. // a.download = fileName
  230. // document.body.appendChild(a)
  231. // a.click()
  232. // window.URL.revokeObjectURL(dataUrl)
  233. // document.body.removeChild(a)
  234. // let link = document.createElement('a')
  235. // // link.href = blobFile
  236. // link.href = window.URL.createObjectURL(blob)
  237. // link.download = fileName
  238. // link.click()
  239. // window.URL.revokeObjectURL(link.href) //释放内存
  240. };
  241. },
  242. // 获取视频信息
  243. async getVideoInfo() {
  244. const { data: res } = await getModelDetailById(this.$route.query.id);
  245. if (res.videoId) {
  246. this.videoId = res.videoId;
  247. const { data: res2 } = await doGetVideoDetialById(res.videoId);
  248. this.videoSrc = res2.videoUrl;
  249. this.title = res2.title;
  250. this.coverUrl = res2.coverURL;
  251. this.height = res2.height;
  252. return;
  253. }
  254. this.videoSrc = res.videoPathUri + res.videoPath;
  255. this.title = res.title;
  256. this.coverUrl = res.coverURL;
  257. },
  258. // 处理时间
  259. formatTime(time) {
  260. const minutes = Math.floor(time / 60);
  261. const seconds = Math.round(time % 60);
  262. return `${minutes}:${seconds.toString().padStart(2, '0')}`;
  263. },
  264. ...mapActions(['setCharacters']),
  265. go(url) {
  266. this.$router.push(url);
  267. }
  268. }
  269. };
  270. </script>
  271. <style lang="scss" scoped>
  272. #player-container {
  273. width: 100%;
  274. height: calc(100vh - 40px);
  275. overflow: hidden;
  276. }
  277. .page {
  278. // width: 375px;
  279. // height: 100vh;
  280. overflow: hidden;
  281. }
  282. .content {
  283. width: 100%;
  284. height: calc(100vh - 40px);
  285. overflow: hidden;
  286. }
  287. .videoBox {
  288. display: flex;
  289. align-items: center;
  290. position: relative;
  291. width: 100%;
  292. height: calc(100vh - 40px);
  293. background-color: #f8f9f9;
  294. &-download {
  295. position: absolute;
  296. right: 15px;
  297. top: 15px;
  298. display: inline-block;
  299. width: 24px;
  300. height: 24px;
  301. z-index: 99;
  302. img {
  303. width: 100%;
  304. height: 100%;
  305. }
  306. }
  307. }
  308. </style>