From bb080c2d185b26c2028174fea8071b3a434d9e5b Mon Sep 17 00:00:00 2001
From: lyswhut <lyswhut@qq.com>
Date: Sat, 14 Mar 2020 20:58:03 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=AD=8C=E5=8D=95=E6=89=93?=
 =?UTF-8?q?=E5=BC=80=E6=9C=BA=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 publish/changeLog.md                      |  1 +
 src/renderer/lang/cns/view/song_list.json |  3 +-
 src/renderer/lang/cnt/view/song_list.json |  3 +-
 src/renderer/lang/en/view/song_list.json  |  3 +-
 src/renderer/store/modules/songList.js    | 19 +++++-
 src/renderer/utils/music/bd/songList.js   | 11 +++-
 src/renderer/utils/music/kg/songList.js   |  5 ++
 src/renderer/utils/music/kw/songList.js   |  7 +++
 src/renderer/utils/music/mg/songList.js   |  6 ++
 src/renderer/utils/music/tx/songList.js   | 77 +++++++++++++++--------
 src/renderer/utils/music/wy/songList.js   |  6 ++
 src/renderer/views/SongList.vue           | 65 +++++--------------
 12 files changed, 124 insertions(+), 82 deletions(-)

diff --git a/publish/changeLog.md b/publish/changeLog.md
index af8ae7ff..5ef6a42e 100644
--- a/publish/changeLog.md
+++ b/publish/changeLog.md
@@ -13,6 +13,7 @@
 - 优化月里嫦娥皮肤侧栏鼠标悬浮颜色
 - 优化播放进度条的动画效果
 - 现在添加下载任务时,后面添加的任务会在列表顶部插入
+- 优化歌单打开机制,现在歌单加载失败时会提示加载失败了,并且支持企鹅歌单的短链接了
 
 ### 修复
 
diff --git a/src/renderer/lang/cns/view/song_list.json b/src/renderer/lang/cns/view/song_list.json
index df6676f5..cff89875 100644
--- a/src/renderer/lang/cns/view/song_list.json
+++ b/src/renderer/lang/cns/view/song_list.json
@@ -1,4 +1,5 @@
 {
   "back": "返回",
-  "loding_list": "列表加載中..."
+  "loding_list": "列表加載中...",
+  "loding_list_fail": "列表加载失败"
 }
diff --git a/src/renderer/lang/cnt/view/song_list.json b/src/renderer/lang/cnt/view/song_list.json
index e5ebd831..41b47663 100644
--- a/src/renderer/lang/cnt/view/song_list.json
+++ b/src/renderer/lang/cnt/view/song_list.json
@@ -1,4 +1,5 @@
 {
   "back": "返回",
-  "loding_list": "列表加载中..."
+  "loding_list": "列表加载中...",
+  "loding_list_fail": "列表加載失敗"
 }
diff --git a/src/renderer/lang/en/view/song_list.json b/src/renderer/lang/en/view/song_list.json
index 986f6428..6985b820 100644
--- a/src/renderer/lang/en/view/song_list.json
+++ b/src/renderer/lang/en/view/song_list.json
@@ -1,4 +1,5 @@
 {
   "back": "Back",
-  "loding_list": "List loading..."
+  "loding_list": "List loading...",
+  "loding_list_fail": "List loading failed"
 }
