This is an old article and only available in Chinese. If you need a translation, please leave a comment and I will do my best to provide it as soon as possible.
usingNewtonsoft.Json;usingNewtonsoft.Json.Linq;usingSystem;usingSystem.Collections.Generic;usingSystem.Data.SQLite;usingSystem.IO;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespacencm_playlist_export{classPlayList{publicInt64pid;publicdynamicplaylist;}classSongDetail{publicdynamicdetail;publicstringrelative_path;publicstringreal_suffix;}classFailedRecorods{publicInt64tid;publicstringreason;publicFailedRecorods(Int64_in_tid,string_in_reason){tid=_in_tid;reason=_in_reason;}}classProgram{staticSQLiteConnectionconn=null;staticList<PlayList>FetchPlaylist(){varret=newList<PlayList>();varcmd=conn.CreateCommand();cmd.CommandText="SELECT * FROM web_playlist";varreader=cmd.ExecuteReader();while(reader.Read()){varelem=newPlayList();elem.pid=Convert.ToInt64(reader["pid"]);elem.playlist=JObject.Parse(reader["playlist"].ToString());ret.Add(elem);}returnret;}staticList<Int64>FetchPlaylistSongs(Int64pid){varret=newList<Int64>();varcmd=conn.CreateCommand();cmd.CommandText="SELECT pid, tid FROM web_playlist_track WHERE pid = "+pid+" ORDER BY `order`";varreader=cmd.ExecuteReader();while(reader.Read()){ret.Add(Convert.ToInt64(reader["tid"]));}returnret;}staticSongDetailFetchSongDetail(Int64tid){varret=newSongDetail();varcmd=conn.CreateCommand();cmd.CommandText="SELECT detail, relative_path, real_suffix FROM web_offline_track WHERE track_id ="+tid;varreader=cmd.ExecuteReader();if(reader.Read()){ret.detail=JObject.Parse(reader["detail"].ToString());ret.relative_path=reader["relative_path"].ToString();ret.real_suffix=reader["real_suffix"].ToString();returnret;}returnnull;}staticstringGetSafeFilename(stringfilename){returnstring.Join("_",filename.Split(Path.GetInvalidFileNameChars()));}staticvoidMain(string[]args){varlocalAppData=Environment.GetEnvironmentVariable("LocalAppData");varlrcPath=Environment.GetEnvironmentVariable("LocalAppData")+"\\Netease\\CloudMusic\\webdata\\lyric";conn=newSQLiteConnection("Data Source="+localAppData+"\\Netease\\CloudMusic\\Library\\webdb.dat;Version=3;New=True;Compress=True;");conn.Open();varplaylists=FetchPlaylist();intn=1;foreach(varplaylistinplaylists){intsongsCount=0,skippedCount=0;stringm3u8="";varsongs=FetchPlaylistSongs(playlist.pid);varskipeedReasons=newList<FailedRecorods>();if(songs.Count==0){continue;}for(vari=0;i<songs.Count;i++){vardetail=FetchSongDetail(songs[i]);if(detail==null){skipeedReasons.Add(newFailedRecorods(songs[i],"Detail data not found."));skippedCount++;continue;}if(detail.relative_path==""){skipeedReasons.Add(newFailedRecorods(songs[i],"Relative path is empty."));skippedCount++;continue;}if(detail.relative_path.Substring(detail.relative_path.Length-3)=="ncm"){detail.relative_path=detail.relative_path.Replace("ncm",detail.real_suffix);}if(false&&File.Exists(lrcPath+"\\"+songs[i])){varlrcJson=File.ReadAllText(lrcPath+"\\"+songs[i]);dynamiclrc=JObject.Parse(lrcJson);if(lrc["nolyric"]!=true){File.WriteAllText(Path.ChangeExtension(detail.relative_path,"lrc"),(string)lrc.lrc.lyric);}}songsCount++;m3u8+="#EXTINF:"+detail.detail.duration+","+detail.detail.name+"\n";m3u8+="Cloudmusic/"+detail.relative_path.Replace('\\','/')+"\n\n";}if(songsCount==0){continue;}System.IO.File.WriteAllText(n.ToString("00")+". "+GetSafeFilename(""+playlist.playlist.name)+".m3u8",m3u8);Console.WriteLine("Finish generating playlist: "+playlist.playlist.name+", Songs count: "+songsCount+", Skipped count: "+skippedCount);if(skipeedReasons.Count!=0){for(inti=0;i<(skippedCount<5?skippedCount:5);i++){Console.WriteLine("Track ID: "+skipeedReasons[i].tid+", Reason: "+skipeedReasons[i].reason);}}n++;Console.WriteLine("");}Console.WriteLine("Finished!");}}}