reghao преди 3 години
родител
ревизия
86d15b97fd
променени са 6 файла, в които са добавени 406 реда и са изтрити 2 реда
  1. 65 2
      package-lock.json
  2. 1 0
      package.json
  3. 3 0
      src/components/player/player.vue
  4. 100 0
      src/utils/ws/socket-instance.js
  5. 236 0
      src/utils/ws/ws-socket.js
  6. 1 0
      src/views/user/vip.vue

+ 65 - 2
package-lock.json

@@ -2512,6 +2512,14 @@
       "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=",
       "dev": true
     },
+    "async-validator": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-1.8.5.tgz",
+      "integrity": "sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==",
+      "requires": {
+        "babel-runtime": "6.x"
+      }
+    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
@@ -2596,6 +2604,11 @@
         "resolve": "^1.12.0"
       }
     },
+    "babel-helper-vue-jsx-merge-props": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
+      "integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
+    },
     "babel-loader": {
       "version": "8.1.0",
       "resolved": "https://registry.npm.taobao.org/babel-loader/download/babel-loader-8.1.0.tgz?cache=0&sync_timestamp=1584715910722&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-loader%2Fdownload%2Fbabel-loader-8.1.0.tgz",
@@ -2618,6 +2631,27 @@
         "object.assign": "^4.1.0"
       }
     },
+    "babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz",
+      "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+      "requires": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.6.12",
+          "resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
+          "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
+        },
+        "regenerator-runtime": {
+          "version": "0.11.1",
+          "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+          "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+        }
+      }
+    },
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz",
@@ -4270,8 +4304,7 @@
     "deepmerge": {
       "version": "1.5.2",
       "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-1.5.2.tgz?cache=0&sync_timestamp=1572279556265&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdeepmerge%2Fdownload%2Fdeepmerge-1.5.2.tgz",
-      "integrity": "sha1-EEmdhohEza1P7ghC34x/bwyVp1M=",
-      "dev": true
+      "integrity": "sha1-EEmdhohEza1P7ghC34x/bwyVp1M="
     },
     "default-gateway": {
       "version": "5.0.5",
@@ -4728,6 +4761,26 @@
       "integrity": "sha1-LnAjRITgPXx+kDENfXn9N3U3nDQ=",
       "dev": true
     },
+    "element-ui": {
+      "version": "2.13.2",
+      "resolved": "https://registry.npmmirror.com/element-ui/-/element-ui-2.13.2.tgz",
+      "integrity": "sha512-r761DRPssMPKDiJZWFlG+4e4vr0cRG/atKr3Eqr8Xi0tQMNbtmYU1QXvFnKiFPFFGkgJ6zS6ASkG+sellcoHlQ==",
+      "requires": {
+        "async-validator": "~1.8.1",
+        "babel-helper-vue-jsx-merge-props": "^2.0.0",
+        "deepmerge": "^1.2.0",
+        "normalize-wheel": "^1.0.1",
+        "resize-observer-polyfill": "^1.5.0",
+        "throttle-debounce": "^1.0.1"
+      },
+      "dependencies": {
+        "throttle-debounce": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz",
+          "integrity": "sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg=="
+        }
+      }
+    },
     "elliptic": {
       "version": "6.5.3",
       "resolved": "https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.3.tgz?cache=0&sync_timestamp=1592492844326&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felliptic%2Fdownload%2Felliptic-6.5.3.tgz",
@@ -7839,6 +7892,11 @@
       "integrity": "sha1-suHE3E98bVd0PfczpPWXjRhlBVk=",
       "dev": true
     },
+    "normalize-wheel": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
+      "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA=="
+    },
     "npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npm.taobao.org/npm-run-path/download/npm-run-path-2.0.2.tgz",
@@ -9585,6 +9643,11 @@
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
       "dev": true
     },
