第八章 Electron 实现音乐播放器之爬虫播放音乐

news2024/11/26 22:47:31

一、介绍 🚀 ✈️ 🚁 🚂

我在第五章已经有一篇Electron爬虫的文章,主要写的爬取图片资源的案例。这篇开始讲解如何到一个音乐网站爬取音乐资源,并且进行在线播放,下载等等。

那么什么是爬虫呢。百度百科上爬虫既是网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。我个人的理解就是,爬虫是一种自动化程序,它可以模拟人类浏览网页的行为,从互联网上爬取数据并进行处理。

二、爬虫准备 🚀 ✈️ 🚁 🚂

1、编程语言准备 🔅 🔅 🔅

除了node爬虫,常用的编程语言还有Python、Java、C++等,其中Python是最为流行的爬虫语言之一。不管是那种,思路和步骤基本上都是一样的。

2、数据储存 🔅 🔅 🔅

爬虫爬取的数据需要进行存储,常用的数据存储方式有MySQL、MongoDB、Redis、SQLite3、Excel、txt、json等。具体采用什么方式储存需要看具体的数据类型、结构、大小等。

3、资源网站的观察 🔅 🔅 🔅

在开始爬虫之前我们得去资源网站进行观察,python、java、node等语言爬虫都是需要对网站结构进行分析的。比如我们需要爬取一个网站内的分类,对于其他非分类的内容来说是没有任何意义的。可以观察网站中分类是否具有唯一的class或者id,又或者分类是在div容器中还是ul容器中。根据多方面的条件能够让我们很快,也能最简单的去通过一些工具取到想要的内容。这是结构的一个观察,还有请求的要求,有些接口请求的话会有一些繁琐的头部定义(Header)。如果不设置的话可能会出现401、404、50*等等各种情况。这些都是需要我们去观察以后,在脚本中去进行设置的。

三、爬虫能够做些啥 🚀 ✈️ 🚁 🚂

1、数据采集  🔅 🔅 🔅 🔅

爬虫可以自动化地从互联网上采集数据,包括文字、图片、音乐、视频等。

2、数据分析  🔅 🔅 🔅 🔅

爬虫可以对采集到的数据进行分析和处理,例如数据清洗、数据挖掘等。

3、搜索引擎优化  🔅 🔅 🔅 🔅

爬虫可以帮助网站提升搜索引擎排名,从而增加网站的流量和曝光度。

4、网络安全  🔅 🔅 🔅 🔅

爬虫可以帮助企业进行网络安全测试,发现漏洞和安全隐患。

四、爬虫技巧 🚀 ✈️ 🚁 🚂

1、合理设置爬虫请求频率,避免对服务器造成过大的负担。🔅 🔅 🔅

2、使用多个IP地址进行爬取,避免被封禁。🔅 🔅 🔅

3、使用随机的User-Agent头信息,避免被网站识别为爬虫。🔅 🔅 🔅

4、避免爬取敏感信息,遵守网站的robots.txt协议。🔅 🔅 🔅

五、开始爬取音乐 🚀 ✈️ 🚁 🚂

安装依赖

yarn add cheerio

yarn add @types/cheerio -D

这里的话具体的网站地址我就不放出来了,避免一些不必要的麻烦,如果有需要的小伙伴可以私信我。

首先我搜索周杰伦以后,就会出来一个搜索结果的页面,页面内还包含了分页数据。所以页面内我需要的数据就是内容和分页。通过对页面的分析,$('.list-unstyled .media')就能帮我定位到内容。

$('.pagination li')就能帮我定位的分页。

点击相对应的歌曲进入以后,我们还可以获取到歌曲的相信信息,歌曲名称、歌手、歌词等等

当我们点击播放的时候,可以在Network中观察到,它请求的地址,这个地址复制在浏览器中打开是可以直接播放音乐的。也能直接下载,所有我们需要这个地址。

综合以上的全部分析,具体的实现代码如下:

/** 根据歌曲名称查询对应的列表
 * @Description:
 * @CreationDate 2023-05-08 10:11:51
 */
getListByMusicName(musicName: string, page?: number) {
	if (!page) {
		this.resetGetParam(musicName)
	}
	const path = `search-${encodeURIComponent(musicName)}-1-${ page ? page : this.queryMusicResult.music_current_page }.htm`
	try {
		axios.get('https://www.******.com/' + path).then((res: any) => {
			const $: any = cheerio.load(res.data)
			const musicContent = $('.list-unstyled .media')
			const pagination = $('.pagination li')
			// 获取总页数
			if (pagination.eq(pagination.length - 2).children('a').html()) {
				const pageTotal = (pagination.eq(pagination.length - 2).children('a').html()).match(/\d+/g)[0]
				if (pageTotal && pageTotal > 0) {
					this.queryMusicResult.music_page_count = Number(pageTotal)
				}
			}

			if (this.queryMusicResult.music_current_page === this.queryMusicResult.music_page_count) {
				this.queryMusicResult.music_noMore = true
			}

			// 如何搜索出的结果为0就直接结束
			if (musicContent.length === 0) {
				this.queryMusicResult.music_loading = false
				this.queryMusicResult.music_noMore = false
			} else {
				this.circulateGetMusicList(musicContent)
			}
		}).catch((e: any) => {
			this.queryMusicResult.music_loading = false
			console.log('👉👉👉-----------------', e);
		})
	} catch (e) {
		this.queryMusicResult.music_loading = false
		console.log('👉👉👉-----------------', e);
	}
},
/**
 * @Description: 循环查询当前页面的歌曲相关信息
 * @CreationDate 2023-05-08 11:48:43
 */
