ShareVideo.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <template>
  2. <el-main>
  3. <router-view />
  4. <el-row v-if="video !== null" class="movie-list">
  5. <el-col :md="15">
  6. <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
  7. <el-card class="box-card">
  8. <div slot="header" class="clearfix">
  9. <el-row>
  10. <el-button style="float: right; padding: 3px 0" type="text" @click="goToVideo">
  11. 原视频
  12. </el-button>
  13. </el-row>
  14. <el-row>
  15. <h3 v-html="video.title" />
  16. </el-row>
  17. <el-row style="color: #999;font-size: 16px;padding-top: 0px;">
  18. <span><i class="el-icon-video-play">{{ video.view }}</i></span>
  19. <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
  20. <span><i class="el-icon-s-comment">{{ video.comment }}</i></span>
  21. <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
  22. <span><i class="el-icon-watch">{{ video.pubDate }}</i></span>
  23. </el-row>
  24. </div>
  25. <div class="text item">
  26. <div id="dplayer" ref="dplayer" style="height: 480px;" />
  27. </div>
  28. </el-card>
  29. </el-row>
  30. <el-row style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
  31. <el-card class="box-card">
  32. <div class="text item">
  33. <!--视频描述行-->
  34. <span>
  35. <p style="white-space:pre-wrap" v-html="video.description" />
  36. </span>
  37. <el-divider />
  38. <!--视频标签行-->
  39. <div>
  40. <el-tag
  41. v-for="(tag,index) in video.tags"
  42. :key="index"
  43. class="tag"
  44. size="medium"
  45. effect="plain"
  46. >
  47. <router-link style="text-decoration-line: none" target="_blank" :to="`/video/tag/` + tag">
  48. {{ tag }}
  49. </router-link>
  50. </el-tag>
  51. </div>
  52. </div>
  53. </el-card>
  54. </el-row>
  55. </el-col>
  56. </el-row>
  57. </el-main>
  58. </template>
  59. <script>
  60. import UserAvatarCard from '@/components/card/UserAvatarCard'
  61. import DPlayer from 'dplayer'
  62. import { videoInfo, videoUrl } from '@/api/video'
  63. import { getShortVideo } from '@/api/video'
  64. import { getUserInfo } from '@/api/user'
  65. import { getAccessToken } from '@/utils/auth'
  66. export default {
  67. name: 'ShortVideo',
  68. data() {
  69. return {
  70. // 屏幕宽度, 为了控制分页条的大小
  71. screenWidth: document.body.clientWidth,
  72. shareId: null,
  73. video: null,
  74. user: null,
  75. userToken: null,
  76. danmaku: {
  77. api: process.env.VUE_APP_SERVER_URL + '/api/comment/danmaku/'
  78. }
  79. }
  80. },
  81. watch: {
  82. // 地址栏 url 发生变化时重新加载本页面
  83. $route() {
  84. this.$router.go()
  85. }
  86. },
  87. created() {
  88. this.userToken = getAccessToken()
  89. this.shareId = this.$route.params.shareId
  90. this.getVideoInfo(this.shareId)
  91. },
  92. mounted() {
  93. const header = this.$refs.header
  94. if (header !== undefined && header !== null) {
  95. this.wrapStyle = `height: calc(100vh - ${header.clientHeight + 20}px)`
  96. }
  97. },
  98. methods: {
  99. // 获取视频的详细信息
  100. getVideoInfo(videoId) {
  101. videoInfo(videoId).then(resp => {
  102. if (resp.code === 0) {
  103. this.video = resp.data
  104. this.getVideoUrl(this.video.videoId)
  105. document.title = resp.data.title
  106. this.userId = resp.data.userId
  107. getUserInfo(this.userId).then(resp => {
  108. if (resp.code === 0) {
  109. this.user = resp.data
  110. } else {
  111. this.$notify.error({
  112. message: '用户数据获取失败',
  113. type: 'warning',
  114. duration: 3000
  115. })
  116. }
  117. })
  118. }
  119. }).catch(error => {
  120. this.$notify.error({
  121. message: error.message,
  122. type: 'warning',
  123. duration: 3000
  124. })
  125. })
  126. },
  127. getShortVideoWrapper() {
  128. getShortVideo().then(resp => {
  129. if (resp.code === 0) {
  130. this.video = resp.data
  131. document.title = resp.data.title
  132. this.getVideoUrl(this.video.videoId)
  133. this.userId = resp.data.userId
  134. getUserInfo(this.userId).then(resp => {
  135. if (resp.code === 0) {
  136. this.user = resp.data
  137. } else {
  138. this.$notify.error({
  139. message: '用户数据获取失败',
  140. type: 'warning',
  141. duration: 3000
  142. })
  143. }
  144. })
  145. }
  146. }).catch(error => {
  147. this.$notify.error({
  148. message: error.message,
  149. type: 'warning',
  150. duration: 3000
  151. })
  152. })
  153. },
  154. async getVideoUrl(videoId) {
  155. videoUrl(videoId).then(res => {
  156. if (res.code === 0) {
  157. var sendEvent = false
  158. const urlType = res.data.type
  159. if (urlType === 'mp4') {
  160. const urls = res.data.urls
  161. for (const url of urls) {
  162. url.type = 'normal'
  163. }
  164. this.initMp4Player(this.userId, videoId, this.video.coverUrl, urls, res.data.currentTime, sendEvent)
  165. } else {
  166. this.$notify.error({
  167. message: '视频 url 类型不合法',
  168. type: 'warning',
  169. duration: 3000
  170. })
  171. }
  172. } else {
  173. this.$notify.error({
  174. message: '视频 url 获取失败',
  175. type: 'warning',
  176. duration: 3000
  177. })
  178. }
  179. }).catch(error => {
  180. this.$notify.error({
  181. message: error.message,
  182. type: 'error',
  183. duration: 3000
  184. })
  185. })
  186. },
  187. initMp4Player(userId, videoId, coverUrl, urls, pos, sendEvent) {
  188. const player = new DPlayer({
  189. container: document.querySelector('#dplayer'),
  190. lang: 'zh-cn',
  191. screenshot: true,
  192. autoplay: false,
  193. volume: 0.1,
  194. mutex: true,
  195. video: {
  196. pic: coverUrl,
  197. defaultQuality: 0,
  198. quality: urls,
  199. hotkey: true
  200. },
  201. danmaku: {
  202. id: videoId,
  203. maximum: 10000,
  204. api: this.danmaku.api,
  205. token: this.userToken,
  206. bottom: '15%',
  207. unlimited: true
  208. }
  209. })
  210. // 设置音量
  211. // player.volume(0.1, true, false)
  212. // 跳转到上次看到的位置
  213. player.seek(pos)
  214. var ended = false
  215. /* 事件绑定 */
  216. const that = this
  217. player.on('play', function() {
  218. if (sendEvent) {
  219. clearInterval(that.intervalEvent)
  220. that.intervalEvent = setInterval(() => {
  221. if (!ended) {
  222. const payload = {}
  223. payload.mediaId = videoId
  224. payload.mediaType = 1
  225. payload.currentTime = player.video.currentTime
  226. payload.ended = ended
  227. const jsonData = {}
  228. jsonData.event = 'media_progress'
  229. jsonData.data = JSON.stringify(payload)
  230. that.wsClient.send(jsonData)
  231. }
  232. }, 5000)
  233. }
  234. })
  235. player.on('ended', () => {
  236. clearInterval(that.intervalEvent)
  237. ended = true
  238. if (sendEvent) {
  239. const payload = {}
  240. payload.mediaId = videoId
  241. payload.mediaType = 1
  242. payload.currentTime = player.video.currentTime
  243. payload.ended = ended
  244. const jsonData = {}
  245. jsonData.event = 'media_progress'
  246. jsonData.data = JSON.stringify(payload)
  247. that.wsClient.send(jsonData)
  248. }
  249. })
  250. player.on('volumechange', () => {
  251. console.log('声音改变')
  252. })
  253. },
  254. goToVideo() {
  255. console.log(this.shareId)
  256. this.$router.push('/video/' + this.video.videoId)
  257. }
  258. }
  259. }
  260. </script>
  261. <style scoped>
  262. /*处于手机屏幕时*/
  263. @media screen and (max-width: 768px) {
  264. .movie-list {
  265. padding-top: 8px;
  266. padding-left: 0.5%;
  267. padding-right: 0.5%;
  268. }
  269. }
  270. .movie-list {
  271. padding-top: 15px;
  272. padding-left: 5%;
  273. padding-right: 5%;
  274. }
  275. .clearfix:before,
  276. .clearfix:after {
  277. display: table;
  278. content: "";
  279. }
  280. .clearfix:after {
  281. clear: both;
  282. }
  283. .logo {
  284. width: 30px;
  285. position: relative;
  286. }
  287. </style>