package player

import (
	"container/list"
	"encoding/json"
	"fmt"
	. "netease_control_center/globals"
	"netease_control_center/interfaces/eventPoint"
	"netease_control_center/interfaces/http"
	"netease_control_center/interfaces/innerTask"
	"netease_control_center/interfaces/log"
	"netease_control_center/interfaces/schedule"
	"netease_control_center/interfaces/vui"
	"strconv"
	"strings"
	"sync"
	"time"
	"math"
)

var (
	err error

	curSongInfo *SongInfo = nil //当前播放歌曲信息

	forcePlayCur bool = false // 暂时时间小于t1,重复播放歌单时，首先是播放当前暂停的音乐,完成后,如果有更新，此时强制播放歌单列表当前节点歌曲,保证更新的歌单第1首能被播放

	listIndex MusicLenType = 0 // 记录歌单拉取的index

	playUserLists    bool         = false           // 当前是否在播放我的歌单
	curUserListsType MusicSrcType = MUSIC_LIST_NULL // 播放我的歌单时，当前的type类型

	updateHistoryLock sync.Mutex // 历史列表更新互斥锁
	playCtrlLock      sync.Mutex // 对外接口的互斥锁，所有对外的控制接口都需要获取到锁才能执行

	errWifiPlayCnt uint8 = 0 // 统计播放器异常重复播放次数

	curPosNew *list.Element

	wifiErrTimer *time.Timer = nil
)

const (
	DENY_MAX_TIMES = 10
)

/**
播放模式切换时，设置当前播放歌曲在非活动播放列表中位置
*/
func ResetUnPlayListCurNode() {
	playIndex := GetMusicListIndex()
	unPlayIndex := getUnPlayListIndex(playIndex)
	// 定位当前歌曲记录节点到播放歌曲位置
	if nil != LocalMusicList[playIndex].GetCurPos() {
		LocalMusicList[unPlayIndex].Search(&(LocalMusicList[playIndex].GetCurPos().Value.(*SongInfo).Id),
			true)
		log.Println(log.DebugLog, "unPlayIndex:", unPlayIndex, "  id:",
			LocalMusicList[unPlayIndex].GetCurPos().Value.(*SongInfo).Id)
	} else {
		log.Println(log.DebugLog, "current play list is nil")
	}
}

/**
wifiPlayApi
*/
func wifiPlayApi(e interface{}) bool {
	if nil == e {
		sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)

		//songId := PlayerHander[PlayerType_Wifi].GetPlayerStatus().Uuid
		//songDetail, err := getSongDetailInfo(PlayerType_Wifi, &songId, false)
		//if nil != err {
		//	log.Println(log.ErrorLog, err.Error())
		//	return
		//}
		//if songDetail.Song.IsLiked {
		//	vui.IconLed_iconMusic003.Show();
		//	log.Println(log.DebugLog, "like icon.")
		//} else {
		//	vui.IconLed_iconMusic001.Show();
		//	log.Println(log.DebugLog, "normal icon.")
		//}
		return true
	}

	switch param := e.(type) {
	case *ServerMusicList, *ServerMusicLists:
		if err := playMusic(e); err == MUSIC_ERR_ACTION {
			return false
		}
	case *AppPlayReq:
		playAppMusic(param)
	case *SongInfo:
		commonPlayApi(commonPlayApiArg{
			prepare:            false,
			songId:             param,
			songUrl:            nil,
			tts:                "",
			ttsUrl:             "",
			ttsNow:             true,
			needChangeNHDIndex: false,
			srcType:            SINGLE_SONG,
			nextOrPreSong:      true,
			filterTts:          false,
			rspId:              "",
		})
	default:
		log.Println(log.WarningLog, "err play type")
		return false
	}

	return true
}

func wifiPauseApi() {
	sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PAUSE)
}

func wifiStopApi() {
	sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_STOP)
}

func wifiNextApi(arg interface{}) {
	nextMusic(arg)
}

func FilterMusic(tts *string) {
	if nil != tts {
		base := ServerPlayerBase{InnerTts: *tts}
		nextMusic(schedule.PlayerArg{GloablePause: false, NotShowIcon: false, ServerPlayerBase: base})
	}
}

func wifiPreApi(arg interface{}) {
	prevMusic(arg)
}

func wifiPrepareNextApi() {
	err = prepareNextMusic()
	if nil != err {
		log.Println(log.ErrorLog, err.Error())
	}
}

func wifiSeekApi(pos uint32) {
	sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_SEEKTO, pos)
}

func wifiSeekPlayApi(pos uint32) {
	sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_SEEKTO_PLAY, pos)
}

/**

 */
func wifiPlayerStHd(status schedule.PlayerDetail) error {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	updateHistoryLock.Lock()
	defer updateHistoryLock.Unlock()

	switch status.Status {
	case MUSIC_ST_PLAYING:
		if 0 != errWifiPlayCnt {
			errWifiPlayCnt = 0
			log.Println(log.WarningLog, "resume pos when err play, seek to pos:", wifiPlayerSt.CurPlayingPos)
			wifiSeekApi(wifiPlayerSt.CurPlayingPos)
		}
		if "" != status.Uuid {
			if song, err := GetCurWifiSongInfo(); nil == err {
				if informTop3Test {
					tts := "消息推送通知测试"
					song.Tts = &tts
				}
				if nil != song.Tts {
					schedule.PlayNoticeAsync(AdSrcType_TTS_MIX, *song.Tts, CONST_AUDIO_ID, true, nil, nil)
					top3Inform := Top3PlayInform{AlbumId: song.AlbumId, ArtistId: song.ArtistId, SongId: song.Id, Tts: *song.Tts}

					song.Tts = nil
					if data, err := json.Marshal(top3Inform); nil == err {
						if song.Single {
							http.InformMsgToServer(http.Type_Inform_Top3_Play_Sigle, string(data))
						} else {
							http.InformMsgToServer(http.Type_Inform_Top3_Play, string(data))
						}

					} else {
						log.Println(log.WarningLog, err.Error())
					}
				}
			}

			if wifiNeedSeekToPos {
				wifiNeedSeekToPos = false
				sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_SEEKTO, wifiSeekPos)
			}

			if nil == wifiPlayerSt.MusicId || *wifiPlayerSt.MusicId != status.Uuid ||
				(time.Now().Unix()-wifiPlayerSt.ContinuousStChangeTime.Unix()) > MUSIC_MAX_LEN {
				addToHistoryMusicList()
			}

			updateWifiPlayerStatus(status)

			if ttsMusicUuid != status.Uuid {
				ttsNoMusic = false
			}
		}
	case MUSIC_ST_FOR_NEXT:
		err = prepareNextMusic()
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
		} else {
			log.Println(log.DebugLog, "prepare for next...")
		}
	case MUSIC_ST_COMPLETED:
		if nil != curPosNew {
			LocalMusicList[GetMusicListIndex()].SetCurPos(curPosNew)
			curPosNew = nil
			log.Println(log.DebugLog, "complete:", LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo).Id)
		}
		if ttsNoMusic {
			ttsNoMusic = false
			forcePausePlayer(PlayerType_Wifi)
			//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, ttsInfo, CONST_AUDIO_ID, AdSrcType_TTS, TTS_LEVEL_1,
			//	true, false)
			if ttsInfo == TTS_TEXT_ORDER_PLAY_END_FLAG {
				PlayListSrcRec.ListHasEnd = true
				log.Println(log.WarningLog, "play list end, pause player!")
			} else {
				mscToastTts(ttsInfo, CONST_AUDIO_ID, true, false, nil)
				log.Println(log.DebugLog, "tts when music end:", ttsInfo)
			}
		}

		if "" != status.Uuid {
			// 播放器准备下一首时，将uuid替换成了下一首的，导致完成时，传上来的uuid是下一首的,此处修正下
			status.Uuid = *wifiPlayerSt.MusicId
			updateWifiPlayerStatus(status)
		}

		innerTask.ICmdFunc(innerTask.ICmd_Trig_Exec)
	case MUSIC_ST_PAUSED, MUSIC_ST_IDLE:
		if "" != status.Uuid {
			updateWifiPlayerStatus(status)
		}
	case MUSIC_ST_PREPARING:
		log.Println(log.DebugLog, "preparing...")
	case MUSIC_ST_PREPARED:
		log.Println(log.DebugLog, "prepared")
	case MUSIC_ST_PREPARING_TIMEOUT:
		log.Println(log.ErrorLog, "music preparing timeout")
		//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, vui.SoundNotice_Song_Get_Timeout.GetPath(), CONST_AUDIO_ID, AdSrcType_FILE, TTS_LEVEL_1,
		//	true, false)
		mscToastFile(vui.SoundNotice_Song_Get_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
		eventPoint.PrepareTimeout(PlayerType_Wifi, status.Uuid)
	case PLAYER_ERR_TINA_NOTIFY, PLAYER_ERR_TINA_START, PLAYER_ERR_TINA_RESET,
		PLAYER_ERR_TINA_SETSOURCE, PLAYER_ERR_TINA_PREPARE:
		log.Println(log.DebugLog, "...tinaplayer err...", status.Status)

		if nil != wifiErrTimer {
			wifiErrTimer.Stop()
		}
		wifiErrTimer = time.AfterFunc(500*time.Millisecond, func() {
			wifiErrTimer = nil

			//如果status为PLAYER_ERR_TINA_NOTIFY说明网络已经有问题了，需要重新拉取
			if status.Status != PLAYER_ERR_TINA_NOTIFY && PlayerHander[PlayerType_Wifi].IsPlaying() {
				log.Println(log.DebugLog, "...tinaplayer err...but player is playing")
			} else {
				//nextMusic(nil)
				if errWifiPlayCnt < ERR_PLAY_MAX {
					errWifiPlayCnt++
					log.Println(log.WarningLog, "play fail, try:", errWifiPlayCnt)
					curMusic()
				} else {
					errWifiPlayCnt = 0
					log.Println(log.ErrorLog, "SYSTEM ERR: tinaplayer err, and has try ", ERR_PLAY_MAX, " times")
					mscToastFile(vui.SoundNotice_Song_Get_Timeout.GetPath(), CONST_AUDIO_ID, true, false, nil)
				}
			}
		})
	case PLAYER_ERR_TINA_SEEKTO:
		log.Println(log.WarningLog, "tina play seek err.")
	case PLAYER_ERR_TINA_SETVOL:
		log.Println(log.WarningLog, "tina play set vol err")
	default:
		log.Printf(log.WarningLog, "invalid st:0x%x\n", status.Status)
	}

	return nil
}

func playMusic(e interface{}) MusicAction {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	// 如果参数为空，且当前在暂停状态，恢复播放
	if nil == e {
		if MUSIC_ST_PAUSED == wifiPlayerSt.ContinuousTaskSt {
			timePause := time.Now().Sub(wifiPlayerSt.ContinuousStChangeTime)
			if timePause < T1Timeout.GetCfgListTime() {
				sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
			} else if timePause < T2Timeout.GetCfgListTime() {
				log.Println(log.DebugLog, "t1~t2 now:", time.Now().Unix(), "  listTime:",
					wifiPlayerSt.ContinuousStChangeTime.Unix())
				var songInfo SongInfo
				song, _, found := LocalMusicList[GetMusicListIndex()].Search(wifiPlayerSt.MusicId, false)
				if found {
					songInfo = *song
				} else {
					songInfo = SongInfo{Id: *wifiPlayerSt.MusicId}
				}

				var action, actionSrc string = "", ""
				switch eVal := e.(type) {
				case *ServerMusicList:
					action = eVal.Action
					actionSrc = eVal.ActionSrc
				case *ServerMusicLists:
					action = eVal.Action
					actionSrc = eVal.ActionSrc
				default:
					log.Println(log.ErrorLog, "err params when play music")
				}

				commonPlayApi(commonPlayApiArg{
					prepare:            false,
					songId:             &songInfo,
					songUrl:            nil,
					tts:                "",
					ttsUrl:             "",
					ttsNow:             true,
					needChangeNHDIndex: false,
					srcType:            SINGLE_SONG,
					nextOrPreSong:      true,
					filterTts:          false,
					rspId:              "",
					action:             action,
					actionSrc:          actionSrc,
				})
			} else {
				log.Println(log.DebugLog, "t2~ now:", time.Now().Unix(), "  listTime:",
					wifiPlayerSt.ContinuousStChangeTime.Unix())
			}

			return MUSIC_REPLAY_ACTION
		}
		log.Println(log.DebugLog, "nil params when play music")
		return MUSIC_REPLAY_NULL
	}
	var srcType MusicSrcType
	var source MusicSrcType //歌单来源
	var serverCmd uint16
	var songList []SongInfo
	var copyrightTts CopyrightPullTts
	var rspId string
	switch eVal := e.(type) {
	case *ServerMusicList:
		if SINGLE_SONG == eVal.MusicList.Type {
			// 单曲
			playSingleMusic(eVal)
			return MUSIC_SINGLE_ACTION
		} else {
			// 歌单
			srcType = eVal.MusicList.Type
			serverCmd = eVal.ServerCmd
			songList = eVal.MusicList.SongList
			copyrightTts = eVal.MusicList.CopyrightTts
			rspId = eVal.RspId
		}
	case *ServerMusicLists:
		listIndex = 0 // 歌单记录索引清零
		// 歌单列表
		srcType = eVal.MusicLists[0].Type
		serverCmd = eVal.ServerCmd
		songList = eVal.MusicLists[0].SongList
		copyrightTts = eVal.MusicLists[0].CopyrightTts
		source = eVal.MusicLists[0].Source
		rspId = eVal.RspId
	default:
		log.Println(log.ErrorLog, "err params when play music")
		return MUSIC_ERR_ACTION
	}
	// 检查歌单是否为空
	if nil == songList || len(songList) == 0 {
		log.Println(log.ErrorLog, "nil songlist from server")
		return MUSIC_ERR_ACTION
	}

	/* 播放推荐，直接根据serverCMD区分 */
	if uint16(MSC_CMD_PLAY_RCMD) == serverCmd {
		playRcmd(e.(*ServerMusicList))
		return MUSIC_RCMD_ACTION
	}

	log.Println(log.DebugLog, "music list type:", srcType, ", local: ", PlayListSrcRec.MusicSrc)

	PlayListSrcRec.CopyrightTts = copyrightTts
	if copyrightTts.TtsType != TTS_TYPE_NULL {
		log.Printf(log.WarningLog, "copyrightTts info push:%+v \n", copyrightTts)
	}

	/* 处理我的歌单 : 服务器推下来的时候，如果不是我的歌单，播放标志位的flag清除 */
	if USER_LISTS == srcType {
		playUserLists = true
		if source == LIST_SOURCE_CREATE {
			curUserListsType = USER_LISTS_CREATE
			log.Println(log.DebugLog, "list push from create user lists")
		} else if source == LIST_SOURCE_COLLECT {
			curUserListsType = USER_LISTS_COLLECT
			log.Println(log.DebugLog, "list push from collect user lists")
		} else {
			curUserListsType = USER_LISTS_CREATE
			log.Println(log.DebugLog, "list push from default create user lists")
		}
	} else {
		playUserLists = false
	}

	// 清除歌曲过滤列表
	LocalMusicFilter.Init()
	PlayListSrcRec.ListHasEnd = false //歌单播放结束标志位false

	switch srcType {
	case NEW_TOPLIST, HOT_TOPLIST, DAILY_PUSH, DENON_TOPLIST, CLASSIC_TOPLIST, BEATPORT_TOPLIST,
		ORIGINAL_TOPLIST, SORIGN_TOPLIST, ACG_TOPLIST, US_BILLBOARD_TOPLIST, JP_ORICON_TOPLIST:
		playNewHotDailyMusic(e.(*ServerMusicList), false)
	case PRIVATE_FM, MUSIC_SHARE_RCMD: // 云村推荐，处理逻辑和私人FM一致
		playPrivateFM(e.(*ServerMusicList))
	case MUSIC_LIST_ID, MUSIC_LIST, ALBUM_ID, ALBUM_ARTIST_ID, ARTIST_ID, SIMILAR_SONG,
		USER_LISTS_COLLECT, USER_LISTS_CREATE, USER_LISTS, RED_FM, MUSIC_LIST_WORD,
		MUSIC_LIST_SERACH_WORD, OTHERS,
		CH_NEW_TOPLIST, CH_HOT_TOPLIST, JP_NEW_LIST, KOREA_NEW_LIST, EU_US_NEW_LIST,
		HISTORY_SONG_LIST, RECOMMEND_SONG_LIST, NATURAL_SOUND_LIST, RADIO_STATION_LIST:
		playNormalListMusic(true, e)
	default:
		log.Println(log.WarningLog, "unkonw srcType")
		mscToastTts(TTS_NEED_UPDATE, rspId, true, false, nil)
		//push tts to server
		http.InformMsgToServer(http.Type_Inform_TtsDelay, TTS_NEED_UPDATE)
		return MUSIC_ERR_ACTION
	}
	return MUSIC_LISTS_ACTION
}