circulateGetMusicList(musicContent: any) {
	try {
		for (let i = 0; i < musicContent.length; i++) {
			const musicHref = musicContent['eq'](i).children('div .media-body').children('div .subject').children('a')
			axios.get('https://www.*****.com/' + musicHref.attr('href')).then(info => {
				const $info: any = cheerio.load(info.data)
				const musicInfo = $info('.card-thread')['children']('div .card-body').children('div .break-all')
				if (musicContent.length === (i + 1)) {
					this.queryMusicResult.music_loading = false
				}
				if (musicInfo.children('script') && musicInfo.children('script').get(1) && musicInfo.children('script').get(1).children) {
					const musicInfos = musicInfo.children('script').get(1).children[0]['data']
					const S = musicInfos.indexOf('[')
					const E = musicInfos.indexOf(']')
					let data = musicInfos.substring(S + 1, E - 3).replace(/\s*/g, '')
					if (data) {
						data = data.substring(0, data.length - 1)
						data = eval('(' + data + ')')
						this.queryMusicResult.music_current_list.push({
							name: data.title,
							url: data.url.includes('https') ? data.url.replace('-', '%20-%20') : 'https://www.*****.com/' + data.url,
							author: data.author,
							pic: data.pic
						} as MusicType)
					}
				}
			})
		}
	} catch (e) {
		this.queryMusicResult.music_loading = false
		console.log('👉👉👉-----------------', e)
	}
},

这里的话我们就实现了爬虫的部分代码,是不是超级简单,现在就要对爬取的资源进行一个展示,以及音乐的播放,下载等等。

我将上一章中的目录结构进行了更改。创建src/views/music目录,原本home下面的index.vue更改为localAndDownload.vue,并移动到music目录下。同时创建index.vue页面。接下来,我们将爬取的资源在music/index.vue中进行一个实现。

六、完整案例 🚀 ✈️ 🚁 🚂

首先创建路由。这里的话我就不做菜单了,自行去完成,我直接将根路径访问指向music/index

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'

export const asyncRoutes: RouteRecordRaw[] = [
    {
        path: '/',
        component: () => import('@/views/music/index.vue')
    },
    {
        path: '/home',
        component: () => import('@/views/home/index.vue')
    },
    {
        path: '/401',
        component: () => import('@/views/401.vue'),
    },
    {
        path: '/localAndDownload',
        component: () => import('@/views/music/localAndDownload.vue')
    },
]

const router = createRouter({
    history: createWebHashHistory(),
    routes: asyncRoutes,
    scrollBehavior: () => ({ left: 0, top: 0 })
})

export default router

修改src/views/music/index.vue

<template>
    <div class="ts-music">
        <div class="ts-music--search">
            <el-input v-model="musicName" style="width: 400px" placeholder="关键词" class="input-with-select">
                <template #append>
                    <el-button @click="searchMusic">
                        <i-carbon-search style="color: #03a9a9;" />
                    </el-button>
                </template>
            </el-input>
        </div>
        <div style="display: flex;align-items: center;padding: 0;font-size: 12px;color: #a55151;justify-content: center;">注: 双击歌曲进行音乐播放</div>
        <div class="ts-music--content">
            <ul
                v-infinite-scroll="loadList"
                    class="ts-music--content__ul"
                :infinite-scroll-disabled="queryMusicResult.music_loading || queryMusicResult.music_noMore"
            >
                <li class="ts-music--content__li" style="font-weight: bold;">
                    <div style="width: 40px;display: flex;justify-content: space-around;align-items: center;height: 60px;font-size: 18px;">
                        序号
                    </div>
                    <div style="text-align: center;width: calc(100% - 380px)">标题</div>
                    <div style="width: 160px;text-align: center;">歌手</div>
                    <div style="text-align: center;width: 240px">专辑</div>
                </li>
                <li v-for="(item, index) in queryMusicResult.music_current_list" :key="index" class="ts-music--content__li" @dblclick="dblclick(item, index)">
                    <div style="width: 40px;display: flex;justify-content: space-around;align-items: center;height: 60px;font-size: 18px;">
                        <i-noto-speaker-high-volume v-if="playerSetting.play_state && playerSetting.play_name === item.name" />
                        <i-noto-speaker-low-volume v-else-if="playerSetting.play_name === item.name && !playerSetting.play_state" />
                        <span v-else>{{ `${index > 8 ? '' : 0 }${index + 1}` }}</span>
                        <i-carbon-download v-if="item.percentage !== 100 && checkIncluded(item)" @click="downLoad(item)" style="font-size: 18px;cursor: pointer;" title="下载" />
                        <el-progress v-else-if="item.percentage > 0 && item.percentage < 100" :percentage="item.percentage" type="circle" width="20" stroke-width="2" :show-text="false" />
                        <i-carbon-checkmark-outline v-else style="color: #009169" />
                    </div>
                    <div style="text-align: center;width: calc(100% - 380px)" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.name}}</div>
                    <div style="width: 160px;text-align: center;" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.author}}</div>
                    <div style="text-align: center;width: 240px" :style="{color: playerSetting.play_name === item.name ? '#03a9a9' : ''}">{{item.album ? item.album : '-'}}</div>
                </li>
            </ul>
        </div>
        <Player />
    </div>
</template>

