Using checksums to detect modified stats in the iTunes Library

This commit is contained in:
Deluan 2016-03-15 11:04:17 -04:00
parent bb5d4c920d
commit 12aedc0996
3 changed files with 103 additions and 9 deletions

View File

@ -28,9 +28,11 @@ func init() {
utils.DefineSingleton(new(engine.Playlists), engine.NewPlaylists)
utils.DefineSingleton(new(engine.Search), engine.NewSearch)
utils.DefineSingleton(new(scanner.CheckSumRepository), persistence.NewCheckSumRepository)
utils.DefineSingleton(new(scanner.Scanner), scanner.NewItunesScanner)
// Other dependencies
utils.DefineSingleton(new(itunesbridge.ItunesControl), itunesbridge.NewItunesControl)
utils.DefineSingleton(new(scanner.Scanner), scanner.NewItunesScanner)
utils.DefineSingleton(new(gomate.DB), func() gomate.DB {
return gomate.NewLedisEmbeddedDB(persistence.Db())
})

View File

@ -0,0 +1,61 @@
package persistence
import (
"errors"
"github.com/astaxie/beego"
"github.com/deluan/gosonic/scanner"
"github.com/siddontang/ledisdb/ledis"
)
var (
keyName = []byte("checksums")
)
type checkSumRepository struct {
data map[string]string
}
func NewCheckSumRepository() scanner.CheckSumRepository {
r := &checkSumRepository{}
r.loadData()
return r
}
func (r *checkSumRepository) loadData() {
r.data = make(map[string]string)
pairs, err := Db().HGetAll(keyName)
if err != nil {
beego.Error("Error loading CheckSums:", err)
}
for _, p := range pairs {
r.data[string(p.Field)] = string(p.Value)
}
}
func (r *checkSumRepository) Put(id, sum string) error {
if id == "" {
return errors.New("Id is required")
}
_, err := Db().HSet(keyName, []byte(id), []byte(sum))
return err
}
func (r *checkSumRepository) Get(id string) (string, error) {
return r.data[id], nil
}
func (r *checkSumRepository) SetData(newSums map[string]string) error {
Db().HClear(keyName)
pairs := make([]ledis.FVPair, len(newSums))
r.data = make(map[string]string)
i := 0
for id, sum := range newSums {
p := ledis.FVPair{Field: []byte(id), Value: []byte(sum)}
pairs[i] = p
r.data[id] = sum
i++
}
return Db().HMset(keyName, pairs...)
}

View File

@ -3,19 +3,16 @@ package scanner
import (
"crypto/md5"
"fmt"
"html"
"mime"
"net/url"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"html"
"regexp"
"mime"
"github.com/astaxie/beego"
"github.com/deluan/gosonic/domain"
"github.com/deluan/itl"
@ -29,10 +26,18 @@ type ItunesScanner struct {
playlists map[string]*domain.Playlist
pplaylists map[string]plsRelation
lastModifiedSince time.Time
checksumRepo CheckSumRepository
newSums map[string]string
}
func NewItunesScanner() *ItunesScanner {
return &ItunesScanner{}
func NewItunesScanner(checksumRepo CheckSumRepository) *ItunesScanner {
return &ItunesScanner{checksumRepo: checksumRepo}
}
type CheckSumRepository interface {
Put(id, sum string) error
Get(id string) (string, error)
SetData(newSums map[string]string) error
}
type plsRelation struct {
@ -57,6 +62,7 @@ func (s *ItunesScanner) ScanLibrary(lastModifiedSince time.Time, path string) (i
s.artists = make(map[string]*domain.Artist)
s.playlists = make(map[string]*domain.Playlist)
s.pplaylists = make(map[string]plsRelation)
s.newSums = make(map[string]string)
i := 0
for _, t := range l.Tracks {
@ -71,6 +77,12 @@ func (s *ItunesScanner) ScanLibrary(lastModifiedSince time.Time, path string) (i
}
}
if err := s.checksumRepo.SetData(s.newSums); err != nil {
beego.Error("Error saving checksums:", err)
} else {
beego.Debug("Saved", len(s.newSums), "checksums")
}
ignFolders, _ := beego.AppConfig.Bool("plsIgnoreFolders")
ignPatterns := beego.AppConfig.Strings("plsIgnoredPatterns")
for _, p := range l.Playlists {
@ -158,6 +170,9 @@ func (s *ItunesScanner) fullPath(pID string) string {
}
func (s *ItunesScanner) lastChangedDate(t *itl.Track) time.Time {
if s.hasChanged(t) {
return time.Now()
}
allDates := []time.Time{t.DateModified, t.PlayDateUTC}
c := time.Time{}
for _, d := range allDates {
@ -247,6 +262,14 @@ func (s *ItunesScanner) collectAlbums(t *itl.Track, mf *domain.MediaFile, ar *do
return al
}
func (s *ItunesScanner) hasChanged(t *itl.Track) bool {
id := strconv.Itoa(t.TrackID)
oldSum, _ := s.checksumRepo.Get(id)
newSum := calcCheckSum(t)
s.newSums[id] = newSum
return oldSum != newSum
}
func (s *ItunesScanner) collectArtists(t *itl.Track) *domain.Artist {
id := artistId(t)
_, found := s.artists[id]
@ -317,4 +340,12 @@ func realArtistName(t *itl.Track) string {
return t.Artist
}
// Calc sum of stats fields (whose changes are not reflected in DataModified)
func calcCheckSum(t *itl.Track) string {
data := fmt.Sprint(t.DateModified, t.PlayCount, t.PlayDate, t.ArtworkCount, t.Loved, t.AlbumLoved,
t.Rating, t.AlbumRating, t.SkipCount, t.SkipDate)
return fmt.Sprintf("%x", md5.Sum([]byte(data)))
}
var _ Scanner = (*ItunesScanner)(nil)