/**
播放 app 下发的play命令
*/
func playAppMusic(appReq *AppPlayReq) (MusicAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	if nil == appReq {
		log.Println(log.WarningLog, "param nil when play from app")
		return APP_NIL_ID_ACTION, MusicList_ERR_MUSICINFO_NULL
	}

	log.Printf(log.DebugLog, "param when play from app:%+v\n", appReq)
	PlayListSrcRec.ListHasEnd = false //歌单播放结束标志位false

	if INVAILED_INDEX != appReq.Index {
		//　当前播放列表中歌曲
		listType := GetMusicListIndex()
		songinfo := SongInfo{Id: (*appReq).Id}
		var songId *SongInfo = &songinfo
		if nil == songId || len(songId.Id) < 1 {
			log.Println(log.ErrorLog, "songId err:", (*appReq).Id)
			return APP_NIL_ID_ACTION, MusicList_ERR_MUSICINFO_NULL
		}

		if 0 >= LocalMusicList[CUR_MUSIC_LIST].List.Len() {
			log.Println(log.ErrorLog, "cur list is nil")
			return APP_NIL_ID_ACTION, List_ERR_NULL
		}

		songIdTmp, _, hasSongId := LocalMusicList[listType].Search(songId, true)
		var action MusicAction
		if hasSongId {
			songId = songIdTmp
			// 列表中有该歌曲，直接播放该歌曲，且将curpos定位到该歌曲
			log.Println(log.DebugLog, "play list has this songId:", songId.Id)
			err = commonPlayApi(commonPlayApiArg{
				prepare:            false,
				songId:             songId,
				songUrl:            nil,
				tts:                "",
				ttsUrl:             "",
				ttsNow:             true,
				needChangeNHDIndex: false,
				srcType:            SINGLE_SONG,
				nextOrPreSong:      true,
				filterTts:          false,
				rspId:              "",
				action:             appReq.Action,
				actionSrc:          appReq.ActionSrc,
			})
			if err != nil {
				log.Println(log.ErrorLog, err.Error())
				return APP_ERR_ACTION, err
			}
			action = APP_CUR_PAGE_ACTION
		} else {
			// 列表中没有该歌曲，拉取该歌曲对应的页
			index := GetPageIndexFromIndex((*appReq).Index, PAGE_SIZE)
			srcType := getMusicSrcType(PlayerType_Wifi)
			id := getServiceId(srcType)

			serverList, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     srcType,
				Id:      id,
				Params:  nil,
				Start:   index,
				Size:    PAGE_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,

				ExtraData: PlayListSrcRec.ExtraData,
				BizType:   PlayListSrcRec.BizType,
				Order:     PlayListSrcRec.Order,
				AlbumID:   curSongInfo.AlbumId,
			})
			//serverList, err := getMusicList(PlayerType_Wifi, srcType, id, nil, index,
			//	PAGE_SIZE, true, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return APP_ERR_ACTION, err
			}

			if songId.Id == serverList.MusicList.SongList[serverList.MusicList.Index].Id {
				// 推送的url和当前需要播放歌曲的url同，直接播放
				songId.Gain = serverList.MusicList.Gain
			} else {
				// 需要在此跟服务器拉取播放信息
				song, err := getSongDetailInfo(PlayerType_Wifi, &(songId.Id), true)
				if nil != err {
					log.Println(log.ErrorLog, err.Error())
					return APP_ERR_ACTION, err
				} else if "" == song.Song.Url {
					log.Println(log.ErrorLog, "song url is nil")
					return APP_ERR_ACTION, err
				}
				serverList.MusicList.Url = song.Song.Url
				serverList.MusicList.Gain = song.Song.Gain

				songId = &song.Song.SongInfo
			}
			// 更新本地播放列表
			UpdatePlayListAndPage(serverList, appReq.Index, true)

			// 更新当前播放列表来源信息
			UpdatePlayListSrcRec(serverList, false)
			switch PlayListSrcRec.MusicSrc {
			case NEW_TOPLIST, HOT_TOPLIST, DAILY_PUSH, DENON_TOPLIST, CLASSIC_TOPLIST, BEATPORT_TOPLIST,
				ORIGINAL_TOPLIST, SORIGN_TOPLIST, ACG_TOPLIST, US_BILLBOARD_TOPLIST, JP_ORICON_TOPLIST:
				// 更新榜单、每日推荐的本地存储信息
				indexOffset := getCurPage() * PAGE_SIZE
				NewHotDailyRec[PlayListSrcRec.MusicSrc-NEW_TOPLIST].MusicListTimeStamp = serverList.MusicList.UpdateTime
				NewHotDailyRec[PlayListSrcRec.MusicSrc-NEW_TOPLIST].MusicListIndex = serverList.MusicList.Index + indexOffset
				log.Println(log.DebugLog, "NewHotDailyRec src:", PlayListSrcRec.MusicSrc, "  index:",
					NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex)
			}
			// 定位当前歌曲记录节点到播放歌曲位置
			_, _, found := LocalMusicList[listType].Search(songId, true)
			if !found {
				//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, TTS_APP_NOT_SEARCH_SONG, CONST_AUDIO_ID, AdSrcType_TTS, TTS_LEVEL_1,
				//	true, false)
				mscToastTts(TTS_APP_NOT_SEARCH_SONG, CONST_AUDIO_ID, true, false, nil)
				return APP_ERR_ACTION, err
			}

			// 播放歌曲
			err = commonPlayApi(commonPlayApiArg{
				prepare:            false,
				songId:             songId,
				songUrl:            &(serverList.MusicList.Url),
				tts:                "",
				ttsUrl:             "",
				ttsNow:             true,
				needChangeNHDIndex: false,
				srcType:            PlayListSrcRec.MusicSrc,
				nextOrPreSong:      true,
				filterTts:          false,
				rspId:              "",
				action:             appReq.Action,
				actionSrc:          appReq.ActionSrc,
			})
			if nil != err {
				log.Println(log.WarningLog, err.Error())
				return APP_ERR_ACTION, err
			}
			action = APP_NEXT_PAGE_ACTION
		}
		// 定位当前歌曲记录节点到播放歌曲位置
		LocalMusicList[listType].Search(songId, true)
		return action, nil
	} else {
		// 清除歌曲过滤列表
		if SINGLE_SONG != appReq.Type {
			LocalMusicFilter.Init()
		}

		if USER_LISTS == appReq.Type {
			playUserLists = true
			appReq.Type = USER_LISTS_CREATE
			log.Println(log.DebugLog, "type change from USER_LISTS to USER_LISTS_CREATE.")
		} else {
			playUserLists = false
		}

		// 卡片歌曲
		var tmp interface{}
		var err error
		switch appReq.Type {
		case SINGLE_SONG:
			tmp = createServerMusicList(appReq, int8(eventPoint.EpOs_App))
		case USER_LISTS_COLLECT,
			USER_LISTS_CREATE,
			USER_LISTS,
			ALBUM_ARTIST_ID:
			// 歌单列表
			tmp, err = GetMusicListsNew(PlayerType_Wifi, false, http.MusicListServerType{
				Src:     appReq.Type,
				Id:      &appReq.ListId,
				Params:  getServiceParams(appReq.Type),
				Start:   0,
				Size:    LIST_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})
			//tmp, err = getMusicLists(PlayerType_Wifi, appReq.Type, &appReq.ListId,
			//	getServiceParams(appReq.Type), 0, LIST_SIZE, false, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return APP_ERR_ACTION, err
			}

			if len(tmp.(*ServerMusicLists).MusicLists) > 1 {
				for k, _ := range tmp.(*ServerMusicLists).MusicLists[0].SongList {
					tmp.(*ServerMusicLists).MusicLists[0].SongList[k].SourceOs = int8(eventPoint.EpOs_App)
				}
			} else {
				log.Println(log.ErrorLog, "MusicLists nil")
			}
		case MUSIC_LIST_WORD,
			MUSIC_LIST_SERACH_WORD:
			// 歌单列表 分类词
			tmp, err = GetMusicListsNew(PlayerType_Wifi, false, http.MusicListServerType{
				Src:     appReq.Type,
				Id:      &appReq.Oftag,
				Params:  getServiceParams(appReq.Type),
				Start:   0,
				Size:    LIST_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})

			//tmp, err = getMusicLists(PlayerType_Wifi, appReq.Type, &appReq.Oftag,
			//	getServiceParams(appReq.Type), 0, LIST_SIZE, false, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return APP_ERR_ACTION, err
			}

			if len(tmp.(*ServerMusicLists).MusicLists[0].SongList) > 1 {
				for k, _ := range tmp.(*ServerMusicLists).MusicLists[0].SongList {
					tmp.(*ServerMusicLists).MusicLists[0].SongList[k].SourceOs = int8(eventPoint.EpOs_App)
				}
			} else {
				log.Println(log.ErrorLog, "songlist nil")
			}
		case NEW_TOPLIST,
			HOT_TOPLIST,
			DENON_TOPLIST,
			CLASSIC_TOPLIST,
			BEATPORT_TOPLIST,
			ORIGINAL_TOPLIST,
			SORIGN_TOPLIST,
			ACG_TOPLIST,
			US_BILLBOARD_TOPLIST,
			JP_ORICON_TOPLIST:
			tmp, err = GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     appReq.Type,
				Id:      getServiceId(appReq.Type),
				Params:  nil,
				Start:   0,
				Size:    PAGE_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})
			//tmp, err = getMusicList(PlayerType_Wifi, appReq.Type, getServiceId(appReq.Type), nil,
			//	0, PAGE_SIZE, true, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return NHD_ERR_ACTION, err
			}

			for k, _ := range tmp.(*ServerMusicList).MusicList.SongList {
				tmp.(*ServerMusicList).MusicList.SongList[k].SourceOs = int8(eventPoint.EpOs_App)
			}
		default:
			// 歌单id
			tmp, err = GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     appReq.Type,
				Id:      &appReq.ListId,
				Params:  nil,
				Start:   0,
				Size:    PAGE_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,

				ExtraData: PlayListSrcRec.ExtraData,
				BizType:   PlayListSrcRec.BizType,
				Order:     PlayListSrcRec.Order,
				AlbumID:   curSongInfo.AlbumId,
			})
			//tmp, err = getMusicList(PlayerType_Wifi, appReq.Type, &appReq.ListId, nil,
			//	0, PAGE_SIZE, true, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return NHD_ERR_ACTION, err
			}

			for k, _ := range tmp.(*ServerMusicList).MusicList.SongList {
				tmp.(*ServerMusicList).MusicList.SongList[k].SourceOs = int8(eventPoint.EpOs_App)
			}
		}
		//log.Printf(log.DebugLog, "app params:%+v\n", tmp)
		if appReq.ServerCmd == uint16(MSC_CMD_REPEAT) {
			switch eVal := tmp.(type) {
			case *ServerMusicList:
				eVal.ServerCmd = uint16(MSC_CMD_REPEAT)
			case *ServerMusicLists:
				eVal.ServerCmd = uint16(MSC_CMD_REPEAT)
			default:
				log.Println(log.WarningLog, "unkonw type")
			}
		}

		switch tmp.(type) {
		case *ServerMusicList:
			tmp.(*ServerMusicList).Action = appReq.Action
			tmp.(*ServerMusicList).ActionSrc = appReq.ActionSrc
		case *ServerMusicLists:
			tmp.(*ServerMusicLists).Action = appReq.Action
			tmp.(*ServerMusicLists).ActionSrc = appReq.ActionSrc
		default:
			log.Println(log.WarningLog, "unkonw type")
		}

		playMusic(tmp)

		return APP_CARD_ACTION, List_ERR_UNSUPPORT
	}
}

func createServerMusicList(appReq *AppPlayReq, srcOs int8) *ServerMusicList {
	if nil == appReq {
		return nil
	}
	var tmp ServerMusicList

	tmp.MusicList.Type = SINGLE_SONG
	tmp.MusicList.Id = ""
	tmp.MusicList.Index = 0
	tmp.MusicList.Url = ""
	tmp.MusicList.SongList = make([]SongInfo, 1)

	tmp.MusicList.SongList[0].Id = appReq.Id
	tmp.MusicList.SongList[0].CanPlay = true
	tmp.MusicList.SongList[0].SourceOs = srcOs

	tmp.Action = appReq.Action
	tmp.ActionSrc = appReq.ActionSrc

	return &tmp
}

func newHotDailyMusicRepeat(serverData *ServerMusicList) bool {
	if serverData.ModeId == PlayListSrcRec.ModeId && (serverData.MusicList.Type == PlayListSrcRec.MusicSrc ||
		(DAILY_PUSH == serverData.MusicList.Type && DAILY_FM_MIX == PlayListSrcRec.MusicSrc)) &&
		serverData.ServerCmd != uint16(MSC_CMD_REPEAT) && serverData.CloudId == PlayListSrcRec.CloudId &&
		!serverData.IgnoreListRepeat {
		return true
	} else {
		return false
	}
}

/**
播放榜单以及每日推荐接口
*/
func playNewHotDailyMusic(serverData *ServerMusicList, rcmdSrc bool) (MusicAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var musicAction MusicAction
	oldCurPlayingMode := CurPlayingMode
	if MODE_NULL != serverData.PlayMode {
		CurPlayingMode = serverData.PlayMode
	} else {
		// 默认：顺序播放
		CurPlayingMode = LIST_ORDER_CYCLE
	}
	log.Println(log.DebugLog, "playNewHotDailyMusic, cur play mode:", CurPlayingMode, ", serverMode:", serverData.PlayMode,
		" BackIndex:", NormalListMap[NORMAL_LIST_PAGE].BackIndex)

	if serverData.MusicList.Type < NEW_TOPLIST || serverData.MusicList.Type > JP_ORICON_TOPLIST {
		log.Println(log.ErrorLog, "err type is:", serverData.MusicList.Type)
		return NHD_ERR_ACTION, nil
	}
	if 0 != NormalListMap[NORMAL_LIST_PAGE].BackIndex {
		NewHotDailyRec[serverData.MusicList.Type-NEW_TOPLIST].MusicListIndex = NormalListMap[NORMAL_LIST_PAGE].BackIndex
		NormalListMap[NORMAL_LIST_PAGE].BackIndex = 0
	} else {
		// 检测记录的日期是否发生了变化
		timeRec := time.Unix(int64(NewHotDailyRec[serverData.MusicList.Type-NEW_TOPLIST].MusicListTimeStamp/1000), 0)
		timeServer := time.Unix(int64(serverData.MusicList.UpdateTime/1000), 0)
		log.Printf(log.DebugLog, "timeRec:%+v  timeServer:%+v timeRec.Day:%+v timeServer.Day:%+v\n",
			timeRec, timeServer, timeRec.Day(), timeServer.Day())
		if timeServer.Day() != timeRec.Day() {
			NewHotDailyRec[serverData.MusicList.Type-NEW_TOPLIST].MusicListIndex = 0
			log.Println(log.DebugLog, "change MusicListIndex to 0 becauce of other day")
		}
	}
	indexOffset := GetPageIndexFromIndex(NewHotDailyRec[serverData.MusicList.Type-NEW_TOPLIST].MusicListIndex,
		PAGE_SIZE)

	srcType := serverData.MusicList.Type
	var needUpdate bool = true
	var songId *SongInfo = nil
	var songUrl *string
	var needReplay bool = false
	if isNeedRestart() && newHotDailyMusicRepeat(serverData) { // 时间小于T1,且歌单type未改变
		/* 此时，不需要对推送的歌单进行鉴权保护，因为此时是直接播放暂停的歌曲，歌单鉴权保护，等待歌曲切换时再进行保护　*/
		//　恢复播放
		log.Println(log.DebugLog, "replay playNewHotDailyMusic")
		//sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
		//　检查更新
		if NewHotDailyRec[srcType-NEW_TOPLIST].MusicListTimeStamp != serverData.MusicList.UpdateTime || PlayListSrcRec.HasInsert {
			// 歌单有更新
			log.Println(log.DebugLog, "Update,t1:", NewHotDailyRec[srcType-NEW_TOPLIST].MusicListTimeStamp,
				" t2:", serverData.MusicList.UpdateTime)
			musicAction = NHD_LESS_T1_UPDATE

			//forcePlayCur = true 现在产品的逻辑是重复意图切换下一首
		} else {
			needReplay = true
			// 歌单无更新
			needUpdate = false
			musicAction = NHD_LESS_T1_NOT_UPDATE

			if oldCurPlayingMode != CurPlayingMode {
				tmpPlayingMode := CurPlayingMode
				CurPlayingMode = oldCurPlayingMode
				ResetUnPlayListCurNode()
				CurPlayingMode = tmpPlayingMode
			}
		}
	} else { // 时间大于等于T1
		//重复播放歌单
		if newHotDailyMusicRepeat(serverData) {
			if NewHotDailyRec[srcType-NEW_TOPLIST].MusicListTimeStamp != serverData.MusicList.UpdateTime ||
				PlayListSrcRec.HasInsert || serverData.MusicList.Type != PlayListSrcRec.MusicSrc ||
				serverData.ServerCmd == uint16(MSC_CMD_REPEAT) {
				// 歌单有更新, 直接使用服务器下发的url进行播放
				log.Println(log.DebugLog, "Update,t1:", NewHotDailyRec[srcType-NEW_TOPLIST].MusicListTimeStamp,
					" t2:", serverData.MusicList.UpdateTime)
				musicAction = NHD_MORE_T1_UPDATE
				// 播放歌曲信息
				songId, songUrl = getCanPlaySong(serverData)
			} else {
				// 重复播放歌单，播放断点位置歌曲
				if nil != LocalMusicList[GetMusicListIndex()].GetCurPos() {
					songId = LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo)
				}
				needUpdate = false
				musicAction = NHD_MORE_T1_NOT_UPDATE_DIRECT_URL

				if oldCurPlayingMode != CurPlayingMode {
					tmpPlayingMode := CurPlayingMode
					CurPlayingMode = oldCurPlayingMode
					ResetUnPlayListCurNode()
					CurPlayingMode = tmpPlayingMode
				}
			}
		} else {
			// 非重复播放歌单,首先对比服务器下发的url和当前url索引是否相同，如果不同，拉取本地存储索引的url
			if DAILY_PUSH == srcType && // 账户不同时,每日推荐播放记录清空
				(serverData.ModeId != PlayListSrcRec.ModeId || serverData.CloudId != PlayListSrcRec.CloudId) {
				NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex = 0
				log.Println(log.WarningLog, "reset DAILY_PUSH MusicListIndex because of modeId change")
			}

			log.Println(log.DebugLog, "local:", NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex,
				" remote:", (serverData.MusicList.Index + indexOffset))

			if NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex == serverData.MusicList.Index+indexOffset ||
				0 == NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex /* 本地缓存记录为0,直接播放服务器下发的url */ {
				// 直接播放服务器该次下发的url
				musicAction = NHD_MORE_T1_NOT_UPDATE_CUR_PAGE
			} else {
				// 从服务器拉取本地存储的index歌曲
				if NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex < MusicLenType(len(serverData.MusicList.SongList)) {
					log.Println(log.DebugLog, "get from cur page")
					// 本地歌曲是当前页的

					NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex = serverData.MusicList.Index
					song, err := getSongPlayInfo(PlayerType_Wifi,
						&(serverData.MusicList.SongList[serverData.MusicList.Index].Id),
						true)
					if nil != err {
						log.Println(log.ErrorLog, err.Error())
						NewHotDailyRec[serverData.MusicList.Type-NEW_TOPLIST].MusicListIndex = 0
						log.Println(log.DebugLog, "change MusicListIndex to 0 becauce of song Cannot Play")
						return NHD_ERR_ACTION, err
					}
					serverData.MusicList.Url = song.SongPlay.Url
					musicAction = NHD_MORE_T1_NOT_UPDATE_CUR_PAGE
				} else {
					log.Println(log.DebugLog, "get from next page")
					// 本地歌曲不是当前页，拉取本地歌曲所在的分页
					index := GetPageIndexFromIndex(NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex, PAGE_SIZE)

					serverList, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
						Src:     srcType,
						Id:      getServiceId(srcType),
						Params:  nil,
						Start:   index,
						Size:    PAGE_SIZE,
						WantTts: true,

						Oftag:        &serverData.Oftag,
						OftagForShow: &serverData.OftagForShow,
						Mode:         CurPlayingMode,
						CloudId:      serverData.CloudId,
						ModeId:       serverData.ModeId,
					})
					//serverList, err := getMusicList(PlayerType_Wifi, srcType, getServiceId(srcType), nil, index, PAGE_SIZE,
					//	true, CurPlayingMode)
					if nil != err {
						log.Println(log.ErrorLog, err.Error())
						NewHotDailyRec[serverData.MusicList.Type-NEW_TOPLIST].MusicListIndex = 0
						log.Println(log.DebugLog, "change MusicListIndex to 0 becauce of get list err")
						return NHD_ERR_ACTION, err
					}
					serverData = serverList
					if NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex == (serverList.MusicList.Index + indexOffset) {
						// 服务器推送的url索引和本地存储同, 直接播放推送的url
					} else {
						if UintTest {
							goto unitOut
						}
						song, err := getSongPlayInfo(PlayerType_Wifi,
							&(serverData.MusicList.SongList[NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex-indexOffset].Id),
							true)
						if nil != err {
							log.Println(log.ErrorLog, err.Error())
							NewHotDailyRec[serverData.MusicList.Type-NEW_TOPLIST].MusicListIndex = 0
							log.Println(log.DebugLog, "change MusicListIndex to 0 becauce of song Cannot Play")
							return NHD_ERR_ACTION, err
						}
						serverData.MusicList.Url = song.SongPlay.Url
						serverData.MusicList.Index = NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex - indexOffset
					}
				unitOut:
					musicAction = NHD_MORE_T1_NOT_UPDATE_NEXT_PAGE
				}
			}
			// 播放歌曲信息
			songId, songUrl = getCanPlaySong(serverData)
		}

	}
	log.Println(log.DebugLog, "NHD musicAction:", musicAction)

	if !needUpdate {
		log.Println(log.DebugLog, "playNewHotDailyMusic not needUpdate")
	} else {
		NormalListMap[NORMAL_LIST_PAGE].Init()

		// 替换当前的播放列表和更新page
		UpdatePlayListAndPage(serverData, serverData.MusicList.Index+indexOffset, true)
		// 定位当前歌曲记录节点到播放歌曲位置
		if nil != songId {
			LocalMusicList[GetMusicListIndex()].Search(songId, true)
		}

		// 更新当前播放列表来源信息
		UpdatePlayListSrcRec(serverData, true)

		// 更新榜单、每日推荐的本地存储信息
		NewHotDailyRec[srcType-NEW_TOPLIST].MusicListTimeStamp = serverData.MusicList.UpdateTime
		NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex = serverData.MusicList.Index + indexOffset
		log.Println(log.DebugLog, "NewHotDailyRec src:", PlayListSrcRec.MusicSrc, "  index:",
			NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex)

		// 如果是推荐指令，替换掉存储的歌曲来源信息
		if rcmdSrc {
			PlayListSrcRec.MusicSrc = DAILY_FM_MIX
		}
	}
	if needReplay {
		/*sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
		// 歌曲续播的，该次语音的rspid清除掉
		RspId = nil*/
		//重复意图，直接切歌
		log.Println(log.WarningLog, "playNewHotDailyMusic, switch next song for some list id")
		base := ServerPlayerBase{RspId: serverData.RspId, InnerTts: serverData.InnerTts, InnerTtsUrl: serverData.InnerTtsUrl}
		nextMusic(schedule.PlayerArg{GloablePause: false, NotShowIcon: false, ServerPlayerBase: base})
	} else {
		err = commonPlayApi(commonPlayApiArg{
			prepare:            false,
			songId:             songId,
			songUrl:            songUrl,
			tts:                serverData.InnerTts,
			ttsUrl:             serverData.InnerTtsUrl,
			ttsNow:             true,
			needChangeNHDIndex: false,
			srcType:            PlayListSrcRec.MusicSrc,
			nextOrPreSong:      true,
			filterTts:          false,
			rspId:              serverData.RspId,
			action:             serverData.Action,
			actionSrc:          serverData.ActionSrc,
		})
		if err != nil {
			log.Println(log.ErrorLog, err.Error())
			return NHD_ERR_ACTION, err
		}
	}

	return musicAction, nil
}

