@@ -11,6 +11,7 @@ class MusiclistManager {
1111 * @param {import("./LoginManager.js") } loginManager
1212 */
1313 constructor ( playlistManager , musicSearcher , cacheManager , loginManager ) {
14+ this . isUpdating = false ;
1415 this . playlistManager = playlistManager ;
1516 this . musicSearcher = musicSearcher ;
1617 this . cacheManager = cacheManager ;
@@ -203,6 +204,9 @@ class MusiclistManager {
203204 lyricSearchTypeSelect . addEventListener ( "change" , ( e ) => {
204205 this . lyricSearchType = e . target . value ;
205206 } ) ;
207+ document . getElementById ( 'autoUpdateBtn' ) . addEventListener ( 'click' , ( ) => {
208+ this . updateBiliFav ( this . activePlaylistIndex ) ;
209+ } ) ;
206210 // 点击新建歌单按钮事件
207211 this . newPlaylistBtn . addEventListener ( "click" , ( ) => {
208212 const input = document . createElement ( "input" ) ;
@@ -830,7 +834,12 @@ class MusiclistManager {
830834 this . playlists . push ( {
831835 id : this . generateUUID ( ) ,
832836 name : playlistTitle ,
833- songs : [ ]
837+ songs : [ ] ,
838+ meta : {
839+ mediaId : favId ,
840+ type : 'bilibili' ,
841+ total : favInfo . media_count
842+ }
834843 } ) ;
835844
836845 // 2. 收集所有要添加的歌曲信息
@@ -910,6 +919,231 @@ class MusiclistManager {
910919 } ;
911920 }
912921 }
922+ async updateBiliFav ( playlistIndex ) {
923+ const playlist = this . playlists [ playlistIndex ] ;
924+ let updateNotification = null ;
925+
926+ // 新增状态检查防止重复点击
927+ if ( this . isUpdating ) {
928+ this . uiManager . showNotification ( '更新正在进行中,请稍后再试' , 'warning' ) ;
929+ return ;
930+ }
931+
932+ try {
933+ this . isUpdating = true ; // 设置更新状态
934+ // 增强类型验证逻辑
935+ const { mediaId, seasonId, type } = playlist . meta || { } ;
936+
937+ // 根据不同类型选择更新方式
938+ if ( type === 'bilibili-season' && seasonId ) {
939+ return await this . updateBiliSeason ( playlistIndex ) ;
940+ }
941+
942+ // 原有收藏夹更新逻辑
943+ if ( ! mediaId ) {
944+ throw new Error ( '此歌单未关联B站收藏夹,请重新添加收藏夹' ) ;
945+ }
946+
947+ // 修改API请求使用v3接口
948+ const favResponse = await axios . get (
949+ `https://api.bilibili.com/x/v3/fav/folder/info?media_id=${ mediaId } `
950+ ) ;
951+
952+ const favInfo = favResponse . data . data ;
953+ const totalCount = favInfo . media_count ;
954+ const existingBVids = new Set ( playlist . songs . map ( song => song . bvid ) ) ;
955+
956+ // 设置进度通知
957+ updateNotification = this . uiManager . showNotification (
958+ `正在检查更新 (0/${ totalCount } )` ,
959+ 'info' ,
960+ { showProgress : true , progress : 0 }
961+ ) ;
962+
963+ let newSongs = [ ] ;
964+ const pageSize = 20 ;
965+ const totalPages = Math . ceil ( totalCount / pageSize ) ;
966+
967+ // 遍历所有页面
968+ for ( let page = 1 ; page <= totalPages ; page ++ ) {
969+ const res = await axios . get (
970+ `https://api.bilibili.com/x/v3/fav/resource/list?media_id=${ mediaId } &pn=${ page } &ps=${ pageSize } `
971+ ) ;
972+
973+ if ( res . data . code === 0 ) {
974+ const medias = res . data . data . medias || [ ] ;
975+
976+ // 过滤已存在的视频
977+ const filtered = medias . filter ( media =>
978+ ! existingBVids . has ( media . bvid ) && media . attr !== 1
979+ ) ;
980+
981+ // 转换格式并添加到新歌曲列表
982+ newSongs . push ( ...await Promise . all ( filtered . map ( async media => ( {
983+ title : media . title ,
984+ artist : media . upper . name ,
985+ bvid : media . bvid ,
986+ duration : media . duration ,
987+ poster : media . cover ,
988+ meta : { mediaId } // 保存收藏夹ID用于后续更新
989+ } ) ) ) ) ;
990+
991+ // 更新进度
992+ const processed = page * pageSize ;
993+ const progress = Math . min ( processed / totalCount * 100 , 100 ) ;
994+ const progressBar = updateNotification . querySelector ( '.notification-progress-inner' ) ; // 修正选择器
995+ const progressText = updateNotification . querySelector ( '.notification-message' ) ;
996+
997+ if ( progressText && progressBar ) { // 添加空值检查
998+ progressText . textContent =
999+ `正在检查更新 (${ Math . min ( processed , totalCount ) } /${ totalCount } )` ;
1000+ progressBar . style . width = `${ progress } %` ;
1001+ }
1002+ }
1003+ }
1004+
1005+ // 应用更新
1006+ if ( newSongs . length > 0 ) {
1007+ // 处理新增歌曲的歌词
1008+ const newSongsWithLyrics = await this . processSongsLyrics ( newSongs ) ; // 新增歌词处理
1009+
1010+ playlist . songs = [ ...newSongsWithLyrics , ...playlist . songs ] ; // 使用带歌词的新数据
1011+ this . savePlaylists ( ) ;
1012+ this . uiManager . showNotification ( `发现${ newSongs . length } 首新歌曲` , 'success' ) ;
1013+
1014+ // 强制刷新歌单列表并保持激活状态
1015+ this . renderPlaylistList ( ) ;
1016+ this . renderSongList ( ) ;
1017+
1018+ // 高亮更新后的歌单条目
1019+ const updatedElement = document . querySelector ( `li[data-id="${ playlist . id } "]` ) ;
1020+ if ( updatedElement ) {
1021+ updatedElement . classList . add ( 'active' ) ;
1022+ updatedElement . click ( ) ;
1023+ }
1024+ } else {
1025+ this . uiManager . showNotification ( '当前歌单已是最新' , 'info' ) ;
1026+ }
1027+ } catch ( error ) {
1028+ this . uiManager . showNotification ( `更新失败: ${ error . message } ` , 'error' ) ;
1029+ } finally {
1030+ this . isUpdating = false ; // 重置更新状态
1031+ if ( updateNotification ) { // 添加安全判断
1032+ updateNotification . remove ( ) ;
1033+ }
1034+ }
1035+ }
1036+
1037+ async updateBiliSeason ( playlistIndex ) {
1038+ const playlist = this . playlists [ playlistIndex ] ;
1039+ let updateNotification = null ;
1040+ try {
1041+ const { seasonId, mid } = playlist . meta || { } ;
1042+ if ( ! seasonId || ! mid ) {
1043+ throw new Error ( '此歌单未关联B站合集或信息不完整' ) ;
1044+ }
1045+
1046+ // 获取合集信息
1047+ const seasonResponse = await axios . get (
1048+ `https://api.bilibili.com/x/polymer/web-space/seasons_archives_list?mid=${ mid } &season_id=${ seasonId } &page_num=1&page_size=100`
1049+ ) ;
1050+
1051+ if ( seasonResponse . data . code !== 0 ) {
1052+ throw new Error ( '获取合集信息失败: ' + seasonResponse . data . message ) ;
1053+ }
1054+
1055+ const videos = seasonResponse . data . data . archives ;
1056+ const totalCount = videos . length ;
1057+ const existingBVids = new Set ( playlist . songs . map ( song => song . bvid ) ) ;
1058+
1059+ // 获取合集名称
1060+ const seasonName = playlist . name . replace ( / \( [ ^ ) ] * \) $ / , '' ) . trim ( ) ;
1061+
1062+ // 设置进度通知
1063+ updateNotification = this . uiManager . showNotification (
1064+ `正在检查更新 (0/${ totalCount } )` ,
1065+ 'info' ,
1066+ { showProgress : true , progress : 0 }
1067+ ) ;
1068+
1069+ let newSongs = [ ] ;
1070+ let processedCount = 0 ;
1071+
1072+ // 处理每个视频
1073+ for ( const video of videos ) {
1074+ try {
1075+ // 跳过已存在的视频
1076+ if ( existingBVids . has ( video . bvid ) ) {
1077+ processedCount ++ ;
1078+ continue ;
1079+ }
1080+
1081+ // 获取视频详情以获取CID
1082+ let cid = video . cid ;
1083+ if ( ! cid ) {
1084+ const videoDetailResponse = await axios . get (
1085+ `https://api.bilibili.com/x/web-interface/view?bvid=${ video . bvid } `
1086+ ) ;
1087+ if ( videoDetailResponse . data . code === 0 ) {
1088+ cid = videoDetailResponse . data . data . cid ;
1089+ }
1090+ }
1091+
1092+ newSongs . push ( {
1093+ title : video . title ,
1094+ artist : seasonName ,
1095+ bvid : video . bvid ,
1096+ cid : cid ,
1097+ duration : video . duration ,
1098+ poster : video . pic ,
1099+ meta : { seasonId, mid }
1100+ } ) ;
1101+
1102+ // 更新进度
1103+ processedCount ++ ;
1104+ const progress = ( processedCount / totalCount ) * 100 ;
1105+ updateNotification . querySelector ( ".notification-message" ) . textContent =
1106+ `正在检查更新 (${ processedCount } /${ totalCount } )` ;
1107+ updateNotification . querySelector ( ".notification-progress-inner" ) . style . width =
1108+ `${ progress } %` ;
1109+
1110+ } catch ( error ) {
1111+ console . warn ( `处理视频 ${ video . bvid } 失败:` , error ) ;
1112+ continue ;
1113+ }
1114+ }
1115+
1116+ // 应用更新
1117+ if ( newSongs . length > 0 ) {
1118+ // 处理新增歌曲的歌词
1119+ const newSongsWithLyrics = await this . processSongsLyrics ( newSongs ) ;
1120+
1121+ playlist . songs = [ ...newSongsWithLyrics , ...playlist . songs ] ;
1122+ this . savePlaylists ( ) ;
1123+ this . uiManager . showNotification ( `发现${ newSongs . length } 首新歌曲` , 'success' ) ;
1124+
1125+ // 强制刷新歌单列表并保持激活状态
1126+ this . renderPlaylistList ( ) ;
1127+ this . renderSongList ( ) ;
1128+
1129+ // 高亮更新后的歌单条目
1130+ const updatedElement = document . querySelector ( `li[data-id="${ playlist . id } "]` ) ;
1131+ if ( updatedElement ) {
1132+ updatedElement . classList . add ( 'active' ) ;
1133+ updatedElement . click ( ) ;
1134+ }
1135+ } else {
1136+ this . uiManager . showNotification ( '当前歌单已是最新' , 'info' ) ;
1137+ }
1138+ } catch ( error ) {
1139+ this . uiManager . showNotification ( `更新失败: ${ error . message } ` , 'error' ) ;
1140+ } finally {
1141+ if ( updateNotification ) {
1142+ updateNotification . remove ( ) ;
1143+ }
1144+ }
1145+ }
1146+
9131147 async showLyricSearchDialog ( song ) {
9141148 return new Promise ( ( resolve ) => {
9151149 const dialog = document . getElementById ( "lyricSearchDialog" ) ;
@@ -1032,7 +1266,13 @@ class MusiclistManager {
10321266 this . playlists . push ( {
10331267 id : this . generateUUID ( ) ,
10341268 name : playlistTitle ,
1035- songs : [ ]
1269+ songs : [ ] ,
1270+ meta : {
1271+ seasonId : seasonId ,
1272+ mid : mid ,
1273+ type : 'bilibili-season' ,
1274+ total : totalCount
1275+ }
10361276 } ) ;
10371277
10381278 // 收集所有要添加的歌曲信息
0 commit comments