+    "resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+    },
     "resolve": {
       "version": "1.17.0",
       "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.17.0.tgz",

+ 1 - 0
package.json

@@ -22,6 +22,7 @@
     "hls.js": "^1.1.2",
     "js-cookie": "2.2.0",
     "jsencrypt": "^3.2.1",
+    "element-ui": "2.13.2",
     "shaka-player": "^3.2.1",
     "v-viewer": "^1.6.4",
     "vue": "^2.6.12",

+ 3 - 0
src/components/player/player.vue

@@ -63,6 +63,8 @@
 
 <script>
 import { videoUrl, submitPlayRecord } from '@/api/media/video'
+import SocketInstance from '@/utils/ws/socket-instance'
+
 const hls = require('hls.js')
 const dashjs = require('dashjs')
 const flv = require('flv.js')
@@ -117,6 +119,7 @@ export default {
     }
   },
   mounted() {
+    SocketInstance.connect()
     const videoId = this.videoProp.videoId
     if (this.getUrl) {
       this.getVideoUrl(videoId)

+ 100 - 0
src/utils/ws/socket-instance.js

@@ -0,0 +1,100 @@
+import WsSocket from '@/utils/ws/ws-socket'
+import { getToken } from '@/utils/auth'
+import { Notification } from 'element-ui'
+
+/**
+ * SocketInstance 连接实例
+ *
+ * 注释: 所有 WebSocket 消息接收处理在此实例中处理
+ */
+class SocketInstance {
+  /**
+   * WsSocket 实例
+   */
+  socket
+
+  /**
+   * SocketInstance 初始化实例
+   */
+  constructor() {
+    this.socket = new WsSocket(
+      () => {
+        const token = getToken()
+        if (token === null || token === '') {
+          return null
+        } else {
+          return 'ws://push.reghao.cn/ws/push?token=' + token
+        }
+      },
+      {
+        onError: evt => {
+          console.log('Websocket 连接失败回调方法')
+        },
+        // Websocket 连接成功回调方法
+        onOpen: evt => {
+          // 更新 WebSocket 连接状态
+          console.log('ws 连接成功')
+        },
+        // Websocket 断开连接回调方法
+        onClose: evt => {
+          // 更新 WebSocket 连接状态
+          console.log('ws 连接断开')
+        }
+      }
+    )
+
+    this.registerEvents()
+  }
+
+  // 连接 WebSocket 服务
+  connect() {
+    this.socket.connection()
+  }
+
+  /**
+   * 注册回调消息处理事件
+   */
+  registerEvents() {
+    this.socket.on('heartbeat', data => {})
+
+    this.socket.on('censorVideo', data => {
+      console.log('----------')
+      console.log(data)
+      Notification({
+        title: '友情提示',
+        message: data,
+        type: 'warning',
+        duration: 3000
+      })
+    })
+
+    this.socket.on('event_error', data => {
+      Notification({
+        title: '友情提示',
+        message: data.message,
+        type: 'warning'
+      })
+    })
+  }
+
+  /**
+   * 聊天发送数据
+   *
+   * @param {Object} mesage
+   */
+  send(mesage) {
+    this.socket.send(mesage)
+  }
+
+  /**
+   * 推送消息
+   *
+   * @param {String} event 事件名
+   * @param {Object} data 数据
+   */
+  emit(event, data) {
+    this.socket.emit(event, data)
+  }
+}
+
+export default new SocketInstance()

+ 236 - 0
src/utils/ws/ws-socket.js

@@ -0,0 +1,236 @@
+class WsSocket {
+  /**
+   * Websocket 连接
+   *
+   * @var Websocket
+   */
+  connect = null
+
+  /**
+   * 配置信息
+   *
+   * @var Object
+   */
+  config = {
+    heartbeat: {
+      setInterval: null,
+      pingInterval: 20000,
+      pingTimeout: 60000
+    },
+    reconnect: {
+      lockReconnect: false,
+      setTimeout: null, // 计时器对象
+      time: 5000, // 重连间隔时间
+      number: 1000 // 重连次数
+    }
+  }
+
+  // 最后心跳时间
+  lastTime = 0
+
+  /**
+   * 自定义绑定消息事件
+   *
+   * @var Array
+   */
+  onCallBacks = []
+
+  defaultEvent = {
+    onError: evt => {},
+    onOpen: evt => {},
+    onClose: evt => {}
+  }
+
+  /**
+   * 创建 WsSocket 的实例
+   *
+   * @param {Function} urlCallBack url闭包函数
+   * @param {Object} events 原生 WebSocket 绑定事件
+   */
+  constructor(urlCallBack, events) {
+    this.urlCallBack = urlCallBack
+
+    // 定义 WebSocket 原生方法
+    this.events = Object.assign({}, this.defaultEvent, events)
+
+    this.on('connect', data => {
+      this.config.heartbeat.pingInterval = data.ping_interval * 1000
+      this.config.heartbeat.pingTimeout = data.ping_timeout * 1000
+      this.heartbeat()
+    })
+  }
+
+  /**
+   * 事件绑定
+   *
+   * @param {String} event 事件名
+   * @param {Function} callBack 回调方法
+   */
+  on(event, callBack) {
+    this.onCallBacks[event] = callBack
+    return this
+  }
+
+  /**
+   * 加载 WebSocket
+   */
+  loadSocket() {
+    const url = this.urlCallBack()
+    const connect = new WebSocket(url)
+    connect.onerror = this.onError.bind(this)
+    connect.onopen = this.onOpen.bind(this)
+    connect.onmessage = this.onMessage.bind(this)
+    connect.onclose = this.onClose.bind(this)
+    this.connect = connect
+  }
+
+  /**
+   * 连接 Websocket
+   */
+  connection() {
+    this.connect === null && this.loadSocket()
+  }
+
+  /**
+   * 掉线重连 Websocket
+   */
+  reconnect() {
+    // 没连接上会一直重连,设置延迟避免请求过多
+    clearTimeout(this.config.reconnect.setTimeout)
+
+    this.config.reconnect.setTimeout = setTimeout(() => {
+      this.connection()
+
+      console.log(`网络连接已断开,正在尝试重新连接...`)
+    }, this.config.reconnect.time)
+  }
+
+  /**
+   * 解析接受的消息
+   *
+   * @param {Object} evt Websocket 消息
+   */
+  onParse(evt) {
+    const { event, payload } = JSON.parse(evt.data)
+
+    return {
+      event: event,
+      payload: payload,
+      orginData: evt.data
+    }
+  }
+
+  /**
+   * 打开连接
+   *
+   * @param {Object} evt Websocket 消息
+   */
+  onOpen(evt) {
+    this.lastTime = new Date().getTime()
+
+    this.events.onOpen(evt)
+
+    this.ping()
+  }
+
+  /**
+   * 关闭连接
+   *
+   * @param {Object} evt Websocket 消息
+   */
+  onClose(evt) {
+    this.events.onClose(evt)
+
+    this.connect.close()
+
+    this.connect = null
+
+    evt.code === 1006 && this.reconnect()
+  }
+
+  /**
+   * 连接错误
+   *
+   * @param {Object} evt Websocket 消息
+   */
+  onError(evt) {
+    this.events.onError(evt)
+    this.connect.close()
+    this.connect = null
+    this.reconnect()
+  }
+
+  /**
+   * 接收消息
+   *
+   * @param {Object} evt Websocket 消息
+   */
+  onMessage(evt) {
+    this.lastTime = new Date().getTime()
+    const payload = evt.data
+    console.log(payload)
+    const result = this.onParse(evt)
+
+    // 判断消息事件是否被绑定
+    // eslint-disable-next-line no-prototype-builtins
+    if (this.onCallBacks.hasOwnProperty(result.event)) {
+      this.onCallBacks[result.event](result.payload, result.orginData)
+    } else {
+      console.warn(`WsSocket 消息事件[${result.event}]未绑定...`)
+    }
+  }
+
+  /**
+   * WebSocket 心跳检测
+   */
+  heartbeat() {
+    this.config.heartbeat.setInterval = setInterval(() => {
+      const t = new Date().getTime()
+
+      if (t - this.lastTime > this.config.heartbeat.pingTimeout) {
+        this.reconnect()
+      } else {
+        this.ping()
+      }
+    }, this.config.heartbeat.pingInterval)
+  }
+
+  ping() {
+    this.connect.send('{"event":"heartbeat","payload":"ping"}')
+  }
+
+  /**
+   * 聊天发送数据
+   *
+   * @param {Object} mesage
+   */
+  send(mesage) {
+    this.connect.send(JSON.stringify(mesage))
+  }
+
+  /**
+   * 关闭连接
+   */
+  close() {
+    this.connect.close()
+  }
+
+  /**
+   * 推送消息
+   *
+   * @param {String} event 事件名
+   * @param {Object} data 数据
+   */
+  emit(event, data) {
+    const payload = JSON.stringify({ event, data })
+
+    if (this.connect && this.connect.readyState === 1) {
+      this.connect.send(payload)
+    } else {
+      alert('WebSocket 连接已关闭...')
+      console.error('WebSocket 连接已关闭...', this.connect)
+    }
+  }
+}
+
+export default WsSocket

+ 1 - 0
src/views/user/vip.vue

@@ -65,6 +65,7 @@ export default {
     }
   },
   created() {
+    console.log(this.$store.state.user.userInfo.vip)
   }
 }
 </script>