func privateFMRepeat(serverData *ServerMusicList) bool {
	if serverData.ModeId == PlayListSrcRec.ModeId && serverData.MusicList.Type == PlayListSrcRec.MusicSrc &&
		serverData.ServerCmd != uint16(MSC_CMD_REPEAT) && serverData.CloudId == PlayListSrcRec.CloudId &&
		!serverData.IgnoreListRepeat {
		return true
	} else {
		return false
	}
}

/**
播放私人FM接口
*/
func playPrivateFM(serverData *ServerMusicList) (MusicAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	oldCurPlayingMode := CurPlayingMode
	if MODE_NULL != serverData.PlayMode {
		CurPlayingMode = serverData.PlayMode
	} else {
		// 默认：顺序播放
		CurPlayingMode = LIST_ORDER_CYCLE
	}
	log.Println(log.DebugLog, "playPrivateFM:", CurPlayingMode, "  serverMode:", serverData.PlayMode)

	var replace bool
	var index MusicLenType = 0
	var musicAction MusicAction

	//目前播放FM还是重复播放FM意图都顶替原来的list
	replace = true
	NormalListMap[NORMAL_LIST_PAGE].Init()

	if nil != NormalListMap[NORMAL_LIST_PAGE].CurPos {
		index = NormalListMap[NORMAL_LIST_PAGE].CurPos.Value.(*PageNode).PageIndex * FM_SIZE
	} else {
		if 0 != NormalListMap[NORMAL_LIST_PAGE].BackIndex {
			index = NormalListMap[NORMAL_LIST_PAGE].BackIndex
			NormalListMap[NORMAL_LIST_PAGE].BackIndex = 0
		}
	}

	var needUpdate bool = true
	var songId *SongInfo = nil
	var songUrl *string = nil
	var needReplay = false
	if isNeedRestart() && privateFMRepeat(serverData) {
		// 恢复播放
		//log.Println(log.DebugLog, "replay playPrivateFM")
		//needReplay = true //全新开始，不是replay
		//sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
		//musicAction = FM_LESS_T1
		//needUpdate = false //全新开始，需要update
		log.Println(log.DebugLog, "some FM repeat cmd, now get fm list again..")

		if oldCurPlayingMode != CurPlayingMode {
			tmpPlayingMode := CurPlayingMode
			CurPlayingMode = oldCurPlayingMode
			ResetUnPlayListCurNode()
			CurPlayingMode = tmpPlayingMode
		}
	}

	// 播放推送过来的歌曲信息
	songId, songUrl = getCanPlaySong(serverData)
	musicAction = FM_MORE_T1

	if !needUpdate {
		log.Println(log.DebugLog, "playPrivateFM not needUpdate")
	} else {
		// 更新播放列表和page信息
		UpdatePlayListAndPage(serverData, index, replace)
		if nil != songId {
			LocalMusicList[GetMusicListIndex()].Search(songId, true)
		}
		// 更新当前播放列表来源信息
		UpdatePlayListSrcRec(serverData, true)
	}

	log.Println(log.DebugLog, "FM play start with songId:", songId)

	if needReplay {
		/*sendMusicCmd((PlayerType_Wifi, CMD_PLAYER_PLAY)
		// 歌曲续播的，该次语音的rspid清除掉
		RspId = nil*/
		//重复意图，直接切歌
		log.Println(log.WarningLog, "playPrivateFM, switch next song for some list id")
		base := ServerPlayerBase{RspId: serverData.RspId, InnerTts: serverData.InnerTts, InnerTtsUrl: serverData.InnerTtsUrl}
		nextMusic(schedule.PlayerArg{GloablePause: false, NotShowIcon: false, ServerPlayerBase: base})
	} else {
		err = commonPlayApi(commonPlayApiArg{
			prepare:            false,
			songId:             songId,
			songUrl:            songUrl,
			tts:                serverData.InnerTts,
			ttsUrl:             serverData.InnerTtsUrl,
			ttsNow:             true,
			needChangeNHDIndex: false,
			srcType:            PlayListSrcRec.MusicSrc,
			nextOrPreSong:      true,
			filterTts:          false,
			rspId:              serverData.RspId,
			action:             serverData.Action,
			actionSrc:          serverData.ActionSrc,
		})
		if err != nil {
			log.Println(log.ErrorLog, err.Error())
			return FM_ERR_ACTION, err
		}
	}

	return musicAction, nil
}

func normalListMusicRepeat(modeId int64, id *string, srcType MusicSrcType, serverCmd uint16,
	cloudId string, ignoreRepeat bool) bool {
	if modeId == PlayListSrcRec.ModeId &&
		(nil != id && nil != PlayListSrcRec.MusicListId && len(*id) > 0 && *id == *PlayListSrcRec.MusicListId) &&
		srcType == PlayListSrcRec.MusicSrc && serverCmd != uint16(MSC_CMD_REPEAT) && cloudId == PlayListSrcRec.CloudId &&
		!ignoreRepeat {
		log.Printf(log.DebugLog, "playListSrcRec:%+v\n", PlayListSrcRec)
		log.Println(log.DebugLog, "modeId:", modeId, ", id =", id, ", srcType=", srcType, ", serverCmd = ", serverCmd, ", cloudId=", cloudId, ", ignoreRepeat = ", ignoreRepeat)
		return true
	} else {
		return false
	}
}

/**
e 的格式为：*ServerMusicList 或者 *ServerMusicLists
*/
func playNormalListMusic(replace bool, e interface{}) (MusicAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var id *string
	var updateTime uint64
	var musicAction MusicAction
	var mode MusicPlayMode
	var srcType MusicSrcType
	var serverCmd uint16
	var modeIdTmp int64
	var tts, ttsUrl string = "", ""
	var rspId string = ""
	var cloudId string
	var ignoreRepeat bool = false
	var action, actionsrc string = "", ""
	var bizType BizTypeDef
	var index MusicLenType = 0

	switch eVal := e.(type) {
	case *ServerMusicList:
		fmt.Printf("params:%+v\n", *(eVal))
		id = &(eVal.MusicList.Id)

		updateTime = eVal.MusicList.UpdateTime
		mode = eVal.PlayMode
		srcType = eVal.MusicList.Type
		serverCmd = eVal.ServerCmd
		modeIdTmp = eVal.ModeId
		tts = eVal.InnerTts
		ttsUrl = eVal.InnerTtsUrl
		rspId = eVal.RspId
		cloudId = eVal.CloudId
		ignoreRepeat = eVal.IgnoreListRepeat
		action = eVal.Action
		actionsrc = eVal.ActionSrc
		bizType = eVal.MusicList.BizType
		index = eVal.MusicList.Offset

	case *ServerMusicLists:
		fmt.Printf("params:%+v\n", *eVal)
		id = &(eVal.MusicLists[0].Id)

		updateTime = eVal.MusicLists[0].UpdateTime
		mode = eVal.PlayMode
		srcType = eVal.MusicLists[0].Type

		serverCmd = eVal.ServerCmd
		modeIdTmp = eVal.ModeId
		tts = eVal.InnerTts
		ttsUrl = eVal.InnerTtsUrl
		rspId = eVal.RspId
		cloudId = eVal.CloudId
		ignoreRepeat = eVal.IgnoreListRepeat
		action = eVal.Action
		actionsrc = eVal.ActionSrc
		bizType = eVal.MusicLists[0].BizType
		index = eVal.MusicLists[0].Offset
	default:
		log.Println(log.WarningLog, "err params")
		return NORMAL_ERR_ACTION, Normal_ERR_ACTION
	}

	oldCurPlayingMode := CurPlayingMode
	if MODE_NULL != mode {
		CurPlayingMode = mode
	} else {
		switch bizType {
		case BIZ_TYPE_NATURAL_SOUND:
			// 默认：顺序播放
			CurPlayingMode = LIST_ORDER_PLAY
		case BIZ_TYPE_RADIO_STATION:
			// 默认：顺序播放
			CurPlayingMode = LIST_ORDER_PLAY
		default:
			// 默认：随机播放
			CurPlayingMode = LIST_RANDOM_CYCLE
		}

	}
	log.Println(log.DebugLog, "playNormalListMusic:", CurPlayingMode, "  serverMode:", mode)

	var needUpdate bool = true
	var songId *SongInfo = nil
	var songUrl *string = nil
	var needReplay bool = false
	if isNeedRestart() && normalListMusicRepeat(modeIdTmp, id, srcType, serverCmd, cloudId, ignoreRepeat) && bizType != BIZ_TYPE_NATURAL_SOUND { // 歌单id相同时/自然声也不走重复逻辑
		// 时间小于T1，恢复播放
		log.Println(log.DebugLog, "replay playNormalListMusic")
		if updateTime == PlayListSrcRec.UpdateTime && !PlayListSrcRec.HasInsert {
			needReplay = true
			log.Println(log.DebugLog, "play list update time not change and no song insert in list, so replay the list before.")
			// 更新时间不变，歌单不需要替换
			needUpdate = false
			musicAction = NORMAL_LESS_T1_NOT_UPDATE

			if oldCurPlayingMode != CurPlayingMode {
				tmpPlayingMode := CurPlayingMode
				CurPlayingMode = oldCurPlayingMode
				ResetUnPlayListCurNode()
				CurPlayingMode = tmpPlayingMode
			}
		} else {
			// 更新时间变，需要替换歌单
			musicAction = NORMAL_LESS_T1_UPDATE

			//forcePlayCur = true //现在产品的逻辑是重复意图切换下一首
			log.Println(log.DebugLog, "play list changed, will replace the list, updateTime:", updateTime, ", PlayListSrcRec.UpdateTime:", PlayListSrcRec.UpdateTime)
			songId, songUrl = getCanPlaySong(e)
		}
	} else {
		// 时间大于等于T1，或者歌单id变化
		if normalListMusicRepeat(modeIdTmp, id, srcType, serverCmd, cloudId, ignoreRepeat) &&
			updateTime == PlayListSrcRec.UpdateTime &&
			nil != LocalMusicList[GetMusicListIndex()].GetCurPos() &&
			!PlayListSrcRec.HasInsert { // 没有单曲插入过
			// 歌单id相同，且更新时间不变，且当前播放列表不为空，歌单不需要替换
			needUpdate = false
			// 拉取当前歌曲播放
			songId = LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo)
			musicAction = NORMAL_MORE_T1_NOT_UPDATE

			if oldCurPlayingMode != CurPlayingMode {
				tmpPlayingMode := CurPlayingMode
				CurPlayingMode = oldCurPlayingMode
				ResetUnPlayListCurNode()
				CurPlayingMode = tmpPlayingMode
			}
		} else {
			// 歌单id不同，或者更新时间变，或者当前播放列表空，需要替换歌单
			songId, songUrl = getCanPlaySong(e)
			musicAction = NORMAL_MORE_T1_UPDATE
		}
	}

	if !needUpdate {
		log.Println(log.DebugLog, "playNormalListMusic not needUpdate")
	} else {
		// 更新页信息,且第一次推下来
		NormalListMap[NORMAL_LIST_PAGE].Init()

		if 0 != NormalListMap[NORMAL_LIST_PAGE].BackIndex {
			index = NormalListMap[NORMAL_LIST_PAGE].BackIndex
			NormalListMap[NORMAL_LIST_PAGE].BackIndex = 0
		}
		UpdatePlayListAndPage(e, index, replace)

		// 定位当前歌曲记录节点到播放歌曲位置
		if nil != songId {
			LocalMusicList[GetMusicListIndex()].Search(songId, true)
		}
		// 更新当前播放列表来源信息
		UpdatePlayListSrcRec(e, true)
		log.Println(log.DebugLog, "playNormalListMusic play list updated")
	}

	if needReplay {
		/*sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
		// 歌曲续播的，该次语音的rspid清除掉
		RspId = nil*/
		//重复意图，直接切歌
		log.Println(log.WarningLog, "playNormalListMusic, switch next song for some list id, tts:", tts, ", ttsurl:", ttsUrl)
		base := ServerPlayerBase{RspId: rspId, InnerTts: tts, InnerTtsUrl: ttsUrl}
		nextMusic(schedule.PlayerArg{GloablePause: false, NotShowIcon: false, ServerPlayerBase: base})
	} else {
		err = commonPlayApi(commonPlayApiArg{
			prepare:            false,
			songId:             songId,
			songUrl:            songUrl,
			tts:                tts,
			ttsUrl:             ttsUrl,
			ttsNow:             true,
			needChangeNHDIndex: false,
			srcType:            PlayListSrcRec.MusicSrc,
			nextOrPreSong:      true,
			filterTts:          false,
			rspId:              rspId,
			action:             action,
			actionSrc:          actionsrc,
		})
		if err != nil {
			log.Println(log.ErrorLog, err.Error())
			return NORMAL_ERR_ACTION, err
		}
	}
	return musicAction, nil
}

func playSingleMusic(serverData *ServerMusicList) MusicAction {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if PlayListSrcRec.BizType != BIZ_TYPE_MUSIC {
		log.Println(log.WarningLog, "playSingleMusic: will reset play list since last BizType:", PlayListSrcRec.BizType)
		PlayListSrcRec.BizType = serverData.MusicList.BizType
		PlayListSrcRec.ExtraData = serverData.ExtraData
		PlayListSrcRec.Order = serverData.MusicList.Order

		// 清除歌曲过滤列表
		LocalMusicFilter.Init()
		NormalListMap[NORMAL_LIST_PAGE].Init()
		LocalMusicList[CUR_MUSIC_LIST].Init()
		LocalMusicList[RANDOM_MUSIC_LIST].Init()

		// 更新当前播放列表来源信息
		PlayListSrcRec.BizType = serverData.MusicList.BizType
		PlayListSrcRec.MusicSrc = MUSIC_LIST_NULL
		CurPlayingMode = LIST_ORDER_CYCLE
		log.Println(log.DebugLog, "change to Single Music play")
	}

	if MODE_NULL != serverData.PlayMode {
		CurPlayingMode = serverData.PlayMode
	}
	log.Println(log.DebugLog, "playSingleMusic:", CurPlayingMode, "  serverMode:", serverData.PlayMode)

	log.Println(log.DebugLog, "wifiPlayerSt:", wifiPlayerSt.ContinuousTaskSt)
	var musicAction MusicAction
	if serverData.IsMatch && PlayerHander[PlayerType_Wifi].IsPlaying() && serverData.ServerCmd != uint16(MSC_CMD_REPEAT) {
		// 歌曲播放中
		if nil != wifiPlayerSt.MusicId {
			if *wifiPlayerSt.MusicId != serverData.MusicList.SongList[0].Id {
				// 与当前播放歌曲不同
				// 处理路径与音乐未播放一样
			} else {
				//prd修改为每次播放单曲都从头开始，目前这段代码暂时注释掉
				/*				mscToastTts(TTS_ALREADY_THE_SONG, CONST_AUDIO_ID, true, false, nil)

								// 歌曲续播的，该次语音的rspid清除掉
								RspId = nil
								return SIGNAL_PLAYING*/
				log.Println(log.WarningLog, "playSingleMusic, replay from beginning")
			}
		}
	}

	_, _, hasThisSong := LocalMusicList[GetMusicListIndex()].Search(&serverData.MusicList.SongList[0], true)
	serverData.MusicList.SongList[0].Single = true
	if !hasThisSong {
		LocalMusicList[CUR_MUSIC_LIST].Push(&serverData.MusicList.SongList[0], true)
		LocalMusicList[RANDOM_MUSIC_LIST].Push(&serverData.MusicList.SongList[0], true)
		// 插入过单曲，此时认为歌单不会与任何歌单同
		PlayListSrcRec.HasInsert = true
		musicAction = SIGNAL_NOT_HAS_SONG
	} else {
		LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo).Single = true
		// 插入过单曲，此时认为歌单不会与任何歌单同
		PlayListSrcRec.HasInsert = true

		musicAction = SIGNAL_HAS_SONG
	}

	err = commonPlayApi(commonPlayApiArg{
		prepare:            false,
		songId:             &serverData.MusicList.SongList[0],
		songUrl:            &serverData.MusicList.Url,
		tts:                serverData.InnerTts,
		ttsUrl:             serverData.InnerTtsUrl,
		ttsNow:             true,
		needChangeNHDIndex: false,
		srcType:            SINGLE_SONG,
		nextOrPreSong:      true,
		filterTts:          false,
		rspId:              serverData.RspId,
		action:             serverData.Action,
		actionSrc:          serverData.ActionSrc,
	})
	if err != nil {
		log.Println(log.ErrorLog, err.Error())
		return SIGNAL_ERR_ACTION
	}

	return musicAction
}

/**
播放推荐接口
*/
func playRcmd(serverData *ServerMusicList) error {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	log.Println(log.DebugLog, "playRcmd")

	if (DailyPushPlayedInfo.HasPlayed && isDateEqual(DailyPushPlayedInfo.PlayedTime, time.Now())) ||
		true { // 现在推荐命令直接播放私人FM，不再播放每日歌曲推荐
		log.Println(log.DebugLog, "Daily rcmd has played")
		// 播放过,直接播放私人FM
		serverData.MusicList.Type = PRIVATE_FM
		// 开始插入fm，重置page
		NormalListMap[NORMAL_LIST_PAGE].Init()

		// 调用私人fm接口播放
		playPrivateFM(serverData)
	} else {
		if UintTest {
			log.Println(log.DebugLog, "Daily rcmd has not played")
		}
		// 未播放过，拉取每日歌曲推荐播放
		dailyList, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
			Src:     DAILY_PUSH,
			Id:      nil,
			Params:  nil,
			Start:   0,
			Size:    PAGE_SIZE,
			WantTts: true,

			Oftag:        &serverData.Oftag,
			OftagForShow: &serverData.OftagForShow,
			Mode:         CurPlayingMode,
			CloudId:      serverData.CloudId,
			ModeId:       serverData.ModeId,
		})
		//dailyList, err := getMusicList(PlayerType_Wifi, DAILY_PUSH, nil, nil, 0, PAGE_SIZE,
		//	true, CurPlayingMode)
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return err
		}

		indexOffset := GetPageIndexFromIndex(NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListIndex, PAGE_SIZE)

		if dailyList.MusicList.UpdateTime == NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListTimeStamp {
			// 未更新
			if UintTest {
				log.Println(log.DebugLog, "daily not update")
			}

			if NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListIndex >= (MusicLenType(len(dailyList.MusicList.SongList))) {
				// 播放歌曲位置超出了当前页, 目前每日歌曲推荐只有一页，因此不会走到该分支
				startIndex := GetPageIndexFromIndex(NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListIndex, PAGE_SIZE)

				daily, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
					Src:     DAILY_PUSH,
					Id:      nil,
					Params:  nil,
					Start:   startIndex,
					Size:    PAGE_SIZE,
					WantTts: true,

					Oftag:        &serverData.Oftag,
					OftagForShow: &serverData.OftagForShow,
					Mode:         CurPlayingMode,
					CloudId:      serverData.CloudId,
					ModeId:       serverData.ModeId,
				})
				//daily, err := getMusicList(PlayerType_Wifi, DAILY_PUSH, nil, nil, startIndex, PAGE_SIZE,
				//	true, CurPlayingMode)
				if nil != err {
					log.Println(log.ErrorLog, err.Error())
					return err
				}
				if NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListIndex != (daily.MusicList.Index + indexOffset) {
					// 获取本地存储位置歌曲的播放信息
					dailyList.MusicList.Index = NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListIndex - indexOffset
					song, err := getSongPlayInfo(PlayerType_Wifi,
						&(dailyList.MusicList.SongList[dailyList.MusicList.Index].Id), true)
					if nil != err {
						log.Println(log.ErrorLog, err.Error())
						return err
					}
					dailyList.MusicList.Url = song.SongPlay.Url
				} else {
					// 本地位置和服务器推送的url索引相同，直接播放即可
				}

			} else {
				if NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListIndex != dailyList.MusicList.Index {
					// 本地位置和服务器推送的url索引不同，需要再次到服务器拉取
					song, err := getSongPlayInfo(PlayerType_Wifi,
						&(dailyList.MusicList.SongList[NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListIndex].Id), true)
					if nil != err {
						log.Println(log.ErrorLog, err.Error())
						return err
					}
					dailyList.MusicList.Index = NewHotDailyRec[DAILY_PUSH-NEW_TOPLIST].MusicListIndex
					dailyList.MusicList.Url = song.SongPlay.Url

				} else {
					// 本地位置和服务器推送的url索引相同，直接播放即可
				}
			}
		} else {
			// 更新过
			if UintTest {
				log.Println(log.DebugLog, "daily update")
			}
		}
		playNewHotDailyMusic(dailyList, true)
	}

	return nil
}

