Bläddra i källkod

添加直播相关页面

reghao 3 år sedan
förälder
incheckning
05a6671121

+ 59 - 0
src/components/card/live-card.vue

@@ -0,0 +1,59 @@
+<template>
+  <!-- padding-left: 10px; padding-right: 10px; -->
+  <div style="width: 320px">
+    <router-link :to="`/video/${videoInfo.videoId}`">
+      <div style="position: relative; width: 320px; height: 180px;">
+        <v-img
+          :src="videoInfo.coverUrl"
+          outlined
+          aspect-ratio="1.77"
+        />
+        <span style="position: absolute; bottom: 0; right: 0; color:red">{{ videoInfo.duration }}</span>
+      </div>
+    </router-link>
+    <v-row>
+      <v-col cols="2">
+        <router-link v-if="videoInfo.userId !== null" :to="`/u/${videoInfo.userId}`">
+          <v-avatar size="48">
+            <v-img :src="videoInfo.avatarUrl" />
+          </v-avatar>
+        </router-link>
+      </v-col>
+      <v-col cols="10">
+        <p style="font-size: 15px; margin-bottom: 0px;color: black;">
+          <router-link :to="`/video/${videoInfo.videoId}`" style="color: black;"> {{ videoInfo.title }} </router-link>
+        </p>
+        <p style="font-size: 10px; color: #606060;">
+          <router-link v-if="videoInfo.userId !== null" :to="`/u/${videoInfo.userId}`"> {{ videoInfo.username }}</router-link>
+          <br>
+          {{ videoInfo.viewCount }} 观看 <span v-html="`&nbsp;&nbsp;`" />
+          {{ videoInfo.commentCount }} 评论 <span v-html="`&nbsp;&nbsp;`" />
+        </p>
+      </v-col>
+    </v-row>
+  </div>
+</template>
+
+<script>
+import TimeUtil from '@/utils/time-util.vue'
+export default {
+  name: 'LiveCard',
+  props: {
+    video: {
+      type: Object,
+      default: () => {}
+    }
+  },
+  data() {
+    return {
+      TimeUtil,
+      videoInfo: this.video
+    }
+  },
+  created() {
+  }
+}
+</script>
+
+<style>
+</style>

+ 14 - 20
src/components/player/live-player.vue

@@ -1,17 +1,18 @@
 <template>
   <video
-      id="videoElement"
-      ref="videoElement"
-      controls
-      muted
-      width="100%"
-      height="100%"
-  ></video>
+    id="videoElement"
+    ref="videoElement"
+    controls
+    muted
+    width="60%"
+    height="30%"
+  />
 </template>
 
 <script>
 import { videoUrl } from '@/api/media/video'
 const flvjs = require('flv.js')