diff --git a/src/renderer/store/modules/songList.js b/src/renderer/store/modules/songList.js
index 0f7af9da..ff516871 100644
--- a/src/renderer/store/modules/songList.js
+++ b/src/renderer/store/modules/songList.js
@@ -73,8 +73,13 @@ const actions = {
   getListDetail({ state, rootState, commit }, { id, page }) {
     let source = rootState.setting.songList.source
     let key = `sdetail__${source}__${id}__${page}`
-    if (state.listDetail.list.length && state.listDetail.key == key) return true
-    return (cache.has(key) ? Promise.resolve(cache.get(key)) : music[source].songList.getListDetail(id, page)).then(result => commit('setListDetail', { result, key, page }))
+    if (state.listDetail.list.length && state.listDetail.key == key) return Promise.resolve()
+    commit('clearListDetail')
+    return (
+      cache.has(key)
+        ? Promise.resolve(cache.get(key))
+        : music[source].songList.getListDetail(id, page)
+    ).then(result => commit('setListDetail', { result, key, page }))
   },
 /*   getListDetailAll({ state, rootState }, id) {
     let source = rootState.setting.songList.source
@@ -141,7 +146,15 @@ const mutations = {
     state.selectListInfo = info
   },
   clearListDetail(state) {
-    state.listDetail.list = []
+    state.listDetail = {
+      list: [],
+      desc: null,
+      total: 0,
+      page: 1,
+      limit: 30,
+      key: null,
+      info: {},
+    }
   },
 }
 
diff --git a/src/renderer/utils/music/bd/songList.js b/src/renderer/utils/music/bd/songList.js
index a42f3ede..63bbdce1 100644
--- a/src/renderer/utils/music/bd/songList.js
+++ b/src/renderer/utils/music/bd/songList.js
@@ -20,6 +20,10 @@ export default {
       id: '0',
     },
   ],
+  regExps: {
+    // http://music.taihe.com/songlist/566347741
+    listDetailLink: /^.+\/songlist\/(\d+)(?:\?.*|&.*$|#.*$|$)/,
+  },
   aesPassEncod(jsonData) {
     let timestamp = Math.floor(Date.now() / 1000)
     let privateKey = toMD5('baidu_taihe_music_secret_key' + timestamp).substr(8, 16)
@@ -184,10 +188,11 @@ export default {
 
   // 获取歌曲列表内的音乐
   getListDetail(id, page, tryNum = 0) {
-    if (this._requestObj_listDetail) {
-      this._requestObj_listDetail.cancelHttp()
-    }
+    if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
     if (tryNum > 2) return Promise.reject(new Error('try max num'))
+
+    if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
+
     this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page))
     return this._requestObj_listDetail.promise.then(({ body }) => {
       if (body.error_code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
diff --git a/src/renderer/utils/music/kg/songList.js b/src/renderer/utils/music/kg/songList.js
index 123418ce..9a7dfcad 100644
--- a/src/renderer/utils/music/kg/songList.js
+++ b/src/renderer/utils/music/kg/songList.js
@@ -36,6 +36,8 @@ export default {
   regExps: {
     listData: /global\.data = (\[.+\]);/,
     listInfo: /global = {[\s\S]+?name: "(.+)"[\s\S]+?pic: "(.+)"[\s\S]+?};/,
+    // https://www.kugou.com/yy/special/single/1067062.html
+    listDetailLink: /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/,
   },
   getInfoUrl(tagId) {
     return tagId
@@ -145,6 +147,9 @@ export default {
   getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐
     if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
     if (tryNum > 2) return Promise.reject(new Error('try max num'))
+
+    if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
+
     this._requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id))
     return this._requestObj_listDetail.promise.then(({ body }) => {
       let listData = body.match(this.regExps.listData)
diff --git a/src/renderer/utils/music/kw/songList.js b/src/renderer/utils/music/kw/songList.js
index 38de9bf9..192b1067 100644
--- a/src/renderer/utils/music/kw/songList.js
+++ b/src/renderer/utils/music/kw/songList.js
@@ -20,6 +20,10 @@ export default {
       id: 'hot',
     },
   ],
+  regExps: {
+    // http://www.kuwo.cn/playlist_detail/2886046289
+    listDetailLink: /^.+\/playlist_detail\/(\d+)(?:\?.*|&.*$|#.*$|$)/,
+  },
   tagsUrl: 'http://wapi.kuwo.cn/api/pc/classify/playlist/getTagList?cmd=rcm_keyword_playlist&user=0&prod=kwplayer_pc_9.0.5.0&vipver=9.0.5.0&source=kwplayer_pc_9.0.5.0&loginUid=0&loginSid=0&appUid=76039576',
   hotTagUrl: 'http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmTagList?loginUid=0&loginSid=0&appUid=76039576',
   getListUrl({ sortId, id, type, page }) {
@@ -161,6 +165,9 @@ export default {
       this._requestObj_listDetail.cancelHttp()
     }
     if (tryNum > 2) return Promise.reject(new Error('try max num'))
+
+    if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
+
     this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page))
     return this._requestObj_listDetail.promise.then(({ body }) => {
       if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
diff --git a/src/renderer/utils/music/mg/songList.js b/src/renderer/utils/music/mg/songList.js
index daff4567..e09959e8 100644
--- a/src/renderer/utils/music/mg/songList.js
+++ b/src/renderer/utils/music/mg/songList.js
@@ -21,6 +21,9 @@ export default {
   regExps: {
     list: /<li><div class="thumb">.+?<\/li>/g,
     listInfo: /.+data-original="(.+?)".*data-id="(\d+)".*<div class="song-list-name"><a\s.*?>(.+?)<\/a>.+<i class="iconfont cf-bofangliang"><\/i>(.+?)<\/div>/,
+
+    // http://music.migu.cn/v3/music/playlist/161044573?page=1
+    listDetailLink: /^.+\/playlist\/(\d+)(?:\?.*|&.*$|#.*$|$)/,
   },
   tagsUrl: 'https://app.c.nf.migu.cn/MIGUM2.0/v1.0/content/indexTagPage.do?needAll=0',
   getSongListUrl(sortId, tagId, page) {
@@ -58,6 +61,9 @@ export default {
   getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐
     if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
     if (tryNum > 2) return Promise.reject(new Error('try max num'))
+
+    if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
+
     this._requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id, page), { headers: this.defaultHeaders })
     return this._requestObj_listDetail.promise.then(({ body }) => {
       if (body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
diff --git a/src/renderer/utils/music/tx/songList.js b/src/renderer/utils/music/tx/songList.js
index 33be8f7a..092a5a91 100644
--- a/src/renderer/utils/music/tx/songList.js
+++ b/src/renderer/utils/music/tx/songList.js
@@ -7,6 +7,7 @@ export default {
   _requestObj_hotTags: null,
   _requestObj_list: null,
   _requestObj_listDetail: null,
+  _requestObj_listDetailLink: null,
   limit_list: 36,
   limit_song: 10000000,
   successCode: 0,
@@ -20,9 +21,14 @@ export default {
       id: 2,
     },
   ],
-  regexps: {
+  regExps: {
     hotTagHtml: /class="c_bg_link js_tag_item" data-id="\w+">.+?<\/a>/g,
     hotTag: /data-id="(\w+)">(.+?)<\/a>/,
+
+    // https://y.qq.com/n/yqq/playlist/7217720898.html
+    // https://i.y.qq.com/n2/m/share/details/taoge.html?platform=11&appshare=android_qq&appversion=9050006&id=7217720898&ADTAG=qfshare
+    listDetailLink1: /^.+(?:\?|&)id=(\d+)(?:&.*$|#.*$|$)/,
+    listDetailLink2: /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/,
   },
   tagsUrl: 'https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=%7B%22tags%22%3A%7B%22method%22%3A%22get_all_categories%22%2C%22param%22%3A%7B%22qq%22%3A%22%22%7D%2C%22module%22%3A%22playlist.PlaylistAllCategoriesServer%22%7D%7D',
   hotTagUrl: 'https://c.y.qq.com/node/pc/wk_v15/category_playlist.html',
@@ -76,12 +82,12 @@ export default {
     })
   },
   filterInfoHotTag(html) {
-    let hotTag = html.match(this.regexps.hotTagHtml)
+    let hotTag = html.match(this.regExps.hotTagHtml)
     const hotTags = []
     if (!hotTag) return hotTags
 
     hotTag.forEach(tagHtml => {
-      let result = tagHtml.match(this.regexps.hotTag)
+      let result = tagHtml.match(this.regExps.hotTag)
       if (!result) return
       hotTags.push({
         id: parseInt(result[1]),
@@ -167,36 +173,57 @@ export default {
     }
   },
 
+  async handleParseId(link, retryNum = 0) {
+    if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp()
+    if (retryNum > 2) return Promise.reject(new Error('link try max num'))
+
+    this._requestObj_listDetailLink = httpFetch(link)
+    const { headers: { location }, statusCode } = await this._requestObj_listDetailLink.promise
+    // console.log(headers)
+    if (statusCode > 400) return this.handleParseId(link, ++retryNum)
+    return location == null ? link : location
+  },
+
   // 获取歌曲列表内的音乐
-  getListDetail(id, tryNum = 0) {
-    if (this._requestObj_listDetail) {
-      this._requestObj_listDetail.cancelHttp()
-    }
+  async getListDetail(id, tryNum = 0) {
+    if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
     if (tryNum > 2) return Promise.reject(new Error('try max num'))
+
+    if ((/[?&:/]/.test(id))) {
+      let regx = /\/\/i\.y\.qq\.com/.test(id) ? this.regExps.listDetailLink1 : this.regExps.listDetailLink2
+      if (!regx.test(id)) {
+        id = await this.handleParseId(id)
+        regx = this.regExps.listDetailLink1
+        console.log(id)
+      }
+      id = id.replace(regx, '$1')
+      // console.log(id)
+    }
+
     this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id), {
       headers: {
         Origin: 'https://y.qq.com',
         Referer: `https://y.qq.com/n/yqq/playsquare/${id}.html`,
       },
     })
-    return this._requestObj_listDetail.promise.then(({ body }) => {
-      if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
-      const cdlist = body.cdlist[0]
-      return {
-        list: this.filterListDetail(cdlist.songlist),
-        page: 1,
-        limit: cdlist.songlist.length + 1,
-        total: cdlist.songlist.length,
-        source: 'tx',
-        info: {
-          name: cdlist.dissname,
-          img: cdlist.logo,
-          desc: jshtmlencode.htmlDecode(cdlist.desc).replace(/<br>/g, '\n'),
-          author: cdlist.nickname,
-          play_count: this.formatPlayCount(cdlist.visitnum),
-        },
-      }
-    })
+    const { body } = await this._requestObj_listDetail.promise
+
+    if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
+    const cdlist = body.cdlist[0]
+    return {
+      list: this.filterListDetail(cdlist.songlist),
+      page: 1,
+      limit: cdlist.songlist.length + 1,
+      total: cdlist.songlist.length,
+      source: 'tx',
+      info: {
+        name: cdlist.dissname,
+        img: cdlist.logo,
+        desc: jshtmlencode.htmlDecode(cdlist.desc).replace(/<br>/g, '\n'),
+        author: cdlist.nickname,
+        play_count: this.formatPlayCount(cdlist.visitnum),
+      },
+    }
   },
   getSinger(singers) {
     let arr = []
diff --git a/src/renderer/utils/music/wy/songList.js b/src/renderer/utils/music/wy/songList.js
index ea1f4fbf..e395665e 100644
--- a/src/renderer/utils/music/wy/songList.js
+++ b/src/renderer/utils/music/wy/songList.js
@@ -25,6 +25,9 @@ export default {
       id: 'new',
     },
   ],
+  regExps: {
+    listDetailLink: /^.+(?:\?|&)id=(\d+)(?:&.*$|#.*$|$)/,
+  },
   /**
    * 格式化播放数量
    * @param {*} num
@@ -45,6 +48,9 @@ export default {
   getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐
     if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
     if (tryNum > 2) return Promise.reject(new Error('try max num'))
+
+    if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
+
     this._requestObj_listDetail = httpFetch('https://music.163.com/api/linux/forward', {
       method: 'post',
       'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
diff --git a/src/renderer/views/SongList.vue b/src/renderer/views/SongList.vue
index 42c933ac..02bb1f5c 100644
--- a/src/renderer/views/SongList.vue
+++ b/src/renderer/views/SongList.vue
@@ -15,7 +15,8 @@
             //- material-btn(:class="$style.closeDetailButton" :disabled="detailLoading" @click="playSongListDetail") 播放
             //- | &nbsp;
             material-btn(:class="$style.closeDetailButton" @click="hideListDetail") {{$t('view.song_list.back')}}
-        material-song-list(v-model="selectdData" @action="handleSongListAction" :source="source" :page="listDetail.page" :limit="listDetail.limit" :total="listDetail.total" :noItem="$t('material.song_list.loding_list')" :list="listDetail.list")
+        material-song-list(v-model="selectdData" @action="handleSongListAction" :source="source" :page="listDetail.page" :limit="listDetail.limit"
+         :total="listDetail.total" :noItem="isGetDetailFailed ? $t('view.song_list.loding_list_fail') : $t('view.song_list.loding_list')" :list="listDetail.list")
     transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
       div(:class="$style.songListContainer" v-if="!isVisibleListDetail")
         div(:class="$style.header")
@@ -74,6 +75,7 @@ export default {
       isShowListAddMultiple: false,
       importSongListText: '',
       listWidth: 645,
+      isGetDetailFailed: false,
       // detailLoading: true,
     }
   },
@@ -164,7 +166,7 @@ export default {
   methods: {
     ...mapMutations(['setSongList']),
     ...mapActions('songList', ['getTags', 'getList', 'getListDetail']),
-    ...mapMutations('songList', ['setVisibleListDetail', 'setSelectListInfo', 'clearListDetail']),
+    ...mapMutations('songList', ['setVisibleListDetail', 'setSelectListInfo']),
     ...mapActions('download', ['createDownload', 'createDownloadMultiple']),
     ...mapMutations('list', ['listAdd', 'listAddMultiple']),
     ...mapMutations('player', ['setList']),
@@ -229,7 +231,7 @@ export default {
       })
     },
     handleToggleListDetailPage(page) {
-      this.getListDetail({ id: this.selectListInfo.id, page }).then(() => {
+      this.handleGetListDetail(this.selectListInfo.id, page).then(() => {
         this.$nextTick(() => {
           scrollTo(this.$refs.dom_scrollContent, 0)
         })
@@ -254,9 +256,8 @@ export default {
       // this.detailLoading = true
       this.setSelectListInfo(this.listData.list[index])
       this.setVisibleListDetail(true)
-      this.clearListDetail()
       this.$nextTick(() => {
-        this.getListDetail({ id: this.selectListInfo.id, page: 1 })
+        this.handleGetListDetail(this.selectListInfo.id, 1)
       })
     },
     handleFlowBtnClick(action) {
@@ -301,13 +302,11 @@ export default {
         case 'submit':
           this.handleGetSongListDetail()
           break
-        case 'blur':
-          this.parseImportSongListInputText()
-          break
+        // case 'blur':
+        //   break
       }
     },
     handleGetSongListDetail() {
-      this.parseImportSongListInputText()
       this.setSelectListInfo({
         play_count: null,
         id: this.importSongListText,
@@ -317,45 +316,8 @@ export default {
         desc: '',
         source: this.source,
       })
-      this.clearListDetail()
-      this.$nextTick(() => {
-        this.setVisibleListDetail(true)
-        this.getListDetail({ id: this.importSongListText, page: 1 })
-      })
-    },
-    parseImportSongListInputText() {
-      if (!(/[?&:/]/.test(this.importSongListText))) return
-      const text = this.importSongListText
-      let regx
-      switch (this.source) {
-        case 'wy':
-          regx = /^.+(?:\?|&)id=(\d+)(?:&.*$|#.*$|$)/
-          break
-        case 'tx':
-          // https://y.qq.com/n/yqq/playlist/7217720898.html
-          // https://i.y.qq.com/n2/m/share/details/taoge.html?platform=11&appshare=android_qq&appversion=9050006&id=7217720898&ADTAG=qfshare
-          regx = /\/\/i\.y\.qq\.com/.test(text) ? /^.+(?:\?|&)id=(\d+)(?:&.*$|#.*$|$)/ : /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/
-          break
-        case 'kw':
-          // http://www.kuwo.cn/playlist_detail/2886046289
-          regx = /^.+\/playlist_detail\/(\d+)(?:\?.*|&.*$|#.*$|$)/
-          break
-        case 'bd':
-          // http://music.taihe.com/songlist/566347741
-          regx = /^.+\/songlist\/(\d+)(?:\?.*|&.*$|#.*$|$)/
-          break
-        case 'mg':
-          // http://music.migu.cn/v3/music/playlist/161044573?page=1
-          regx = /^.+\/playlist\/(\d+)(?:\?.*|&.*$|#.*$|$)/
-          break
-        case 'kg':
-          // https://www.kugou.com/yy/special/single/1067062.html
-          regx = /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/
-          break
-        default:
-          return
-      }
-      this.importSongListText = text.replace(regx, '$1')
+      this.setVisibleListDetail(true)
+      this.handleGetListDetail(this.importSongListText, 1)
     },
     filterList(list) {
       return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : [...list]
@@ -363,6 +325,13 @@ export default {
     setTagListWidth() {
       this.listWidth = this.$refs.tagList.$el.clientWidth + this.$refs.tab.$el.clientWidth + 2
     },
+    handleGetListDetail(id, page) {
+      this.isGetDetailFailed = false
+      this.getListDetail({ id, page }).catch(err => {
+        this.isGetDetailFailed = true
+        return Promise.reject(err)
+      })
+    },
     /*     addSongListDetail() {
       // this.detailLoading = true
       // this.getListDetailAll(this.selectListInfo.id).then(() => {