func UpdatePlayListAndPage(e interface{}, index MusicLenType, replace bool) {
	defer func() {
		if playerRecoverLevel <= HIGH_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var songs *[]SongInfo
	var lenType MusicLenType = 0
	var topIndex MusicLenType
	var hasMore bool = false // 查看歌曲是否还有更多
	var srcType MusicSrcType

	switch eVal := e.(type) {
	case *ServerMusicList:
		songs = &(eVal.MusicList.SongList)
		lenType = MusicLenType(len(e.(*ServerMusicList).MusicList.SongList))
		topIndex = MusicLenType(eVal.MusicList.Index)
		hasMore = eVal.MusicList.HasMore

		srcType = eVal.MusicList.Type

		log.Println(log.DebugLog, "Has more musics:", eVal.MusicList.HasMore)
	case *ServerMusicLists:
		songs = &(eVal.MusicLists[0].SongList)
		lenType = MusicLenType(len(e.(*ServerMusicLists).MusicLists[0].SongList))
		topIndex = MusicLenType(eVal.MusicLists[0].Index)
		hasMore = eVal.MusicLists[0].HasMore

		srcType = eVal.MusicLists[0].Type

		log.Println(log.DebugLog, "Has more musics:", eVal.MusicLists[0].HasMore)
		log.Println(log.DebugLog, "Has more Lists:", PlayListSrcRec.HasMore)
	default:
		log.Println(log.WarningLog, "err params")
		return
	}

	var pageSize MusicLenType
	if MUSIC_SHARE_RCMD == srcType {
		pageSize = MUSIC_SHARE_SIZE
	} else if PRIVATE_FM == srcType {
		pageSize = FM_SIZE
	} else {
		pageSize = PAGE_SIZE
	}

	pageNode := GetPageFromIndex(index, pageSize)
	err := NormalListMap[NORMAL_LIST_PAGE].Push(pageNode)
	if nil != err {
		log.Println(log.WarningLog, err.Error(), "   pageNode:", pageNode)
	}
	NormalListMap[NORMAL_LIST_PAGE].HasMore = hasMore
	if !hasMore {
		// 获取最大的页面值
		NormalListMap[NORMAL_LIST_PAGE].MaxPage = pageNode.PageIndex
		log.Println(log.DebugLog, "MaxPage:", pageNode.PageIndex)
	}

	if NormalListMap[NORMAL_LIST_PAGE].MaxPage == MusicLenType(NormalListMap[NORMAL_LIST_PAGE].List.Len()) {
		NormalListMap[NORMAL_LIST_PAGE].HasMore = false
		NormalListMap[NORMAL_LIST_PAGE].MaxPage = 0
		log.Println(log.DebugLog, "reset music hasMore")
	}

	if replace {
		log.Println(log.DebugLog, "replace list, lenType:", lenType, "  topIndex:", topIndex)
		// 更新播放列表信息
		LocalMusicList[CUR_MUSIC_LIST].ReplaceBySlice(songs, lenType, 0, topIndex)
		LocalMusicList[RANDOM_MUSIC_LIST].ReplaceBySlice(songs, lenType, 0, topIndex)
	} else {
		var offset MusicLenType = GetPageIndexFromIndex(index, pageSize)
		log.Println(log.DebugLog, "add list, lenType:", lenType, "  offset:", offset,
			"  topIndex:", topIndex)
		// 更新播放列表信息
		LocalMusicList[CUR_MUSIC_LIST].AddBySlice(songs, lenType, 0, topIndex)
		LocalMusicList[RANDOM_MUSIC_LIST].AddBySlice(songs, lenType, 0, topIndex)
	}
}

/**
forceChangePlayListSrcRec : 如果e的类型为ServerMusicList时有效，否则忽略
*/
func UpdatePlayListSrcRec(e interface{}, forceChangePlayListSrcRec bool) {
	defer func() {
		if playerRecoverLevel <= HIGH_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	PlayListSrcRec.HasInsert = false
	switch eVal := e.(type) {
	case *ServerMusicList:
		PlayListSrcRec.MusicListId = &(eVal.MusicList.Id)
		//服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
		if eVal.ModeId != -1 {
			PlayListSrcRec.TagName = &(eVal.Oftag)
			PlayListSrcRec.TagNameShow = &eVal.OftagForShow
			PlayListSrcRec.UpdateTime = eVal.MusicList.UpdateTime
			PlayListSrcRec.CloudId = eVal.CloudId
			PlayListSrcRec.ModeId = eVal.ModeId
			PlayListSrcRec.ExtraData = eVal.ExtraData
			PlayListSrcRec.BizType = eVal.MusicList.BizType
			PlayListSrcRec.Order = eVal.MusicList.Order
		}

		if len(eVal.MusicList.ArtistId) > 0 {
			PlayListSrcRec.ArtistId = &(eVal.MusicList.ArtistId)
		}

		if forceChangePlayListSrcRec {
			PlayListSrcRec.MusicSrc = eVal.MusicList.Type
			PlayListSrcRec.HasMore = false

			PlayListSrcRec.MusicListIdIndex = INVALID_MUSIC_LEN
			PlayListSrcRec.MusicListIds = nil
			PlayListSrcRec.MusicListIdsLen = INVALID_MUSIC_LEN
			PlayListSrcRec.MusicListMaxLen = INVALID_MUSIC_LEN
		}
	case *ServerMusicLists:
		PlayListSrcRec.MusicListId = &(eVal.MusicLists[0].Id)
		PlayListSrcRec.MusicListIdIndex = 0

		//服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
		if eVal.ModeId != -1 {
			PlayListSrcRec.TagName = &(eVal.Oftag)
			PlayListSrcRec.TagNameShow = &eVal.OftagForShow
			PlayListSrcRec.CloudId = eVal.CloudId
			PlayListSrcRec.ModeId = eVal.ModeId
			PlayListSrcRec.BizType = eVal.MusicLists[0].BizType
			PlayListSrcRec.Order = eVal.MusicLists[0].Order
		}

		PlayListSrcRec.ArtistId = &(eVal.MusicLists[0].ArtistId)

		if playUserLists {
			// 如果当前播放的是我的歌单
			PlayListSrcRec.MusicSrc = USER_LISTS
		} else {
			PlayListSrcRec.MusicSrc = eVal.MusicLists[0].Type
		}
		PlayListSrcRec.UpdateTime = e.(*ServerMusicLists).MusicLists[0].UpdateTime
		PlayListSrcRec.HasMore = e.(*ServerMusicLists).HasMore

		PlayListSrcRec.MusicListIdsLen = MusicLenType(len(eVal.MusicLists))
		var listIds []ListIds = make([]ListIds, PlayListSrcRec.MusicListIdsLen)
		for k, v := range eVal.MusicLists {
			listIds[k].Id = &(eVal.MusicLists[k].Id)
			listIds[k].UpdateTime = v.UpdateTime
		}
		PlayListSrcRec.MusicListIds = &listIds

		log.Println(log.DebugLog, "MusicListIdIndex: ", PlayListSrcRec.MusicListIdIndex,
			" PlayListSrcRec.MusicListIdsLen:", PlayListSrcRec.MusicListIdsLen)
		for k, v := range *PlayListSrcRec.MusicListIds {
			log.Println(log.DebugLog, k, ": ", *v.Id, " updateTime:", v.UpdateTime)
		}

		listIndex += PlayListSrcRec.MusicListIdsLen

		if PlayListSrcRec.HasMore {
			PlayListSrcRec.MusicListMaxLen = listIndex
		}
	default:
		log.Println(log.WarningLog, "err params")
	}
	log.Println(log.DebugLog, "MusicListId:", *PlayListSrcRec.MusicListId, "  MusicListIdIndex:", PlayListSrcRec.MusicListIdIndex,
		"  MusicSrc:", PlayListSrcRec.MusicSrc, "  UpdateTime:", PlayListSrcRec.UpdateTime, ", bizType:", PlayListSrcRec.BizType)
}

func prepareNextMusic() error {
	curMusicList := GetMusicListIndex()

	CurPosRecTmp = LocalMusicList[curMusicList].GetCurPos()
	defer func() {
		if nil != CurPosRecTmp {
			log.Println(log.DebugLog, "before restore:", LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).Id)
			curPosNew = LocalMusicList[curMusicList].GetCurPos()
			LocalMusicList[curMusicList].SetCurPos(CurPosRecTmp)
			log.Println(log.DebugLog, "after restore:", LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).Id)
			CurPosRecTmp = nil
		}

		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var songId *SongInfo = nil
	var songUrl *string = nil
	var needNHDIndex bool = false
	var tts, ttsUrl string = "", ""
	var filterTts bool = true
	var needPlayCopyrightTTs bool = false
	var isRepeat bool = false

	if PlayListSrcRec.CopyrightTts.TtsType != TTS_TYPE_NULL {
		needPlayCopyrightTTs = true
	}
	_, isNewHotDailyPlaying := isNewHotDailySrc()
	if SINGLE_CYCLE == CurPlayingMode || forcePlayCur {
		// 播放当前,本地不存储url，根据id去服务器拉取
		songId = LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo)
		forcePlayCur = false
	} else {
		action := getPrepareNextAction()
	pullNextListByIdAgain:
		var index MusicLenType = 0
		switch action {
		case ACTION_NULL:
			// 当前列表空
		case ACTION_PLAY_DIRECT:
			LocalMusicList[curMusicList].GetValidNext()
			songId = LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo)
		case ACTION_ADD_LIST_FM, ACTION_ADD_LIST_MUSIC_SHARE:
			var src MusicSrcType = PRIVATE_FM
			var pageSize MusicLenType = FM_SIZE
			var replace bool = false
			if ACTION_ADD_LIST_MUSIC_SHARE == action {
				src = MUSIC_SHARE_RCMD
				pageSize = MUSIC_SHARE_SIZE
				replace = true
			}
			// 拉取私人FM
			list, err := GetMusicListNew(PlayerType_Wifi, false, http.MusicListServerType{
				Src:     src,
				Id:      nil,
				Params:  nil,
				Start:   0,
				Size:    pageSize,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})
			//list, err := getMusicList(PlayerType_Wifi, src, nil, nil, 0, pageSize,
			//	false, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return err
			}
			if val, err := NormalListMap[NORMAL_LIST_PAGE].GetLast(); nil == err {
				index = val.Value.(*PageNode).PageIndex * pageSize
			}
			UpdatePlayListAndPage(list, index, replace)

			songId, songUrl = getCanPlaySong(list)
			if len(list.PullTts) > 0 || len(list.PullTtsUrl) > 0 {
				tts = list.PullTts
				ttsUrl = list.PullTtsUrl
				filterTts = false
			}
		case ACTION_NEXT_PAGE_REPLACE_LIST, ACTION_MISS_PAGE_REPLACE_LIST, ACTION_ADD_FM: // 还有分页 /有缺失页 /最后一页，且当前是播放推荐命令
			var replace bool = true
			var pageSize MusicLenType = PAGE_SIZE
			if ACTION_NEXT_PAGE_REPLACE_LIST == action {
				// 还有剩余分页，拉取分页，不重置分页信息
				if val, err := NormalListMap[NORMAL_LIST_PAGE].GetLast(); nil == err {
					index = val.Value.(*PageNode).PageIndex * PAGE_SIZE
				}
			} else if ACTION_MISS_PAGE_REPLACE_LIST == action {
				// 有缺失的分页，拉取分页，不重置分页信息
				index = (NormalListMap[NORMAL_LIST_PAGE].GetMissPage() - 1) * PAGE_SIZE
			} else {
				NormalListMap[NORMAL_LIST_PAGE].Init()

				// 当前是在播放推荐，开始拉取FM
				PlayListSrcRec.MusicSrc = PRIVATE_FM
				pageSize = FM_SIZE
				replace = false

				dailyPushPlayStChange()
			}

			srcType := getMusicSrcType(PlayerType_Wifi)

			list, err := GetMusicListNew(PlayerType_Wifi, false, http.MusicListServerType{
				Src:     srcType,
				Id:      getServiceId(srcType),
				Params:  getServiceParams(srcType),
				Start:   index,
				Size:    pageSize,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,

				ExtraData: PlayListSrcRec.ExtraData,
				BizType:   PlayListSrcRec.BizType,
				Order:     PlayListSrcRec.Order,
				AlbumID:   curSongInfo.AlbumId,
			})
			//list, err := getMusicList(PlayerType_Wifi, srcType, getServiceId(srcType),
			//	getServiceParams(srcType), index, pageSize, false, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return err
			}
			UpdatePlayListAndPage(list, index, replace)

			songId, songUrl = getCanPlaySong(list)
		case ACTION_ONLY_ONE_LIST_REPLACE_LIST: // 有且只有一个歌单
			back, _ := NormalListMap[NORMAL_LIST_PAGE].GetLast()
			if nil == back || 1 == back.Value.(*PageNode).PageIndex ||
				MUSIC_LIST_NULL == PlayListSrcRec.MusicSrc {
				if CurPlayingMode != LIST_ORDER_PLAY {
					tmpCurPos, err := LocalMusicList[curMusicList].GetFirst()
					if nil != err {
						log.Println(log.ErrorLog, err.Error())
						return err
					}
					LocalMusicList[curMusicList].SetCurPos(tmpCurPos)

					if LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).CanPlay {

					} else {
						LocalMusicList[curMusicList].GetValidNext()
					}

					songId = LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo)
				}

				log.Println(log.DebugLog, "loop list for only one list, one page") //开始重复播放，有且只有一个歌单，只有一页
				isRepeat = true
			} else {
				if CurPlayingMode != LIST_ORDER_PLAY {
					// 拉取第一页
					list, err := GetMusicListNew(PlayerType_Wifi, false, http.MusicListServerType{
						Src:     PlayListSrcRec.MusicSrc,
						Id:      getServiceId(PlayListSrcRec.MusicSrc),
						Params:  getServiceParams(PlayListSrcRec.MusicSrc),
						Start:   index,
						Size:    PAGE_SIZE,
						WantTts: true,

						Oftag:        PlayListSrcRec.TagName,
						OftagForShow: PlayListSrcRec.TagNameShow,
						Mode:         CurPlayingMode,
						CloudId:      PlayListSrcRec.CloudId,
						ModeId:       PlayListSrcRec.ModeId,

						ExtraData: PlayListSrcRec.ExtraData,
						BizType:   PlayListSrcRec.BizType,
						Order:     PlayListSrcRec.Order,
						AlbumID:   curSongInfo.AlbumId,
					})
					//list, err := getMusicList(PlayerType_Wifi, PlayListSrcRec.MusicSrc,
					//	getServiceId(PlayListSrcRec.MusicSrc),
					//	getServiceParams(PlayListSrcRec.MusicSrc), index, PAGE_SIZE, false, CurPlayingMode)
					if nil != err {
						log.Println(log.ErrorLog, err.Error())
						return err
					}
					NormalListMap[NORMAL_LIST_PAGE].Init()

					UpdatePlayListAndPage(list, index, true)

					songId, songUrl = getCanPlaySong(list)
				}

				log.Println(log.DebugLog, "loop list for only one list") //开始重复播放，有且只有一个歌单
				isRepeat = true
			}
		case ACTION_NEXT_LIST_REPLACE_LIST, ACTION_CYCLY_TO_FRONT_LIST_REPLACE_LIST: // 有下一个歌单 / 歌单播放完毕，没有更多，重新播放第一个歌单
			// 服务器下发的数据有歌单列表，肯定不是榜单、每日推荐
			if ACTION_NEXT_LIST_REPLACE_LIST == action {
				PlayListSrcRec.MusicListIdIndex += 1
			} else if ACTION_CYCLY_TO_FRONT_LIST_REPLACE_LIST == action {
				log.Println(log.DebugLog, "loop list for get front list")
				isRepeat = true
				if CurPlayingMode == LIST_ORDER_PLAY {
					break
				}
				//开始重复播放，重新播放第一个歌单
				PlayListSrcRec.MusicListIdIndex = 0
			}

			var listCnt MusicLenType = 0
		pullListByIdAgain:
			PlayListSrcRec.MusicListId = (*PlayListSrcRec.MusicListIds)[PlayListSrcRec.MusicListIdIndex].Id
			PlayListSrcRec.UpdateTime = (*PlayListSrcRec.MusicListIds)[PlayListSrcRec.MusicListIdIndex].UpdateTime
			//todo :需要判断当前歌单是否为空，如果空，需要继续拉取
			srcType := getMusicSrcType(PlayerType_Wifi)

			list, err := GetMusicListNew(PlayerType_Wifi, false, http.MusicListServerType{
				Src:     srcType,
				Id:      getServiceId(srcType),
				Params:  getServiceParams(srcType),
				Start:   0,
				Size:    PAGE_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})
			//list, err := getMusicList(PlayerType_Wifi, srcType, getServiceId(srcType),
			//	getServiceParams(srcType), 0, PAGE_SIZE, false, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				if MusicList_ERR_MUSICINFO_NULL == err {
					if listCnt < PlayListSrcRec.MusicListIdsLen {
						listCnt++
						log.Println(log.DebugLog, "list nil, try:", listCnt)
						PlayListSrcRec.MusicListIdIndex += 1
						if PlayListSrcRec.MusicListIdIndex >= PlayListSrcRec.MusicListIdsLen {
							PlayListSrcRec.MusicListIdIndex = 0
						}
						goto pullListByIdAgain
					}
				}
				return err
			}

			list.ModeId = -1 //服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
			UpdatePlayListSrcRec(list, false)

			NormalListMap[NORMAL_LIST_PAGE].Init()

			UpdatePlayListAndPage(list, 0, true)

			songId, songUrl = getCanPlaySong(list)

			//如果没有可以播放的歌曲，说明这个歌单全部无效，就开始切换下一个
			if songId == nil {
				if (PlayListSrcRec.MusicListIdIndex + 1) >= PlayListSrcRec.MusicListIdsLen {
					if playUserLists {
						// 当前播放的是我的歌单，在创建的歌单和收藏的歌单之间循环
						action = ACTION_USER_LIST_CYCLE
					} else {
						log.Println(log.DebugLog, "loop list for get front list")
						isRepeat = true
						if CurPlayingMode == LIST_ORDER_PLAY {
							break
						}

						// 歌单播放完毕，且后续没有歌单，重新播放第一个歌单
						action = ACTION_CYCLY_TO_FRONT_LIST_REPLACE_LIST
					}
				} else {
					//下一个歌单
					action = ACTION_NEXT_LIST_REPLACE_LIST
				}
				log.Println(log.DebugLog, "action:", action, "   PlayListSrcRec.MusicSrc:", PlayListSrcRec.MusicSrc)
				goto pullNextListByIdAgain
			}
		case ACTION_GET_NEW_LIST_REPLACE_LIST, ACTION_USER_LIST_CYCLE: // 歌单播放完毕，后续还有歌单
			// 服务器下发的数据有歌单列表，肯定不是榜单、每日推荐
			srcType := PlayListSrcRec.MusicSrc
			if playUserLists {
				if ACTION_USER_LIST_CYCLE == action {
					// 切换时，歌单index需要清零
					listIndex = 0
					if USER_LISTS_COLLECT == curUserListsType {
						log.Println(log.DebugLog, "loop list for get create user list")
						isRepeat = true
						if CurPlayingMode == LIST_ORDER_PLAY {
							break
						}
						srcType = USER_LISTS_CREATE
					} else {
						srcType = USER_LISTS_COLLECT
					}
					curUserListsType = srcType
				} else {
					srcType = curUserListsType
				}
			}
			list, err := GetMusicListsNew(PlayerType_Wifi, false, http.MusicListServerType{
				Src:     srcType,
				Id:      getServiceId(srcType),
				Params:  getServiceParams(srcType),
				Start:   listIndex,
				Size:    LIST_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})

			//list, err := getMusicLists(PlayerType_Wifi, srcType, getServiceId(srcType),
			//	getServiceParams(srcType), listIndex, LIST_SIZE, false, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return err
			}

			list.ModeId = -1 //服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
			UpdatePlayListSrcRec(list, true)

			if srcType != curUserListsType {
				log.Println(log.DebugLog, "loop list for no collect user list") //没有收藏的歌单,开始重复播放
				isRepeat = true
				if CurPlayingMode == LIST_ORDER_PLAY {
					break
				}
			}

			NormalListMap[NORMAL_LIST_PAGE].Init()

			UpdatePlayListAndPage(list, 0, true)

			songId, songUrl = getCanPlaySong(list)
		}

	}
	if isNewHotDailyPlaying {
		if !LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).Single {
			needNHDIndex = true
		}
	}

	//第一次循环播放时，播放copyright tts
	var isFirstRepeat bool = false
	if isRepeat {
		if CurPlayingMode == LIST_ORDER_PLAY {
			log.Println(log.DebugLog, "play list end because list order play mode")
			ttsNoMusic = true
			ttsMusicUuid = curSongInfo.Id
			ttsInfo = TTS_TEXT_ORDER_PLAY_END_FLAG
			return nil
		}
		if needPlayCopyrightTTs == true && len(tts) == 0 && len(ttsUrl) == 0 {
			log.Printf(log.ErrorLog, "loop list, play copyrightTts: %+v\n", PlayListSrcRec.CopyrightTts)
			var tts2Server string = ""
			switch PlayListSrcRec.CopyrightTts.TtsType {
			case TTS_TYPE_TEXT:
				tts = PlayListSrcRec.CopyrightTts.Tts
				tts2Server = tts
				if len(tts) != 0 {
					isFirstRepeat = true
				}
			case TTS_TYPE_URL:
				ttsUrl = PlayListSrcRec.CopyrightTts.Tts
				tts2Server = PlayListSrcRec.CopyrightTts.Remark
				if len(ttsUrl) != 0 {
					isFirstRepeat = true
				}
			}

			if isFirstRepeat == true && tts2Server != "" {
				//push tts to server
				http.InformMsgToServer(http.Type_Inform_TtsDelay, tts2Server)
			}
			PlayListSrcRec.CopyrightTts.TtsType = 0
		}
	}

	return commonPlayApi(commonPlayApiArg{
		prepare:            true,
		songId:             songId,
		songUrl:            songUrl,
		tts:                tts,
		ttsUrl:             ttsUrl,
		ttsNow:             false,
		needChangeNHDIndex: needNHDIndex,
		srcType:            PlayListSrcRec.MusicSrc,
		nextOrPreSong:      true,
		filterTts:          filterTts,
		rspId:              "",
		action:             MUSIC_ACTION_NEXT,
		actionSrc:          MUSIC_ACTION_SRC_LOCAL,
		IsFirstRepeat:      isFirstRepeat,
	})
}