<script lang="ts">
import  {defineComponent, onMounted, reactive, toRefs} from "vue";
import appStore from "@/pinia";
import axios from "axios";
import {storeToRefs} from "pinia";
import {MusicType} from "@/pinia/modules/music.modules";
import {useIpcRenderer} from "@vueuse/electron";

export interface StateType {
    musicName: string
    loading: boolean
    currentPageTotal: number
    musicList: MusicType[]
    list?: any[]
}

export default defineComponent({
    setup() {

        const ipcRenderer = useIpcRenderer()

        const { queryMusicResult, playerSetting, downloadMusicList } = storeToRefs(appStore.musicModule)

        const state = reactive({
            musicName: '' , // 搜索的歌名
            loading: false , // loading状态
            currentPageTotal: 0, // 搜索结果总数
            musicList: [],
            list: []
        } as StateType)

        /**
         * @Description: 搜索功能
         * @CreationDate 2023-05-05 10:15:13
         */
        const searchMusic = async () => {
            resetSearchData()
            appStore.musicModule.getListByMusicName(state.musicName)
        }

        /**
         * @Description: 重置搜索结果
         * @CreationDate 2023-05-05 10:12:29
         */
        const resetSearchData = () => {
            state.loading = true
            state.currentPageTotal = 0
            state.musicList = []
        }

        /**
         * @Description: 监听回车事件
         * @CreationDate 2023-05-22 10:38:06
         */
        const keydownEvent = () => {
            document.onkeydown = (e: any) => {
                if (e.defaultPrevented) {
                    return;
                }
                if (e.keyCode === 13) {
                    searchMusic()
                }
            }
        }

        /**
         * @Description: 双击播放音乐
         * @CreationDate 2023-05-22 10:38:14
         */
        const dblclick = (item: MusicType, index: number) => {
            if (item.url) {
                axios.get(item.url).then(res => {
                    appStore.musicModule.setCurrentMusicInfo({
                        author: item.author,
                        pic: item.pic,
                        name: item.name,
                        url: res.request.responseURL
                    })
                    appStore.musicModule.play(index)
                    playerSetting.value.current_list_name = 'queryMusicResult'
                })
            }
        }

        const downLoad = (item) => {
            // const win = remote.getCurrentWindow()
            // win.webContents.downloadURL(url)
            ipcRenderer.send('download', {
                downloadPath: item.url,
                fileName: item.name,
                author: item.author,
                pic: item.pic
            })
            ipcRenderer.removeAllListeners('updateProgressing');
            ipcRenderer.on('updateProgressing', (e, value) => {
                item.percentage = value ? value * 100 : 0
            })
        }

        const checkIncluded = (item: MusicType) => {
            return downloadMusicList.value.findIndex(down => down.name === item.name && down.author === item.author) === -1
        }

        const loadList = () => {
            appStore.musicModule.loadMore()
        }

        onMounted(() => {
            keydownEvent()
        })

        return {
            ...toRefs(state),
            searchMusic,
            dblclick,
            queryMusicResult,
            playerSetting,
            checkIncluded,
            downLoad,
            loadList,
        }
    }
})
</script>

<style scoped lang="scss">
::v-deep(.el-table__fixed-body-wrapper) {
  z-index: auto !important;
}

.ts-music {
  height: calc(100% - 60px);
  width: 100%;
  background: #171717;
  font-size: 14px;
  display: flex;
  color: white;
  flex-direction: column;
  overflow: hidden;
  -webkit-touch-callout:none; /*系统默认菜单被禁用*/
  -webkit-user-select:none; /*webkit浏览器*/
  -khtml-user-select:none; /*早期浏览器*/
  -moz-user-select:none;/*火狐*/
  -ms-user-select:none; /*IE10*/
  user-select:none;

  .ts-music--search {
    display:flex;
    justify-content: center;
    padding: 10px 0;
  }

  .ts-music--content {
    height: calc(100% - 130px);
    overflow-y: hidden;
    padding: 0 20px 0 20px;

    .ts-music--content__ul {
      height: 94%;
      list-style: none;
      padding: 0;
      overflow-y: auto;
      border: 1px solid #4e4e4f;
      border-radius: 8px;

      .ts-music--content__li {
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        height: 80px;
        padding: 0 20px;
        border-bottom: 1px solid #4e4e4f;

        &:last-child {
          border-bottom: none;
        }
      }
    }
  }
}
</style>

修改src/pinia/modules/music.modules.ts

import { defineStore } from 'pinia';
import {durationConversionTime} from "@/utils/DateTime";
// @ts-ignore
import {ElMessage} from "element-plus";
import axios from "axios";
// @ts-ignore
const cheerio = require('cheerio')

/**
 * @Description: 歌曲信息类型
 * @CreationDate 2023-05-08 11:50:36
 */
export interface MusicType {
    name?: string
    album?: string
    url?: string
    author?: string
    pic?: string
    percentage?: number
}

/**
 * @Description: 搜索歌曲结果
 * @CreationDate 2023-05-08 17:02:57
 */
export interface QueryMusicResultType {
    music_page_count: number // 搜索出来的歌曲总页数
    music_current_page: number // 当前搜索匹配的页数
    music_loading: boolean // 搜索歌曲时的状态
    music_noMore: boolean // 没有更多了
    music_name: string // 当前搜索歌曲的名称
    music_current_list: MusicType[] // 搜索出来的歌曲列表,叠加
}

/**
 * @Description: 当前播放音乐信息类型
 * @CreationDate 2023-05-08 11:51:00
 */
export interface CurrentPlayMusicType extends MusicType{
    current_play_audio_ready?: boolean //
}

