Attached is a patch which adds a source which reads a beets library
database using Emacs' built-in SQLite support.

This source is a bit weird; since the beets database also contains the
metadata of the tracks, you don't need to use any info function.  It
read (and saved to the cache, with metadata) a library of ~1450 tracks
in under a second on my PC (I can honestly just disable the cache at
this point).

I've thought about just providing an info function which queries the
beets database (and maybe it could be added anyway), but since it also
contains the paths of all the tracks it made sense to use a source.

I've also thought about integrating the beets database by setting the
various cache function variables to functions which use the database as
the "cache", but I'm not really sure how it would work with tracks
outside the beets library, and it would also require far greater
proficiency in SQL than I currently posses (which is basically nothing).
A huge benefit to this approach theoretically would be the ability to
automatically synchronize edits using the tag editor with the library
database.

The command could be extended in the future to allow more fine-tuned
control over the tracks added.  The beets database contains a table of
albums, and it's probably possible to get a list of album artists from
the items (tracks) table, so it should be possible to allow filtering
the tracks added from the database.

Daniel

>From d0dae3706e084e827399be7e78c652efb51177bc Mon Sep 17 00:00:00 2001
From: Daniel Semyonov <dan...@dsemy.com>
Date: Mon, 11 Dec 2023 13:55:34 +0200
Subject: [PATCH] Add an EMMS source for beets library databases

---
 emms-source-beets.el | 83 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 emms-source-beets.el

diff --git a/emms-source-beets.el b/emms-source-beets.el
new file mode 100644
index 0000000..4768a83
--- /dev/null
+++ b/emms-source-beets.el
@@ -0,0 +1,83 @@
+;;; emms-source-beets.el --- Native Emacs Lisp info method for EMMS -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; Author: Daniel Semyonov <dan...@dsemy.com>
+
+;; This file is part of EMMS.
+
+;; EMMS is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; EMMS is distributed in the hope that it will be useful, but WITHOUT
+;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+;; License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with EMMS; see the file COPYING. If not, write to the Free
+;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+;; MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This file provides an EMMS source which utilizes Emacs' SQLite
+;; support to read a beets library database.
+
+;;; Code:
+
+(require 'emms)
+
+(defgroup emms-source-beets nil
+  "EMMS source for beets library databases."
+  :group 'emms-source
+  :prefix "emms-source-beets-")
+
+(defcustom emms-source-beets-database
+  (expand-file-name "beets/library.db"
+                    (or (getenv "XDG_CONFIG_HOME") "~/.config"))
+  "Database containing beets library information."
+  :type 'file)
+
+;;;###autoload (autoload 'emms-play-beets "emms-source-beets" nil t)
+;;;###autoload (autoload 'emms-add-beets "emms-source-beets" nil t)
+(define-emms-source beets ()
+  "An EMMS source for beets library databases."
+  (interactive)
+  (unless (and (fboundp 'sqlite-available-p)
+               (sqlite-available-p))
+    (user-error
+     "SQLite support is not available (required to read beets databases)"))
+  (let* ((db (sqlite-open emms-source-beets-database))
+         (types (mapcar
+                 (lambda (type)
+                   (cons (intern (concat "info-" (cadr type))) (car type)))
+                 (sqlite-select db "PRAGMA table_info(items)"))))
+    (mapc (lambda (item)
+            (let* ((path (decode-coding-string
+                          (nth (cdr (assq 'info-path types)) item)
+                          'utf-8 t))
+                   (track (emms-track 'file path))
+                   (total (ceiling (nth (cdr (assq 'info-length types)) item))))
+              (mapc (lambda (type)
+                      (unless (eq (car type) 'info-mtime) ; confuses EMMS
+                        (emms-track-set track (car type)
+                                        (nth (cdr type) item))))
+                    types)
+              (emms-track-set track 'info-playing-time-min (/ total 60))
+              (emms-track-set track 'info-playing-time-sec (% total 60))
+              (emms-track-set track 'info-playing-time total)
+              (emms-track-set track 'info-file path)
+              (emms-track-set track 'info-tracknumber
+                              (number-to-string
+                               (emms-track-get track 'info-track)))
+              (emms-playlist-insert-track track)
+              (when (fboundp emms-cache-set-function)
+                (funcall emms-cache-set-function 'file path track))))
+          (sqlite-select db "select * from items"))))
+
+(provide 'emms-source-beets)
+
+;;; emms-source-beets.el ends here...
-- 
2.43.0

Reply via email to