/**
上一曲， 只在当前歌单中切换
*/
func prevMusic(arg interface{}) (MusicChangeAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	var rspId string = ""
	var tts, ttsUrl string = "", ""
	//var ttsR *string = nil
	var musicAction, musicActionSrc string = "", ""
	switch eVal := arg.(type) {
	case schedule.PlayerArg:
		rspId = eVal.RspId
		tts = eVal.InnerTts
		ttsUrl = eVal.InnerTtsUrl
		//ttsR = eVal.TtsNextPre
		musicAction = eVal.Action
		musicActionSrc = eVal.ActionSrc
	default:

	}

	curMusicList := GetMusicListIndex()
	if nil == LocalMusicList[curMusicList].GetCurPos() {
		//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, List_ERR_NULL.Error(), CONST_AUDIO_ID, AdSrcType_TTS,
		//	TTS_LEVEL_1, true, false)
		mscToastTts(List_ERR_NULL.Error(), CONST_AUDIO_ID, true, false, nil)
		return ACTION_NULL, List_ERR_NULL
	}
	action := getNextPrevAction(false)
	if UintTest {
		return action, nil
	}

	var songId *SongInfo = nil
	var songUrl *string = nil
	var needNHDIndex bool = false
	var filterTts bool = true
	_, isNewHotDailyPlaying := isNewHotDailySrc()

	if (SINGLE_CYCLE == CurPlayingMode && false) || forcePlayCur { // 上/下一曲忽略单曲循环
		// 单曲循环时，直接拿当前的音乐节点播放
		songId = LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo)
		forcePlayCur = false
	} else {
		var index MusicLenType
		switch action {
		case ACTION_NULL:
			// 当前列表空
		case ACTION_PLAY_DIRECT:
			LocalMusicList[curMusicList].GetValidPre()
		case ACTION_ADD_LIST_MUSIC_SHARE:
			// 拉取云村推荐
			list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     MUSIC_SHARE_RCMD,
				Id:      nil,
				Params:  nil,
				Start:   0,
				Size:    MUSIC_SHARE_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})
			//list, err := getMusicList(PlayerType_Wifi, MUSIC_SHARE_RCMD, nil, nil, 0, MUSIC_SHARE_SIZE,
			//	true, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return ACTION_NULL, err
			}
			if val, err := NormalListMap[NORMAL_LIST_PAGE].GetLast(); nil == err {
				index = val.Value.(*PageNode).PageIndex * MUSIC_SHARE_SIZE
			}
			UpdatePlayListAndPage(list, index, true)

			songId, songUrl = getCanPlaySong(list)
			if len(list.PullTts) > 0 || len(list.PullTtsUrl) > 0 {
				tts = list.PullTts
				ttsUrl = list.PullTtsUrl
				filterTts = false
			}
		case ACTION_PLAY_BACK:
			if CurPlayingMode == LIST_ORDER_PLAY {
				if schedule.GetCurPlayer().IsPlaying() {
					sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PAUSE)
				}

				log.Println(log.DebugLog, "play list no back because list order play mode")
				var noPreOne string
				if PlayListSrcRec.BizType == BIZ_TYPE_MUSIC {
					noPreOne = TTS_TEXT_FIRST_MUSIC
				} else {
					noPreOne = TTS_TEXT_FIRST_RADIO
				}

				schedule.PlayNoticeAsync(AdSrcType_TTS, noPreOne, CONST_AUDIO_ID, true, nil,
					func(id uint32, reason schedule.NoticeStopReason) {
						switch reason {
						case schedule.NoticeStart_Play:
							ttsExecVuiStart()

						case schedule.NoticeStopReason_Complete,
							schedule.NoticeStopReason_Interrupt,
							schedule.NoticeStopReason_Cancel,
							schedule.NoticeStopReason_Error:
							ttsExecVuiStop()

							//schedule.SendSCmd(schedule.SCMD_MusicPlayer_Play, nil)
							sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
						default:

						}
					})

				//push tts to server
				http.PushInfoToServerForApp(http.InfoForApp{ResponseId: rspId, Cmd: int(MSC_CMD_PRE), Data: noPreOne, Tts: noPreOne,
					PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
				return ACTION_NULL, nil
			} else {
				tmpCurPos, err := LocalMusicList[curMusicList].GetLast()
				if nil != err {
					log.Println(log.ErrorLog, err.Error())
					return ACTION_NULL, err
				}
				LocalMusicList[curMusicList].SetCurPos(tmpCurPos)

				if LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).CanPlay {

				} else {
					LocalMusicList[curMusicList].GetValidPre()
				}
			}
		case ACTION_PRE_PAGE_REPLACE_LIST, ACTION_MISS_PAGE_REPLACE_LIST, ACTION_LAST_PAGE, ACTION_NEXT_PAGE_REPLACE_LIST:
			if ACTION_PRE_PAGE_REPLACE_LIST == action {
				// 还有前一个分页，拉取分页
				index = (NormalListMap[NORMAL_LIST_PAGE].CurPos.Value.(*PageNode).PageIndex - 2) * PAGE_SIZE
			} else if ACTION_MISS_PAGE_REPLACE_LIST == action {
				// 有缺失的分页，拉取分页
				index = (NormalListMap[NORMAL_LIST_PAGE].GetMissPage() - 1) * PAGE_SIZE
			} else {
				if CurPlayingMode == LIST_ORDER_PLAY {
					if schedule.GetCurPlayer().IsPlaying() {
						sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PAUSE)
					}

					log.Println(log.DebugLog, "play list no back because list order play mode")
					var noPreOne string
					if PlayListSrcRec.BizType == BIZ_TYPE_MUSIC {
						noPreOne = TTS_TEXT_FIRST_MUSIC
					} else {
						noPreOne = TTS_TEXT_FIRST_RADIO
					}

					//push tts to server
					http.PushInfoToServerForApp(http.InfoForApp{ResponseId: rspId, Cmd: int(MSC_CMD_PRE), Data: TTS_MUSIC_PRE, Tts: noPreOne,
						PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})

					schedule.PlayNoticeAsync(AdSrcType_TTS, noPreOne, CONST_AUDIO_ID, true, nil,
						func(id uint32, reason schedule.NoticeStopReason) {
							switch reason {
							case schedule.NoticeStart_Play:
								ttsExecVuiStart()

							case schedule.NoticeStopReason_Complete,
								schedule.NoticeStopReason_Interrupt,
								schedule.NoticeStopReason_Cancel,
								schedule.NoticeStopReason_Error:
								ttsExecVuiStop()

								//schedule.SendSCmd(schedule.SCMD_MusicPlayer_Play, nil)
								sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
							default:

							}
						})
					return ACTION_NULL, nil
				}

				var err error
				var playListCnt MusicLenType = 0
				//从服务器拉取节目歌单总数
				if PlayListSrcRec.BizType == BIZ_TYPE_MUSIC {
					var listInfo *MusicPlayListInfo
					listInfo, err = GetMusicPlayListInfo(PlayerType_Wifi, true, http.PlayListServerType{
						Id:     PlayListSrcRec.MusicListId,
						ModeId: PlayListSrcRec.ModeId,
					})
					if nil == err {
						playListCnt = MusicLenType(listInfo.ListInfo.TrackCount)
					}
				} else if PlayListSrcRec.BizType == BIZ_TYPE_RADIO_STATION {
					var listInfo *RadioPlayListInfo
					listInfo, err = GetRadioPlayListInfo(PlayerType_Wifi, true, http.PlayListServerType{
						Id:     PlayListSrcRec.MusicListId,
						ModeId: PlayListSrcRec.ModeId,
					})
					if nil == err {
						playListCnt = MusicLenType(listInfo.ListInfo.ProgramCount)
					}
				}
				if nil == err {
					index = MusicLenType(math.Ceil(float64(playListCnt)/PAGE_SIZE)-1) * PAGE_SIZE
				} else {
					log.Println(log.ErrorLog, err.Error())
					if val, err := NormalListMap[NORMAL_LIST_PAGE].GetLast(); nil == err {
						index = (val.Value.(*PageNode).PageIndex - 1) * PAGE_SIZE
					} else {
						// 分页全部拉取完毕，重新拉取第一页
						NormalListMap[NORMAL_LIST_PAGE].Init()
						index = 0
					}
				}
			}

			srcType := getMusicSrcType(PlayerType_Wifi)
			list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     srcType,
				Id:      getServiceId(srcType),
				Params:  getServiceParams(srcType),
				Start:   index,
				Size:    PAGE_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,

				ExtraData: PlayListSrcRec.ExtraData,
				BizType:   PlayListSrcRec.BizType,
				Order:     PlayListSrcRec.Order,
				AlbumID:   curSongInfo.AlbumId,
			})
			//list, err := getMusicList(PlayerType_Wifi, srcType, getServiceId(srcType),
			//	getServiceParams(srcType), index, PAGE_SIZE, true, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return ACTION_NULL, err
			}
			UpdatePlayListAndPage(list, index, true)

			tmpCurPos, err := LocalMusicList[curMusicList].GetLast()
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return ACTION_NULL, err
			}
			LocalMusicList[curMusicList].SetCurPos(tmpCurPos)

			if LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).CanPlay {

			} else {
				LocalMusicList[curMusicList].GetValidPre()
			}
		default:
			log.Println(log.WarningLog, "err action")
			return ACTION_NULL, Normal_ERR_ACTION
		}
		if ACTION_NULL != action {
			songId = LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo)
		}
	}
	if isNewHotDailyPlaying {
		if !LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).Single {
			needNHDIndex = true
		}
	}

	return action, commonPlayApi(commonPlayApiArg{
		prepare:            false,
		songId:             songId,
		songUrl:            songUrl,
		tts:                tts,
		ttsUrl:             ttsUrl,
		ttsNow:             true,
		needChangeNHDIndex: needNHDIndex,
		srcType:            PlayListSrcRec.MusicSrc,
		nextOrPreSong:      false,
		filterTts:          filterTts,
		rspId:              rspId,
		action:             musicAction,
		actionSrc:          musicActionSrc,
		switchSongByUser:   true,
	})
}