/**
 * @Description: 歌单类型
 * @CreationDate 2023-05-11 11:00:37
 */
export interface PlaylistsListType {
    id?: number
    name: string // 歌单名称
    describe: string // 描述
}

/**
 * @Description: 我喜欢的音乐
 * @CreationDate 2023-05-13 20:06:33
 */
export interface LikeMusicType extends MusicType {
    id: number // 主键
}

/**
 * @Description: 播放器设置
 * @Author: Etc.End(710962805@qq.com)
 * @Copyright: TigerSong
 * @CreationDate 2023-05-13 00:04:43
 */
interface PlayerSettingType {
    playing: string //
    volume: number // 音量
    total_duration?: number // 当前播放歌曲时长
    total_duration_char?: string // 当前播放歌曲时长字符
    current_duration?: number // 当前播放歌曲时段
    current_duration_char?: string // 当前播放歌曲时段
    current_list_name: 'likeMusicList' | 'queryMusicResult' | 'playlistsList' | 'collectList' | 'downloadMusicList' // 当前播放列表 喜欢、查询、歌单、收藏
    play_mode: 'singleCirculation' | 'tableCirculation' | 'sequentialPlay' | 'randomPlay' // 播放模式 单曲循环 列表循环 顺序播放 随机播放
    previous_is_click: boolean // 可以点击上一首
    next_is_click: boolean // 可以点下一首
    play_is_click: boolean // 可以点播放
    play_index: number // 当前播放歌曲在列表中的索引
    play_name: string // 当前播放歌曲的名称
    play_state: boolean // 当前播放状态
}

/**
 * @Description: musicModule类型
 * @CreationDate 2023-05-08 11:51:15
 */
interface IAppState {
    player?: HTMLAudioElement // 播放器
    queryMusicResult: QueryMusicResultType // 搜索歌曲的结果
    currentPlayMusic: CurrentPlayMusicType // 当前播放音乐的相关信息
    playlistsList: PlaylistsListType[] // 歌单列表
    collectList: MusicType[] // 收藏列表
    likeMusicList: LikeMusicType[] // 喜欢的音乐
    playerSetting: PlayerSettingType // 播放器设置
    downloadMusicList: MusicType[]
}

/**
 * @Description: 音乐播放器状态管理器
 * @Author: Etc.End(710962805@qq.com)
 * @Copyright: TigerSong
 * @CreationDate 2023-05-08 11:51:33
 */
