Browse Source

feat: 歌单操作
1. 歌单的增删改查

kindring 2 months ago
parent
commit
a61cb7316c

+ 4 - 1
src/apis/ApiAction.ts

@@ -8,6 +8,9 @@ export enum Magnet_Actions {
 export enum Music_Actions {
   music_app_start = 'music_app_start',
   play_list_fetch = 'play_list_fetch',
+  play_list_add = 'play_list_add',
+  play_list_delete = 'play_list_delete',
+  play_list_update = 'play_list_update',
   scan_music_select = 'scan_music_select',
   scan_music_add = 'scan_music_add',
   scan_settings = 'scan_settings',
@@ -15,5 +18,5 @@ export enum Music_Actions {
   scan_music_delete = 'scan_music_delete',
   scan_music_fetch = 'scan_music_fetch',
   like_music = 'like_music',
-  playlist_music_fetch = 'playlist_music_fetch'
+  playlist_music_fetch = 'playlist_music_fetch',
 }

+ 20 - 1
src/apis/musicControl.ts

@@ -2,7 +2,7 @@ import api from "./baseApi.ts"
 import {Order, Page, ResponseData} from "@/types/apiTypes.ts";
 import {Music_Actions} from "@/apis/ApiAction.ts";
 import {MusicInfo, MusicScanSetting, param_music_like, PlayList} from "@/types/musicType.ts";
-import {promises} from "fs-extra";
+
 
 export async function musicAppStart()
 {
@@ -75,3 +75,22 @@ export async function api_likeMusic(param: param_music_like): Promise<ResponseDa
     return await promise;
 }
 
+
+export async function api_playlist_update(param: Partial<PlayList>): Promise<ResponseData<boolean>>
+{
+    let [_callId, promise] = api.sendQuery(Music_Actions.play_list_update, param);
+    return await promise;
+}
+
+
+export async function api_playlist_add(param: Partial<PlayList>): Promise<ResponseData<boolean>>
+{
+    let [_callId, promise] = api.sendQuery(Music_Actions.play_list_add, param);
+    return await promise;
+}
+
+export async function api_playlist_delete(id: number): Promise<ResponseData<boolean>>
+{
+    let [_callId, promise] = api.sendQuery(Music_Actions.play_list_delete, id);
+    return await promise;
+}

+ 114 - 1
src/common/db/db_music.ts

@@ -26,6 +26,23 @@ const Music_field = [
     'playCount',
     'scanId',
 ]
+
+const Music_playList_field = [
+    'id',
+    'name',
+    'icon',
+    'cover',
+    'description',
+    'playCount',
+    'trackCount',
+    'createTime',
+    'isTagSearch',
+    'lastPlayTime',
+    'isSync',
+    'isPublic',
+    'isLike',
+]
+
 async function _initScanConfigTable(db : Knex): PromiseResult<boolean> {
     let [err, hasTable] = await handle(
         db.schema.hasTable(MusicTableName.music_scan_setting)
@@ -399,8 +416,47 @@ export async function getPlayList() : PromiseResult<PlayList[]>
     return [null, res as PlayList[]];
 }
 
+export async function playlist_find_by_id(id: number): PromiseResult<PlayList>
+{
+    let db = loadDb(AppDbName.music_db)
+    if(!db){
+        logger.error('数据库初始化失败')
+        return [new Error('音乐数据库初始化失败'), null]
+    }
+    let [err, _res] = await handle(
+        db.select(...Music_playList_field)
+            .from(MusicTableName.music_play_list)
+            .where('id', id)
+            .first()
+    )
+    if (err) {
+        err = err as Error;
+        logger.error(`[获取播放列表失败] ${err.message}`)
+        return [err, null];
+    }
+    return [null, _res as PlayList];
+}
+
+export async function db_playlist_delete(id: number) : PromiseResult<boolean>
+{
+    const __func__ = 'playlist_delete()'
+    let db = loadDb(AppDbName.music_db)
+    if(!db){
+        logger.error(`${__func__} 数据库初始化失败`)
+        return [new Error('音乐数据库初始化失败'), false]
+    }
+    let [err, _res] = await handle(
+        db.delete().from(MusicTableName.music_play_list).where('id', id)
+    )
+    if (err) {
+        err = err as Error;
+        logger.error(`${__func__} [删除播放列表失败] ${err.message}`)
+        return [err, false];
+    }
+    return [null, true];
+}
 
-export async function addPlayList(playList: PlayList) : PromiseResult<boolean>
+export async function addPlayList(playList: Partial<PlayList>) : PromiseResult<boolean>
 {
     let db = loadDb(AppDbName.music_db)
     if(!db){
@@ -508,6 +564,63 @@ export async function removePlayListMusic(playlist_id: number, music_id: number)
     return [null, true];
 }
 
+export async function playlist_song_remove(playlist_id: number) : PromiseResult<boolean>
+{
+    const __func__ = 'playlist_song_remove()'
+    let db = loadDb(AppDbName.music_db)
+    if(!db){
+        logger.error(`${__func__} 数据库初始化失败`)
+        return [new Error('[移除播放列表歌曲] 音乐数据库初始化失败'), false]
+    }
+    let [err, _res] = await handle(
+        db.delete().from(MusicTableName.music_play_list_songs).where('playListId', playlist_id)
+    )
+    if (err) {
+        err = err as Error;
+        logger.error(`${__func__} 移除歌单失败 ${err.message}`)
+        return [err, false];
+    }
+    return [null, true];
+}
+
+export async function editPlayList(playlist: Partial<PlayList>) : PromiseResult<boolean>
+{
+    let db = loadDb(AppDbName.music_db)
+    if(!db){
+        logger.error('数据库初始化失败')
+        return [new Error('音乐数据库初始化失败'), false]
+    }
+    let [err, _res] = await handle(
+        db.update(playlist).into(MusicTableName.music_play_list).where('id', playlist.id)
+    )
+   if (err) {
+       err = err as Error;
+       logger.error(`[编辑播放列表失败] ${err.message}`)
+       return [err, false];
+   }
+   return [null, true];
+}
+
+export async function deletePlayList(playlist_id: number) : PromiseResult<boolean>
+{
+    let db = loadDb(AppDbName.music_db)
+    if(!db){
+        logger.error('数据库初始化失败')
+        return [new Error('音乐数据库初始化失败'), false]
+    }
+    let [err, _res] = await handle(
+        db.delete().from(MusicTableName.music_play_list).where('id', playlist_id)
+    )
+    if (err) {
+        err = err as Error;
+        logger.error(`[删除播放列表失败] ${err.message}`)
+        return [err, false];
+    }
+    return [null, true];
+}
+
+
+
 export async function getMusicByKey(musicKey: string) : PromiseResult<MusicInfo>
 {
     let db = loadDb(AppDbName.music_db)

+ 46 - 2
src/components/music/dialog/addPlayList.vue

@@ -3,6 +3,9 @@ import {defineComponent, onMounted, PropType, ref} from "vue";
 import {PlayList} from "@/types/musicType.ts";
 import KuiInput from "@/components/public/kui/kui-input.vue";
 import KuiCheckbox from "@/components/public/kui/kui-checkbox.vue";
+import message from "@/components/public/kui/message";
+import {ErrorCode} from "@/types/apiTypes.ts";
+import {api_playlist_add, api_playlist_update} from "@/apis/musicControl.ts";
 
 defineComponent({
   name: "addPlayList"
@@ -70,6 +73,45 @@ function closeDialog() {
 
 async function submitHandle() {
   console.log('submit');
+  // 创建歌单
+  let param: Partial<PlayList> = {
+    name: playlistName.value,
+    description: description.value,
+    cover: cover.value,
+    isPublic: isPublic.value,
+    isLike: isLike.value
+  }
+  if (!param.name)
+  {
+    message.error('歌单名称不能为空');
+    return;
+  }
+  if (isEdit)
+  {
+    // 编辑歌单
+    param.id = props.playlist.id;
+    let res = await api_playlist_update(param);
+    if (res.code === ErrorCode.success)
+    {
+      message.success('编辑成功');
+      emits('submit');
+    }
+    else {
+      message.error(res.msg);
+    }
+  }
+  else {
+    // 创建歌单
+    let res = await api_playlist_add(param);
+    if (res.code === ErrorCode.success)
+    {
+      message.success('创建成功');
+      emits('submit');
+    }
+    else {
+      message.error(res.msg);
+    }
+  }
 }
 
 </script>
@@ -93,7 +135,9 @@ async function submitHandle() {
         />
       </div>
 
-      <div class="form-row mt-4" >
+      <div class="form-row mt-4"
+           v-if="isLike"
+      >
         <kui-input
             label="歌单描述"
             placeholder="歌单的描述"
@@ -122,6 +166,6 @@ async function submitHandle() {
   height: 40px;
   display: flex;
   align-items: center;
-  margin: 0 auto;
+  margin: 1rem auto;
 }
 </style>

+ 1 - 2
src/components/music/musicIndex.vue

@@ -133,8 +133,7 @@ const kuiDialog = new KuiDialogCmd({
   showContent: addPlayList,
   mountTarget: props.windowId,
   className: 'dialog',
-  on: {
-  },
+  on: { },
   onClose: closeDialogHandle
 });
 

+ 18 - 2
src/main/control/api_router.ts

@@ -2,8 +2,15 @@ import {ApiType, ErrorCode, RequestData, ResponseData} from "@/types/apiTypes.ts
 import {Magnet_Actions, Music_Actions} from "@/apis/ApiAction.ts";
 import {c_fetchMagnetList, c_magnet_batch_update, c_magnet_delete} from "@/main/control/magnet/magnet.ts";
 import {
-    c_fetchPlayList, c_fetchPlayList_music, c_like_music, c_load_scan_music, c_music_appStart,
-    c_scanMusicAdd, c_scanMusicDelete,
+    c_fetchPlayList,
+    c_fetchPlayList_music,
+    c_like_music,
+    c_load_scan_music,
+    c_music_appStart,
+    c_playList_add,
+    c_playlist_delete, c_playlist_edit,
+    c_scanMusicAdd,
+    c_scanMusicDelete,
     c_scanMusicSelect,
     c_scanMusicUpdate,
     c_scanSettings
@@ -53,6 +60,15 @@ export async function apiRouter(requestData: RequestData<any>){
         case Music_Actions.playlist_music_fetch:
             responseData = await c_fetchPlayList_music(requestData);
             break;
+        case Music_Actions.play_list_add:
+            responseData = await c_playList_add(requestData);
+            break;
+        case Music_Actions.play_list_delete:
+            responseData = await c_playlist_delete(requestData);
+            break;
+        case Music_Actions.play_list_update:
+            responseData = await c_playlist_edit(requestData);
+            break;
         default:
             responseData = {
                 type: ApiType.res,

+ 91 - 4
src/main/control/magnet/music.ts

@@ -5,11 +5,22 @@ import {ErrorCode, Page, RequestData, ResponseData} from "@/types/apiTypes.ts";
 import Logger from "@/util/logger.ts";
 import {MusicInfo, MusicScanSetting, MusicType, param_music_like, PlayList} from "@/types/musicType.ts";
 import {
-    addMusic, addPlayListMusic,
-    addScanConfig,
-    deleteScanConfig, get_like_playlist, getMusicByKey, getMusicsByPlayListId, getMusicsByScanId, getPlayList,
+    addMusic,
+    addPlayList,
+    addPlayListMusic,
+    addScanConfig, db_playlist_delete,
+    deleteScanConfig,
+    editPlayList,
+    get_like_playlist,
+    getMusicByKey,
+    getMusicsByPlayListId,
+    getMusicsByScanId,
+    getPlayList,
     getScanConfig,
-    getScanConfigByPath, initDefaultPlayList, likeMusic, removePlayListMusic,
+    getScanConfigByPath,
+    initDefaultPlayList,
+    likeMusic, playlist_find_by_id, playlist_song_remove,
+    removePlayListMusic,
     updateScanConfig
 } from "@/common/db/db_music.ts";
 import {handle, PromiseResult, ResType} from "@/util/promiseHandle.ts";
@@ -272,6 +283,82 @@ async function _read_music_info(filePath: string) : PromiseResult<IAudioMetadata
 }
 
 
+export async function c_playList_add(requestData: RequestData<Partial<PlayList>>) : Promise<ResponseData<boolean>>
+{
+    let addParam = requestData.data as Partial<PlayList>;
+    // 新添加的歌单都不为默认歌单
+    addParam.isLike = false;
+    let [err, res] = await addPlayList(addParam);
+    if (err) {
+        return t_gen_res(requestData, ErrorCode.db, '添加歌单失败', false)
+    }
+    res = res as boolean;
+    return t_res_ok(requestData,  res);
+}
+
+export async function c_playlist_edit(requestData: RequestData<Partial<PlayList>>) : Promise<ResponseData<boolean>>
+{
+    let err: Error | null = null;
+    let playList: ResType<PlayList> ,
+        res: ResType<boolean>;
+    
+    let editParam = requestData.data as Partial<PlayList>;
+    // 查看歌单是否存在
+    if (editParam.id === -1) {
+        return t_gen_res(requestData, ErrorCode.db, '歌单不存在', false)
+    }
+    [err, playList] = await playlist_find_by_id(editParam.id as number);
+    if (err) {
+        return t_gen_res(requestData, ErrorCode.db, '无法检索歌单', false)
+    }
+    playList = playList as PlayList;
+    if (playList.isLike) {
+        return t_gen_res(requestData, ErrorCode.db, '默认歌单不允许编辑', false)
+    }
+    [err, res] = await editPlayList(editParam);
+    if (err) {
+        return t_gen_res(requestData, ErrorCode.db, '编辑歌单失败', false)
+    }
+    res = res as boolean;
+    return t_res_ok(requestData,  res);
+}
+
+export async function c_playlist_delete(requestData: RequestData<number>) : Promise<ResponseData<boolean>>
+{
+    let id = requestData.data;
+    let err: Error | null = null;
+    let playList: ResType<PlayList>,
+        res: ResType<boolean>;
+    if (id < 0 || id === undefined) {
+        return t_gen_res(requestData, ErrorCode.params, 'id参数错误', false)
+    }
+    [err, playList] = await playlist_find_by_id(id);
+    if (err) {
+        return t_gen_res(requestData, ErrorCode.db, '无法检索歌单', false);
+    }
+    playList = playList as PlayList;
+    if (playList === undefined || playList === null) {
+        return t_gen_res(requestData, ErrorCode.db, '歌单不存在', false)
+    }
+    if (playList.isLike) {
+        return t_gen_res(requestData, ErrorCode.permission, '默认歌单不允许删除', false)
+    }
+    if (playList.id === -1) {
+        return t_gen_res(requestData, ErrorCode.db, '歌单不存在', false)
+    }
+    // 移除歌单对应的歌曲
+    [err, res] = await playlist_song_remove(playList.id);
+    if (err) {
+        return t_gen_res(requestData, ErrorCode.db, '删除歌单歌曲失败', false)
+    }
+    [err, res] = await db_playlist_delete(playList.id);
+    if (res) {
+        return t_gen_res(requestData, ErrorCode.db, '删除歌单失败', false)
+    }
+    res = res as boolean;
+    return t_res_ok(requestData,  res);
+}
+
 /**
  * 获取扫描配置下的音频文件
  */