/**
下一曲， 在全部歌单中切换，和自动切换逻辑相同
*/
func nextMusic(arg interface{}) (MusicChangeAction, error) {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	var rspId string = ""
	var tts, ttsUrl string = "", ""
	//var ttsR *string = nil
	var filterTts bool = true
	var musicAction, musicActionSrc string = "", ""
	switch eVal := arg.(type) {
	case schedule.PlayerArg:
		rspId = eVal.RspId
		tts = eVal.InnerTts
		ttsUrl = eVal.InnerTtsUrl
		//ttsR = eVal.TtsNextPre
		musicAction = eVal.Action
		musicActionSrc = eVal.ActionSrc
	default:

	}

	curMusicList := GetMusicListIndex()
	if nil == LocalMusicList[curMusicList].GetCurPos() {
		//NoticePlayerSendSCmd(schedule.SCMD_SuperTask_Tts_Play, List_ERR_NULL.Error(), CONST_AUDIO_ID, AdSrcType_TTS,
		//	TTS_LEVEL_1, true, false)
		mscToastTts(List_ERR_NULL.Error(), CONST_AUDIO_ID, true, false, nil)
		return ACTION_NULL, List_ERR_NULL
	}
	action := getNextPrevAction(true)
	if UintTest {
		return action, nil
	}

	var songId *SongInfo = nil
	var songUrl *string = nil
	var needNHDIndex bool = false
	var needPlayCopyrightTTs bool = false
	var isRepeat bool = false

	if PlayListSrcRec.CopyrightTts.TtsType != TTS_TYPE_NULL {
		needPlayCopyrightTTs = true
	}
	_, isNewHotDailyPlaying := isNewHotDailySrc()

	if (SINGLE_CYCLE == CurPlayingMode && false) || forcePlayCur { // 上/下一曲忽略单曲循环
		// 单曲循环时，直接拿当前的音乐节点播放
		songId = LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo)
		log.Printf(log.WarningLog, "force play cur song:%+v\n", songId)
		forcePlayCur = false
	} else {
	pullNextListByIdAgain:
		var index MusicLenType = 0
		switch action {
		case ACTION_NULL:
			// 当前列表空
		case ACTION_PLAY_DIRECT:
			LocalMusicList[curMusicList].GetValidNext()
			songId = LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo)
		case ACTION_ADD_LIST_FM, ACTION_ADD_LIST_MUSIC_SHARE:
			var src MusicSrcType = PRIVATE_FM
			var pageSize MusicLenType = FM_SIZE
			var replace bool = false
			if ACTION_ADD_LIST_MUSIC_SHARE == action {
				src = MUSIC_SHARE_RCMD
				pageSize = MUSIC_SHARE_SIZE
				replace = true
			}
			// 拉取私人FM
			list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     src,
				Id:      nil,
				Params:  nil,
				Start:   0,
				Size:    pageSize,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})
			//list, err := getMusicList(PlayerType_Wifi, src, nil, nil, 0, pageSize,
			//	true, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return ACTION_NULL, err
			}
			if val, err := NormalListMap[NORMAL_LIST_PAGE].GetLast(); nil == err {
				index = val.Value.(*PageNode).PageIndex * pageSize
			}
			UpdatePlayListAndPage(list, index, replace)

			songId, songUrl = getCanPlaySong(list)
			if len(list.PullTts) > 0 || len(list.PullTtsUrl) > 0 {
				tts = list.PullTts
				ttsUrl = list.PullTtsUrl
				filterTts = false
			}
		case ACTION_NEXT_PAGE_REPLACE_LIST, ACTION_MISS_PAGE_REPLACE_LIST, ACTION_ADD_FM: // 还有分页 /有缺失页 /最后一页，且当前是播放推荐命令
			var replace bool = true
			var pageSize MusicLenType = PAGE_SIZE
			if ACTION_NEXT_PAGE_REPLACE_LIST == action {
				// 还有剩余分页，拉取分页，不重置分页信息
				index = NormalListMap[NORMAL_LIST_PAGE].CurPos.Value.(*PageNode).PageIndex * PAGE_SIZE
			} else if ACTION_MISS_PAGE_REPLACE_LIST == action {
				// 有缺失的分页，拉取分页，不重置分页信息
				index = (NormalListMap[NORMAL_LIST_PAGE].GetMissPage() - 1) * PAGE_SIZE
			} else {
				NormalListMap[NORMAL_LIST_PAGE].Init()

				// 当前是在播放推荐，开始拉取FM
				PlayListSrcRec.MusicSrc = PRIVATE_FM
				pageSize = FM_SIZE
				replace = false

				dailyPushPlayStChange()
			}

			srcType := getMusicSrcType(PlayerType_Wifi)

			list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     srcType,
				Id:      getServiceId(srcType),
				Params:  getServiceParams(srcType),
				Start:   index,
				Size:    pageSize,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,

				ExtraData: PlayListSrcRec.ExtraData,
				BizType:   PlayListSrcRec.BizType,
				Order:     PlayListSrcRec.Order,
				AlbumID:   curSongInfo.AlbumId,
			})
			//list, err := getMusicList(PlayerType_Wifi, srcType, getServiceId(srcType),
			//	getServiceParams(srcType), index, pageSize, true, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return ACTION_NULL, err
			}
			UpdatePlayListAndPage(list, index, replace)

			songId, songUrl = getCanPlaySong(list)
		case ACTION_ONLY_ONE_LIST_REPLACE_LIST: // 有且只有一个歌单
			back, _ := NormalListMap[NORMAL_LIST_PAGE].GetLast()
			if nil == back || 1 == back.Value.(*PageNode).PageIndex ||
				MUSIC_LIST_NULL == PlayListSrcRec.MusicSrc {
				if CurPlayingMode != LIST_ORDER_PLAY {
					tmpCurPos, err := LocalMusicList[curMusicList].GetFirst()
					if nil != err {
						log.Println(log.ErrorLog, err.Error())
						return ACTION_NULL, err
					}
					LocalMusicList[curMusicList].SetCurPos(tmpCurPos)

					if LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).CanPlay {

					} else {
						LocalMusicList[curMusicList].GetValidNext()
					}

					songId = LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo)
				}

				log.Println(log.DebugLog, "loop list for only one list, one page") //开始重复播放，有且只有一个歌单，只有一页
				isRepeat = true
			} else {
				if CurPlayingMode != LIST_ORDER_PLAY {
					// 拉取第一页
					list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
						Src:     PlayListSrcRec.MusicSrc,
						Id:      getServiceId(PlayListSrcRec.MusicSrc),
						Params:  getServiceParams(PlayListSrcRec.MusicSrc),
						Start:   index,
						Size:    PAGE_SIZE,
						WantTts: true,

						Oftag:        PlayListSrcRec.TagName,
						OftagForShow: PlayListSrcRec.TagNameShow,
						Mode:         CurPlayingMode,
						CloudId:      PlayListSrcRec.CloudId,
						ModeId:       PlayListSrcRec.ModeId,

						ExtraData: PlayListSrcRec.ExtraData,
						BizType:   PlayListSrcRec.BizType,
						Order:     PlayListSrcRec.Order,
						AlbumID:   curSongInfo.AlbumId,
					})
					//list, err := getMusicList(PlayerType_Wifi, PlayListSrcRec.MusicSrc,
					//	getServiceId(PlayListSrcRec.MusicSrc),
					//	getServiceParams(PlayListSrcRec.MusicSrc), index, PAGE_SIZE, false, CurPlayingMode)
					if nil != err {
						log.Println(log.ErrorLog, err.Error())
						return ACTION_NULL, err
					}
					NormalListMap[NORMAL_LIST_PAGE].Init()

					UpdatePlayListAndPage(list, index, true)

					songId, songUrl = getCanPlaySong(list)
				}

				log.Println(log.DebugLog, "loop list for only one list") //开始重复播放，有且只有一个歌单
				isRepeat = true
			}
		case ACTION_NEXT_LIST_REPLACE_LIST, ACTION_CYCLY_TO_FRONT_LIST_REPLACE_LIST: // 有下一个歌单 / 歌单播放完毕，没有更多，重新播放第一个歌单
			// 服务器下发的数据有歌单列表，肯定不是榜单、每日推荐
			if ACTION_NEXT_LIST_REPLACE_LIST == action {
				PlayListSrcRec.MusicListIdIndex += 1
			} else if ACTION_CYCLY_TO_FRONT_LIST_REPLACE_LIST == action {
				log.Println(log.DebugLog, "loop list for get front list")
				isRepeat = true
				if CurPlayingMode == LIST_ORDER_PLAY {
					break
				}

				//开始重复播放，重新播放第一个歌单
				PlayListSrcRec.MusicListIdIndex = 0
			}

			var listCnt MusicLenType = 0
		pullListByIdAgain:
			PlayListSrcRec.MusicListId = (*PlayListSrcRec.MusicListIds)[PlayListSrcRec.MusicListIdIndex].Id
			PlayListSrcRec.UpdateTime = (*PlayListSrcRec.MusicListIds)[PlayListSrcRec.MusicListIdIndex].UpdateTime
			//todo :需要判断当前歌单是否为空，如果空，需要继续拉取
			srcType := getMusicSrcType(PlayerType_Wifi)

			list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     srcType,
				Id:      getServiceId(srcType),
				Params:  getServiceParams(srcType),
				Start:   0,
				Size:    PAGE_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})
			//list, err := getMusicList(PlayerType_Wifi, srcType, getServiceId(srcType),
			//	getServiceParams(srcType), 0, PAGE_SIZE, false, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				if MusicList_ERR_MUSICINFO_NULL == err {
					if listCnt < PlayListSrcRec.MusicListIdsLen {
						listCnt++
						log.Println(log.DebugLog, "list nil, try:", listCnt)
						PlayListSrcRec.MusicListIdIndex += 1
						if PlayListSrcRec.MusicListIdIndex >= PlayListSrcRec.MusicListIdsLen {
							PlayListSrcRec.MusicListIdIndex = 0
						}
						goto pullListByIdAgain
					}
				}
				return ACTION_NULL, err
			}

			list.ModeId = -1 //服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
			UpdatePlayListSrcRec(list, false)

			NormalListMap[NORMAL_LIST_PAGE].Init()

			UpdatePlayListAndPage(list, 0, true)

			songId, songUrl = getCanPlaySong(list)

			//如果没有可以播放的歌曲，说明这个歌单全部无效，就开始切换下一个
			if songId == nil {
				if (PlayListSrcRec.MusicListIdIndex + 1) >= PlayListSrcRec.MusicListIdsLen {
					if playUserLists {
						// 当前播放的是我的歌单，在创建的歌单和收藏的歌单之间循环
						action = ACTION_USER_LIST_CYCLE
					} else {
						log.Println(log.DebugLog, "loop list for get front list")
						isRepeat = true
						if CurPlayingMode == LIST_ORDER_PLAY {
							break
						}
						// 歌单播放完毕，且后续没有歌单，重新播放第一个歌单
						action = ACTION_CYCLY_TO_FRONT_LIST_REPLACE_LIST
					}
				} else {
					//下一个歌单
					action = ACTION_NEXT_LIST_REPLACE_LIST
				}
				log.Println(log.DebugLog, "action:", action, "   PlayListSrcRec.MusicSrc:", PlayListSrcRec.MusicSrc)
				goto pullNextListByIdAgain
			}
		case ACTION_GET_NEW_LIST_REPLACE_LIST, ACTION_USER_LIST_CYCLE: // 歌单播放完毕，后续还有歌单
			// 服务器下发的数据有歌单列表，肯定不是榜单、每日推荐
			srcType := PlayListSrcRec.MusicSrc
			if playUserLists {
				if ACTION_USER_LIST_CYCLE == action {
					// 切换时，歌单index需要清零
					listIndex = 0
					if USER_LISTS_COLLECT == curUserListsType {
						srcType = USER_LISTS_CREATE
						log.Println(log.DebugLog, "loop list for get create user list")
						isRepeat = true
						if CurPlayingMode == LIST_ORDER_PLAY {
							break
						}
					} else {
						srcType = USER_LISTS_COLLECT
					}
					curUserListsType = srcType
				} else {
					srcType = curUserListsType
				}
			}
			list, err := GetMusicListsNew(PlayerType_Wifi, true, http.MusicListServerType{
				Src:     srcType,
				Id:      getServiceId(srcType),
				Params:  getServiceParams(srcType),
				Start:   listIndex,
				Size:    LIST_SIZE,
				WantTts: true,

				Oftag:        PlayListSrcRec.TagName,
				OftagForShow: PlayListSrcRec.TagNameShow,
				Mode:         CurPlayingMode,
				CloudId:      PlayListSrcRec.CloudId,
				ModeId:       PlayListSrcRec.ModeId,
			})

			//list, err := getMusicLists(PlayerType_Wifi, srcType, getServiceId(srcType),
			//	getServiceParams(srcType), listIndex, LIST_SIZE, false, CurPlayingMode)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return ACTION_NULL, err
			}
			list.ModeId = -1 //服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
			UpdatePlayListSrcRec(list, true)

			if srcType != curUserListsType {
				log.Println(log.DebugLog, "loop list for no collect user list") //没有收藏的歌单,开始重复播放
				isRepeat = true
				if CurPlayingMode == LIST_ORDER_PLAY {
					break
				}
			}

			NormalListMap[NORMAL_LIST_PAGE].Init()

			UpdatePlayListAndPage(list, 0, true)

			songId, songUrl = getCanPlaySong(list)
		}

	}
	if isNewHotDailyPlaying {
		if !LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).Single {
			needNHDIndex = true
		}
	}

	//第一次循环播放时，播放copyright tts
	var isFirstRepeat bool = false
	if isRepeat {
		if CurPlayingMode == LIST_ORDER_PLAY {
			if schedule.GetCurPlayer().IsPlaying() {
				sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PAUSE)
			}

			log.Println(log.DebugLog, "play list end because list order play mode")
			var noNextOne string
			if PlayListSrcRec.BizType == BIZ_TYPE_MUSIC {
				noNextOne = TTS_TEXT_LAST_MUSIC
			} else {
				noNextOne = TTS_TEXT_LAST_RADIO
			}

			schedule.PlayNoticeAsync(AdSrcType_TTS, noNextOne, CONST_AUDIO_ID, true, nil,
				func(id uint32, reason schedule.NoticeStopReason) {
					switch reason {
					case schedule.NoticeStart_Play:
						ttsExecVuiStart()

					case schedule.NoticeStopReason_Complete,
						schedule.NoticeStopReason_Interrupt,
						schedule.NoticeStopReason_Cancel,
						schedule.NoticeStopReason_Error:
						ttsExecVuiStop()

						//schedule.SendSCmd(schedule.SCMD_MusicPlayer_Play, nil)
						if !PlayListSrcRec.ListHasEnd {
							sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
						}
					default:

					}
				})

			//push tts to server
			if rspId == CONST_AUDIO_ID {
				http.InformLocalMsgToServer(http.Type_Inform_TtsPush, int(MSC_CMD_NEXT), TTS_MUSIC_NEXT, noNextOne) //快捷指令上传信息流
			} else {
				http.PushInfoToServerForApp(http.InfoForApp{ResponseId: rspId, Cmd: int(MSC_CMD_NEXT), Data: noNextOne, Tts: noNextOne,
					PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
			}

			return ACTION_NULL, nil
		}
		if needPlayCopyrightTTs == true && len(tts) == 0 && len(ttsUrl) == 0 {
			log.Printf(log.ErrorLog, "loop list, play copyrightTts: %+v\n", PlayListSrcRec.CopyrightTts)
			var tts2Server string = ""
			switch PlayListSrcRec.CopyrightTts.TtsType {
			case TTS_TYPE_TEXT:
				tts = PlayListSrcRec.CopyrightTts.Tts
				tts2Server = tts
				if len(tts) != 0 {
					isFirstRepeat = true
				}
			case TTS_TYPE_URL:
				ttsUrl = PlayListSrcRec.CopyrightTts.Tts
				tts2Server = PlayListSrcRec.CopyrightTts.Remark
				if len(ttsUrl) != 0 {
					isFirstRepeat = true
				}
			}

			if isFirstRepeat == true && tts2Server != "" {
				//push tts to server
				http.InformMsgToServer(http.Type_Inform_TtsDelay, tts2Server)
			}
			PlayListSrcRec.CopyrightTts.TtsType = 0
		}
	}

	return action, commonPlayApi(commonPlayApiArg{
		prepare:            false,
		songId:             songId,
		songUrl:            songUrl,
		tts:                tts,
		ttsUrl:             ttsUrl,
		ttsNow:             true,
		needChangeNHDIndex: needNHDIndex,
		srcType:            PlayListSrcRec.MusicSrc,
		nextOrPreSong:      true,
		filterTts:          filterTts,
		rspId:              rspId,
		action:             musicAction,
		actionSrc:          musicActionSrc,
		switchSongByUser:   true,
		IsFirstRepeat:      isFirstRepeat,
	})
}

/**
重复播放,指令来自语音
*/
func curMusic() error {
	curInfo := LocalMusicList[GetMusicListIndex()].GetCurPos()
	if nil == curInfo {
		return List_ERR_NULL
	}

	return commonPlayApi(commonPlayApiArg{
		prepare:            false,
		songId:             curInfo.Value.(*SongInfo),
		songUrl:            nil,
		tts:                "",
		ttsUrl:             "",
		ttsNow:             true,
		needChangeNHDIndex: false,
		srcType:            PlayListSrcRec.MusicSrc,
		nextOrPreSong:      true,
		filterTts:          true,
		rspId:              "",
		action:             MUSIC_ACTION_NEXT,
		actionSrc:          MUSIC_ACTION_SRC_VOICE,
	})
}