export const musicModule = defineStore({
    id: 'music',
    state(): IAppState {
        return {
            currentPlayMusic: {
                current_play_audio_ready: true,
                name: '',
                url: '',
                author: '',
                pic: '',
            },
            queryMusicResult: {
                music_name: '',
                music_page_count: 0,
                music_current_page: 1,
                music_loading: false,
                music_noMore: false,
                music_current_list: []
            },
            playlistsList: [],
            collectList: [],
            likeMusicList: [],
            playerSetting: {
                playing: '',
                volume: 100,
                current_list_name: 'downloadMusicList',
                play_mode: 'tableCirculation',
                previous_is_click: true,
                next_is_click: true,
                play_is_click: true,
                play_index: -1,
                play_name: '',
                play_state: false
            },
            downloadMusicList: []
        };
    },

    actions: {
        /**
         * @Description: 初始化音乐播放器
         * @CreationDate 2023-05-08 11:50:15
         */
        initMusic(container: HTMLAudioElement) {
            this.player = container
            this.setPreviousAndNextClick()
            this.playerSetting.play_index = -1
        },
        /**
         * @Description: 播放
         * @CreationDate 2023-05-08 11:50:03
         */
        play(index?: number):void {
            if (index || index === 0) {
                this.playerSetting.play_index = index;
                (this.player && this.currentPlayMusic.current_play_audio_ready) && this.player.play();
                this.currentPlayMusic.current_play_audio_ready = false
                this.playerSetting.play_state = true
            } else {
                (this.player) && this.player.play();
                this.playerSetting.play_state = true
            }
            this.setPreviousAndNextClick()
        },
        /**
         * @Description: 更新当前歌曲信息
         * @CreationDate 2023-05-08 11:49:06
         */
        setCurrentMusicInfo(info: MusicType):void {
            this.currentPlayMusic = Object.assign(this.currentPlayMusic, info)
            if (this.player && this.currentPlayMusic.url) {
                this.player.src = this.currentPlayMusic.url
                this.playerSetting.play_name = this.currentPlayMusic.name!
            }
        },
        /**
         * @Description: 获取已经下载的所有文件名
         * @CreationDate 2023-05-16 17:24:08
         */
        getDownloadMusicList() {
            const that = this
            const musicList :MusicType[] = [];
            const fs = require('fs')
            const pathList = fs.readdirSync(`${process.cwd()}/download/music`);

            !(async () => {
                for (let i = 0; i < pathList.length; i++) {
                    const data: any = await that.getDownloadMusicInfo(`${process.cwd()}/download/music/${pathList[i]}`)
                    if (data.tags) {
                        musicList.push({
                            name: data.tags.title || pathList[i],
                            album: data.tags.album,
                            author: data.tags.artist,
                            pic: '',
                            url: `file:${process.cwd()}/download/music/${pathList[i]}`
                        });
                    }
                }
                this.downloadMusicList = musicList
            })();
        },
        /**
         * @Description: 根据jsmediatags插件读取下载的歌曲的信息
         * @CreationDate 2023-05-22 09:33:44
         */
        getDownloadMusicInfo(path: string) {
            return new Promise((resolve, reject) => {
                const jsmediatags = require('jsmediatags')
                new jsmediatags.Reader(path).setTagsToRead(["title", "track", "artist", "album", "year"])
                    .read({
                        onSuccess: (tag: any) => {
                            resolve(tag);
                        },
                        onError: (error: any) => {
                            reject(error);
                        }
                    });
            })
        },
        // -----------------------------------------------------------------播放器相关设置-----------------------------------------------------------------
        ready ():void{
            this.currentPlayMusic.current_play_audio_ready = true
            this.playerSetting.total_duration = ~~this.player!.duration;
            this.playerSetting.total_duration && (this.playerSetting.total_duration_char = durationConversionTime(this.playerSetting.total_duration))
        },
        /**
         * @Description: 报错回调
         * @CreationDate 2023-05-13 17:39:59
         */
        error(code: any): void {
            console.log(code)
        },
        /**
         * @Description: 获取音乐当前播放的时段
         * @CreationDate 2023-05-13 20:46:53
         */
        updateTime(): void {
            if (this.player) {
                this.playerSetting.current_duration = ~~this.player.currentTime
                this.playerSetting.current_duration && (this.playerSetting.current_duration_char = durationConversionTime(this.playerSetting.current_duration))
            }
        },
        /**
         * @Description: 设置音乐当前播放的时段
         * @CreationDate 2023-05-13 20:47:17
         */
        settingDuration(val: number): void {
            if (this.player) {
                this.player.currentTime = val
            }
        },
        /**
         * @Description: 设置音量
         * @CreationDate 2023-05-12 23:44:52
         */
        settingVolume(volume: number):void {
            this.player && (this.player.volume = volume)
        },
        /**
         * @Description: 监听音乐播放结束
         * @CreationDate 2023-05-12 22:24:36
         */
        endPlayback ():void {
            this.playerSetting.current_duration = 0
            // 单曲循环
            if (this.playerSetting.play_mode === 'singleCirculation') {
                this.play(this.playerSetting.play_index)
            } else if (this.playerSetting.play_mode === 'sequentialPlay') { // 顺序播放
                let listLength:number = 0
                if (this.playerSetting.current_list_name === 'queryMusicResult') {
                    listLength = this.queryMusicResult.music_current_list.length
                } else {
                    listLength = this[this.playerSetting.current_list_name].length
                }
                if ((this.playerSetting.play_index + 1) === listLength) {
                    this.playerSetting.current_duration_char = '0:00'
                    this.suspend()
                } else {
                    this.play(this.playerSetting.play_index + 1)
                }
            }
            else { // 播放器默认就是列表循环 列表循环
                this.next()
            }
        },
        /**
         * @Description: 切换播放模式
         * @CreationDate 2023-05-15 09:43:34
         */
        changePlayMode (mode: 'singleCirculation' | 'tableCirculation' | 'sequentialPlay' | 'randomPlay'):void {
            this.playerSetting.play_mode = mode
        },
        /**
         * @Description: 上一首
         * @CreationDate 2023-05-08 11:49:31
         */
        previous():void {
            if (this.playerSetting.play_index > 1) {
                this.playerSetting.play_index -= 1
                this.setCurrentMusicInfo(this.queryMusicResult.music_current_list[this.playerSetting.play_index])
                this.play()
            }
        },
        /**
         * @Description: 下一首
         * @CreationDate 2023-05-08 11:49:24
         */
        next():void {
            if (this.playerSetting.current_list_name === 'queryMusicResult') {
                if (this.playerSetting.play_index < this.queryMusicResult.music_current_list.length) {
                    this.playerSetting.play_index += 1
                } else {
                    this.playerSetting.play_index = 0
                }
                this.setCurrentMusicInfo(this.queryMusicResult.music_current_list[this.playerSetting.play_index])
            } else {
                if (this.playerSetting.play_index < this[this.playerSetting.current_list_name].length) {
                    this.playerSetting.play_index += 1
                } else {
                    this.playerSetting.play_index = 0
                }
                this.setCurrentMusicInfo(this[this.playerSetting.current_list_name][this.playerSetting.play_index])
            }
            this.play()
        },
        /**
         * @Description: 单曲循环
         * @CreationDate 2023-05-15 10:47:50
         */
        singleCirculation() :void {
            if (this.player) {
                this.player.currentTime = 0
                this.play()
            }
        },
        /**
         * @Description: 暂停
         * @CreationDate 2023-05-08 11:49:42
         */
        suspend():void {
            this.player && this.player.pause();
            this.playerSetting.play_state = false
        },
        /**
         * @Description: 设置上一首下一首按钮可以点击
         * @CreationDate 2023-05-09 10:15:10
         */
        setPreviousAndNextClick():void {
            this.playerSetting.next_is_click = this.playerSetting.play_index !== -1;
            this.playerSetting.previous_is_click = this.playerSetting.play_index !== -1;
        },
        // -----------------------------------------------------------------爬虫相关-----------------------------------------------------------------
        /** 根据歌曲名称查询对应的列表
         * @Description:
         * @CreationDate 2023-05-08 10:11:51
         */
        getListByMusicName(musicName: string, page?: number) {
            if (!page) {
                this.resetGetParam(musicName)
            }
            const path = `search-${encodeURIComponent(musicName)}-1-${ page ? page : this.queryMusicResult.music_current_page }.htm`
            try {
                axios.get('https://www.****.com/' + path).then((res: any) => {
                    const $: any = cheerio.load(res.data)
                    const musicContent = $('.list-unstyled .media')
                    const pagination = $('.pagination li')
                    // 获取总页数
                    if (pagination.eq(pagination.length - 2).children('a').html()) {
                        const pageTotal = (pagination.eq(pagination.length - 2).children('a').html()).match(/\d+/g)[0]
                        if (pageTotal && pageTotal > 0) {
                            this.queryMusicResult.music_page_count = Number(pageTotal)
                        }
                    }

                    if (this.queryMusicResult.music_current_page === this.queryMusicResult.music_page_count) {
                        this.queryMusicResult.music_noMore = true
                    }

                    // 如何搜索出的结果为0就直接结束
                    if (musicContent.length === 0) {
                        this.queryMusicResult.music_loading = false
                        this.queryMusicResult.music_noMore = false
                    } else {
                        this.circulateGetMusicList(musicContent)
                    }
                }).catch((e: any) => {
                    this.queryMusicResult.music_loading = false
                    console.log('👉👉👉-----------------', e);
                })
            } catch (e) {
                this.queryMusicResult.music_loading = false
                console.log('👉👉👉-----------------', e);
            }
        },
        /**
         * @Description: 循环查询当前页面的歌曲相关信息
         * @CreationDate 2023-05-08 11:48:43
         */
        circulateGetMusicList(musicContent: any) {
            try {
                for (let i = 0; i < musicContent.length; i++) {
                    const musicHref = musicContent['eq'](i).children('div .media-body').children('div .subject').children('a')
                    axios.get('https://www.****.com/' + musicHref.attr('href')).then(info => {
                        const $info: any = cheerio.load(info.data)
                        const musicInfo = $info('.card-thread')['children']('div .card-body').children('div .break-all')
                        if (musicContent.length === (i + 1)) {
                            this.queryMusicResult.music_loading = false
                        }
                        if (musicInfo.children('script') && musicInfo.children('script').get(1) && musicInfo.children('script').get(1).children) {
                            const musicInfos = musicInfo.children('script').get(1).children[0]['data']
                            const S = musicInfos.indexOf('[')
                            const E = musicInfos.indexOf(']')
                            let data = musicInfos.substring(S + 1, E - 3).replace(/\s*/g, '')
                            if (data) {
                                data = data.substring(0, data.length - 1)
                                data = eval('(' + data + ')')
                                this.queryMusicResult.music_current_list.push({
                                    name: data.title,
                                    url: data.url.includes('https') ? data.url.replace('-', '%20-%20') : 'https://www.****.com/' + data.url,
                                    author: data.author,
                                    pic: data.pic
                                } as MusicType)
                            }
                        }
                    })
                }
            } catch (e) {
                this.queryMusicResult.music_loading = false
                console.log('👉👉👉-----------------', e)
            }
        },
        /**
         * @Description: 加载更多
         * @CreationDate 2023-05-08 11:48:21
         */
        loadMore() {
            this.queryMusicResult.music_current_page += 1
            this.getListByMusicName(this.queryMusicResult.music_name, this.queryMusicResult.music_current_page)
        },
        /**
         * @Description: 重置查询条件
         * @CreationDate 2023-05-08 11:48:32
         */
        resetGetParam(musicName: string){
            this.queryMusicResult.music_page_count = 0
            this.queryMusicResult.music_current_page = 1
            this.queryMusicResult.music_noMore = false
            this.queryMusicResult.music_loading = true
            this.queryMusicResult.music_name = musicName
            this.queryMusicResult.music_current_list = []
        },
    },
    getters: {
    },
});

