mirror of
https://github.com/atdunbg/Nekosonic-Music.git
synced 2026-06-22 00:58:51 +08:00
refactor: 创建统一 API 封装层,前端不再直接调用 invoke
- 新增 src/api.ts,按职责分为 MusicApi/AudioApi/DeviceApi/DownloadApi/AppApi 五个命名空间 - 替换 15 个文件中所有 invoke 调用为 API 层方法 - 后端接口变更只需修改 api.ts 一处,便于后期迭代维护
This commit is contained in:
12
src/App.vue
12
src/App.vue
@ -38,7 +38,6 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
|
import { ref, watch, onMounted, onBeforeUnmount } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
|
||||||
import { useUserStore } from './stores/user';
|
import { useUserStore } from './stores/user';
|
||||||
import { useSettingsStore, type CloseAction } from './stores/settings';
|
import { useSettingsStore, type CloseAction } from './stores/settings';
|
||||||
import { usePlayerStore } from './stores/player';
|
import { usePlayerStore } from './stores/player';
|
||||||
@ -55,6 +54,7 @@ import { useUpdater } from './composables/useUpdater';
|
|||||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
import { listen } from '@tauri-apps/api/event';
|
import { listen } from '@tauri-apps/api/event';
|
||||||
import { register, unregister } from '@tauri-apps/plugin-global-shortcut';
|
import { register, unregister } from '@tauri-apps/plugin-global-shortcut';
|
||||||
|
import { MusicApi, AudioApi, DeviceApi, AppApi } from './api';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const player = usePlayerStore();
|
const player = usePlayerStore();
|
||||||
@ -87,9 +87,9 @@ onMounted(async () => {
|
|||||||
if (userStore.isLoggedIn) {
|
if (userStore.isLoggedIn) {
|
||||||
player.loadLikedIds();
|
player.loadLikedIds();
|
||||||
}
|
}
|
||||||
try { await invoke('stop_audio'); } catch { /* 忽略 */ }
|
try { await AudioApi.stopAudio(); } catch { /* 忽略 */ }
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('get_login_status');
|
const jsonStr: string = await MusicApi.getLoginStatus();
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
if (data.account || data.profile) {
|
if (data.account || data.profile) {
|
||||||
const profile = data.profile || data.account;
|
const profile = data.profile || data.account;
|
||||||
@ -105,7 +105,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
if (settings.outputDevice) {
|
if (settings.outputDevice) {
|
||||||
try {
|
try {
|
||||||
await invoke('set_output_device', { device: settings.outputDevice });
|
await DeviceApi.setOutputDevice(settings.outputDevice);
|
||||||
} catch { /* 忽略 */ }
|
} catch { /* 忽略 */ }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -118,7 +118,7 @@ function closeWindow() {
|
|||||||
} else if (settings.closeAction === 'minimize') {
|
} else if (settings.closeAction === 'minimize') {
|
||||||
currentWindow.hide();
|
currentWindow.hide();
|
||||||
} else {
|
} else {
|
||||||
invoke('exit_app');
|
AppApi.exitApp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ function handleCloseAction(action: CloseAction, remember: boolean) {
|
|||||||
if (action === 'minimize') {
|
if (action === 'minimize') {
|
||||||
currentWindow.hide();
|
currentWindow.hide();
|
||||||
} else {
|
} else {
|
||||||
invoke('exit_app');
|
AppApi.exitApp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
196
src/api.ts
Normal file
196
src/api.ts
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
|
export namespace MusicApi {
|
||||||
|
export async function getLoginStatus(): Promise<string> {
|
||||||
|
return invoke('get_login_status');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function logout(): Promise<void> {
|
||||||
|
return invoke('logout');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getQrKey(): Promise<string> {
|
||||||
|
return invoke('get_qr_key');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkQrStatus(key: string): Promise<string> {
|
||||||
|
return invoke('check_qr_status', { query: { key } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function likelist(uid: number): Promise<string> {
|
||||||
|
return invoke('likelist', { uid });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function likeSong(id: number, like: boolean): Promise<void> {
|
||||||
|
return invoke('like_song', { query: { id, like: like ? 'true' : 'false' } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function userPlaylist(uid: number): Promise<string> {
|
||||||
|
return invoke('user_playlist', { uid });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPlaylistDetail(id: number): Promise<string> {
|
||||||
|
return invoke('get_playlist_detail', { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function playlistTrackAll(id: number): Promise<string> {
|
||||||
|
return invoke('playlist_track_all', { query: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function playlistSubscribe(id: number, subscribe: boolean): Promise<void> {
|
||||||
|
return invoke('playlist_subscribe', { query: { id, subscribe } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function recommendResource(): Promise<string> {
|
||||||
|
return invoke('recommend_resource');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function recommendSongs(): Promise<string> {
|
||||||
|
return invoke('recommend_songs');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSongDetail(id: string): Promise<string> {
|
||||||
|
return invoke('get_song_detail', { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSongUrl(query: { id: number; level: string; fm_mode?: boolean }): Promise<string> {
|
||||||
|
return invoke('get_song_url', { query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getLyric(id: number): Promise<string> {
|
||||||
|
return invoke('get_lyric', { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function searchSuggest(keyword: string): Promise<string> {
|
||||||
|
return invoke('search_suggest', { query: { keyword } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getHotSearch(): Promise<string> {
|
||||||
|
return invoke('get_hot_search');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function cloudsearch(query: { keyword: string; searchType: number; limit: number }): Promise<string> {
|
||||||
|
return invoke('cloudsearch', { query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function albumDetail(id: number): Promise<string> {
|
||||||
|
return invoke('album_detail', { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function artistDetail(id: number): Promise<string> {
|
||||||
|
return invoke('artist_detail', { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function artistSongs(query: { id: number; order: string; limit: number; offset: number }): Promise<string> {
|
||||||
|
return invoke('artist_songs', { query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function artistAlbum(id: number, limit: number, offset: number): Promise<string> {
|
||||||
|
return invoke('artist_album', { id, limit, offset });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function artistDesc(id: number): Promise<string> {
|
||||||
|
return invoke('artist_desc', { id });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function commentHot(query: { type: number; id: number; limit: number; offset: number }): Promise<string> {
|
||||||
|
return invoke('comment_hot', { query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function commentLike(query: { t: number; type: number; id: number; cid: number }): Promise<void> {
|
||||||
|
return invoke('comment_like', { query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function personalFm(): Promise<string> {
|
||||||
|
return invoke('personal_fm');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function personalFmMode(query: { mode: string; subMode: string; limit: number }): Promise<string> {
|
||||||
|
return invoke('personal_fm_mode', { query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fmTrash(id: number, time: number): Promise<void> {
|
||||||
|
return invoke('fm_trash', { query: { id, time } });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function scrobble(query: { id: number; sourceid: string; time: number }): Promise<void> {
|
||||||
|
return invoke('scrobble', { query });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace AudioApi {
|
||||||
|
export async function playAudio(url: string): Promise<void> {
|
||||||
|
return invoke('play_audio', { url });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function playLocalAudio(path: string): Promise<void> {
|
||||||
|
return invoke('play_local_audio', { path });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function pauseAudio(): Promise<void> {
|
||||||
|
return invoke('pause_audio');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resumeAudio(): Promise<void> {
|
||||||
|
return invoke('resume_audio');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function stopAudio(): Promise<void> {
|
||||||
|
return invoke('stop_audio');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function seekAudio(time: number): Promise<void> {
|
||||||
|
return invoke('seek_audio', { time });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setVolume(vol: number): Promise<void> {
|
||||||
|
return invoke('set_volume', { vol });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAudioPosition(): Promise<number> {
|
||||||
|
return invoke('get_audio_position');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace DeviceApi {
|
||||||
|
export async function getOutputDevices(): Promise<string[]> {
|
||||||
|
return invoke('get_output_devices');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setOutputDevice(device: string | null): Promise<void> {
|
||||||
|
return invoke('set_output_device', { device });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace DownloadApi {
|
||||||
|
export async function downloadSong(query: {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
artist: string;
|
||||||
|
album: string | null;
|
||||||
|
duration: number | null;
|
||||||
|
coverUrl: string | null;
|
||||||
|
level: string;
|
||||||
|
downloadPath: string | null;
|
||||||
|
}): Promise<void> {
|
||||||
|
return invoke('download_song', { query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listLocalSongs(downloadPath: string | null): Promise<any[]> {
|
||||||
|
return invoke('list_local_songs', { downloadPath });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteLocalSong(query: { id: number; filename: string; downloadPath: string | null }): Promise<void> {
|
||||||
|
return invoke('delete_local_song', { query });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDefaultDownloadPath(): Promise<string> {
|
||||||
|
return invoke('get_default_download_path');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace AppApi {
|
||||||
|
export function exitApp(): Promise<void> {
|
||||||
|
return invoke('exit_app');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
import { ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { MusicApi } from '../api'
|
||||||
import IconMessageSquare from '~icons/lucide/message-square'
|
import IconMessageSquare from '~icons/lucide/message-square'
|
||||||
import IconHeart from '~icons/lucide/heart'
|
import IconHeart from '~icons/lucide/heart'
|
||||||
|
|
||||||
@ -77,13 +77,11 @@ async function fetchComments(reset = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('comment_hot', {
|
const jsonStr: string = await MusicApi.commentHot({
|
||||||
query: {
|
type: props.type,
|
||||||
type: props.type,
|
id: props.id,
|
||||||
id: props.id,
|
limit: pageSize,
|
||||||
limit: pageSize,
|
offset: (pageNo.value - 1) * pageSize
|
||||||
offset: (pageNo.value - 1) * pageSize
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
const data = JSON.parse(jsonStr)
|
const data = JSON.parse(jsonStr)
|
||||||
const list = data.hotComments || []
|
const list = data.hotComments || []
|
||||||
@ -116,13 +114,11 @@ async function likeComment(cid: number) {
|
|||||||
const liked = !!target.liked
|
const liked = !!target.liked
|
||||||
likingSet.value.add(cid)
|
likingSet.value.add(cid)
|
||||||
try {
|
try {
|
||||||
await invoke('comment_like', {
|
await MusicApi.commentLike({
|
||||||
query: {
|
t: liked ? 0 : 1,
|
||||||
t: liked ? 0 : 1,
|
type: props.type,
|
||||||
type: props.type,
|
id: props.id,
|
||||||
id: props.id,
|
cid
|
||||||
cid
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
target.liked = !liked
|
target.liked = !liked
|
||||||
target.likedCount += liked ? -1 : 1
|
target.likedCount += liked ? -1 : 1
|
||||||
|
|||||||
@ -216,7 +216,7 @@ import { usePlayerStore, PlayMode } from '../stores/player';
|
|||||||
import { useDownload } from '../composables/useDownload';
|
import { useDownload } from '../composables/useDownload';
|
||||||
import { formatTime } from '../utils/format';
|
import { formatTime } from '../utils/format';
|
||||||
import { getCoverUrl } from '../utils/song';
|
import { getCoverUrl } from '../utils/song';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { AudioApi } from '../api';
|
||||||
import { showToast } from '../composables/useToast';
|
import { showToast } from '../composables/useToast';
|
||||||
import { listen } from '@tauri-apps/api/event';
|
import { listen } from '@tauri-apps/api/event';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
@ -292,7 +292,7 @@ function toggleMute() {
|
|||||||
} else {
|
} else {
|
||||||
player.volume = prevVolume.value || 100;
|
player.volume = prevVolume.value || 100;
|
||||||
}
|
}
|
||||||
invoke('set_volume', { vol: player.volume / 100 });
|
AudioApi.setVolume(player.volume / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
let onDocMove: ((e: MouseEvent) => void) | null = null;
|
let onDocMove: ((e: MouseEvent) => void) | null = null;
|
||||||
@ -418,7 +418,7 @@ async function handleVolumeChange(e: Event) {
|
|||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
const val = parseInt(target.value, 10);
|
const val = parseInt(target.value, 10);
|
||||||
player.volume = val;
|
player.volume = val;
|
||||||
await invoke('set_volume', { vol: val / 100 });
|
await AudioApi.setVolume(val / 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
const volumeBarBg = computed(() => {
|
const volumeBarBg = computed(() => {
|
||||||
|
|||||||
@ -116,9 +116,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch, onMounted } from 'vue';
|
import { ref, watch, onMounted } from 'vue';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '../stores/user';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
|
import { MusicApi } from '../api';
|
||||||
import IconHome from '~icons/lucide/home';
|
import IconHome from '~icons/lucide/home';
|
||||||
import IconSearch from '~icons/lucide/search';
|
import IconSearch from '~icons/lucide/search';
|
||||||
import IconRadio from '~icons/lucide/radio';
|
import IconRadio from '~icons/lucide/radio';
|
||||||
@ -142,7 +142,7 @@ const showSubPlaylists = ref(true);
|
|||||||
async function loadPlaylists() {
|
async function loadPlaylists() {
|
||||||
if (!userStore.isLoggedIn || !userStore.user) return;
|
if (!userStore.isLoggedIn || !userStore.user) return;
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('user_playlist', { uid: userStore.user.userId });
|
const jsonStr: string = await MusicApi.userPlaylist(userStore.user.userId);
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
createdPlaylists.value = (data.playlist || []).filter((p: any) => !p.subscribed).slice(1);
|
createdPlaylists.value = (data.playlist || []).filter((p: any) => !p.subscribed).slice(1);
|
||||||
subPlaylists.value = (data.playlist || []).filter((p: any) => p.subscribed);
|
subPlaylists.value = (data.playlist || []).filter((p: any) => p.subscribed);
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
|
||||||
import { parseLrc, mergeTranslation, getCurrentLyricIndex, LyricLine } from '../utils/lyric';
|
import { parseLrc, mergeTranslation, getCurrentLyricIndex, LyricLine } from '../utils/lyric';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
|
import { MusicApi } from '../api';
|
||||||
|
|
||||||
export function useLyric() {
|
export function useLyric() {
|
||||||
const player = usePlayerStore();
|
const player = usePlayerStore();
|
||||||
@ -19,7 +19,7 @@ export function useLyric() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('get_lyric', { id: song.id });
|
const jsonStr: string = await MusicApi.getLyric(song.id);
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const lrc = data?.lrc?.lyric || '';
|
const lrc = data?.lrc?.lyric || '';
|
||||||
const tLrc = data?.tlyric?.lyric || '';
|
const tLrc = data?.tlyric?.lyric || '';
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { reactive, watch } from 'vue';
|
import { reactive, watch } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
|
||||||
import { listen } from '@tauri-apps/api/event';
|
import { listen } from '@tauri-apps/api/event';
|
||||||
import { useSettingsStore } from '../stores/settings';
|
import { useSettingsStore } from '../stores/settings';
|
||||||
import { showToast } from '../composables/useToast';
|
import { showToast } from '../composables/useToast';
|
||||||
import { getCoverUrl, type Song } from '../utils/song';
|
import { getCoverUrl, type Song } from '../utils/song';
|
||||||
|
import { DownloadApi } from '../api';
|
||||||
|
|
||||||
interface DownloadTask {
|
interface DownloadTask {
|
||||||
id: number;
|
id: number;
|
||||||
@ -42,7 +42,7 @@ async function setupDownloadListener() {
|
|||||||
async function refreshLocalIds() {
|
async function refreshLocalIds() {
|
||||||
try {
|
try {
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
const list: { id: number }[] = await invoke('list_local_songs', { downloadPath: settings.downloadPath || null });
|
const list: { id: number }[] = await DownloadApi.listLocalSongs(settings.downloadPath || null);
|
||||||
localSongIds.clear();
|
localSongIds.clear();
|
||||||
for (const s of list) {
|
for (const s of list) {
|
||||||
localSongIds.add(s.id);
|
localSongIds.add(s.id);
|
||||||
@ -90,17 +90,15 @@ async function downloadSong(song: Song) {
|
|||||||
tasks.push({ id: song.id, name: song.name, progress: 0 });
|
tasks.push({ id: song.id, name: song.name, progress: 0 });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await invoke('download_song', {
|
await DownloadApi.downloadSong({
|
||||||
query: {
|
id: song.id,
|
||||||
id: song.id,
|
name: song.name,
|
||||||
name: song.name,
|
artist,
|
||||||
artist,
|
album: albumName,
|
||||||
album: albumName,
|
duration: durationVal,
|
||||||
duration: durationVal,
|
coverUrl,
|
||||||
coverUrl,
|
level: settings.audioQuality,
|
||||||
level: settings.audioQuality,
|
downloadPath: settings.downloadPath || null,
|
||||||
downloadPath: settings.downloadPath || null,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
localSongIds.add(song.id);
|
localSongIds.add(song.id);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { ref, watch, nextTick } from 'vue';
|
import { ref, watch, nextTick } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
|
||||||
import { normalizeSong, type Song } from '../utils/song';
|
import { normalizeSong, type Song } from '../utils/song';
|
||||||
import { useSettingsStore } from './settings';
|
import { useSettingsStore } from './settings';
|
||||||
import { useUserStore } from './user';
|
import { useUserStore } from './user';
|
||||||
import { showToast } from '../composables/useToast';
|
import { showToast } from '../composables/useToast';
|
||||||
|
import { MusicApi, AudioApi } from '../api';
|
||||||
|
|
||||||
export type PlayMode = 'loop' | 'shuffle' | 'repeat-one';
|
export type PlayMode = 'loop' | 'shuffle' | 'repeat-one';
|
||||||
export type { Song };
|
export type { Song };
|
||||||
@ -70,7 +70,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
if (!userStore.isLoggedIn) return;
|
if (!userStore.isLoggedIn) return;
|
||||||
try {
|
try {
|
||||||
const json: string = await invoke('likelist', { uid: userStore.user!.userId });
|
const json = await MusicApi.likelist(userStore.user!.userId);
|
||||||
const data = JSON.parse(json);
|
const data = JSON.parse(json);
|
||||||
const ids: number[] = data.ids || data.data?.ids || [];
|
const ids: number[] = data.ids || data.data?.ids || [];
|
||||||
likedIds.value = new Set(ids);
|
likedIds.value = new Set(ids);
|
||||||
@ -83,7 +83,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
const wasLiked = likedIds.value.has(songId);
|
const wasLiked = likedIds.value.has(songId);
|
||||||
const newLike = !wasLiked;
|
const newLike = !wasLiked;
|
||||||
try {
|
try {
|
||||||
await invoke('like_song', { query: { id: songId, like: newLike ? 'true' : 'false' } });
|
await MusicApi.likeSong(songId, newLike);
|
||||||
if (newLike) {
|
if (newLike) {
|
||||||
likedIds.value.add(songId);
|
likedIds.value.add(songId);
|
||||||
} else {
|
} else {
|
||||||
@ -124,12 +124,10 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
if (lastScrobbleId === song.id && lastScrobbleStartTime > 0) {
|
if (lastScrobbleId === song.id && lastScrobbleStartTime > 0) {
|
||||||
const playedSec = Math.round((Date.now() - lastScrobbleStartTime) / 1000);
|
const playedSec = Math.round((Date.now() - lastScrobbleStartTime) / 1000);
|
||||||
if (playedSec > 5) {
|
if (playedSec > 5) {
|
||||||
invoke('scrobble', {
|
MusicApi.scrobble({
|
||||||
query: {
|
id: song.id,
|
||||||
id: song.id,
|
sourceid: isFmMode.value ? String(song.id) : '',
|
||||||
sourceid: isFmMode.value ? String(song.id) : '',
|
time: playedSec,
|
||||||
time: playedSec,
|
|
||||||
},
|
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,7 +156,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
|
|
||||||
async function fmTrash(songId: number) {
|
async function fmTrash(songId: number) {
|
||||||
try {
|
try {
|
||||||
await invoke('fm_trash', { query: { id: songId, time: 25 } });
|
await MusicApi.fmTrash(songId, 25);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('fm_trash 失败', e);
|
console.error('fm_trash 失败', e);
|
||||||
showToast('减少推荐失败', 'error');
|
showToast('减少推荐失败', 'error');
|
||||||
@ -169,13 +167,11 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
async function fetchFmBatch(): Promise<Song[]> {
|
async function fetchFmBatch(): Promise<Song[]> {
|
||||||
const isDefault = fmMode.value === 'DEFAULT' && !fmSubMode.value;
|
const isDefault = fmMode.value === 'DEFAULT' && !fmSubMode.value;
|
||||||
const jsonStr: string = isDefault
|
const jsonStr: string = isDefault
|
||||||
? await invoke('personal_fm')
|
? await MusicApi.personalFm()
|
||||||
: await invoke('personal_fm_mode', {
|
: await MusicApi.personalFmMode({
|
||||||
query: {
|
mode: fmMode.value,
|
||||||
mode: fmMode.value,
|
subMode: fmSubMode.value,
|
||||||
subMode: fmSubMode.value,
|
limit: 3,
|
||||||
limit: 3,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const raw = data.data || data;
|
const raw = data.data || data;
|
||||||
@ -191,7 +187,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
reportScrobble();
|
reportScrobble();
|
||||||
if (!song.dt || song.dt === 0) {
|
if (!song.dt || song.dt === 0) {
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('get_song_detail', { id: String(song.id) });
|
const jsonStr = await MusicApi.getSongDetail(String(song.id));
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const full = data.songs?.[0];
|
const full = data.songs?.[0];
|
||||||
if (full) {
|
if (full) {
|
||||||
@ -202,7 +198,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
} catch { /* 忽略 */ }
|
} catch { /* 忽略 */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
await invoke('stop_audio');
|
await AudioApi.stopAudio();
|
||||||
queue.value = [];
|
queue.value = [];
|
||||||
currentIndex.value = -1;
|
currentIndex.value = -1;
|
||||||
playing.value = false;
|
playing.value = false;
|
||||||
@ -210,7 +206,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
fmSong.value = song;
|
fmSong.value = song;
|
||||||
currentSong.value = song;
|
currentSong.value = song;
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('get_song_url', { query: { id: Number(song.id), level: settings.audioQuality, fm_mode: true } });
|
const jsonStr = await MusicApi.getSongUrl({ id: Number(song.id), level: settings.audioQuality, fm_mode: true });
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const url: string | undefined = data.url;
|
const url: string | undefined = data.url;
|
||||||
if (!url) throw new Error('无播放源');
|
if (!url) throw new Error('无播放源');
|
||||||
@ -234,7 +230,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmVipSkipCount = 0;
|
fmVipSkipCount = 0;
|
||||||
await invoke('play_audio', { url });
|
await AudioApi.playAudio(url);
|
||||||
await waitForAudioStart();
|
await waitForAudioStart();
|
||||||
playing.value = true;
|
playing.value = true;
|
||||||
duration.value = (song.dt || 0) / 1000;
|
duration.value = (song.dt || 0) / 1000;
|
||||||
@ -260,7 +256,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
const idx = queue.value.findIndex(s => s.id === song.id);
|
const idx = queue.value.findIndex(s => s.id === song.id);
|
||||||
if (idx !== -1 && idx === currentIndex.value && currentSong.value?.id === song.id) {
|
if (idx !== -1 && idx === currentIndex.value && currentSong.value?.id === song.id) {
|
||||||
if (!playing.value) {
|
if (!playing.value) {
|
||||||
await invoke('resume_audio');
|
await AudioApi.resumeAudio();
|
||||||
playing.value = true;
|
playing.value = true;
|
||||||
startTick();
|
startTick();
|
||||||
}
|
}
|
||||||
@ -286,7 +282,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
&& queue.value.every((s, i) => s.id === songs[i].id);
|
&& queue.value.every((s, i) => s.id === songs[i].id);
|
||||||
if (sameQueue) {
|
if (sameQueue) {
|
||||||
if (!playing.value) {
|
if (!playing.value) {
|
||||||
await invoke('resume_audio');
|
await AudioApi.resumeAudio();
|
||||||
playing.value = true;
|
playing.value = true;
|
||||||
startTick();
|
startTick();
|
||||||
}
|
}
|
||||||
@ -324,16 +320,16 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
duration.value = (song.dt || 0) / 1000;
|
duration.value = (song.dt || 0) / 1000;
|
||||||
|
|
||||||
if (song.localPath) {
|
if (song.localPath) {
|
||||||
await invoke('play_local_audio', { path: song.localPath });
|
await AudioApi.playLocalAudio(song.localPath);
|
||||||
await waitForAudioStart();
|
await waitForAudioStart();
|
||||||
playing.value = true;
|
playing.value = true;
|
||||||
startTick();
|
startTick();
|
||||||
addRecent(song);
|
addRecent(song);
|
||||||
emitPlaybackState();
|
emitPlaybackState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const jsonStr: string = await invoke('get_song_url', { query: { id: Number(song.id), level: settings.audioQuality } });
|
const jsonStr = await MusicApi.getSongUrl({ id: Number(song.id), level: settings.audioQuality });
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const url: string | undefined = data.url;
|
const url: string | undefined = data.url;
|
||||||
|
|
||||||
@ -355,7 +351,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await invoke('play_audio', { url });
|
await AudioApi.playAudio(url);
|
||||||
await waitForAudioStart();
|
await waitForAudioStart();
|
||||||
playing.value = true;
|
playing.value = true;
|
||||||
startTick();
|
startTick();
|
||||||
@ -385,7 +381,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
if (syncCounter >= 2) {
|
if (syncCounter >= 2) {
|
||||||
syncCounter = 0;
|
syncCounter = 0;
|
||||||
try {
|
try {
|
||||||
const pos = await invoke<number>('get_audio_position');
|
const pos = await AudioApi.getAudioPosition();
|
||||||
if (pos >= currentTime.value - 0.5) {
|
if (pos >= currentTime.value - 0.5) {
|
||||||
currentTime.value = pos;
|
currentTime.value = pos;
|
||||||
}
|
}
|
||||||
@ -416,17 +412,17 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
|
|
||||||
async function toggle() {
|
async function toggle() {
|
||||||
if (playing.value) {
|
if (playing.value) {
|
||||||
await invoke('pause_audio');
|
await AudioApi.pauseAudio();
|
||||||
playing.value = false;
|
playing.value = false;
|
||||||
} else {
|
} else {
|
||||||
await invoke('resume_audio');
|
await AudioApi.resumeAudio();
|
||||||
playing.value = true;
|
playing.value = true;
|
||||||
}
|
}
|
||||||
emitPlaybackState();
|
emitPlaybackState();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stop() {
|
async function stop() {
|
||||||
await invoke('stop_audio');
|
await AudioApi.stopAudio();
|
||||||
playing.value = false;
|
playing.value = false;
|
||||||
currentSong.value = null;
|
currentSong.value = null;
|
||||||
currentTime.value = 0;
|
currentTime.value = 0;
|
||||||
@ -481,7 +477,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
try {
|
try {
|
||||||
currentTime.value = time;
|
currentTime.value = time;
|
||||||
if (onSeekStart) onSeekStart();
|
if (onSeekStart) onSeekStart();
|
||||||
await invoke('seek_audio', { time });
|
await AudioApi.seekAudio(time);
|
||||||
startTick();
|
startTick();
|
||||||
emitPlaybackState();
|
emitPlaybackState();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -492,7 +488,7 @@ export const usePlayerStore = defineStore('player', () => {
|
|||||||
async function adjustVolume(delta: number) {
|
async function adjustVolume(delta: number) {
|
||||||
const newVol = Math.max(0, Math.min(100, volume.value + delta));
|
const newVol = Math.max(0, Math.min(100, volume.value + delta));
|
||||||
volume.value = newVol;
|
volume.value = newVol;
|
||||||
await invoke('set_volume', { vol: newVol / 100 });
|
await AudioApi.setVolume(newVol / 100);
|
||||||
emitPlaybackState();
|
emitPlaybackState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -654,7 +650,7 @@ listen<string>('mpris-command', (event) => {
|
|||||||
const vol = parseFloat(cmd.slice(10));
|
const vol = parseFloat(cmd.slice(10));
|
||||||
if (!isNaN(vol)) {
|
if (!isNaN(vol)) {
|
||||||
player.volume = Math.round(vol * 100);
|
player.volume = Math.round(vol * 100);
|
||||||
invoke('set_volume', { vol }).catch(() => {});
|
AudioApi.setVolume(vol).catch(() => {});
|
||||||
}
|
}
|
||||||
} else if (cmd.startsWith('Seek:')) {
|
} else if (cmd.startsWith('Seek:')) {
|
||||||
const offsetUs = parseInt(cmd.slice(5), 10);
|
const offsetUs = parseInt(cmd.slice(5), 10);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
|
|
||||||
export interface UserProfile {
|
export interface UserProfile {
|
||||||
userId: number;
|
userId: number;
|
||||||
@ -21,7 +21,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function logout() {
|
async function logout() {
|
||||||
try { await invoke('logout'); } catch { /* 忽略 */ }
|
try { await MusicApi.logout(); } catch { /* 忽略 */ }
|
||||||
user.value = null;
|
user.value = null;
|
||||||
isLoggedIn.value = false;
|
isLoggedIn.value = false;
|
||||||
localStorage.removeItem('user_profile');
|
localStorage.removeItem('user_profile');
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, watch } from 'vue';
|
import { ref, onMounted, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import { normalizeSong, type Song } from '../utils/song';
|
import { normalizeSong, type Song } from '../utils/song';
|
||||||
import { formatDate } from '../utils/format';
|
import { formatDate } from '../utils/format';
|
||||||
@ -79,7 +79,7 @@ async function fetchAlbum(id: number) {
|
|||||||
album.value = null;
|
album.value = null;
|
||||||
songs.value = [];
|
songs.value = [];
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('album_detail', { id });
|
const jsonStr: string = await MusicApi.albumDetail(id);
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
album.value = data.album;
|
album.value = data.album;
|
||||||
songs.value = (data.songs || []).map(normalizeSong);
|
songs.value = (data.songs || []).map(normalizeSong);
|
||||||
|
|||||||
@ -83,7 +83,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, watch } from 'vue';
|
import { ref, onMounted, watch } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import { formatPlayCount, formatDate } from '../utils/format';
|
import { formatPlayCount, formatDate } from '../utils/format';
|
||||||
import { normalizeSong, type Song } from '../utils/song';
|
import { normalizeSong, type Song } from '../utils/song';
|
||||||
@ -115,10 +115,10 @@ async function fetchArtist(id: number) {
|
|||||||
briefDesc.value = '';
|
briefDesc.value = '';
|
||||||
try {
|
try {
|
||||||
const [detailStr, songsStr, albumStr, descStr] = await Promise.all([
|
const [detailStr, songsStr, albumStr, descStr] = await Promise.all([
|
||||||
invoke('artist_detail', { id }) as Promise<string>,
|
MusicApi.artistDetail(id),
|
||||||
invoke('artist_songs', { query: { id, order: 'hot', limit: 50, offset: 0 } }) as Promise<string>,
|
MusicApi.artistSongs({ id, order: 'hot', limit: 50, offset: 0 }),
|
||||||
invoke('artist_album', { id, limit: 30, offset: 0 }) as Promise<string>,
|
MusicApi.artistAlbum(id, 30, 0),
|
||||||
invoke('artist_desc', { id }) as Promise<string>,
|
MusicApi.artistDesc(id),
|
||||||
]);
|
]);
|
||||||
const detailData = JSON.parse(detailStr);
|
const detailData = JSON.parse(detailStr);
|
||||||
artist.value = detailData.artist;
|
artist.value = detailData.artist;
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onActivated, watch } from 'vue';
|
import { ref, onMounted, onActivated, watch } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
import SongListItem from '../components/SongListItem.vue';
|
import SongListItem from '../components/SongListItem.vue';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import { pageCacheGet, pageCacheSet, pageCacheInvalidate, pageCacheIsStale } from '../composables/usePageCache';
|
import { pageCacheGet, pageCacheSet, pageCacheInvalidate, pageCacheIsStale } from '../composables/usePageCache';
|
||||||
@ -63,7 +63,7 @@ async function loadData() {
|
|||||||
}
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('recommend_songs');
|
const jsonStr: string = await MusicApi.recommendSongs();
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
songs.value = (data.data?.dailySongs || []).map(normalizeSong);
|
songs.value = (data.data?.dailySongs || []).map(normalizeSong);
|
||||||
pageCacheSet('dailySongs', songs.value);
|
pageCacheSet('dailySongs', songs.value);
|
||||||
|
|||||||
@ -145,7 +145,7 @@ defineOptions({ name: 'DiscoverView' });
|
|||||||
|
|
||||||
import { ref, computed, onMounted, onActivated, watch, nextTick } from 'vue';
|
import { ref, computed, onMounted, onActivated, watch, nextTick } from 'vue';
|
||||||
import { useRouter, useRoute } from 'vue-router';
|
import { useRouter, useRoute } from 'vue-router';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import SongListItem from '../components/SongListItem.vue';
|
import SongListItem from '../components/SongListItem.vue';
|
||||||
import { normalizeSong, type Song } from '../utils/song';
|
import { normalizeSong, type Song } from '../utils/song';
|
||||||
@ -231,7 +231,7 @@ function onInputChange() {
|
|||||||
}
|
}
|
||||||
suggestTimer = setTimeout(async () => {
|
suggestTimer = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('search_suggest', { query: { keyword: keyword.value.trim() } });
|
const jsonStr: string = await MusicApi.searchSuggest(keyword.value.trim());
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const all = data.result?.allMatch || [];
|
const all = data.result?.allMatch || [];
|
||||||
suggestions.value = all.map((m: any) => m.keyword).slice(0, 8);
|
suggestions.value = all.map((m: any) => m.keyword).slice(0, 8);
|
||||||
@ -254,7 +254,7 @@ async function loadHotTags() {
|
|||||||
hotTags.value = cached;
|
hotTags.value = cached;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const json = await invoke('get_hot_search');
|
const json = await MusicApi.getHotSearch();
|
||||||
const data = JSON.parse(json as string);
|
const data = JSON.parse(json as string);
|
||||||
hotTags.value = (data.data || []).slice(0, 12);
|
hotTags.value = (data.data || []).slice(0, 12);
|
||||||
pageCacheSet('discover_hotTags', hotTags.value);
|
pageCacheSet('discover_hotTags', hotTags.value);
|
||||||
@ -317,8 +317,8 @@ async function fetchTabResults(type: number) {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
cacheError.value = false;
|
cacheError.value = false;
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('cloudsearch', {
|
const jsonStr: string = await MusicApi.cloudsearch({
|
||||||
query: { keyword: lastSearchKeyword.value, searchType: type, limit: 30 }
|
keyword: lastSearchKeyword.value, searchType: type, limit: 30
|
||||||
});
|
});
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const result = data.result || {};
|
const result = data.result || {};
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onActivated, watch } from 'vue';
|
import { ref, onMounted, onActivated, watch } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
import SongListItem from '../components/SongListItem.vue';
|
import SongListItem from '../components/SongListItem.vue';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '../stores/user';
|
||||||
@ -71,7 +71,7 @@ async function loadData() {
|
|||||||
}
|
}
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const playlistJson: string = await invoke('user_playlist', { uid: userStore.user!.userId });
|
const playlistJson: string = await MusicApi.userPlaylist(userStore.user!.userId);
|
||||||
const playlistData = JSON.parse(playlistJson);
|
const playlistData = JSON.parse(playlistJson);
|
||||||
const created = (playlistData.playlist || []).filter((p: any) => !p.subscribed);
|
const created = (playlistData.playlist || []).filter((p: any) => !p.subscribed);
|
||||||
if (created.length === 0) {
|
if (created.length === 0) {
|
||||||
@ -79,7 +79,7 @@ async function loadData() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const likePlaylistId = created[0].id;
|
const likePlaylistId = created[0].id;
|
||||||
const trackJson: string = await invoke('playlist_track_all', { query: { id: likePlaylistId } });
|
const trackJson: string = await MusicApi.playlistTrackAll(likePlaylistId);
|
||||||
const trackData = JSON.parse(trackJson);
|
const trackData = JSON.parse(trackJson);
|
||||||
songs.value = (trackData.songs || []).map(normalizeSong);
|
songs.value = (trackData.songs || []).map(normalizeSong);
|
||||||
pageCacheSet('favoriteSongs', songs.value);
|
pageCacheSet('favoriteSongs', songs.value);
|
||||||
|
|||||||
@ -104,7 +104,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onActivated, watch } from 'vue';
|
import { ref, onMounted, onActivated, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '../stores/user';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import { pageCacheGet, pageCacheSet, pageCacheInvalidate, pageCacheIsStale } from '../composables/usePageCache';
|
import { pageCacheGet, pageCacheSet, pageCacheInvalidate, pageCacheIsStale } from '../composables/usePageCache';
|
||||||
@ -169,7 +169,7 @@ async function loadData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const results = await Promise.allSettled(
|
const results = await Promise.allSettled(
|
||||||
RANK_IDS.map(id => invoke('get_playlist_detail', { id }))
|
RANK_IDS.map(id => MusicApi.getPlaylistDetail(id))
|
||||||
);
|
);
|
||||||
rankPlaylists.value = results
|
rankPlaylists.value = results
|
||||||
.filter(r => r.status === 'fulfilled')
|
.filter(r => r.status === 'fulfilled')
|
||||||
@ -181,7 +181,7 @@ async function loadData() {
|
|||||||
|
|
||||||
if (userStore.isLoggedIn) {
|
if (userStore.isLoggedIn) {
|
||||||
try {
|
try {
|
||||||
const json = await invoke('recommend_resource');
|
const json = await MusicApi.recommendResource();
|
||||||
const data = JSON.parse(json as string);
|
const data = JSON.parse(json as string);
|
||||||
recPlaylists.value = data.recommend || [];
|
recPlaylists.value = data.recommend || [];
|
||||||
} catch { /* 忽略 */ }
|
} catch { /* 忽略 */ }
|
||||||
|
|||||||
@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onActivated, onBeforeUnmount, watch } from 'vue';
|
import { ref, computed, onMounted, onActivated, onBeforeUnmount, watch } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi, DownloadApi } from '../api';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import { useDownload } from '../composables/useDownload';
|
import { useDownload } from '../composables/useDownload';
|
||||||
import { useSettingsStore } from '../stores/settings';
|
import { useSettingsStore } from '../stores/settings';
|
||||||
@ -129,7 +129,7 @@ async function refresh() {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
pageCacheInvalidate('localMusic');
|
pageCacheInvalidate('localMusic');
|
||||||
try {
|
try {
|
||||||
const list = await invoke<LocalSong[]>('list_local_songs', { downloadPath: settings.downloadPath || null });
|
const list = await DownloadApi.listLocalSongs(settings.downloadPath || null);
|
||||||
songs.value = list;
|
songs.value = list;
|
||||||
pageCacheSet('localMusic', list);
|
pageCacheSet('localMusic', list);
|
||||||
fetchMissingCovers();
|
fetchMissingCovers();
|
||||||
@ -145,7 +145,7 @@ async function fetchMissingCovers() {
|
|||||||
if (missing.length === 0) return;
|
if (missing.length === 0) return;
|
||||||
const ids = [...new Set(missing.map(s => s.id))];
|
const ids = [...new Set(missing.map(s => s.id))];
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('get_song_detail', { id: JSON.stringify(ids) });
|
const jsonStr: string = await MusicApi.getSongDetail(JSON.stringify(ids));
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const detailMap = new Map<number, string>();
|
const detailMap = new Map<number, string>();
|
||||||
for (const s of data.songs || []) {
|
for (const s of data.songs || []) {
|
||||||
@ -194,7 +194,7 @@ function confirmDelete(song: LocalSong) {
|
|||||||
async function doDelete() {
|
async function doDelete() {
|
||||||
if (!deleteTarget.value) return;
|
if (!deleteTarget.value) return;
|
||||||
try {
|
try {
|
||||||
await invoke('delete_local_song', { query: { id: deleteTarget.value.id, filename: deleteTarget.value.filename, downloadPath: settings.downloadPath || null } });
|
await DownloadApi.deleteLocalSong({ id: deleteTarget.value.id, filename: deleteTarget.value.filename, downloadPath: settings.downloadPath || null });
|
||||||
songs.value = songs.value.filter(s => s.id !== deleteTarget.value!.id);
|
songs.value = songs.value.filter(s => s.id !== deleteTarget.value!.id);
|
||||||
download.localSongIds.delete(deleteTarget.value.id);
|
download.localSongIds.delete(deleteTarget.value.id);
|
||||||
showToast('已删除', 'success');
|
showToast('已删除', 'success');
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '../stores/user';
|
||||||
import QRCode from 'qrcode';
|
import QRCode from 'qrcode';
|
||||||
@ -52,7 +52,7 @@ async function refreshQr() {
|
|||||||
qrError.value = '';
|
qrError.value = '';
|
||||||
if (pollTimer) clearInterval(pollTimer);
|
if (pollTimer) clearInterval(pollTimer);
|
||||||
try {
|
try {
|
||||||
qrKey = await invoke('get_qr_key');
|
qrKey = await MusicApi.getQrKey();
|
||||||
if (!qrKey) {
|
if (!qrKey) {
|
||||||
qrError.value = '未获取到登录密钥';
|
qrError.value = '未获取到登录密钥';
|
||||||
qrLoading.value = false;
|
qrLoading.value = false;
|
||||||
@ -76,7 +76,7 @@ async function refreshQr() {
|
|||||||
function startPolling() {
|
function startPolling() {
|
||||||
pollTimer = setInterval(async () => {
|
pollTimer = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('check_qr_status', { query: { key: qrKey } });
|
const jsonStr: string = await MusicApi.checkQrStatus(qrKey);
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
const code = data.code;
|
const code = data.code;
|
||||||
if (code === 800) {
|
if (code === 800) {
|
||||||
@ -104,7 +104,7 @@ function startPolling() {
|
|||||||
|
|
||||||
async function fetchUserProfile() {
|
async function fetchUserProfile() {
|
||||||
try {
|
try {
|
||||||
const profileJson: string = await invoke('get_login_status');
|
const profileJson: string = await MusicApi.getLoginStatus();
|
||||||
const profile = JSON.parse(profileJson);
|
const profile = JSON.parse(profileJson);
|
||||||
if (profile.profile) {
|
if (profile.profile) {
|
||||||
userStore.setUser({
|
userStore.setUser({
|
||||||
|
|||||||
@ -68,7 +68,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, watch } from 'vue';
|
import { ref, computed, onMounted, watch } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { MusicApi } from '../api';
|
||||||
import { usePlayerStore } from '../stores/player';
|
import { usePlayerStore } from '../stores/player';
|
||||||
import { useUserStore } from '../stores/user';
|
import { useUserStore } from '../stores/user';
|
||||||
import { showToast } from '../composables/useToast';
|
import { showToast } from '../composables/useToast';
|
||||||
@ -98,7 +98,7 @@ async function fetchPlaylist(id: number) {
|
|||||||
playlist.value = null;
|
playlist.value = null;
|
||||||
songs.value = [];
|
songs.value = [];
|
||||||
try {
|
try {
|
||||||
const jsonStr: string = await invoke('get_playlist_detail', { id });
|
const jsonStr: string = await MusicApi.getPlaylistDetail(id);
|
||||||
const data = JSON.parse(jsonStr);
|
const data = JSON.parse(jsonStr);
|
||||||
playlist.value = data.playlist;
|
playlist.value = data.playlist;
|
||||||
songs.value = (data.playlist.tracks || []).map(normalizeSong);
|
songs.value = (data.playlist.tracks || []).map(normalizeSong);
|
||||||
@ -128,7 +128,7 @@ async function toggleSubscribe() {
|
|||||||
if (!playlist.value) return;
|
if (!playlist.value) return;
|
||||||
const newSubscribed = !subscribed.value;
|
const newSubscribed = !subscribed.value;
|
||||||
try {
|
try {
|
||||||
await invoke('playlist_subscribe', { query: { id: Number(playlist.value.id), subscribe: newSubscribed } });
|
await MusicApi.playlistSubscribe(Number(playlist.value.id), newSubscribed);
|
||||||
subscribed.value = newSubscribed;
|
subscribed.value = newSubscribed;
|
||||||
showToast(subscribed.value ? '已收藏歌单' : '已取消收藏', 'success');
|
showToast(subscribed.value ? '已收藏歌单' : '已取消收藏', 'success');
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@ -258,7 +258,7 @@ import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
|||||||
import { useSettingsStore, qualityLabels, closeActionLabels, defaultShortcuts, themeLabels, themeColors, appearanceLabels, type CloseAction } from '../stores/settings';
|
import { useSettingsStore, qualityLabels, closeActionLabels, defaultShortcuts, themeLabels, themeColors, appearanceLabels, type CloseAction } from '../stores/settings';
|
||||||
import { useToast } from '../composables/useToast';
|
import { useToast } from '../composables/useToast';
|
||||||
import { useUpdater } from '../composables/useUpdater';
|
import { useUpdater } from '../composables/useUpdater';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { DeviceApi, DownloadApi } from '../api';
|
||||||
import { getVersion } from '@tauri-apps/api/app';
|
import { getVersion } from '@tauri-apps/api/app';
|
||||||
import { openUrl } from '@tauri-apps/plugin-opener';
|
import { openUrl } from '@tauri-apps/plugin-opener';
|
||||||
import { open } from '@tauri-apps/plugin-dialog';
|
import { open } from '@tauri-apps/plugin-dialog';
|
||||||
@ -287,7 +287,7 @@ const selectedDevice = computed({
|
|||||||
set: (val: string) => {
|
set: (val: string) => {
|
||||||
const device = val === '' ? null : val;
|
const device = val === '' ? null : val;
|
||||||
settings.setOutputDevice(device);
|
settings.setOutputDevice(device);
|
||||||
invoke('set_output_device', { device }).then(() => {
|
DeviceApi.setOutputDevice(device).then(() => {
|
||||||
showToast(device ? `已切换到: ${device}` : '已切换到系统默认', 'success');
|
showToast(device ? `已切换到: ${device}` : '已切换到系统默认', 'success');
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
console.error('切换设备失败: ', e);
|
console.error('切换设备失败: ', e);
|
||||||
@ -298,7 +298,7 @@ const selectedDevice = computed({
|
|||||||
|
|
||||||
async function loadDevices() {
|
async function loadDevices() {
|
||||||
try {
|
try {
|
||||||
devices.value = await invoke<string[]>('get_output_devices');
|
devices.value = await DeviceApi.getOutputDevices();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('获取设备失败: ', e);
|
console.error('获取设备失败: ', e);
|
||||||
}
|
}
|
||||||
@ -309,7 +309,7 @@ const defaultDownloadPath = ref('');
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
appVersion.value = await getVersion();
|
appVersion.value = await getVersion();
|
||||||
try {
|
try {
|
||||||
defaultDownloadPath.value = await invoke<string>('get_default_download_path');
|
defaultDownloadPath.value = await DownloadApi.getDefaultDownloadPath();
|
||||||
} catch { /* 忽略 */ }
|
} catch { /* 忽略 */ }
|
||||||
loadDevices();
|
loadDevices();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user