/* 会对歌单进行鉴权保护　*/
func commonPlayApi(arg commonPlayApiArg) error {
	defer func() {
		if playerRecoverLevel <= HIGH_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if "" != arg.action {
		MusicActionNew = &arg.action
		MusicActionSrcNew = &arg.actionSrc
	}

	//log.Println(log.ErrorLog, "PlayListSrcRec.MusicListId:", *PlayListSrcRec.MusicListId)

	var tts *string = nil
	var ttsR *string = &arg.tts
	var ttsAdSrcType AdSrcType         //混音tts type
	var ttsAdSrcTypeInterput AdSrcType //打断tts type
	if len(arg.ttsUrl) > 0 {
		tts = &arg.ttsUrl
		ttsAdSrcType = AdSrcType_FILE_MIX
		ttsAdSrcTypeInterput = AdSrcType_FILE
	} else if len(arg.tts) > 0 {
		tts = &arg.tts
		ttsAdSrcType = AdSrcType_TTS_MIX
		ttsAdSrcTypeInterput = AdSrcType_TTS
	}

	var songShield bool = false
	if arg.filterTts {
		songShield = LocalMusicFilter.Search(arg.songId)
	}

	var musicCnt MusicLenType = 0
	var pageCnt MusicLenType = 1
	var listCnt MusicLenType = PlayListSrcRec.MusicListIdIndex + 1 // 当前歌单播放位置
	var denyTimesCnt int8 = 0
	var needComfirm bool = false
	var songDetail *ServerSongDetail = nil
	var err error
	var createCollectTrans bool = false // 是否在收藏创建歌单之间切换过

	var needPlayCopyrightTTs bool = false
	var isRepeat bool = false
	var isFirstRepeat bool = arg.IsFirstRepeat

	if PlayListSrcRec.CopyrightTts.TtsType != TTS_TYPE_NULL {
		needPlayCopyrightTTs = true
	}

	if arg.songId != nil {
		log.Printf(log.DebugLog, "common Play:song will play:%+v\n", arg.songId)
		if SongInfo, err := GetCurWifiSongInfo(); err == nil {
			arg.songId.AlbumId = SongInfo.AlbumId
		}
	} else {
		log.Println(log.DebugLog, "common Play:songId is nil")
	}

	curMusicList := GetMusicListIndex()
	if nil == arg.songUrl || len(*arg.songUrl) == 0 || songShield {
	searchMusic:
		if arg.filterTts {
			songShield = LocalMusicFilter.Search(arg.songId)
		}

		if songShield && arg.songId != nil {
			log.Printf(log.WarningLog, "song is filter:%+v\n", arg.songId)
		}

		if nil != arg.songId && len(arg.songId.Id) > 0 && !songShield {
			songDetail, err = getSongDetailInfo(PlayerType_Wifi, &arg.songId.Id, arg.ttsNow)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				if Server_ERR_RET == err {
					return err
				}
				if SINGLE_SONG == arg.srcType {
					// 如果是单曲，服务端需要保证该单曲是可以播放的，因此不会进入该分之，如果进入，拉取下一首播放
				}
				needComfirm = true
				// change canplay to false
				arg.songId.CanPlay = false
			} else if "" == songDetail.Song.Url {
				needComfirm = true
			} else {
				needComfirm = false
				arg.songUrl = &songDetail.Song.Url

				arg.songId.Gain = songDetail.Song.Gain
			}
		} else {
			needComfirm = true
		}
		if needComfirm {
			// 播放失败，需要做拉取并鉴权处理
			var found bool
			var step MusicLenType
			if arg.nextOrPreSong {
				found, step = LocalMusicList[curMusicList].GetValidNext()
			} else {
				found, step = LocalMusicList[curMusicList].GetValidPre()
			}
			log.Println(log.DebugLog, "found:", found, " step:", step, " musicCnt:", musicCnt,
				" musicListLen:", MusicLenType(LocalMusicList[curMusicList].List.Len()))
			if !found || musicCnt >= MusicLenType(LocalMusicList[curMusicList].List.Len()) {
			searchPage:
			// case 1: 歌单中还有分页需要处理
				if denyTimesCnt < DENY_MAX_TIMES && (NormalListMap[NORMAL_LIST_PAGE].HasMore ||
					pageCnt < NormalListMap[NORMAL_LIST_PAGE].MaxPage) {
					denyTimesCnt++

					log.Printf(log.DebugLog, "has page to pull.%d\n", arg.srcType)
					// 还有分页
					arg.songId, err = hasPageNeedComfirm()
					pageCnt++
					if nil != err {
						goto searchPage
					} else {
						musicCnt = 0
						goto searchMusic
					}
				}
				// case 2: 还有歌单列表需要处理
			pullLocalList:
				if PlayListSrcRec.HasMore ||
					(listCnt < PlayListSrcRec.MusicListIdsLen && INVALID_MUSIC_LEN != PlayListSrcRec.MusicListIdsLen) {
					denyTimesCnt = 0
					log.Printf(log.DebugLog, "has lists to pull:%d,  listCnt:%d, listLen:%d\n",
						getMusicSrcType(PlayerType_Wifi),
						listCnt,
						PlayListSrcRec.MusicListIdsLen)
					// 还有歌单
					if listCnt < PlayListSrcRec.MusicListIdsLen {
						// 本地歌单列表中还有歌单待拉取
						PlayListSrcRec.MusicListIdIndex += 1
						if PlayListSrcRec.MusicListIdIndex >= PlayListSrcRec.MusicListIdsLen {
							log.Println(log.DebugLog, "loop list for get front list")
							isRepeat = true
							if CurPlayingMode == LIST_ORDER_PLAY {
								goto out
							}
							//开始重复播放，重新播放第一个歌单
							PlayListSrcRec.MusicListIdIndex = 0
						}
						list, err := GetMusicListNew(PlayerType_Wifi, false, http.MusicListServerType{
							Src:     getMusicSrcType(PlayerType_Wifi),
							Id:      (*PlayListSrcRec.MusicListIds)[PlayListSrcRec.MusicListIdIndex].Id,
							Params:  getServiceParams(PlayListSrcRec.MusicSrc),
							Start:   0,
							Size:    PAGE_SIZE,
							WantTts: true,

							Oftag:        PlayListSrcRec.TagName,
							OftagForShow: PlayListSrcRec.TagNameShow,
							Mode:         CurPlayingMode,
							CloudId:      PlayListSrcRec.CloudId,
							ModeId:       PlayListSrcRec.ModeId,

							ExtraData: PlayListSrcRec.ExtraData,
							BizType:   PlayListSrcRec.BizType,
							Order:     PlayListSrcRec.Order,
							AlbumID:   curSongInfo.AlbumId,
						})
						//list, err := getMusicList(PlayerType_Wifi, getMusicSrcType(PlayerType_Wifi),
						//	(*PlayListSrcRec.MusicListIds)[PlayListSrcRec.MusicListIdIndex].Id,
						//getServiceParams(PlayListSrcRec.MusicSrc), 0, PAGE_SIZE, false, CurPlayingMode)
						listCnt++
						//err = MusicList_ERR_MUSICINFO_NULL
						if nil != err {
							if MusicList_ERR_MUSICINFO_NULL == err {
								goto pullLocalList
							} else {
								log.Println(log.ErrorLog, err.Error())
								return err
							}
						}
						// index + 1 计算page时，可以获取到当前页的下一页
						NormalListMap[NORMAL_LIST_PAGE].Init()

						UpdatePlayListAndPage(list, 0, true)
						list.ModeId = -1 //服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
						UpdatePlayListSrcRec(list, false)

						if nil != LocalMusicList[GetMusicListIndex()].GetCurPos() {
							arg.songId = LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo)
							goto searchMusic
						} else {
							log.Println(log.ErrorLog, MusicList_ERR_MUSIC_NULL.Error())
							//return MusicList_ERR_MUSIC_NULL
							goto pullLocalList
						}
					}
					if PlayListSrcRec.HasMore {
						err = getListsByIdComfirm(PlayListSrcRec.MusicSrc)
						if nil != err {
							return err
						}

						if nil != LocalMusicList[GetMusicListIndex()].GetCurPos() {
							arg.songId = LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo)
							goto searchMusic
						} else {
							log.Println(log.ErrorLog, MusicList_ERR_MUSIC_NULL.Error())
							goto pullLocalList
						}
					}

				}
				// case 3: 如果此时在播放我的歌单，需要在收藏和创建歌单之间切换
				if playUserLists && !PlayListSrcRec.HasMore && !createCollectTrans {
					denyTimesCnt = 0
					createCollectTrans = true

					log.Printf(log.DebugLog, "play user lists, need to pull more, type: %d\n", arg.srcType)
					listIndex = 0
					if USER_LISTS_COLLECT == curUserListsType {
						log.Println(log.DebugLog, "loop list for get create user list")
						isRepeat = true
						if CurPlayingMode == LIST_ORDER_PLAY {
							goto out
						}
						curUserListsType = USER_LISTS_CREATE
					} else {
						curUserListsType = USER_LISTS_COLLECT
					}

					UserListsType := curUserListsType
					err = getListsByIdComfirm(curUserListsType)
					if nil != err {
						return err
					}

					if curUserListsType != UserListsType {
						log.Println(log.DebugLog, "loop list for no collect user list") //没有收藏的歌单,开始重复播放
						isRepeat = true
						if CurPlayingMode == LIST_ORDER_PLAY {
							goto out
						}
					}

					listCnt = 1
					log.Println(log.ErrorLog, "list Cnt:", listCnt, ", listIdsLen:", PlayListSrcRec.MusicListIdsLen,
						", GetCurPos:", LocalMusicList[GetMusicListIndex()].GetCurPos())
					if nil != LocalMusicList[GetMusicListIndex()].GetCurPos() {
						arg.songId = LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo)
						goto searchMusic
					} else {
						if listCnt < PlayListSrcRec.MusicListIdsLen {
							goto pullLocalList
						} else {
							log.Println(log.ErrorLog, MusicList_ERR_MUSIC_NULL.Error())
							goto specialOut
						}
					}
				}
				if arg.filterTts {
					PlayListSrcRec.CopyrightTts.TtsType = 0 //开始拉取特殊通道，清除版权的ttstype标记
					// 如果过滤情况下,没有有效歌曲,拉取特殊通道歌曲
					list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
						Src:     MUSIC_SPECIAL_RCMD,
						Id:      nil,
						Params:  nil,
						Start:   0,
						Size:    FM_SIZE,
						WantTts: true,

						Oftag:        PlayListSrcRec.TagName,
						OftagForShow: PlayListSrcRec.TagNameShow,
						Mode:         CurPlayingMode,
						CloudId:      PlayListSrcRec.CloudId,
						ModeId:       PlayListSrcRec.ModeId,
					})
					//list, err := getMusicList(PlayerType_Wifi, PRIVATE_FM, nil, nil, 0, FM_SIZE,
					//	true, CurPlayingMode)
					if nil != err {
						//err != nil, means http request error, as prd, report network error
						mscToastFile(vui.SoundNotice_Song_Get_Timeout.GetPath(), arg.rspId, true, false, nil)
						log.Println(log.ErrorLog, err.Error())

						return err
					}
					NormalListMap[NORMAL_LIST_PAGE].Init()

					var index MusicLenType = 0
					if val, err := NormalListMap[NORMAL_LIST_PAGE].GetLast(); nil == err {
						index = val.Value.(*PageNode).PageIndex * FM_SIZE
					}
					UpdatePlayListAndPage(list, index, true) //改成true,替换无效的歌单
					list.ModeId = -1                         //服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
					UpdatePlayListSrcRec(list, true)

					arg.songId, arg.songUrl = getCanPlaySong(list)
					goto out
				}
			specialOut:
			//// case 4:　错误信息播报
			//	log.Printf(log.DebugLog, "\n can not play because of lists is invalid:%v srcType:%d\n",
			//		MusicErrTtsMap[string(arg.srcType)], arg.srcType)
			//
			//	forcePausePlayer(PlayerType_Wifi)
			//
			//	mscToastTts(MusicErrTtsMap[string(arg.srcType)], CONST_AUDIO_ID, true, false, nil)
			//	ResetWifiPlayer()
			//	return MusicList_ERR_MUSIC_NULL
			// prd reverse 20180428 : 拉取特定的接口，如果失败，播放备用的tts
				log.Println(log.WarningLog, "get special music list from server...")
				//  case 4:　拉取特定接口
				PlayListSrcRec.CopyrightTts.TtsType = 0 //开始拉取特殊通道，清除版权的ttstype标记
				list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
					Src:     MUSIC_SPECIAL_RCMD,
					Id:      nil,
					Params:  nil,
					Start:   0,
					Size:    PAGE_SIZE,
					WantTts: true,

					Oftag:        PlayListSrcRec.TagName,
					OftagForShow: PlayListSrcRec.TagNameShow,
					Mode:         CurPlayingMode,
					CloudId:      PlayListSrcRec.CloudId,
					ModeId:       PlayListSrcRec.ModeId,
				})
				if nil != err {
					if nil != list {
						mscToastTts(list.ErrTts, arg.rspId, true, false, nil)
					} else {
						mscToastTts(MusicErrTtsMap[string(arg.srcType)], arg.rspId, true, false, nil)
					}
					log.Println(log.ErrorLog, err.Error())
					return err
				}
				NormalListMap[NORMAL_LIST_PAGE].Init()

				var index MusicLenType = 0
				if val, err := NormalListMap[NORMAL_LIST_PAGE].GetLast(); nil == err {
					index = val.Value.(*PageNode).PageIndex * PAGE_SIZE
				}
				UpdatePlayListAndPage(list, index, true)
				list.ModeId = -1 //服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
				UpdatePlayListSrcRec(list, true)

				arg.songId, arg.songUrl = getCanPlaySong(list)

				if len(list.PullTtsUrl) > 0 {
					tts = &list.PullTtsUrl
					ttsAdSrcType = AdSrcType_FILE_MIX
				} else if len(list.PullTts) > 0 {
					tts = &list.PullTts
					ttsAdSrcType = AdSrcType_TTS_MIX
				}
			} else {
				musicCnt += step
				arg.songId = LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo)
				goto searchMusic
			}
		}
	out:
	}

	if isRepeat {
		if CurPlayingMode == LIST_ORDER_PLAY {
			log.Println(log.DebugLog, "play list end because list order play mode")
			if curSongInfo != nil && curSongInfo.MusicListId != *PlayListSrcRec.MusicListId {
				//如果是版权问题引起的，这里的list id会恢复回正在播放的listId，否则APP歌单列表会错误
				log.Println(log.WarningLog, "restore music list id, curSongInfo MusicListId:", curSongInfo.MusicListId,
					", PlayListSrcRec.MusicListId:", *PlayListSrcRec.MusicListId)
				PlayListSrcRec.MusicListId = &(curSongInfo.MusicListId)
			}

			//用户主动切换就播报tts
			if arg.switchSongByUser {
				if schedule.GetCurPlayer().IsPlaying() {
					sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PAUSE)
				}

				var noNextOne string
				if PlayListSrcRec.BizType == BIZ_TYPE_MUSIC {
					noNextOne = TTS_TEXT_LAST_MUSIC
				} else {
					noNextOne = TTS_TEXT_LAST_RADIO
				}

				schedule.PlayNoticeAsync(AdSrcType_TTS, noNextOne, CONST_AUDIO_ID, true, nil,
					func(id uint32, reason schedule.NoticeStopReason) {
						switch reason {
						case schedule.NoticeStart_Play:
							ttsExecVuiStart()

						case schedule.NoticeStopReason_Complete,
							schedule.NoticeStopReason_Interrupt,
							schedule.NoticeStopReason_Cancel,
							schedule.NoticeStopReason_Error:
							ttsExecVuiStop()

							//schedule.SendSCmd(schedule.SCMD_MusicPlayer_Play, nil)
							if !PlayListSrcRec.ListHasEnd {
								sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
							}
						default:

						}
					})

				//push tts to server
				if arg.rspId == CONST_AUDIO_ID {
					http.InformLocalMsgToServer(http.Type_Inform_TtsPush, int(MSC_CMD_NEXT), TTS_MUSIC_NEXT, noNextOne) //快捷指令上传信息流
				} else {
					http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_NEXT), Data: noNextOne, Tts: noNextOne,
						PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
				}
			}
			return nil
		}
	}

	srcType, isNewHotDailyPlaying := isNewHotDailySrc()
	if isNewHotDailyPlaying && arg.needChangeNHDIndex {
		_, index, ret := LocalMusicList[CUR_MUSIC_LIST].Search(arg.songId, false)
		var pageIndex MusicLenType
		if nil == NormalListMap[NORMAL_LIST_PAGE].CurPos {
			pageIndex = 0
		} else {
			pageIndex = NormalListMap[NORMAL_LIST_PAGE].CurPos.Value.(*PageNode).PageIndex - 1
		}
		if ret {
			NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex = index + PAGE_SIZE*pageIndex
			if MUSIC_LIST_MAXLEN < NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex {
				NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex = 0
				log.Println(log.WarningLog, "cnt err ,reset to 0")
			}
		} else {
			log.Println(log.WarningLog, "search err,reset to 0")
			NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex = 0
		}

		log.Println(log.DebugLog, "NewHotDailyRec src:", srcType, "  index:",
			NewHotDailyRec[srcType-NEW_TOPLIST].MusicListIndex, " pageSize:", pageIndex)
	}

	if (nil == arg.songUrl || len(*arg.songUrl) == 0) && nil == songDetail {
		songDetail, err = getSongDetailInfo(PlayerType_Wifi, &arg.songId.Id, false)
		if nil != err {
			log.Println(log.ErrorLog, err.Error())
			return err
		}
		fmt.Printf("songDetail:%v\n", songDetail)

		//update song detail
		arg.songUrl = &songDetail.Song.Url
		arg.songId.Duration = songDetail.Song.Duration
		arg.songId.Gain = songDetail.Song.Gain
		arg.songId.BGUrl = songDetail.Song.BGUrl
	}

	//暂时不需显示icon，为了加快播放，第一次拉取歌曲的时候的getSongDetailInfo不执行,后续需要的话可以考虑异步获取
	//LocalMusicList[CUR_MUSIC_LIST].GetCurPos().Value.(*SongInfo).IsLiked = songDetail.Song.IsLiked

	//用户切换上下一首歌，包含语音和APP的命令
	if arg.switchSongByUser {
		//如果当前的歌曲和将要切的歌的ID一样，说明只有一首歌了
		if curSongInfo != nil && curSongInfo.Id == arg.songId.Id && 1 == LocalMusicList[CUR_MUSIC_LIST].GetValidMusicLen() {
			if schedule.GetCurPlayer().IsPlaying() {
				//schedule.SendSCmd(schedule.SCMD_MusicPlayer_Pause, nil)//改为直接发送暂停，否则这个命令会比下面的PlayNoticeAsync调度的更晚，造成时序错误
				sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PAUSE)
			}

			var listOnlyOne string
			if PlayListSrcRec.BizType == BIZ_TYPE_MUSIC {
				listOnlyOne = TTS_MUSIC_LIST_ONE
			} else {
				listOnlyOne = TTS_RADIO_LIST_ONE
			}

			//当前只有一首歌了，播报tts后，返回
			log.Println(log.DebugLog, "only one song at music list")
			schedule.PlayNoticeAsync(AdSrcType_TTS, listOnlyOne, CONST_AUDIO_ID, true, nil,
				func(id uint32, reason schedule.NoticeStopReason) {
					switch reason {
					case schedule.NoticeStart_Play:
						ttsExecVuiStart()

					case schedule.NoticeStopReason_Complete,
						schedule.NoticeStopReason_Interrupt,
						schedule.NoticeStopReason_Cancel,
						schedule.NoticeStopReason_Error:
						ttsExecVuiStop()

						//schedule.SendSCmd(schedule.SCMD_MusicPlayer_Play, nil)
						sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY)
					default:

					}
				})

			//push tts to server
			if arg.nextOrPreSong {
				if arg.rspId == CONST_AUDIO_ID {
					http.InformLocalMsgToServer(http.Type_Inform_TtsPush, int(MSC_CMD_NEXT), TTS_MUSIC_NEXT, listOnlyOne) //快捷指令上传信息流
				} else {
					http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_NEXT), Data: listOnlyOne, Tts: listOnlyOne,
						PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
				}
			} else {
				http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_PRE), Data: listOnlyOne, Tts: listOnlyOne,
					PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
			}
			return nil

		} else {
			//还有歌曲，上报信息流
			if nil != ttsR && len(*ttsR) > 0 {
				if strings.Contains(*ttsR, "%s") && songDetail != nil { //判断服务器下发的tts里是否有%s字段
					*ttsR = fmt.Sprintf(*ttsR, songDetail.Song.Name)
				}
				if arg.nextOrPreSong {
					/*目前这块产品没有要求，服务器是立即推送了信息流给APP，所以暂时不需要
					isSongDeny := LocalMusicFilter.Search(curSongInfo)
					if isSongDeny {
						//否定意图触发的下一首，暂时
						http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_MUSIC_DENY), Data: *ttsR, Tts: *ttsR,
							PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING,})
					}else {
						http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_NEXT), Data: *ttsR, Tts: *ttsR,
							PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING,})
					}*/
					if arg.rspId == CONST_AUDIO_ID {
						http.InformLocalMsgToServer(http.Type_Inform_TtsPush, int(MSC_CMD_NEXT), *ttsR, *ttsR) //快捷指令上传信息流
					} else {
						http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_NEXT), Data: *ttsR, Tts: *ttsR,
							PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
					}
				} else {
					http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_PRE), Data: *ttsR, Tts: *ttsR,
						PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
				}
			} else {
				if arg.nextOrPreSong {
					if arg.rspId == CONST_AUDIO_ID {
						http.InformLocalMsgToServer(http.Type_Inform_TtsPush, int(MSC_CMD_NEXT), TTS_MUSIC_NEXT, TTS_MUSIC_NEXT) //快捷指令上传信息流
					} else {
						http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_NEXT), Data: TTS_MUSIC_NEXT, Tts: TTS_MUSIC_NEXT,
							PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
					}
				} else {
					http.PushInfoToServerForApp(http.InfoForApp{ResponseId: arg.rspId, Cmd: int(MSC_CMD_PRE), Data: TTS_MUSIC_PRE, Tts: TTS_MUSIC_PRE,
						PlayMode: CurPlayingMode, DataType: http.DATA_TYPE_STRING})
				}
			}
		}
	}
	log.Printf(log.DebugLog, "Arg:%+v\n", arg)

	//第一次循环播放时，播放copyright tts
	if needPlayCopyrightTTs == true && isRepeat == true && tts == nil {
		log.Printf(log.ErrorLog, "loop list, play copyrightTts: %+v\n", PlayListSrcRec.CopyrightTts)
		var tts2Server string = ""
		switch PlayListSrcRec.CopyrightTts.TtsType {
		case TTS_TYPE_TEXT:
			tts = &PlayListSrcRec.CopyrightTts.Tts
			tts2Server = PlayListSrcRec.CopyrightTts.Tts
			ttsAdSrcType = AdSrcType_TTS_MIX
		case TTS_TYPE_URL:
			tts = &PlayListSrcRec.CopyrightTts.Tts
			tts2Server = PlayListSrcRec.CopyrightTts.Remark
			ttsAdSrcType = AdSrcType_FILE_MIX
		}

		if tts != nil {
			if tts2Server != "" {
				//push tts to server
				http.InformMsgToServer(http.Type_Inform_TtsDelay, tts2Server)
			}
			isFirstRepeat = true
		}

		PlayListSrcRec.CopyrightTts.TtsType = 0
	}

	curSongInfo = arg.songId
	if PlayListSrcRec.MusicListId != nil {
		//备份listId，由于版权问题，顺序播放的时候，如果版权有问题，list可能被覆盖，会造成同步listId不对，需要恢复listId
		curSongInfo.MusicListId = *PlayListSrcRec.MusicListId
	}
	if arg.prepare {
		/* 准备下一首是播放器自己调度的,播放器内部修改一下状态 */
		status := PlayerHander[PlayerType_Wifi].GetPlayerStatus()
		status.Status = MUSIC_ST_PREPARING
		status.Uuid = arg.songId.Id
		status.Duration = arg.songId.Duration
		status.CurPos = 1
		status.PlayerId = uint32(PlayerType_Wifi)
		PlayerHander[PlayerType_Wifi].SetPlayerStatus(status)

		sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PERPARE_NEXT, &arg.songId.Id, arg.songUrl, tts, ttsAdSrcType, arg.songId.Duration, arg.songId.Gain, arg.songId.BGUrl)
	} else {
		if backGroundPlayerTest && nil == arg.songId.BGUrl && nil != tts && len(*tts) > 0 { // 有tts播报的音乐播放背景音测试
			bgUrl := vui.SoundNotice_Volume.GetPath()
			arg.songId.BGUrl = &bgUrl
		}

		if arg.filterTts && !isFirstRepeat {
			sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY, &arg.songId.Id, arg.songUrl, nil, ttsAdSrcType, arg.songId.Duration, arg.songId.Gain, arg.songId.BGUrl)
		} else {
			if nil != tts && len(*tts) > 0 {
				if strings.Contains(*tts, "%s") && songDetail != nil { //判断服务器下发的tts里是否有%s字段
					*tts = fmt.Sprintf(*tts, songDetail.Song.Name)
				}
			}
			sendMusicCmd(PlayerType_Wifi, CMD_PLAYER_PLAY, &arg.songId.Id, arg.songUrl, tts, ttsAdSrcType, arg.songId.Duration, arg.songId.Gain, arg.songId.BGUrl, MusicCmdExtra{RespId: arg.rspId})
		}
	}
	errPush := LocalMusicList[PLAYSONG_MUSIC_LIST].Push(arg.songId, true)
	if nil != errPush {
		log.Println(log.WarningLog, "Push to PLAYSONG_MUSIC_LIST err:", err.Error())
	}

	// not change icon
	//if LocalMusicList[CUR_MUSIC_LIST].GetCurPos().Value.(*SongInfo).IsLiked {
	//	vui.IconLed_iconMusic003.Show();
	//} else {
	//	vui.IconLed_iconMusic001.Show();
	//}

	if !isFirstRepeat && arg.filterTts && nil != tts && len(*tts) > 0 {
		if nil == songDetail {
			songDetail, err = getSongDetailInfo(PlayerType_Wifi, &arg.songId.Id, false)
			if nil != err {
				log.Println(log.ErrorLog, err.Error())
				return err
			}
			fmt.Printf("songDetail:%v\n", songDetail)
		}

		ttsNew := *tts
		if AdSrcType_TTS == ttsAdSrcTypeInterput && strings.Contains(*tts, "%s") { //判断服务器下发的tts里是否有%s字段
			ttsNew = fmt.Sprintf(*tts, songDetail.Song.Name)
		}

		schedule.PlayNoticeAsync(ttsAdSrcTypeInterput, ttsNew, arg.rspId, true, nil,
			func(id uint32, reason schedule.NoticeStopReason) {
				switch reason {
				case schedule.NoticeStart_Play:
					ttsExecVuiStart()

				case schedule.NoticeStopReason_Complete,
					schedule.NoticeStopReason_Interrupt,
					schedule.NoticeStopReason_Cancel,
					schedule.NoticeStopReason_Error:
					ttsExecVuiStop()

					schedule.SendSCmd(schedule.SCMD_MusicPlayer_Play, nil)
				default:
				}
			})

	}
	return nil
}

// 鉴权时，处理还有分页待拉取的情况
func hasPageNeedComfirm() (songId *SongInfo, err error) {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var index MusicLenType
	var pageSize MusicLenType = PAGE_SIZE
	var replace bool = true
	if NormalListMap[NORMAL_LIST_PAGE].HasMore {
		// 还有分页
		if val, err := NormalListMap[NORMAL_LIST_PAGE].GetLast(); nil == err {
			index = val.Value.(*PageNode).PageIndex * PAGE_SIZE
		}
	} else {
		// 从第１页开始拉取
		NormalListMap[NORMAL_LIST_PAGE].Init()

		if DAILY_FM_MIX == PlayListSrcRec.MusicSrc || DAILY_PUSH == PlayListSrcRec.MusicSrc {
			// 如果当前是在播放推荐，开始拉取FM
			PlayListSrcRec.MusicSrc = PRIVATE_FM
			pageSize = FM_SIZE
			replace = false
			dailyPushPlayStChange()
		}
		index = 0
	}
	srcType := getMusicSrcType(PlayerType_Wifi)
	list, err := GetMusicListNew(PlayerType_Wifi, true, http.MusicListServerType{
		Src:     srcType,
		Id:      getServiceId(srcType),
		Params:  getServiceParams(srcType),
		Start:   index,
		Size:    pageSize,
		WantTts: true,

		Oftag:        PlayListSrcRec.TagName,
		OftagForShow: PlayListSrcRec.TagNameShow,
		Mode:         CurPlayingMode,
		CloudId:      PlayListSrcRec.CloudId,
		ModeId:       PlayListSrcRec.ModeId,

		ExtraData: PlayListSrcRec.ExtraData,
		BizType:   PlayListSrcRec.BizType,
		Order:     PlayListSrcRec.Order,
		AlbumID:   curSongInfo.AlbumId,
	})
	//list, err := getMusicList(PlayerType_Wifi, srcType, getServiceId(srcType),
	//	getServiceParams(srcType), index, pageSize, true, CurPlayingMode)
	if nil != err {
		log.Println(log.ErrorLog, err.Error())
		return nil, err
	}
	UpdatePlayListAndPage(list, index, replace)

	hasVailed, _ := LocalMusicList[GetMusicListIndex()].GetValidNextIncludeCur()
	log.Println(log.DebugLog, "hasVailed:", hasVailed)
	if hasVailed {
		return LocalMusicList[GetMusicListIndex()].GetCurPos().Value.(*SongInfo), nil
	} else {
		return nil, MusicList_ERR_MUSIC_NULL
	}
}