修改electron/main.ts主进程,增加下载监听

const fs = require('fs')
const { app, BrowserWindow, ipcMain } = require('electron')
const path = require('path')
const remote = require("@electron/remote/main");
remote.initialize();

const NODE_ENV = process.env.NODE_ENV
let win

/**
 * @Description: electron程序入口
 * @Author: Etc.End
 * @Copyright: TigerSong
 * @CreationDate 2023-05-20 14:39:26
 */
const createWindow = () => {
    win = new BrowserWindow({
        icon: './public/logo.png',
        frame: false, // 去掉导航最大化最小化以及关闭按钮
        width: 1200,
        height: 800,
        minWidth: 1200,
        minHeight: 800,
        center: true,
        skipTaskbar: false,
        transparent: false,
        webPreferences: {
            nodeIntegration: true,
            contextIsolation: false,
            webSecurity: false,
        }
    })

    try {
        const activation = fs.readFileSync('./config/core.tiger', 'utf8')
        if (activation) {
            // 这里就可以把pinia中的逻辑放在这里,如果激活码不正确的话,就不加载某些脚本。
            // 也可以根据判断激活码来生成路由或删除路由数据,方案很多,自由发挥。
        }
    } catch (e) {
        console.log('👉👉👉-----------------注册码读取失败', e.message)
    }

    win.loadURL(
        NODE_ENV === 'development' ? 'http://localhost:5173/' : `file://${path.join(__dirname, '../dist/index.html')}`
    )

    if (NODE_ENV === 'development') {
        win.webContents.openDevTools()
    }

    remote.enable(win.webContents);
}

// 监听渲染进程发出的download事件
ipcMain.on('download', (evt, args) => {
    downloadFileToMusic(args.downloadPath, args.fileName, args.author, args.pic)
})

