about summary refs log tree commit diff
path: root/tools/mpd-stats
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mpd-stats')
-rw-r--r--tools/mpd-stats/LICENSE.txt20
-rw-r--r--tools/mpd-stats/Makefile12
-rw-r--r--tools/mpd-stats/README.org22
-rw-r--r--tools/mpd-stats/cmd/mpd-scrobbler/main.go57
-rw-r--r--tools/mpd-stats/go.mod9
-rw-r--r--tools/mpd-stats/go.sum6
-rw-r--r--tools/mpd-stats/internal/mpd/mpd.go54
-rw-r--r--tools/mpd-stats/internal/scrobbler/db.go55
-rw-r--r--tools/mpd-stats/internal/scrobbler/record.go42
-rw-r--r--tools/mpd-stats/internal/scrobbler/record_test.go53
-rw-r--r--tools/mpd-stats/internal/scrobbler/scrobbler.go118
-rw-r--r--tools/mpd-stats/systemd/mpd-scrobbler.service42
12 files changed, 0 insertions, 490 deletions
diff --git a/tools/mpd-stats/LICENSE.txt b/tools/mpd-stats/LICENSE.txt
deleted file mode 100644
index e614d87..0000000
--- a/tools/mpd-stats/LICENSE.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2021 Franck Cuny
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/tools/mpd-stats/Makefile b/tools/mpd-stats/Makefile
deleted file mode 100644
index 0edb9dd..0000000
--- a/tools/mpd-stats/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-GO_INSTALL_ARGS := -trimpath
-
-.PHONY: install
-
-install:
-	@go install $(GO_INSTALL_ARGS) ./cmd/mpd-scrobbler
-	@install -m 0644 systemd/mpd-scrobbler.service $(HOME)/.config/systemd/user/
-	@echo "reloading systemd"
-	@systemctl --user daemon-reload
-	@echo "starting the unit"
-	@systemctl --user restart mpd-scrobbler
-	@systemctl --user status mpd-scrobbler
diff --git a/tools/mpd-stats/README.org b/tools/mpd-stats/README.org
deleted file mode 100644
index 8c0a7d9..0000000
--- a/tools/mpd-stats/README.org
+++ /dev/null
@@ -1,22 +0,0 @@
-#+TITLE: mpd-stats
-
-Log played songs to extract statistics. This is similar to what libre.fm used to do, but locally.
-
-* Logging
-Collect logs from mpd. A log record is composed of the following fields:
-- id: UUID
-- song's name: the name of the song
-- song's album: the name of the album
-- song's artist: the name of the artist
-- song's duration: the duration of the song
-- date: date the song was played
-
-The logs are recorded in a database (sqlite3 to start).
-* Install
-The Makefile assumes the system is running Linux and systemd.
-
-Run =make install=. This will:
-- install the binary in your =GOPATH= (using =go install=)
-- install a systemd unit file under =$HOME/.config/systemd/user=
-- reload systemd unit files
-- start the service
diff --git a/tools/mpd-stats/cmd/mpd-scrobbler/main.go b/tools/mpd-stats/cmd/mpd-scrobbler/main.go
deleted file mode 100644
index c2693a4..0000000
--- a/tools/mpd-stats/cmd/mpd-scrobbler/main.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package main
-
-import (
-	"flag"
-	"fmt"
-	"log"
-	"os"
-	"path/filepath"
-
-	"golang.fcuny.net/mpd-stats/internal/scrobbler"
-)
-
-func main() {
-	var (
-		mpdHost = flag.String("host", "localhost", "The MPD server to connect  to (default: localhost)")
-		mpdPort = flag.Int("port", 6600, "The TCP port of the MPD server to connect to (default: 6600)")
-	)
-	flag.Parse()
-
-	net := "tcp"
-	addr := fmt.Sprintf("%s:%d", *mpdHost, *mpdPort)
-
-	dbpath, err := getDbPath()
-	if err != nil {
-		log.Fatalf("failed to get the path to the database: %v", err)
-	}
-
-	s, err := scrobbler.NewScrobbler(net, addr, dbpath)
-	if err != nil {
-		log.Fatalf("failed to create a client: %v", err)
-	}
-
-	defer func() {
-		if err := s.Close(); err != nil {
-			log.Fatalf("failed to close the scrobbler: %v", err)
-		}
-	}()
-
-	s.Run()
-}
-
-func getDbPath() (string, error) {
-	xch := os.Getenv("XDG_CONFIG_HOME")
-	if xch == "" {
-		home := os.Getenv("HOME")
-		xch = filepath.Join(home, ".config")
-	}
-
-	scrobblerHome := filepath.Join(xch, "mpd-scrobbler")
-	if _, err := os.Stat(scrobblerHome); os.IsNotExist(err) {
-		if err := os.Mkdir(scrobblerHome, 0755); err != nil {
-			return "", err
-		}
-	}
-
-	return filepath.Join(scrobblerHome, "scrobbler.sql"), nil
-}
diff --git a/tools/mpd-stats/go.mod b/tools/mpd-stats/go.mod
deleted file mode 100644
index cc9971c..0000000
--- a/tools/mpd-stats/go.mod
+++ /dev/null
@@ -1,9 +0,0 @@
-module golang.fcuny.net/mpd-stats
-
-go 1.17
-
-require (
-	github.com/fhs/gompd/v2 v2.2.0
-	github.com/google/uuid v1.3.0
-	github.com/mattn/go-sqlite3 v1.14.8
-)
diff --git a/tools/mpd-stats/go.sum b/tools/mpd-stats/go.sum
deleted file mode 100644
index fab0f00..0000000
--- a/tools/mpd-stats/go.sum
+++ /dev/null
@@ -1,6 +0,0 @@
-github.com/fhs/gompd/v2 v2.2.0 h1:zdSYAAOzQ5cCCgYa5CoXkL0Vr0Cqb/b5JmTobirLc90=
-github.com/fhs/gompd/v2 v2.2.0/go.mod h1:nNdZtcpD5VpmzZbRl5rV6RhxeMmAWTxEsSIMBkmMIy4=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU=
-github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
diff --git a/tools/mpd-stats/internal/mpd/mpd.go b/tools/mpd-stats/internal/mpd/mpd.go
deleted file mode 100644
index 859348e..0000000
--- a/tools/mpd-stats/internal/mpd/mpd.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package mpd
-
-import (
-	"log"
-	"time"
-
-	"github.com/fhs/gompd/v2/mpd"
-)
-
-const (
-	// List of subsystems: https://mpd.readthedocs.io/en/latest/protocol.html#querying-mpd-s-status
-	SubSystemPlayer = "player"
-)
-
-type Player struct {
-	Watcher *mpd.Watcher
-	Client  *mpd.Client
-}
-
-func NewPlayer(net string, addr string) (*Player, error) {
-	var (
-		p   Player
-		err error
-	)
-
-	// We are only subscribing to the player subsystem
-	p.Watcher, err = mpd.NewWatcher(net, addr, "", SubSystemPlayer)
-	if err != nil {
-		log.Fatalf("failed to create a watcher: %v", err)
-	}
-
-	p.Client, err = mpd.Dial(net, addr)
-	if err != nil {
-		log.Fatalf("failed to start mpd client: %v", err)
-	}
-
-	go func() {
-		for range time.Tick(30 * time.Second) {
-			p.Client.Ping()
-		}
-	}()
-
-	return &p, nil
-}
-
-func (p *Player) Close() error {
-	if err := p.Watcher.Close(); err != nil {
-		return err
-	}
-	if err := p.Client.Close(); err != nil {
-		return err
-	}
-	return nil
-}
diff --git a/tools/mpd-stats/internal/scrobbler/db.go b/tools/mpd-stats/internal/scrobbler/db.go
deleted file mode 100644
index 5f80aa4..0000000
--- a/tools/mpd-stats/internal/scrobbler/db.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package scrobbler
-
-import (
-	"database/sql"
-	"fmt"
-	"os"
-
-	_ "github.com/mattn/go-sqlite3"
-)
-
-func initdb(dbpath string) error {
-	if _, err := os.Stat(dbpath); err == nil {
-		return fmt.Errorf("%s already exists", dbpath)
-	}
-
-	db, err := sql.Open("sqlite3", dbpath)
-	if err != nil {
-		return err
-	}
-	defer db.Close()
-
-	sqlStmt := `create table records (id text primary key,
-				title text,
-				artist text,
-                album text,
-                duration int,
-                playtime int,
-                time timestamp
-			);`
-
-	_, err = db.Exec(sqlStmt)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func opendatabase(dbpath string) (*sql.DB, error) {
-	var err error
-	_, err = os.Stat(dbpath)
-
-	if err != nil {
-		if err := initdb(dbpath); err != nil {
-			return nil, err
-		}
-	}
-
-	db, err := sql.Open("sqlite3", dbpath)
-	if err != nil {
-		return nil, fmt.Errorf("unable to open database: %s", err)
-	}
-
-	return db, nil
-}
diff --git a/tools/mpd-stats/internal/scrobbler/record.go b/tools/mpd-stats/internal/scrobbler/record.go
deleted file mode 100644
index e252fd3..0000000
--- a/tools/mpd-stats/internal/scrobbler/record.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package scrobbler
-
-import (
-	"strconv"
-	"time"
-
-	"github.com/fhs/gompd/v2/mpd"
-	"github.com/google/uuid"
-)
-
-type Record struct {
-	Id        uuid.UUID
-	Title     string
-	Album     string
-	Artist    string
-	Duration  time.Duration
-	Timestamp time.Time
-}
-
-func NewRecord(attrs mpd.Attrs) (*Record, error) {
-	record := Record{
-		Id:        uuid.New(),
-		Title:     attrs["Title"],
-		Album:     attrs["Album"],
-		Artist:    attrs["Artist"],
-		Timestamp: time.Now(),
-	}
-
-	dur, err := strconv.ParseFloat(attrs["duration"], 32)
-	if err != nil {
-		return nil, err
-	}
-
-	record.Duration = time.Second * time.Duration(dur)
-	return &record, nil
-}
-
-func (r *Record) EqualAttrs(attrs mpd.Attrs) bool {
-	return r.Title == attrs["Title"] &&
-		r.Album == attrs["Album"] &&
-		r.Artist == attrs["Artist"]
-}
diff --git a/tools/mpd-stats/internal/scrobbler/record_test.go b/tools/mpd-stats/internal/scrobbler/record_test.go
deleted file mode 100644
index 3bf8554..0000000
--- a/tools/mpd-stats/internal/scrobbler/record_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package scrobbler
-
-import (
-	"testing"
-
-	"github.com/fhs/gompd/v2/mpd"
-)
-
-func TestNewRecord(t *testing.T) {
-	song := mpd.Attrs{
-		"Artist":   "Nine Inch Nails",
-		"Album":    "The Downward Spiral",
-		"Title":    "Reptile",
-		"duration": "411.00",
-	}
-
-	record, err := NewRecord(song)
-	if err != nil {
-		t.Errorf("NewRecord returned an error: %s", err)
-	}
-	if record == nil {
-		t.Errorf("NewRecord returned nil record")
-	}
-}
-
-func TestRecordEqualAttrs(t *testing.T) {
-	s1 := mpd.Attrs{
-		"Artist":   "Nine Inch Nails",
-		"Album":    "The Downward Spiral",
-		"Title":    "Reptile",
-		"duration": "411.00",
-	}
-
-	s2 := mpd.Attrs{
-		"Artist":   "Nine Inch Nails",
-		"Album":    "The Downward Spiral",
-		"Title":    "Closer",
-		"duration": "373.00",
-	}
-
-	r, err := NewRecord(s1)
-	if err != nil {
-		t.Errorf("NewRecord returned an error: %s", err)
-	}
-
-	if !r.EqualAttrs(s1) {
-		t.Errorf("EqualAttrs expected true got false")
-	}
-
-	if r.EqualAttrs(s2) {
-		t.Errorf("EqualAttrs expected false got true")
-	}
-}
diff --git a/tools/mpd-stats/internal/scrobbler/scrobbler.go b/tools/mpd-stats/internal/scrobbler/scrobbler.go
deleted file mode 100644
index eb4eb9c..0000000
--- a/tools/mpd-stats/internal/scrobbler/scrobbler.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package scrobbler
-
-import (
-	"database/sql"
-	"log"
-	"time"
-
-	"golang.fcuny.net/mpd-stats/internal/mpd"
-)
-
-type Scrobbler struct {
-	player *mpd.Player
-	db     *sql.DB
-}
-
-func NewScrobbler(net string, addr string, dbpath string) (*Scrobbler, error) {
-	p, err := mpd.NewPlayer(net, addr)
-	if err != nil {
-		return nil, err
-	}
-
-	db, err := opendatabase(dbpath)
-	if err != nil {
-		return nil, err
-	}
-
-	s := Scrobbler{
-		player: p,
-		db:     db,
-	}
-
-	return &s, nil
-}
-
-func (s *Scrobbler) Close() error {
-	return s.player.Close()
-}
-
-func (s *Scrobbler) Run() error {
-	var (
-		currentRecord  *Record
-		previousRecord *Record
-	)
-
-	for {
-		e := <-s.player.Watcher.Event
-		if e == mpd.SubSystemPlayer {
-			status, err := s.player.Client.Status()
-			if err != nil {
-				log.Printf("could not read the status: %v", err)
-			}
-
-			if status["state"] == "stop" {
-				if currentRecord != nil {
-					if err := s.update(currentRecord); err != nil {
-						log.Printf("failed to update record %s: %s", currentRecord.Id, err)
-					}
-					currentRecord = nil
-				}
-				continue
-			}
-
-			attrs, err := s.player.Client.CurrentSong()
-			if err != nil {
-				log.Printf("could not get current song: %v", err)
-			}
-
-			if currentRecord == nil {
-				currentRecord, err = NewRecord(attrs)
-				if err != nil {
-					log.Printf("could not create a log: %v", err)
-				}
-				previousRecord = currentRecord
-				if err := s.save(currentRecord); err != nil {
-					log.Printf("failed to insert record %s: %s", currentRecord.Id, err)
-				}
-				continue
-			}
-
-			if !currentRecord.EqualAttrs(attrs) {
-				currentRecord, err = NewRecord(attrs)
-				if err != nil {
-					log.Printf("could not create a log: %v", err)
-				}
-			}
-
-			if currentRecord.Id != previousRecord.Id {
-				if err := s.update(previousRecord); err != nil {
-					log.Printf("failed to update record %s: %s", previousRecord.Id, err)
-				}
-				previousRecord = currentRecord
-				s.save(currentRecord)
-			}
-		}
-	}
-}
-
-func (s *Scrobbler) save(record *Record) error {
-	_, err := s.db.Exec("insert into records(id, title, artist, album, duration, playtime, time) values(?, ?, ?, ?, ?, 0, ?)",
-		record.Id,
-		record.Title,
-		record.Artist,
-		record.Album,
-		int(record.Duration.Seconds()),
-		record.Timestamp,
-	)
-	return err
-}
-
-func (s *Scrobbler) update(record *Record) error {
-	tnow := time.Now()
-	playtime := tnow.Sub(record.Timestamp).Seconds()
-	_, err := s.db.Exec("update records set playtime = ? where id = ?",
-		int(playtime),
-		record.Id,
-	)
-	return err
-}
diff --git a/tools/mpd-stats/systemd/mpd-scrobbler.service b/tools/mpd-stats/systemd/mpd-scrobbler.service
deleted file mode 100644
index e033a99..0000000
--- a/tools/mpd-stats/systemd/mpd-scrobbler.service
+++ /dev/null
@@ -1,42 +0,0 @@
-[Unit]
-Description=mpd scrobbler
-Documentation=https://git.fcuny.net/fcuny/mpd-stats
-ConditionFileIsExecutable=%h/workspace/go/bin/mpd-scrobbler
-
-[Service]
-ExecStart=%h/workspace/go/bin/mpd-scrobbler
-Restart=on-failure
-
-PrivateTmp=yes
-ProtectSystem=strict
-NoNewPrivileges=yes
-ProtectHome=yes
-
-# Prohibit access to any kind of namespacing:
-RestrictNamespaces=yes
-
-# Make cgroup file system hierarchy inaccessible:
-ProtectControlGroups=yes
-
-# Deny access to other user’s information in /proc:
-ProtectProc=invisible
-
-# Only allow access to /proc pid files, no other files:
-ProcSubset=pid
-
-# This daemon must not create any new files, but set the umask to 077 just in case.
-UMask=077
-
-# Filter dangerous system calls. The following is listed as safe basic choice
-# in systemd.exec(5):
-SystemCallArchitectures=native
-SystemCallFilter=@system-service
-SystemCallFilter=~@privileged
-SystemCallFilter=~@resources
-SystemCallErrorNumber=EPERM
-
-# Deny kernel execution domain changing:
-LockPersonality=yes
-
-# Deny memory mappings that are writable and executable:
-MemoryDenyWriteExecute=yes