+
 export default {
   name: 'LivePlayer',
   data() {
@@ -21,7 +22,7 @@ export default {
     }
   },
   mounted() {
-    const userInfo = this.$store.state.user.userInfo
+    /* const userInfo = this.$store.state.user.userInfo
     if (userInfo != null) {
       this.userId = userInfo.userId.toString()
     } else {
@@ -29,12 +30,11 @@ export default {
     }
 
     this.videoId = this.$route.params.id
-    this.getVideoUrl(this.videoId)
+    this.getVideoUrl(this.videoId)*/
+    const videoUrl = 'http://127.0.0.1:9090/live/first'
+    this.initFlvPlayer(videoUrl)
   },
   methods: {
-    // TODO 获取弹幕配置,将 videoUrl 作为本函数的回调
-    danmakuConfig() {
-    },
     getVideoUrl(videoId) {
       videoUrl(videoId)
         .then(res => {
@@ -43,13 +43,7 @@ export default {
             var coverUrl = res.data.coverUrl
             var videoUrl = res.data.videoUrl
             var urlType = res.data.urlType
-            if (urlType === 'mp4') {
-              this.initMp4Player(this.videoId, coverUrl, videoUrl)
-            } else if (urlType === 'hls') {
-              this.initHlsPlayer(this.videoId, coverUrl, videoUrl)
-            } else if (urlType === 'dash') {
-              this.initDashPlayer(this.videoId, coverUrl, videoUrl)
-            } else if (urlType === 'flv') {
+            if (urlType === 'flv') {
               this.initFlvPlayer(this.videoId, coverUrl, videoUrl)
             } else {
               console.log('无法识别 url 类型')
@@ -62,7 +56,7 @@ export default {
           console.error(error.message)
         })
     },
-    initFlvPlayer(videoId, coverUrl, videoUrl) {
+    initFlvPlayer(videoUrl) {
       var videoElement = document.getElementById('videoElement')
       const flvPlayer = flvjs.createPlayer({
         type: 'flv',

+ 1 - 1
src/layout/index.vue

@@ -8,7 +8,7 @@
       <v-container class="py-0 fill-height">
         <v-toolbar-title
           class="hidden-sm-and-down ml-0 pl-4"
-          style="width: 150px"
+          style="width: 60px"
         >
           <span style="cursor:pointer" @click="goToHome()">{{ this.$store.state.webInfo.name }}</span>
         </v-toolbar-title>

+ 68 - 5
src/views/home/live.vue

@@ -1,15 +1,78 @@
 <template>
-  <v-card-text class="text-h5 font-weight-light">
-    直播页面待开发
-  </v-card-text>
+  <video
+    id="videoElement"
+    ref="videoElement"
+    controls
+    muted
+    width="60%"
+    height="30%"
+  />
 </template>
 
 <script>
+import { videoUrl } from '@/api/media/video'
+const flvjs = require('flv.js')
+
 export default {
-  name: 'Live'
+  name: 'LivePlayer',
+  data() {
+    return {
+      flvjs,
+      videoId: ''
+    }
+  },
+  mounted() {
+    /* const userInfo = this.$store.state.user.userInfo
+    if (userInfo != null) {
+      this.userId = userInfo.userId.toString()
+    } else {
+      this.userId = 111222333
+    }
+
+    this.videoId = this.$route.params.id
+    this.getVideoUrl(this.videoId)*/
+    const videoUrl = 'http://127.0.0.1:9090/live/01.flv'
+    this.initFlvPlayer(videoUrl)
+  },
+  methods: {
+    getVideoUrl(videoId) {
+      videoUrl(videoId)
+        .then(res => {
+          if (res.code === 0) {
+            // TODO 返回一个 dplayer 播放器对象,包含一些常用的属性
+            var coverUrl = res.data.coverUrl
+            var videoUrl = res.data.videoUrl
+            var urlType = res.data.urlType
+            if (urlType === 'flv') {
+              this.initFlvPlayer(this.videoId, coverUrl, videoUrl)
+            } else {
+              console.log('无法识别 url 类型')
+            }
+          } else {
+            console.error(res.msg)
+          }
+        })
+        .catch(error => {
+          console.error(error.message)
+        })
+    },
+    initFlvPlayer(videoUrl) {
+      var videoElement = document.getElementById('videoElement')
+      const flvPlayer = flvjs.createPlayer({
+        type: 'flv',
+        isLive: true,
+        url: videoUrl
+      })
+      flvPlayer.attachMediaElement(videoElement)
+      flvPlayer.load()
+      flvPlayer.play()
+    }
+  }
 }
 </script>
 
 <style>
-
+#dplayer {
+  height: 500px;
+}
 </style>

+ 67 - 0
src/views/home/live1.vue

@@ -0,0 +1,67 @@
+<template>
+  <v-container fill-height fluid style="padding-left: 24px; padding-right: 24px">
+    <div v-infinite-scroll="loadMore" infinite-scroll-disabled="true" infinite-scroll-distance="10">
+      <v-row no-gutters>
+        <v-col
+          v-for="item in videoList"
+          :key="item.id"
+        >
+          <LiveCard :video="item" />
+        </v-col>
+      </v-row>
+    </div>
+  </v-container>
+</template>
+
+<script>
+import { videoRecommend } from '@/api/media/video'
+import LiveCard from '@/components/card/live-card.vue'
+
+export default {
+  name: 'Index',
+  components: {
+    LiveCard
+  },
+  data() {
+    return {
+      videoList: [],
+      busy: false,
+      page: 1
+    }
+  },
+  created() {
+    this.getRecommendVideo(this.page)
+  },
+  methods: {
+    loadMore: function() {
+      this.busy = true
+      setTimeout(() => {
+        this.getRecommendVideo(this.page)
+      }, 1000)
+    },
+    getRecommendVideo(page) {
+      videoRecommend(page)
+        .then(res => {
+          if (res.code === 0) {
+            for (const item of res.data.list) {
+              this.videoList.push(item)
+            }
+            this.page += 1
+            this.busy = false
+          } else {
+            console.error(res.msg)
+          }
+        })
+        .catch(error => {
+          console.error(error.message)
+        })
+    }
+  }
+}
+</script>
+
+<style>
+a {
+  text-decoration: none;
+}
+</style>

+ 393 - 0
src/views/video/liveplayer.vue

@@ -0,0 +1,393 @@
+<template>
+  <div v-if="videoData !== null">
+    <v-container>
+      <v-col>
+        <v-row>
+          <v-col>
+            <h3 v-text="videoData.title" />
+          </v-col>
+        </v-row>
+        <v-row>
+          <v-col style="color: #999;font-size: 12px;padding-top: 0px;">
+            <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
+            <span v-text="videoData.viewCount" /> 次观看 <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
+            <span v-text="TimeUtil.renderTime(videoData.pubDate)" />
+          </v-col>
+        </v-row>
+      </v-col>
+    </v-container>
+    <v-container>
+      <v-row>
+        <v-col>
+          <VideoPlayer v-if="vidProp !== null" :video-prop="vidProp" />
+        </v-col>
+      </v-row>
+    </v-container>
+    <v-container fill-height style="padding-top: 0px;">
+      <v-row v-resize="onResize" no-gutters>
+        <v-col :cols="colsWidth">
+          <v-row>
+            <v-col cols="2">
+              <v-btn icon @click="thumbsupVideo">
+                <v-icon>mdi-thumb-up</v-icon>
+                <span>点赞(10000)</span>
+              </v-btn>
+            </v-col>
+            <v-col cols="2">
+              <v-btn icon @click="collectVideo">
+                <v-icon>mdi-bookmark</v-icon>
+                <span>收藏(10000)</span>
+              </v-btn>
+            </v-col>
+            <v-col cols="2">
+              <v-btn icon @click="openRepostDialog">
+                <v-icon>mdi-repeat</v-icon>
+                <span>转发(10000)</span>
+              </v-btn>
+              <v-dialog
+                v-model="showRepostDialog"
+                persistent
+                max-width="600px"
+              >
+                <v-card>
+                  <v-card-text>
+                    <span>https://bili.reghao.cn/video/lajfda</span>
+                  </v-card-text>
+                  <v-card-actions>
+                    <v-spacer />
+                    <v-btn
+                      color="blue darken-1"
+                      text
+                      @click="showRepostDialog = false"
+                    >
+                      关闭
+                    </v-btn>
+                  </v-card-actions>
+                </v-card>
+              </v-dialog>
+            </v-col>
+            <v-col cols="2">
+              <v-btn icon @click="openSuggestDialog">
+                <v-icon>mdi-thumb-up</v-icon>
+                <span>反馈</span>
+              </v-btn>
+              <v-dialog
+                v-model="showDialog"
+                persistent
+                max-width="600px"
+              >
+                <v-card>
+                  <v-card-title>
+                    <span class="text-h5">问题或建议</span>
+                  </v-card-title>
+                  <v-card-text>
+                    <v-container>
+                      <v-row>
+                        <v-col
+                          cols="12"
+                          sm="6"
+                        >
+                          <v-select
+                            :items="['视频封面', '视频播放', '视频内容']"
+                            label="问题分类"
+                            required
+                          />
+                        </v-col>
+                        <v-col
+                          cols="24"
+                          sm="6"
+                          md="4"
+                        >
+                          <v-text-field
+                            label="问题或建议"
+                            required
+                          />
+                        </v-col>
+                      </v-row>
+                    </v-container>
+                  </v-card-text>
+                  <v-card-actions>
+                    <v-spacer />
+                    <v-btn
+                      color="blue darken-1"
+                      text
+                      @click="showDialog = false"
+                    >
+                      关闭
+                    </v-btn>
+                    <v-btn
+                      color="blue darken-1"
+                      text
+                      @click="submitVideoErr"
+                    >
+                      提交
+                    </v-btn>
+                  </v-card-actions>
+                </v-card>
+              </v-dialog>
+            </v-col>
+            <v-col cols="2">
+              <v-btn icon @click="openEditDialog">
+                <v-icon>mdi-thumb-up</v-icon>
+                <span>编辑</span>
+              </v-btn>
+              <v-dialog
+                v-model="showEditDialog"
+                persistent
+                max-width="600px"
+              >
+                <v-form
+                  ref="form"
+                  v-model="editVideo"
+                  lazy-validation
+                >
+                  <v-text-field
+                    v-model="videoId"
+                    label="视频 ID"
+                    required
+                  />
+
+                  <v-text-field
+                    v-model="editVideo.pubDate"
+                    label="发布日期"
+                    required
+                  />
+
+                  <v-btn
+                    color="success"
+                    class="mr-4"
+                    @click="submitEdit"
+                  >
+                    提交
+                  </v-btn>
+
+                  <v-btn
+                    color="error"
+                    class="mr-4"
+                    @click="resetEdit"
+                  >
+                    重置
+                  </v-btn>
+                  <v-btn
+                    color="error"
+                    class="mr-4"
+                    @click="showEditDialog = false"
+                  >
+                    取消
+                  </v-btn>
+                </v-form>
+              </v-dialog>
+            </v-col>
+          </v-row>
+          <!--<v-row>
+            <v-col style="color: #999;font-size: 12px;padding-top: 0px;">
+              &lt;!&ndash;
+              TODO 视频分类待完成
+              <router-link v-if="videoData.childrenCategory.fatherId !== 0" :to="`/v/${videoData.fatherCategory.id}`" class="category-link">
+                <span v-text="videoData.fatherCategory.name" />
+              </router-link>
+              /
+              <router-link :to="`/v/${videoData.childrenCategory.id}`" class="category-link">
+                <span v-text="videoData.childrenCategory.name" />
+              </router-link>&ndash;&gt;
+              <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
+            </v-col>
+          </v-row>-->
+          <v-divider />
+          <v-row>
+            <v-col>
+              <span v-text="videoData.description" />
+            </v-col>
+          </v-row>
+          <v-divider />
+          <v-row>
+            <v-col>
+              <span >{{videoData.tags}}</span>
+<!--              <span v-for="item in videoData.tags" :key="item">
+                <v-btn rounded small text color="primary" dark @click="jumpToTagPage(item)">{{ item }}</v-btn>
+              </span>-->
+            </v-col>
+          </v-row>
+          <v-divider />
+          <v-row>
+            <v-col cols="2" align-self="end">
+              <router-link :to="`/u/${videoData.userId}`">
+                <v-avatar size="48">
+                  <v-img :src="videoData.avatarUrl" />
+                </v-avatar>
+              </router-link>
+            </v-col>
+            <v-col>
+              <router-link :to="`/u/${videoData.userId}`">
+                <span v-text="videoData.username" />
+              </router-link>
+              <span v-html="'&nbsp;&nbsp;&nbsp;&nbsp;'" />
+              <v-btn small outlined color="primary" @click="followingUser">
+                <span>关注</span>
+                <span v-html="'&nbsp;&nbsp;'" />
+                <span v-text="videoData.followerCount" />
+              </v-btn>
+            </v-col>
+          </v-row>
+        </v-col>
+      </v-row>
+      <v-row>
+        <v-col :cols="colsWidth">
+          <!--          <CommentCard v-if="videoData !== null" :video="videoData" />-->
+          <CommentCard v-if="showComment === true" :video="videoData" />
+        </v-col>
+        <v-col>
+          相关推荐:
+          <v-row no-gutters>
+            <v-col
+              v-for="item in videoList"
+              :key="item.videoId"
+            >
+              <item-card :video="item" />
+            </v-col>
+          </v-row>
+        </v-col>
+      </v-row>
+    </v-container>
+  </div>
+</template>
+
+<script>
+import { similarVideo, videoInfo } from '@/api/media/video'
+import ItemCard from '@/components/card/item-card.vue'
+import CommentCard from '@/components/card/comment-card.vue'
+import VideoPlayer from '@/components/player/player.vue'
+import TimeUtil from '@/utils/time-util.vue'
+
+export default {
+  name: 'Video',
+  components: {
+    ItemCard,
+    CommentCard,
+    VideoPlayer
+  },
+  data() {
+    return {
+      videoList: [],
+      score: 0,
+      TimeUtil,
+      videoId: '',
+      videoData: null,
+      windowSize: {
+      },
+      colsWidth: 8,
+      showDialog: false,
+      showRepostDialog: false,
+      formData: {},
+      collectionDialog: false,
+      isCollected: '收藏',
+      showEditDialog: false,
+      editVideo: {
+        videoId: null,
+        pubDate: null
+      },
+      showComment: false,
+      vidProp: null
+    }
+  },
+  created() {
+    // 获取 url 上的 path 参数
+    this.videoId = this.$route.params.id
+    // 请求后端获取数据
+    this.getVideoInfo(this.videoId)
+    this.onResize()
+    this.getSimilarVideos(this.videoId)
+  },
+  mounted() {
+  },
+  methods: {
+    // 控制页面大小
+    onResize() {
+      this.windowSize = { x: window.innerWidth, y: window.innerHeight }
+      if (this.windowSize.x < 900) {
+        this.colsWidth = 12
+      } else {
+        this.colsWidth = 8
+      }
+    },
+    // 获取视频的详细信息
+    getVideoInfo(videoId) {
+      videoInfo(videoId)
+        .then(res => {
+          if (res.code === 0) {
+            const vidData = res.data
+            this.videoData = vidData
+            document.title = vidData.title
+
+            const vidProp = {}
+            vidProp.videoId = vidData.videoId
+            vidProp.coverUrl = vidData.coverUrl
+            vidProp.scope = vidData.scope
+            this.vidProp = vidProp
+          } else {
+            console.error(res.msg)
+          }
+        })
+        .catch(error => {
+          console.error(error.message)
+        })
+    },
+    // 获取和当前视频类似的其他视频
+    getSimilarVideos(videoId) {
+      similarVideo(videoId)
+        .then(res => {
+          if (res.code === 0) {
+            this.videoList = res.data
+          } else {
+            console.error(res.msg)
+          }
+        })
+        .catch(error => {
+          console.error(error.message)
+        })
+    },
+    followingUser() {
+      console.log('关注 UP 主')
+    },
+    thumbsupVideo() {
+      console.log('点赞 ' + this.videoId)
+    },
+    collectVideo() {
+      console.log('收藏 ' + this.videoId)
+    },
+    jumpToTagPage(value) {
+      console.log('跳转到标签页: ' + value)
+      this.$router.push({
+        path: '/tag/result',
+        query: {
+          tag: value,
+          page: 1
+        }
+      })
+    },
+    openRepostDialog() {
+      this.showRepostDialog = true
+      console.log('获取一个 short url')
+    },
+    openSuggestDialog() {
+      this.showDialog = true
+    },
+    openEditDialog() {
+      this.showEditDialog = true
+    },
+    submitVideoErr() {
+      this.showDialog = false
+      console.log('提交视频错误')
+    },
+    submitEdit() {
+      console.log('提交编辑信息')
+    },
+    resetEdit() {
+      console.log('重置编辑信息')
+    }
+  }
+}
+</script>
+
+<style>
+</style>