function downloadFileToMusic(url, fileName, author, pic) {
    win.webContents.downloadURL(url)
    win.webContents.session.once('will-download', (event, item) => {
        let filePath = path.join(app.getAppPath(), '/download/music', `${fileName}.mp3`);
        if (NODE_ENV !== 'development') {
            filePath = path.join(path.dirname(app.getPath('exe')), '/download/music', `${fileName}.mp3`);
        }
        item.setSavePath(filePath)
        item.on('updated', (event, state) => {
            // 中断
            if (state === 'interrupted') {
                console.log('👉👉👉-----------------下载已中断,但可以继续')
            } else if (state === 'progressing') {
                if (item.isPaused()) {
                    console.log('👉👉👉-----------------这里是暂停的逻辑')
                } else {
                    // const receivedBytes = item.getReceivedBytes()
                    // // 计算每秒下载的速度
                    // item.speed = receivedBytes - prevReceivedBytes
                    // prevReceivedBytes = receivedBytes
                    const progress = item.getReceivedBytes() / item.getTotalBytes()
                    // win.setProgressBar(progress)
                    win.webContents.send('updateProgressing', progress)
                }
            }
        })
        item.once('done', (event, state) => {
            if (state === 'completed') {
                console.log('👉👉👉-----------------下载成功')
            } else {
                console.log('👉👉👉-----------------下载失败:', state)
            }
        })
    })
}

let CONFIG_PATH = path.join(app.getAppPath(), '/config');
if (NODE_ENV !== 'development') {
    CONFIG_PATH = path.join(path.dirname(app.getPath('exe')), '/config');
}

app.whenReady().then(() => {
    createWindow()
    const isExistDir = fs.existsSync(CONFIG_PATH)
    if (!isExistDir) {
        fs.mkdirSync(CONFIG_PATH)
    }
})

ipcMain.on('TSActivateApplication', (evt, args) => {
    fs.writeFile(`${CONFIG_PATH}/core.tiger`, args, function(err) {
        if(err) {
            return console.log('👉👉👉-----------------创建激活码文件失败!')
        }
        setTimeout(() => {
            // 重启
            if (NODE_ENV !== 'development') {
                app.relaunch()
                app.exit()
            }
        }, 2 * 1000);
    })
})

/**
 * @Description: 限制只能打开一个页面
 * @CreationDate 2023-05-20 14:35:52
 */
const gotTheLock = app.requestSingleInstanceLock()
if (!gotTheLock) {
    app.quit()
} else {
    app.on('second-instance', (event, commandLine, workingDirectory) => {
        if (win) {
            if (win.isMinimized()) win.restore()
            win.focus()
        }
    })
}

app.on('window-all-closed', function () {
    if(process.platform !== 'darwin') app.quit()
})

七、最终效果预览 🚀 ✈️ 🚁 🚂

搜索

下载

我是Etc.End。如果文章对你有所帮助,能否帮我点个免费的赞和收藏😍。

 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/649616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

今日小课堂:怎么翻译音频

想象一下&#xff0c;你正在与外国朋友聊天&#xff0c;但是你们之间有语言障碍。不用担心&#xff01;现在有许多翻译语音识别工具可以帮助你轻松应对这种情况。通过这些工具&#xff0c;你可以将语音转换为文字&#xff0c;然后再将其翻译成你所需的语言。接下来&#xff0c;…

会声会影2023中文版本V26.0.0.136

会声会影2023中文版是一款功能强大的视频编辑软件、大型视频制作软件、专业视频剪辑软件。会声会影专业视频编辑处理软件&#xff0c;可以用于剪辑合并视频&#xff0c;制作视频&#xff0c;屏幕录制&#xff0c;光盘制作&#xff0c;视频后期编辑、添加特效、字幕和配音等操作…

爬虫一定要用代理IP吗,不用行不行

目录 1、爬虫一定要用代理IP吗 2、爬虫为什么要用代理IP 3、爬虫怎么使用代理IP 4、爬虫使用代理IP的注意事项 1、爬虫一定要用代理IP吗 很多人觉得&#xff0c;爬虫一定要使用代理IP&#xff0c;否则将寸步难行。但事实上&#xff0c;很多小爬虫不需要使用代理IP照样工作…

【TA100】3.4 前向/延迟渲染管线介绍

一、渲染路径 1.什么是渲染路径&#xff08;Rendering Path&#xff09; ● 是决定光照实现的方式。&#xff08;也就是当前渲染目标使用的光照流程&#xff09; 二、渲染方式 首先看一下两者的直观的不同 前向/正向渲染-Forward Rendering 一句话概括&#xff1a;每个光…

openpose原理以及各种细节的介绍

前言&#xff1a; OpenPose是一个基于深度学习的人体姿势估计库&#xff0c;它可以从图像或视频中准确地检测和估计人体的关键点和姿势信息。OpenPose的目标是将人体姿势估计变成一个实时、多人、准确的任务。——本节介绍openpose的原理部分 把关键点按照定义好的规则从上到下…

Matter实战系列-----5.matter设备证书烧录

一、安装工具 1.1 安装Commander_Linux工具 下载地址 https://www.silabs.com/documents/public/software/SimplicityCommander-Linux.zip 下载完之后解压缩&#xff0c;在压缩包内执行命令如下 tar jxvf Commander_linux_x86_64_1v15p0b1306.tar.bz cd ./commander ./co…

启动appium服务的2种方法(python脚本cmd窗口)

目录 前言&#xff1a; 1. 通过cmd窗口命令启动 1.1 启动单个appium服务 1.2 启动多个appium服务 2. 通过python脚本来启动 2.1 启动单个appium服务 2.2 启动多个appium服务 3. 启动校验 3.1 通过cmd命令查看 3.1.1 查看指定端口号 3.1.2 查看全部端口号 3.2 通过生…