func getListsByIdComfirm(srcType MusicSrcType) error {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()
	list, err := GetMusicListsNew(PlayerType_Wifi, false, http.MusicListServerType{
		Src:     srcType,
		Id:      getServiceId(srcType),
		Params:  getServiceParams(srcType),
		Start:   listIndex,
		Size:    LIST_SIZE,
		WantTts: true,

		Oftag:        PlayListSrcRec.TagName,
		OftagForShow: PlayListSrcRec.TagNameShow,
		Mode:         CurPlayingMode,
		CloudId:      PlayListSrcRec.CloudId,
		ModeId:       PlayListSrcRec.ModeId,

		ExtraData: PlayListSrcRec.ExtraData,
		BizType:   PlayListSrcRec.BizType,
		Order:     PlayListSrcRec.Order,
	})

	//list, err := getMusicLists(PlayerType_Wifi, srcType, getServiceId(srcType),
	//	getServiceParams(srcType), listIndex, LIST_SIZE, false, CurPlayingMode)
	if nil != err {
		log.Println(log.ErrorLog, err.Error())
		return err
	}
	list.ModeId = -1 //服务器主动拉取，不会下发mode，cloudId等信息，此处特殊处理
	UpdatePlayListSrcRec(list, true)

	NormalListMap[NORMAL_LIST_PAGE].Init()

	UpdatePlayListAndPage(list, 0, true)

	return nil
}

func max(a int, b int) int {
	if a > b {
		return a
	}
	return b
}

func getPrepareNextAction() MusicChangeAction {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var ret MusicChangeAction

	curMusicList := GetMusicListIndex()

	if nil == LocalMusicList[curMusicList].GetCurPos() {
		return ACTION_NULL
	}

	tmpMaxpage := MusicLenType(max(int(NormalListMap[NORMAL_LIST_PAGE].MaxPage), NormalListMap[NORMAL_LIST_PAGE].List.Len()))

	back, _ := NormalListMap[NORMAL_LIST_PAGE].GetLast()
	if nil != LocalMusicList[curMusicList].GetValidFrontBackSong(false) &&
		LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).Id != LocalMusicList[curMusicList].GetValidFrontBackSong(false).Id {
		ret = ACTION_PLAY_DIRECT
	} else if PRIVATE_FM == PlayListSrcRec.MusicSrc {
		ret = ACTION_ADD_LIST_FM
	} else if MUSIC_SHARE_RCMD == PlayListSrcRec.MusicSrc {
		ret = ACTION_ADD_LIST_MUSIC_SHARE
	} else if NormalListMap[NORMAL_LIST_PAGE].HasMore {
		// 还有分页
		ret = ACTION_NEXT_PAGE_REPLACE_LIST
	} else if MUSIC_LIST_WORD == PlayListSrcRec.MusicSrc || MUSIC_LIST_SERACH_WORD == PlayListSrcRec.MusicSrc {
		ret = ACTION_ONLY_ONE_LIST_REPLACE_LIST
	} else if nil != back &&
		back.Value.(*PageNode).PageIndex != tmpMaxpage {
		log.Println(log.ErrorLog, "prepare next miss page inex：", back.Value.(*PageNode).PageIndex,
			", list len:", NormalListMap[NORMAL_LIST_PAGE].List.Len(),
			", max page:", NormalListMap[NORMAL_LIST_PAGE].MaxPage)
		// 有缺失的分页
		ret = ACTION_MISS_PAGE_REPLACE_LIST
	} else if DAILY_FM_MIX == PlayListSrcRec.MusicSrc {
		// 最后一页，且当前是播放推荐命令
		ret = ACTION_ADD_FM
	} else if nil != PlayListSrcRec.MusicListIds {
		if PlayListSrcRec.MusicListIdIndex < PlayListSrcRec.MusicListIdsLen-1 {
			// 还有下一个歌单
			ret = ACTION_NEXT_LIST_REPLACE_LIST
		} else if PlayListSrcRec.HasMore {
			// 歌单播放完毕,后续还有歌单，拉取新歌单
			ret = ACTION_GET_NEW_LIST_REPLACE_LIST
		} else {
			if playUserLists {
				// 当前播放的是我的歌单，在创建的歌单和收藏的歌单之间循环
				ret = ACTION_USER_LIST_CYCLE
			} else {
				// 歌单播放完毕，且后续没有歌单，重新播放第一个歌单
				ret = ACTION_CYCLY_TO_FRONT_LIST_REPLACE_LIST
			}
		}
	} else if nil == PlayListSrcRec.MusicListIds || 1 == PlayListSrcRec.MusicListIdsLen {
		if playUserLists {
			// 当前播放的是我的歌单，在创建的歌单和收藏的歌单之间循环
			ret = ACTION_USER_LIST_CYCLE
		} else {
			// 有且只有一个歌单
			ret = ACTION_ONLY_ONE_LIST_REPLACE_LIST
		}
	}
	log.Println(log.DebugLog, "action:", ret, "   PlayListSrcRec.MusicSrc:", PlayListSrcRec.MusicSrc)
	return ret
}

func getNextPrevAction(needNextAction bool) MusicChangeAction {
	defer func() {
		if playerRecoverLevel <= MIDDLE_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	curMusicList := GetMusicListIndex()

	if nil == LocalMusicList[curMusicList].GetCurPos() {
		return ACTION_NULL
	}

	if needNextAction {
		log.Println(log.DebugLog, "Next...")
	} else {
		log.Println(log.DebugLog, "Prev...")
	}

	tmpMaxpage := MusicLenType(max(int(NormalListMap[NORMAL_LIST_PAGE].MaxPage), NormalListMap[NORMAL_LIST_PAGE].List.Len()))

	var ret MusicChangeAction
	back, _ := NormalListMap[NORMAL_LIST_PAGE].GetLast()
	if needNextAction {
		if nil != LocalMusicList[curMusicList].GetValidFrontBackSong(false) &&
			LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).Id != LocalMusicList[curMusicList].GetValidFrontBackSong(false).Id {
			ret = ACTION_PLAY_DIRECT
		} else if PRIVATE_FM == PlayListSrcRec.MusicSrc {
			ret = ACTION_ADD_LIST_FM
		} else if MUSIC_SHARE_RCMD == PlayListSrcRec.MusicSrc {
			ret = ACTION_ADD_LIST_MUSIC_SHARE
		} else if NormalListMap[NORMAL_LIST_PAGE].HasMore {
			// 还有分页
			ret = ACTION_NEXT_PAGE_REPLACE_LIST
		} else if MUSIC_LIST_WORD == PlayListSrcRec.MusicSrc || MUSIC_LIST_SERACH_WORD == PlayListSrcRec.MusicSrc {
			ret = ACTION_ONLY_ONE_LIST_REPLACE_LIST
		} else if nil != back &&
			back.Value.(*PageNode).PageIndex != tmpMaxpage {

			log.Println(log.ErrorLog, "next miss page inex：", back.Value.(*PageNode).PageIndex,
				", list len:", NormalListMap[NORMAL_LIST_PAGE].List.Len(),
				", max page:", NormalListMap[NORMAL_LIST_PAGE].MaxPage)
			// 有缺失的分页，拉取分页
			ret = ACTION_MISS_PAGE_REPLACE_LIST
		} else if nil != PlayListSrcRec.MusicListIds {
			if PlayListSrcRec.MusicListIdIndex < PlayListSrcRec.MusicListIdsLen-1 {
				// 还有下一个歌单
				ret = ACTION_NEXT_LIST_REPLACE_LIST
			} else if PlayListSrcRec.HasMore {
				// 歌单播放完毕,后续还有歌单，拉取新歌单
				ret = ACTION_GET_NEW_LIST_REPLACE_LIST
			} else {
				if playUserLists {
					// 当前播放的是我的歌单，在创建的歌单和收藏的歌单之间循环
					ret = ACTION_USER_LIST_CYCLE
				} else {
					// 歌单播放完毕，且后续没有歌单，重新播放第一个歌单
					ret = ACTION_CYCLY_TO_FRONT_LIST_REPLACE_LIST
				}
			}
		} else if nil == PlayListSrcRec.MusicListIds || 1 == PlayListSrcRec.MusicListIdsLen {
			if playUserLists {
				// 当前播放的是我的歌单，在创建的歌单和收藏的歌单之间循环
				ret = ACTION_USER_LIST_CYCLE
			} else {
				// 有且只有一个歌单
				ret = ACTION_ONLY_ONE_LIST_REPLACE_LIST
			}
		}
	} else {
		if nil != LocalMusicList[curMusicList].GetValidFrontBackSong(true) &&
			LocalMusicList[curMusicList].GetCurPos().Value.(*SongInfo).Id != LocalMusicList[curMusicList].GetValidFrontBackSong(true).Id {
			ret = ACTION_PLAY_DIRECT
		} else if PRIVATE_FM == PlayListSrcRec.MusicSrc {
			ret = ACTION_PLAY_BACK
		} else if MUSIC_SHARE_RCMD == PlayListSrcRec.MusicSrc {
			ret = ACTION_ADD_LIST_MUSIC_SHARE
		} else if NormalListMap[NORMAL_LIST_PAGE].CurPos.Value.(*PageNode).PageIndex >= 2 {
			// 前一分页
			ret = ACTION_PRE_PAGE_REPLACE_LIST
		} else if MUSIC_LIST_WORD == PlayListSrcRec.MusicSrc || MUSIC_LIST_SERACH_WORD == PlayListSrcRec.MusicSrc {
			ret = ACTION_PLAY_BACK
		} else if nil != back &&
			back.Value.(*PageNode).PageIndex != tmpMaxpage {
			log.Println(log.ErrorLog, "pre miss page inex：", back.Value.(*PageNode).PageIndex,
				", list len:", NormalListMap[NORMAL_LIST_PAGE].List.Len(),
				", max page:", NormalListMap[NORMAL_LIST_PAGE].MaxPage)
			// 有缺失的分页，拉取分页
			ret = ACTION_MISS_PAGE_REPLACE_LIST
		} else {
			if 1 == MusicLenType(NormalListMap[NORMAL_LIST_PAGE].List.Len()) ||
				MUSIC_LIST_NULL == PlayListSrcRec.MusicSrc {
				if NormalListMap[NORMAL_LIST_PAGE].HasMore {
					// 当前只有一页信息，而且还有下一页，只能拉取下一分页
					ret = ACTION_LAST_PAGE
				} else {
					// 如果只有1页 / 如果都是单曲插入，直接播放最后1首
					log.Println(log.ErrorLog, "len list:", MusicLenType(NormalListMap[NORMAL_LIST_PAGE].List.Len()), ", PlayListSrcRec.MusicSrc:", PlayListSrcRec.MusicSrc)
					ret = ACTION_PLAY_BACK
				}

			} else {
				ret = ACTION_LAST_PAGE
			}
		}
	}
	log.Println(log.DebugLog, "action:", ret, "   PlayListSrcRec.MusicSrc:", PlayListSrcRec.MusicSrc, " cur Music list:", curMusicList)
	return ret
}

/* 获取当前未在播放状态的播放列表的索引 */
func getUnPlayListIndex(cur MusicListType) MusicListType {
	if CUR_MUSIC_LIST == cur {
		return RANDOM_MUSIC_LIST
	} else {
		return CUR_MUSIC_LIST
	}
}
func addToHistoryMusicList() error {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if nil == curSongInfo {
		log.Println(log.ErrorLog, List_ERR_NULL.Error())
		return List_ERR_NULL
	}
	curSongInfo.PlayedTime = time.Now()

	err = LocalMusicList[HISTORY_MUSIC_LIST].Push(curSongInfo, true)

	if nil != err {
		log.Println(log.ErrorLog, err.Error())
		return err
	}

	return nil
}

/**
更新当前播放状态到CurPlayerStatusToApp数据结构中，用于记录当前状态和发送给app
*/
func updateWifiPlayerStatus(status schedule.PlayerDetail) uint8 {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	if status.Status < MUSIC_ST_MAX {
		wifiPlayerSt.PlayerSrc = CurPlayerSrc
		// 连续任务状态
		wifiPlayerSt.MusicId = &status.Uuid
		wifiPlayerSt.ContinuousTaskSt = status.Status
		wifiPlayerSt.CurPlayingPos = status.CurPos
		wifiPlayerSt.MusicListId = PlayListSrcRec.MusicListId
		wifiPlayerSt.Duration = status.Duration
		wifiPlayerSt.ContinuousStChangeTime = time.Now()
		wifiPlayerSt.Type = PlayListSrcRec.MusicSrc
		wifiPlayerSt.TaskField = FIELD_MUSIC
	} else if status.Status < AUDIO_ST_MAX && status.Status >= AUDIO_ST_IDLE {
		wifiPlayerSt.PlayerSrc = WIFI_SRC
		wifiPlayerSt.SingleTaskSt = status.Status
		wifiPlayerSt.SingleStChangeTime = time.Now()
	}

	var percent uint8
	if 0 == wifiPlayerSt.Duration {
		percent = 0
	} else {
		percent = uint8(wifiPlayerSt.CurPlayingPos * 100 / wifiPlayerSt.Duration)
	}

	var printStr string = ""
	if nil != wifiPlayerSt.MusicId && len(*wifiPlayerSt.MusicId) > 0 {
		printStr += fmt.Sprintf("musicId:%s ", *wifiPlayerSt.MusicId)
	}
	if nil != wifiPlayerSt.MusicListId && len(*wifiPlayerSt.MusicListId) > 0 {
		printStr += fmt.Sprintf("listId:%s ", *wifiPlayerSt.MusicListId)
	}
	printStr += fmt.Sprintf("sSt:0x%x field:%d cSt:0x%x cur:%ds dur:%ds src:%d id:%d(%d%%)",
		wifiPlayerSt.SingleTaskSt,
		wifiPlayerSt.TaskField,
		wifiPlayerSt.ContinuousTaskSt,
		status.CurPos/1000,
		status.Duration/1000,
		wifiPlayerSt.PlayerSrc,
		PlayerType_Wifi,
		percent)
	if MUSIC_ST_PLAYING == status.Status {
		fmt.Printf("%s\n", printStr)
	} else {
		fmt.Printf("%s\n", printStr)
	}
	return percent
}

func isDateEqual(t1 time.Time, t2 time.Time) bool {
	year1, month1, day1 := t1.Date()
	year2, month2, day2 := t2.Date()

	if year1 != year2 || month1 != month2 || day1 != day2 {
		return false
	} else {
		return true
	}
}

func getCurPage() MusicLenType {
	defer func() {
		if playerRecoverLevel <= LOW_RECOVER_LEVEL {
			if err := recover(); err != nil {
				log.Println(log.ErrorLog, err)
			}
		}
	}()

	var curPage MusicLenType = 0
	if nil != NormalListMap[NORMAL_LIST_PAGE].CurPos {
		curPage = NormalListMap[NORMAL_LIST_PAGE].CurPos.Value.(*PageNode).PageIndex
	}
	return curPage
}

func isNeedRestart() bool {
	isChange := PlayerHander[PlayerType_Wifi].IsPlayerChange(true)
	log.Println(log.DebugLog, "CurSt:", wifiPlayerSt.ContinuousTaskSt,
		"  isChange:", isChange)
	if isChange {
		return false
	}

	if MUSIC_ST_PAUSED == wifiPlayerSt.ContinuousTaskSt {
		if time.Now().Sub(wifiPlayerSt.ContinuousStChangeTime) < T1Timeout.GetCfgListTime() {
			return true
		} else {
			log.Println(log.DebugLog, "now:", time.Now().Unix(), "  listTime:",
				wifiPlayerSt.ContinuousStChangeTime.Unix())
		}
	}
	return false
}

func dailyPushPlayStChange() {
	DailyPushPlayedInfo.HasPlayed = true
	DailyPushPlayedInfo.PlayedTime = time.Now()
	log.Println(log.DebugLog, "Daily Push has play at:", time.Now())
}

func getServiceId(src MusicSrcType) *string {
	switch src {
	case MUSIC_LIST_WORD, MUSIC_LIST_SERACH_WORD:
		return PlayListSrcRec.TagName
	case NEW_TOPLIST, HOT_TOPLIST, DAILY_PUSH, DENON_TOPLIST, CLASSIC_TOPLIST, BEATPORT_TOPLIST,
		ORIGINAL_TOPLIST, SORIGN_TOPLIST, ACG_TOPLIST, US_BILLBOARD_TOPLIST, JP_ORICON_TOPLIST,
		CH_NEW_TOPLIST, CH_HOT_TOPLIST, JP_NEW_LIST, KOREA_NEW_LIST, EU_US_NEW_LIST:
		topListId := strconv.Itoa(int(src))
		log.Println(log.DebugLog, "type:", src, "  toplistId:", topListId)
		return &topListId
	case ALBUM_ARTIST_ID:
		return PlayListSrcRec.ArtistId
	default:
		return PlayListSrcRec.MusicListId
	}
}

func getSenceServiceId(src MusicSrcType) *string {
	switch src {
	case MUSIC_LIST_WORD, MUSIC_LIST_SERACH_WORD:
		return ScenesPlayerListSrc.TagName
	case NEW_TOPLIST, HOT_TOPLIST, DAILY_PUSH, DENON_TOPLIST, CLASSIC_TOPLIST, BEATPORT_TOPLIST,
		ORIGINAL_TOPLIST, SORIGN_TOPLIST, ACG_TOPLIST, US_BILLBOARD_TOPLIST, JP_ORICON_TOPLIST,
		CH_NEW_TOPLIST, CH_HOT_TOPLIST, JP_NEW_LIST, KOREA_NEW_LIST, EU_US_NEW_LIST:
		topListId := strconv.Itoa(int(src))
		log.Println(log.DebugLog, "type:", src, "  toplistId:", topListId)
		return &topListId
	case ALBUM_ARTIST_ID:
		return PlayListSrcRec.ArtistId
	default:
		return ScenesPlayerListSrc.MusicListId
	}
}

func getServiceParams(src MusicSrcType) *string {
	var ret string
	switch src {
	case MUSIC_LIST_WORD:
		ret = MUSIC_ORDER_HOT
	default:
		return nil
	}
	return &ret
}

func isNewHotDailySrc() (MusicSrcType, bool) {
	switch PlayListSrcRec.MusicSrc {
	case NEW_TOPLIST, HOT_TOPLIST, DAILY_PUSH, DENON_TOPLIST, CLASSIC_TOPLIST, BEATPORT_TOPLIST,
		ORIGINAL_TOPLIST, SORIGN_TOPLIST, ACG_TOPLIST, US_BILLBOARD_TOPLIST, JP_ORICON_TOPLIST:
		return PlayListSrcRec.MusicSrc, true
	case DAILY_FM_MIX:
		return DAILY_PUSH, true
	default:
		return PlayListSrcRec.MusicSrc, false
	}
}

func ResetWifiPlayer() {
	log.Println(log.DebugLog, "Reset wifi player!")
	playCtrlLock.Lock()
	defer playCtrlLock.Unlock()

	LocalMusicList[CUR_MUSIC_LIST].Init()
	LocalMusicList[RANDOM_MUSIC_LIST].Init()

	CurPosRecTmp = nil
	SceneCurPosRecTmp = nil

	LocalMusicFilter = MusicFilter{SongId: list.New(), AlbumId: list.New(), ArtistName: list.New()}
	PlayListSrcRec = PlayListSrcInfo{MusicListId: nil, MusicListIds: nil, MusicListIdIndex: INVALID_MUSIC_LEN,
		MusicListIdsLen: INVALID_MUSIC_LEN, HasInsert: false, HasMore: false, UpdateTime: 0, MusicSrc: MUSIC_LIST_NULL}

	wifiPlayerSt = PlayerStatus{PlayerSrc: WIFI_SRC, MusicId: nil, MusicListId: nil, ContinuousTaskSt: MUSIC_ST_IDLE, CurPlayingPos: 0,
		TaskField: FIELD_NULL,
		SingleTaskSt: AUDIO_ST_IDLE}

	status := PlayerHander[PlayerType_Wifi].GetPlayerStatus()
	status.Status = MUSIC_ST_IDLE
	PlayerHander[PlayerType_Wifi].SetPlayerStatus(status)

	log.Println(log.DebugLog, "unlock")
}
