*! version 1.14 *! 2021.5.19, Zheng Jingyi, Lian Yujun (arlionn@163.com) cap prog drop imusic prog def imusic version 16 syntax [anything(name=name)][, Platform(string)] [BRowse] [Stop] [Kind(string)] [Link] [Nl] [List(string)] [Markdown] [Autoplay] [NDescription] preserve //保存工作进度 *-Auto install jsonio - user command cap which jsonio if _rc!=0 { dis as text "Installing..., ^-^" cap net install jsonio, from(http://file.lianxh.cn/StataCMD/jsonio) if _rc!=0{ cap ssc install jsonio if _rc{ dis as error "Cannot install package -jsonio-, please install by hand at https://gitee.com/arlionn/imusic" exit } } } /* -sxpose- is incoded as a sub-program *-Auto install sxpose cap which sxpose.ado if _rc!=0 { cap net install sxpose, from(http://file.lianxh.cn/StataCMD/sxpose) if _rc!=0{ cap ssc install sxpose if _rc{ dis as error "Cannot install package -sxpose-, please istall by hand at https://gitee.com/arlionn/imusic" exit } } } */ *停止播放本地音乐的模块 if "`stop'" == "stop" { foreach exe in Music.UI cloudmusic QQMusic{ !taskkill /f /im `exe'.exe } exit } *默认网易云在线播放 if "`platform'" == "" { local platform = "N" } *关于platform选择的模块 if "`platform'" != "" { if "`platform'" != "n" & "`platform'" != "t" & "`platform'" != "l" & "`platform'" != "N" & "`platform'" != "T" & "`platform'" != "L"{ noi di as error "The {bf:platform} you selected is not available in our package." noi di as error "Please choose N for Netease , T for Tencent or L for Local and try again" exit } } *默认只取搜索结果的第一位 if "`list'" == "" local list=1 *默认搜索单曲 if "`kind'" != "1" & "`kind'" != "10" & "`kind'" != "1000" & "`kind'" != "1014" & "`kind'" != "1009" { local kind = "1" } //1,单曲;10,专辑;1000,歌单;1014,MV;1009,主播电台; *如果不写歌曲名,随机播放歌曲 if "`name'" == "" { //实际上是网易云的飙升榜 local platform="Netease" clear jsonio kv, file(http://music.163.com/api/playlist/detail?id=19723756) qui drop if ustrregexm(key, "/creator") == 1 Njsonclean anything , kind(`kind') forvalues i = 1/`list'{ local num_`i' = int(runiform(1,100)) local ID_`i'=valueid[`num_`i''] //歌曲ID local song_name_`i'=valuename[`num_`i''] //歌曲中文名 local singer_`i'=singer[`num_`i''] //歌手名字 //构造最终的URL local URL1="https://music.163.com/#/song?id=" local URL_`i'="`URL1'`ID_`i''" } //autoplay选项 if "`autoplay'" != "" { local U1 = "https://music.163.com/#/outchain/2/" forvalues i = 1/`list' { local UU_`i'="`U1'`ID_`i''" view browse "`UU_`i''" } } if "`list'"=="1"{ if "`nl'" == "" { lyrics anything, id(`ID_1') } } } else { mata:str = urlencode("`name'") mata:st_local("songname",str) //中文歌名转码 *如果选择播放本地音乐 if "`platform'" == "l" | "`platform'" == "L" { if "$songpath"==""{ dis as error "请定义音乐存放路径(global songpath),并以\结尾" exit } local filename = "$songpath"+"`name'"+".mp3" !start `filename' exit } else{ *如果选择网易云 if "`platform'" == "n" | "`platform'" == "N"{ if "`kind'" == "1014" local kind = "1" //不支持MV,转为单曲 local URL1="http://music.163.com/api/search/get/web?csrf_token=hlpretag=&hlposttag=&s=" local URL2="&type=" local URL3="&offset=0&total=true&limit=" local URL="`URL1'`songname'`URL2'`kind'`URL3'`list'" //构造获取歌曲信息的URL clear jsonio kv, file(`URL') //用了外部命令jsonio,获取json文件 Njsonclean anything, kind(`kind') *搜索的是单曲 if "`kind'" == "1" { forvalue i = 1/`list' { local ID_`i'=valueid[`i'] //歌曲ID local song_name_`i'=valuename[`i'] //歌曲中文名 local singer_`i'=singer[`i'] //歌手名字 } if "`list'"== "1" { if "`nl'" == "" { lyrics anything, id(`ID_1') } } } *搜索的是歌单 if "`kind'" == "1000" { forvalue i = 1/`list' { local ID_`i'=valueid[`i'] //歌单ID local description_`i'=valuedescription[`i'] //简介 local song_name_`i'=valuename[`i'] //歌单名字 local singer_`i'=valuecreator[`i'] //创建人名字 } } *搜索的是专辑 if "`kind'" == "10" { forvalue i = 1/`list' { local ID_`i'=valueid[`i'] //专辑ID local description_`i'=valuedescription[`i'] //简介,常空 local song_name_`i'=valuename[`i'] //专辑名字 local singer_`i'=singer[`i'] //歌手名字 } } *搜索的是主播电台 if "`kind'" == "1009" { forvalue i = 1/`list' { local ID_`i'=valueid[`i'] //专辑ID local description_`i'=valuedesc[`i'] //简介,常空 local song_name_`i'=valuename[`i'] //电台名字 local singer_`i'=valuedj[`i'] //创建人名字 } } //autoplay选项 if "`autoplay'" != "" { local U1 = "https://music.163.com/#/outchain/" if "`kind'" =="1" local ty=2 if "`kind'" =="10" local ty=1 if "`kind'" =="1000" local ty=0 if "`kind'"=="1009" local ty =4 forvalues i = 1/`list' { local UU_`i'="`U1'`ty'/`ID_`i''" view browse "`UU_`i''" } } //构造最终的URL local URL1="https://music.163.com/#/" local URL2="?id=" if "`kind'"=="1" local kind = "song" if "`kind'"=="10" local kind = "album" if "`kind'"=="1000" local kind = "playlist" if "`kind'"=="1002" local kind = "user/home" if "`kind'"=="1004" local kind = "video" if "`kind'"=="1006" local kind = "song" if "`kind'"=="1009" local kind = "djradio" forvalue i = 1/`list' { local URL_`i'="`URL1'`kind'`URL2'`ID_`i''" } } *如果选择QQ音乐 if "`platform'" == "t" | "`platform'" == "T"{ *转换为QQ的代码 if "`kind'" == "1" | "`kind'" == "1000" | "`kind'" == "1009" local kind=0 //单曲,歌单和电台不支持,转为搜索单曲 if "`kind'" == "10" local kind=8 //专辑 if "`kind'" == "1014" local kind=12 //MV *获取songmid等各种信息 local URL1="http://c.y.qq.com/soso/fcgi-bin/client_search_cp?p=1&n=" local URL2="&w=" local URL3="&format=json&t=" local URL="`URL1'`list'`URL2'`songname'`URL3'`kind'" //构造获取歌曲信息的URL clear jsonio kv, file(`URL') *搜索的是单曲 if "`kind'" == "0" { qui keep if ustrregexm(key, "/singer/id/name") == 1 | ustrregexm(key, "/songname") == 1 | ustrregexm(key, "/songmid") == 1 forvalues i = 1/`list' { local ID_`i'=value[3+5*(`i'-1)] //歌曲ID local song_name_`i'=value[4+5*(`i'-1)] //歌曲中文名 local singer_`i'=value[1+5*(`i'-1)] //歌手名字 } //获取歌词 if "`list'" == "1" { clear local URL1="http://c.y.qq.com/soso/fcgi-bin/client_search_cp?p=1&n=1&w=" local URL2="&format=json&t=7" local URL="`URL1'`songname'`URL2'" //构造获取歌词的URL jsonio kv, file(`URL') //如果是纯音乐: qui keep if ustrregexm(key, "/data/lyric/list_1/content") == 1 if ustrregexm(value, "没有填词") == 1 { dis as txt char(10) "This is a pure music without lyrics, please enjoy." } //如果不是纯音乐,截取歌词 else { qui drop key qui split value, parse("\n") qui sxpose_copy, clear // by Prof. Cox qui replace _var1=subinstr(_var1,char(10),"",.) //删除换行符 qui keep if _var1!=" " | _var1!= "" | _var1!= "char(9)" qui duplicates tag, generate(dup) qui drop if dup==0 | dup==1 qui duplicates drop qui drop dup //打印在屏幕上 if "`nl'" == "" { local size = _N forvalues i=1/`size'{ dis as txt _var1[`i'] } } } } } *搜索的是专辑 if "`kind'" == "8" { qui keep if ustrregexm(key, "/singerName") == 1 | ustrregexm(key, "/albumName") == 1 | ustrregexm(key, "/albumMID") == 1 forvalues i = 1/`list' { local ID_`i'=value[1+5*(`i'-1)] //专辑ID local song_name_`i'=value[2+5*(`i'-1)] //专辑名字 local singer_`i'=value[4+5*(`i'-1)] //歌手名字 } } *搜索的是MV if "`kind'" == "12" { qui keep if ustrregexm(key, "/singer_name") == 1 | ustrregexm(key, "/mv_name") == 1 | ustrregexm(key, "/v_id") == 1 forvalues i = 1/`list' { local ID_`i'=value[3+3*(`i'-1)] //专辑ID local song_name_`i'=value[1+3*(`i'-1)] //专辑名字 local singer_`i'=value[2+3*(`i'-1)] //歌手名字 } } *autoplay选项,仅在搜索单曲时生效 if "`kind'" == "0" { if "`autoplay'" != "" { *获取播放地址 local UURL1="http://u.y.qq.com/cgi-bin/musicu.fcg?format=json&data=%7B%22req_0%22%3A%7B%22module%22%3A%22vkey.GetVkeyServer%22%2C%22method%22%3A%22CgiGetVkey%22%2C%22param%22%3A%7B%22guid%22%3A%22358840384%22%2C%22songmid%22%3A%5B%22" local UURL2="%22%5D%2C%22songtype%22%3A%5B0%5D%2C%22uin%22%3A%221443481947%22%2C%22loginflag%22%3A1%2C%22platform%22%3A%2220%22%7D%7D%2C%22comm%22%3A%7B%22uin%22%3A%2218585073516%22%2C%22format%22%3A%22json%22%2C%22ct%22%3A24%2C%22cv%22%3A0%7D%7D" forvalues i = 1/`list' { local UURL_`i'="`UURL1'`ID_`i''`UURL2'" //构造获取歌曲信息的URL clear jsonio kv, file(`UURL_`i'') *构造自动播放地址 qui keep if ustrregexm(key, "purl") == 1 | ustrregexm(key, "sip") == 1 local UUURL1=value[2] //歌曲ID local UUURL2=value[1] //歌曲中文名 local UUURL_`i'="`UUURL1'`UUURL2'" view browse "`UUURL_`i''" } } } *构造最终的URL forvalues i = 1/`list' { local URL1="https://y.qq.com/n/yqq/" if "`kind'"=="0" local kind = "song/" if "`kind'"=="8" local kind = "album/" if "`kind'"=="12" local kind = "mv/v/" local URL2=".html" local URL_`i'="`URL1'`kind'`ID_`i''`URL2'" } } } } //打印在屏幕上:歌曲的信息和链接地址 forvalue i = 1/`list' { //有link选项才会显示网址 if "`link'" != ""{ dis as txt `" {browse "`URL_`i''":`URL_`i''} } dis as txt `" {browse "`URL_`i''":{bf: <<`song_name_`i''>>, by `singer_`i''}} //markdown选项生成markdown格式 if "`markdown'" != "" { dis as txt "[`song_name_`i''](`URL_`i'')" } if "`ndescription'"==""{ dis as txt "`description_`i''" } //browse选项自动打开网址 if "`browse'" != "" { view browse "`URL_`i''" //网易云就是网易云音乐的界面,QQ只有一个音频播放页 } } restore end *------------------ *- sub-programs *------------------ cap prog drop lyrics prog def lyrics syntax anything, ID(string) //获取歌词 local URL1="http://music.163.com/api/song/media?id=" local URL="`URL1'`id'" //构造获取歌词的URL clear jsonio kv, file(`URL') //如果是纯音乐: if ustrregexm(key, "/nolyric_1") == 1 & ustrregexm(value, "true") == 1 { dis as txt char(10) "This is a pure music without lyrics, please enjoy." } //如果不是纯音乐,截取歌词 else { qui keep if ustrregexm(key, "/lyric_1") == 1 qui drop key qui split value, parse("[" "]") qui sxpose_copy, clear // sxpose.ado, by Prof. Cox qui replace _var1=subinstr(_var1,char(10),"",.) //删除换行符 qui keep if _var1!=" " | _var1!= "" | _var1!= "char(9)" qui duplicates tag, generate(dup) qui drop if dup==0 qui duplicates drop qui drop dup //打印在屏幕上 local size = _N forvalues i=1/`size'{ dis as txt _var1[`i'] } } end cap prog drop Njsonclean prog def Njsonclean syntax anything, kind(string) qui keep if ustrregexm(key, "\d/name") == 1 | ustrregexm(key, "\d/id") == 1 | ustrregexm(key, "/nickname") == 1 | ustrregexm(key, "/desc") == 1 qui drop if ustrregexm(key, "Music") == 1 if "`kind'"!= "10" qui drop if ustrregexm(key, "/album")==1 qui split key, parse("/") drop key key1 key2 qui drop if key5=="id" qui drop if key4=="" if "`kind'"=="1009"{ qui drop if key5 == "description" } qui drop key5 qui levelsof key4 //r(r) if "`kind'"=="1" local num=r(r)-2 if "`kind'"=="10" local num=r(r)-4 qui reshape wide value, i(key3) j(key4) string if "`kind'"=="1" | "`kind'"=="10" { qui gen singer=valueartists_1 qui drop valueartists_1 local NN=_N if `num' - 1 >0 { forvalues i = 2/`num'{ forvalues n=1/`NN'{ if valueartists_`i'[`n'] !=""{ qui replace singer= singer[`n']+"/"+valueartists_`i'[`n'] if _n == `n' } } qui drop valueartists_`i' } } } qui split key3,parse("_") qui destring key32, replace sort key32 end *-Sub prog-------sxpose_copy.ado----- * copy from sxpose.ado * NJC 1.0.0 14 October 2004 * Author: Nicholas J. Cox, Durham University * Support: email N.J.Cox@durham.ac.uk *cap prog drop sxpose_copy program sxpose_copy version 8 syntax , clear [ force format(string) firstnames destring ] if "`force'" == "" { foreach v of var * { capture confirm string var `v' if _rc { di as err /// "{p}dataset contains numeric variables; " /// "use {cmd:force} option if desired{p_end}" exit 7 } } } local nobs = _N qui d local nvars = r(k) if `nobs' > `c(max_k_theory)' { di as err "{p}not possible; would exceed present limit on " /// "number of variables{p_end}" exit 498 } forval j = 1/`nobs' { local new "`new' _var`j'" } capture confirm new var `new' if _rc { di as err "{p}sxpose would create new variables " /// "_var1-_var`nobs', but names already in use{p_end}" exit 110 } if "`format'" != "" { capture di `format' 1234.56789 if _rc { di as err "invalid %format" exit 120 } } else local format "%12.0g" if `nvars' > `nobs' set obs `nvars' unab varlist: * tokenize `varlist' qui forval j = 1/`nobs' { gen _var`j' = "" forval i = 1/`nvars' { cap replace _var`j' = ``i''[`j'] in `i' if _rc { replace _var`j' = /// string(``i''[`j'], "`format'") in `i' } } } drop `varlist' if `nobs' > `nvars' qui keep in 1/`nvars' qui if "`firstnames'" != "" { forval j = 1/`nobs' { capture rename _var`j' `= _var`j'[1]' } drop in 1 } if "`destring'" != "" destring, replace end