华为笔记本怎么用U盘重装Win10系统?

华为笔记本怎么用U盘重装Win10系统&#xff1f;华为笔记本拥有指纹识别、背光键盘、信号增强等功能&#xff0c;带给用户超棒的操作体验&#xff0c;用户现在想用U盘来重装华为笔记本Win10系统&#xff0c;但不知道具体怎么操作&#xff0c;这时候用户就可以按照以下分享的华为…

CMAC算法介绍

文章目录 一、简介二、符号三、步骤3.1 子秘钥生成3.2 计算MAC值 一、简介 CMAC&#xff08;Cipher Block Chaining-Message Authentication Code&#xff09;&#xff0c;也简称为CBC_MAC&#xff0c;它是一种基于对称秘钥分组加密算法的消息认证码。由于其是基于“对称秘钥分…

网络安全|渗透测试入门学习,从零基础入门到精通—渗透中的开发语言

目录 前面的话 开发语言 1、html 解析 2、JavaScript 用法 3、JAVA 特性 4、PHP 作用 PHP 能做什么&#xff1f; 5、C/C 使用 如何学习 前面的话 关于在渗透中需要学习的语言第一点个人认为就是可以打一下HTML&#xff0c;JS那些基础知识&#xff0c;磨刀不误砍柴…

RTU遥测终端机的应用场景有哪些?

遥测终端机又称智能RTU遥测终端机&#xff0c;是一种用于采集、传输和处理遥测数据的设备。在现代科技的发展中&#xff0c;遥测终端机扮演着重要的角色。它是一种能够实现远程监测和控制的关键设备&#xff0c;广泛应用于各个领域&#xff0c;包括水文水利、环境监测、工业自动…

Linux系统:优化命令sar

目录 一、理论 1.命令描述 2.命令作用 3.命令参数 4.实用实例 二、实验 1.压力测试 三、问题 1.Linux系统五大负载如何解决 2.为什么使用ab命令进行网络传输数据的压力测试 3.ab命令发送请求测试失败 四、总结 1.sar命令 2.ab命令 3.五大负载 一、理论 1.命令描…

MySQL的索引(我把梦想卖了 换成了柴米油盐)

文章目录 一、索引的概念二、索引的作用如何实现&#xff1f; 三、索引的副作用四、创建索引的原则依据创建索引的依据 五、索引的分类六、索引的增删改查1.创建索引&#xff08;1&#xff09;创建普通索引&#xff08;2&#xff09;创建唯一索引&#xff08;3&#xff09;创建…

Spring Boot进阶(51):Spring Boot项目如何集成 HTML?| 超级详细,建议收藏

1. 前言&#x1f525; 我们都知道&#xff0c;Spring Boot作为一款广泛应用于企业级的开发框架&#xff0c;其通过简化开发过程、提高开发效率赢得了众多开发者的青睐。在实际项目开发中&#xff0c;集成 HTML作为 Web 应用程序中的一个基本需求&#xff0c;也是现在极其常见的…

618最值得入手的数码产品有哪些?四款必入数码产品数码推荐

时间飞逝&#xff0c;不知不觉已经过了6月中旬&#xff0c;大家心心念念的618年中大促也即将迎来最后一波高潮。这次618大促各大品牌的优惠力度都非常可观&#xff0c;特别是数码产品类&#xff0c;可以说是今年最值得入手的时期。今天也为大家推荐几款高颜值数码好物&#xff…

在 Apple silicon Mac 上 DFU 模式修复或恢复 macOS 固件

搭载 Apple 芯片的 Mac 电脑 DFU 模式全新安装 macOS Ventura 请访问原文链接&#xff1a;https://sysin.org/blog/apple-silicon-mac-dfu/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Mac computers with Apple silicon&a…

聚焦2023数博会|高端对话,大咖观点精彩荟萃(上)

当前数据作为新型生产要素&#xff0c;是数字化、网络化、智能化的基础&#xff0c;已快速融入生产、分配、流通、消费和社会服务管理等各环节&#xff0c;深刻改变着生产方式、生活方式和社会治理方式。数据基础制度建设事关国家发展和安全大局。为加快构建数据基础制度&#…

【C++】手撕跳表

文章目录 跳表简介时间复杂度 代码实现节点类跳表类 源代码&#xff08;附详细注释&#xff09;参考 跳表 简介 跳表全称为跳跃列表&#xff0c;它允许快速查询&#xff0c;插入和删除一个有序连续元素的数据链表。跳跃列表的平均查找和插入时间复杂度都是O(logn)。快速查询是…

代码随想录二叉树 Java(三)

文章目录 &#xff08;简单&#xff09;501. 二叉搜索树中的众数&#xff08;*中等&#xff09;236. 二叉树的最近公共祖先&#xff08;中等&#xff09;235. 二叉搜索树的最近公共祖先&#xff08;中等&#xff09;701. 二叉搜索树中的插入操作&#xff08;*中等&#xff09;4…

软件测试中如何编写单元测试用例(白盒测试)

目录 前言&#xff1a; 一、 单元测试的概念 二、开始测试前的准备 三、开始测试 四、完成测试 前言&#xff1a; 单元测试是软件测试中一种重要的测试方法&#xff0c;它是在代码级别进行测试&#xff0c;通过对每个模块或功能进行独立测试来保障代码的正确性和可靠性。…