icecastLastPlayedSongs was replaced by icecastCurrentSong that fetch only one song at the very end of a playlist.log. Also added a benchmark for this func.
This commit is contained in:
parent
7bb91cfcbe
commit
60045d4ca2
@ -19,7 +19,9 @@ const (
|
|||||||
IcecastPlaylistDateFormat = "02/Jan/2006:15:04:05 -0700"
|
IcecastPlaylistDateFormat = "02/Jan/2006:15:04:05 -0700"
|
||||||
SongTimeFormat = "2006 15:04-0700"
|
SongTimeFormat = "2006 15:04-0700"
|
||||||
|
|
||||||
bufferSizePerLine = 320
|
bufferSizePerLine = 512
|
||||||
|
separatorOne = 26
|
||||||
|
separatorTwo = 38 + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -96,61 +98,46 @@ func IcecastLastSong(playlistPath string) *Song {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func icecastLastPlayedSongs(playlistPath string, n int) ([]Song, error) {
|
func icecastCurrentSong(playlistPath string) (*Song, error) {
|
||||||
var buf []byte
|
fd, err := os.Open(playlistPath)
|
||||||
var offset int64 = 0
|
|
||||||
var bufferSize int64 = int64(n) * bufferSizePerLine
|
|
||||||
|
|
||||||
playlist, err := os.Open(playlistPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer playlist.Close()
|
defer fd.Close()
|
||||||
|
|
||||||
playlist_stat, _ := playlist.Stat()
|
fdSize, _ := fd.Seek(0, io.SeekEnd)
|
||||||
|
if fdSize == 0 {
|
||||||
if playlist_stat.Size() == 0 {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if playlist_stat.Size() < bufferSize {
|
var bufSize int64 = bufferSizePerLine
|
||||||
buf = make([]byte, playlist_stat.Size())
|
if fdSize < bufferSizePerLine {
|
||||||
} else {
|
bufSize = fdSize
|
||||||
buf = make([]byte, bufferSize)
|
|
||||||
offset = playlist_stat.Size() - bufferSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = playlist.ReadAt(buf, offset)
|
_, err = fd.Seek(-bufSize, io.SeekEnd)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lines := bytes.Split(buf, []byte("\n"))
|
buf := make([]byte, bufSize)
|
||||||
|
|
||||||
if len(lines) < 2 {
|
_, err = fd.Read(buf)
|
||||||
return nil, nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = lines[:len(lines)-1]
|
curSongEnd := bytes.LastIndexByte(buf, '\n')
|
||||||
|
line := buf[bytes.LastIndexByte(buf[:curSongEnd], '\n')+1 : curSongEnd]
|
||||||
|
|
||||||
if len(lines) > n {
|
songTime, _ := time.Parse(IcecastPlaylistDateFormat, string(line[:separatorOne]))
|
||||||
lines = lines[len(lines)-n:]
|
|
||||||
}
|
|
||||||
|
|
||||||
songs := make([]Song, 0, len(lines))
|
separatorThree := bytes.LastIndexByte(line, '|')
|
||||||
|
|
||||||
for _, line := range lines {
|
return &Song{
|
||||||
fields := bytes.Split(line, []byte("|"))
|
Time: songTime.Format(SongTimeFormat),
|
||||||
|
Listeners: string(line[separatorTwo:separatorThree]),
|
||||||
tim, _ := time.Parse(IcecastPlaylistDateFormat, string(fields[0]))
|
Song: string(line[separatorThree+1:])}, nil
|
||||||
|
|
||||||
songs = append(songs, Song{
|
|
||||||
Time: tim.Format(SongTimeFormat),
|
|
||||||
Listeners: string(fields[2]),
|
|
||||||
Song: string(fields[3])})
|
|
||||||
}
|
|
||||||
|
|
||||||
return songs, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PlaylistLogWatcher struct {
|
type PlaylistLogWatcher struct {
|
||||||
@ -180,13 +167,14 @@ func (pw *PlaylistLogWatcher) Watch(playlistPath string, n int) (err error) {
|
|||||||
pw.watcher.WatchForMask(pw.changed, watcher.ModIgnMask)
|
pw.watcher.WatchForMask(pw.changed, watcher.ModIgnMask)
|
||||||
|
|
||||||
if lastPlayedCache == nil {
|
if lastPlayedCache == nil {
|
||||||
lastPlayedCache = make([]Song, n)
|
lastPlayedCache = make([]Song, 0, n)
|
||||||
songs, err := icecastLastPlayedSongs(playlistPath, n)
|
|
||||||
if err == nil && len(songs) > 0 {
|
|
||||||
copy(lastPlayedCache, songs)
|
|
||||||
} else if err != nil {
|
|
||||||
log.Fatalln("failed to retrieve last songs:", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cur, err := icecastCurrentSong(playlistPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to fetch current song:", err)
|
||||||
|
} else if cur != nil && currentlyPlaying.Time != "" {
|
||||||
|
currentlyPlaying = *cur
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -195,19 +183,19 @@ func (pw *PlaylistLogWatcher) Watch(playlistPath string, n int) (err error) {
|
|||||||
|
|
||||||
if mask&syscall.IN_MODIFY > 0 {
|
if mask&syscall.IN_MODIFY > 0 {
|
||||||
lastPlayedCacheMutex.Lock()
|
lastPlayedCacheMutex.Lock()
|
||||||
songs, err := icecastLastPlayedSongs(playlistPath, 1)
|
song, err := icecastCurrentSong(playlistPath)
|
||||||
if err == nil && len(songs) > 0 {
|
if err == nil && song != nil {
|
||||||
CheckAndUpdateMostListenedSong(song, ¤tlyPlaying)
|
CheckAndUpdateMostListenedSong(song, ¤tlyPlaying)
|
||||||
if currentlyPlaying.Time == "" {
|
if currentlyPlaying.Time == "" {
|
||||||
currentlyPlaying = songs[0]
|
currentlyPlaying = *song
|
||||||
} else {
|
} else {
|
||||||
currentlyPlaying.Listeners = songs[0].Listeners
|
currentlyPlaying.Listeners = song.Listeners
|
||||||
if len(lastPlayedCache) == n {
|
if len(lastPlayedCache) == n {
|
||||||
lastPlayedCache = append(lastPlayedCache[1:], currentlyPlaying)
|
lastPlayedCache = append(lastPlayedCache[1:], currentlyPlaying)
|
||||||
} else {
|
} else {
|
||||||
lastPlayedCache = append(lastPlayedCache, currentlyPlaying)
|
lastPlayedCache = append(lastPlayedCache, currentlyPlaying)
|
||||||
}
|
}
|
||||||
currentlyPlaying = songs[0]
|
currentlyPlaying = *song
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
log.Println("failed to retrieve last songs:", err)
|
log.Println("failed to retrieve last songs:", err)
|
||||||
|
12
internal/radio/icecast_test.go
Normal file
12
internal/radio/icecast_test.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package radio
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
const playlistPath = "../../p.log"
|
||||||
|
|
||||||
|
func BenchmarkIcecastCurrentSong(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
s, err := icecastCurrentSong(playlistPath)
|
||||||
|
b.Log(s, err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user