diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..80a35a8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,74 @@ +matrix: + include: + - os: osx + osx_image: xcode10.2 + language: node_js + node_js: '10' + env: + - ELECTRON_CACHE=$HOME/.cache/electron + - ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder + + - os: linux + services: docker + language: generic + +cache: + directories: + - node_modules + - $HOME/.cache/electron + - $HOME/.cache/electron-builder + +before_install: + - | + if [ "$TRAVIS_OS_NAME" == "linux" ]; then + # download aliyun OSS Linux client + wget -nc http://gosspublic.alicdn.com/ossutil/1.6.5/ossutil64 + chmod 755 ossutil64 + # config aliyun OSS Linux client https://help.aliyun.com/document_detail/50455.html + ./ossutil64 config -e "${OSS_ENDPOINT}" -i "${OSS_AKI}" -k "${OSS_AKS}" + else + # download aliyun OSS Linux client + wget -nc http://gosspublic.alicdn.com/ossutil/1.6.5/ossutilmac64 + chmod 755 ossutilmac64 + # config aliyun OSS Linux client https://help.aliyun.com/document_detail/50455.html + ./ossutilmac64 config -e "${OSS_ENDPOINT}" -i "${OSS_AKI}" -k "${OSS_AKS}" + fi + +script: + - | + if [ "$TRAVIS_OS_NAME" == "linux" ]; then + docker run --rm \ + --env-file <(env | grep -vE '\r|\n' | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS_TAG|TRAVIS|TRAVIS_REPO_|TRAVIS_BUILD_|TRAVIS_BRANCH|TRAVIS_PULL_REQUEST_|APPVEYOR_|CSC_|GH_|GITHUB_|BT_|AWS_|STRIP|BUILD_') \ + --env ELECTRON_CACHE="/root/.cache/electron" \ + --env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" \ + -v ${PWD}:/project \ + -v ~/.cache/electron:/root/.cache/electron \ + -v ~/.cache/electron-builder:/root/.cache/electron-builder \ + electronuserland/builder:wine \ + /bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn electron:build --bundler builder --target linux" + linux_file=`ls dist/electron/Packaged/ | grep '.AppImage$'` + ./ossutil64 cp "dist/electron/Packaged/${linux_file}" "${BUCKET}/${linux_file}" -f + + docker run --rm \ + --env-file <(env | grep -vE '\r|\n' | grep -iE 'DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS_TAG|TRAVIS|TRAVIS_REPO_|TRAVIS_BUILD_|TRAVIS_BRANCH|TRAVIS_PULL_REQUEST_|APPVEYOR_|CSC_|GH_|GITHUB_|BT_|AWS_|STRIP|BUILD_') \ + --env ELECTRON_CACHE="/root/.cache/electron" \ + --env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" \ + -v ${PWD}:/project \ + -v ~/.cache/electron:/root/.cache/electron \ + -v ~/.cache/electron-builder:/root/.cache/electron-builder \ + electronuserland/builder:wine \ + /bin/bash -c "yarn --link-duplicates --pure-lockfile && yarn electron:build --bundler builder --target win" + win_file=`ls dist/electron/Packaged/ | grep '.zip$'` + ./ossutil64 cp "dist/electron/Packaged/${win_file}" "${BUCKET}/${win_file}" -f + else + yarn electron:build + osx_file=`ls dist/electron/Packaged/ | grep '.dmg$'` + ./ossutilmac64 cp "dist/electron/Packaged/${osx_file}" "${BUCKET}/${osx_file}" -f + fi + +before_cache: + - rm -rf $HOME/.cache/electron-builder/wine + +branches: + except: + - 'master' diff --git a/package.json b/package.json index e71582d..ea16d35 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "h-player", - "version": "0.7.0", + "version": "0.8.0", "description": "A Quasar Framework app", "productName": "h-player", "cordovaId": "org.cordova.quasar.app", diff --git a/quasar.conf.js b/quasar.conf.js index 85aecab..19a5092 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -173,7 +173,7 @@ module.exports = function (ctx) { }, electron: { - bundler: 'packager', // 'builder' or 'packager' + bundler: 'builder', // 'builder' or 'packager' extendWebpack(cfg) { // do something with Electron main process Webpack cfg @@ -202,7 +202,17 @@ module.exports = function (ctx) { builder: { // https://www.electron.build/configuration/configuration - // appId: 'h-player' + appId: 'com.electron.h-player', + mac: { + category: 'public.app-category.video', + target: 'dmg', + }, + win: { + target: 'zip', + }, + linux: { + target: 'AppImage', + }, }, }, }; diff --git a/src/App.vue b/src/App.vue index 8d25a17..038c3ba 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,9 +5,15 @@ diff --git a/src/api/maccms-v10.d.ts b/src/api/maccms-v10.d.ts new file mode 100644 index 0000000..c25054d --- /dev/null +++ b/src/api/maccms-v10.d.ts @@ -0,0 +1,71 @@ +export interface getListQuery { + /** + action + Default: list + */ + ac?: string; + + /** + 返回数据类型 + Default: xml + */ + at?: string; + + /** + 类别ID + */ + t?: string; + + /** + 页码 + */ + pg?: string; + + /** + 搜索关键字 + */ + wd?: string; + + /** + 几小时内的数据 + */ + h?: string; +} + +export function getList(apt: string, query: getListQuery): Promise; + +export interface getDetailQuery { + /** + action + Default: detail + */ + ac?: string; + + /** + 返回数据类型 + Default: xml + */ + at?: string; + + /** + 类别ID + */ + t?: string; + + /** + 页码 + */ + pg?: string; + + /** + 数据ID,多个ID逗号分割 + */ + ids?: string; + + /** + 几小时内的数据 + */ + h?: string; +} + +export function getDetail(apt: string, query: getDetailQuery): Promise; diff --git a/src/api/maccms-v10.js b/src/api/maccms-v10.js new file mode 100644 index 0000000..daef499 --- /dev/null +++ b/src/api/maccms-v10.js @@ -0,0 +1,31 @@ +import axios from 'axios'; +import { stringify } from 'query-string'; + +export async function getList(api, query) { + const defaultParams = { + ac: 'list', + at: 'xml', + }; + const params = Object.assign(defaultParams, query); + const response = await axios.get(api, { + params, + }); + return response; +} + +export async function getDetail(api, query) { + const defaultParams = { + ac: 'detail', + at: 'xml', + }; + const params = Object.assign(defaultParams, query); + const response = await axios.get(api, { + params, + paramsSerializer(qs) { + return stringify(qs, { + arrayFormat: 'comma', + }); + }, + }); + return response; +} diff --git a/src/api/maccms-v8.d.ts b/src/api/maccms-v8.d.ts new file mode 100644 index 0000000..360f35a --- /dev/null +++ b/src/api/maccms-v8.d.ts @@ -0,0 +1,59 @@ +export interface getListQuery { + /** + action + Default: list + */ + ac?: string; + + /** + 类别ID + */ + t?: string; + + /** + 页码 + */ + pg?: string; + + /** + 搜索关键字 + */ + wd?: string; + + /** + 几小时内的数据 + */ + h?: string; +} + +export function getList(apt: string, query: getListQuery): Promise; + +export interface getDetailQuery { + /** + action + Default: videolist + */ + ac?: string; + + /** + 类别ID + */ + t?: string; + + /** + 页码 + */ + pg?: string; + + /** + 数据ID,多个ID逗号分割 + */ + ids?: string; + + /** + 几小时内的数据 + */ + h?: string; +} + +export function getDetail(apt: string, query: getDetailQuery): Promise; diff --git a/src/api/maccms-v8.js b/src/api/maccms-v8.js new file mode 100644 index 0000000..e2bbf30 --- /dev/null +++ b/src/api/maccms-v8.js @@ -0,0 +1,29 @@ +import axios from 'axios'; +import { stringify } from 'query-string'; + +export async function getList(api, query) { + const defaultParams = { + ac: 'list', + }; + const params = Object.assign(defaultParams, query); + const response = await axios.get(api, { + params, + }); + return response; +} + +export async function getDetail(api, query) { + const defaultParams = { + ac: 'videolist', + }; + const params = Object.assign(defaultParams, query); + const response = await axios.get(api, { + params, + paramsSerializer(qs) { + return stringify(qs, { + arrayFormat: 'comma', + }); + }, + }); + return response; +} diff --git a/src/layouts/Config.vue b/src/layouts/Config.vue index dada0ba..a4e17af 100644 --- a/src/layouts/Config.vue +++ b/src/layouts/Config.vue @@ -304,6 +304,14 @@ label="是否使用https" /> +
+ +
软件信息
@@ -409,7 +417,6 @@ export default { data: { handler() { this.setSiteList(clonedeep(this.data)); - this.$electronStore.set('siteList', this.siteList); }, deep: true, }, @@ -456,7 +463,6 @@ export default { }); if (dialogResult) { const importedFile = await fs.readJSON(dialogResult[0]); - this.$electronStore.set('siteList', importedFile); this.setSiteList(importedFile); this.data = clonedeep(this.siteList); } @@ -516,6 +522,17 @@ export default { repo: 'h-player-v2', }); }, + toggleDevTools() { + const win = this.$q.electron.remote.BrowserWindow.getFocusedWindow(); + if (win) { + const { webContents } = win; + if (webContents.isDevToolsOpened()) { + webContents.closeDevTools(); + } else { + webContents.openDevTools(); + } + } + }, }, }; diff --git a/src/layouts/Home.vue b/src/layouts/Home.vue index be55dbe..3a25e1c 100644 --- a/src/layouts/Home.vue +++ b/src/layouts/Home.vue @@ -72,7 +72,7 @@ > - + @@ -115,7 +121,7 @@ elevated class="bg-grey-8 text-white" > - + @@ -124,15 +130,14 @@ import titleBar from 'components/titleBar'; import footerContent from 'components/footerContent'; import { mapState, mapMutations, mapGetters } from 'vuex'; -import util from 'util'; import isAbsoluteUrl from 'is-absolute-url'; -import { parseString } from 'xml2js'; - import { URL } from 'url'; import path from 'path'; import { stringify } from 'query-string'; - -const parseStringSync = util.promisify(parseString); +import _toInteger from 'lodash/toInteger'; +import _get from 'lodash/get'; +import { getList, getDetail } from '../api/maccms-v8.js'; +import { parseXML } from '../utils/parse-xml.js'; export default { data() { @@ -140,10 +145,14 @@ export default { loading: true, error: false, keyWord: '', - tab: 1, + tab: '', videoClass: [], left: this.$q.platform.is.desktop, httoOrHttps: false, + ids: [], + page: 1, + total: 0, + videoList: [], }; }, components: { @@ -151,28 +160,28 @@ export default { footerContent, }, created() { - const storeSiteList = this.$electronStore.get('siteList'); - this.setSiteList(storeSiteList); - const ipc = this.$q.electron.ipcRenderer; - ipc.on('from-mini', (event, message) => { - this.gotoPlayer(message); - }); - this.setCurrentClass('all'); - this.setCurrentSiteId(this.tab); - this.getClass(); - this.$router.push('/'); + if (!this.siteList || this.siteList.length === 0) { + this.$router.push('/import'); + } else { + this.setCurrentClass('all'); + this.tab = this.siteList[0].id; + this.setCurrentSiteId(this.tab); + } }, watch: { tab() { + console.log('tab'); this.setCurrentClass('all'); this.setCurrentSiteId(this.tab); - this.getClass(); + this.page = 1; + this.fetchInfo(); this.$router.push('/'); }, - keyWord() { - if (this.keyWord === '') { - this.$store.commit('setKeyWord', this.keyWord); - } + globalKw() { + console.log('globalKw'); + this.setCurrentClass('all'); + this.page = 1; + this.fetchInfo(); }, }, methods: { @@ -182,37 +191,66 @@ export default { 'setCurrentVideo', 'setSiteList', ]), - getClass() { + + async getVideoList() { this.loading = true; this.error = false; - this.$axios(this.currentSite.httpsApi, { - params: { - ac: 'list', - }, - }) - .then(res => parseStringSync(res.data, { explicitArray: false })) - .then((data) => { - this.videoClass = data.rss.class.ty; - this.videoClass.unshift({ - _: '全部', - $: { - id: 'all', - }, - }); - }) - .catch((err) => { - this.error = true; - console.log(err); - }) - .finally(() => { - this.loading = false; - }); + const uri = this.currentUri; + // get video list and class + const params = { + pg: this.page, + }; + if (this.currentClass !== 'all') { + params.t = this.currentClass; + } + if (this.keyWord) { + params.wd = this.keyWord; + } + const listResponse = await getList(uri, params); + const parsedList = await parseXML(listResponse.data); + this.videoClass = parsedList.rss.class[0].ty; + this.total = _toInteger(parsedList.rss.list[0].$.pagecount); + // get video detail + const videoInfo = _get(parsedList, 'rss.list[0].video', []); + this.ids = videoInfo.map(item => item.id[0]); + }, + + async getVideoDetail() { + this.loading = true; + this.error = false; + const detailResponse = await getDetail(this.currentUri, { + ids: this.ids, + }); + const parsedDetail = await parseXML(detailResponse.data); + this.videoList = _get(parsedDetail, 'rss.list[0].video', []); + }, + + async fetchInfo() { + console.log('fetchInfo'); + try { + this.loading = true; + this.error = false; + this.$q.loadingBar.start(); + await this.getVideoList(); + if (this.ids.length !== 0) { + await this.getVideoDetail(); + } else { + this.videoList = []; + } + } catch (error) { + this.error = true; + console.error(error); + } finally { + this.loading = false; + this.$q.loadingBar.stop(); + } }, configClick() { this.$router.push('/config'); }, changeClass(currentClass) { this.setCurrentClass(currentClass); + this.fetchInfo(); this.$router.push('/'); }, gotoPlayer(video) { @@ -270,12 +308,21 @@ export default { this.$store.commit('setKeyWord', this.keyWord); } }, + pageChange(value) { + console.log('pageChange'); + this.page = value; + if (this.page !== 1) { + this.fetchInfo(); + } + }, }, computed: { ...mapGetters(['currentSite']), ...mapState({ siteList: state => state.site.siteList, currentClass: state => state.site.currentClass, + globalKw: state => state.site.keyWord, + https: state => state.app.https, }), thumbStyle() { return { @@ -286,6 +333,19 @@ export default { opacity: 0.75, }; }, + processdVideoClass() { + return [ + { + _: '全部', + $: { + id: 'all', + }, + }, + ].concat(this.videoClass); + }, + currentUri() { + return this.https ? this.currentSite.httpsApi : this.currentSite.httpApi; + }, }, }; diff --git a/src/layouts/Import.vue b/src/layouts/Import.vue index 71645df..9c1d336 100644 --- a/src/layouts/Import.vue +++ b/src/layouts/Import.vue @@ -57,7 +57,6 @@ export default { }); if (dialogResult) { const importedFile = await fs.readJSON(dialogResult[0]); - this.$electronStore.set('siteList', importedFile); this.setSiteList(importedFile); this.$router.push('/'); } diff --git a/src/pages/MiniVideo.vue b/src/pages/MiniVideo.vue index ce03ee5..8c95cb9 100644 --- a/src/pages/MiniVideo.vue +++ b/src/pages/MiniVideo.vue @@ -25,10 +25,7 @@