go-rekordbox

module
v0.0.0-...-009cde4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 8, 2023 License: MIT

README

Golang wrapper for rekordbox databases

This is a wrapper to query and interact with the rekordbox 6 database

Motivation behind this tool is to enable better automation and integration with rekordbox. In previous versions it was possible to just use the rekordbox XML to query data, but since rekordbox 6 this is no longer possible

Note: This is a quickly hacked together project. Use at own risk, and make a backup of your database.

What is this exactly?

This wrapper is mostly automatically generated by connecting to the rekordbox database and using it's schema to create a bunch of models including helper functions around SQL commands. You can find those in rekordbox/, and the dumped schema in db/schema.sql

Docs are available at https://pkg.go.dev/github.com/dvcrn/go-rekordbox/rekordbox

Install

go get github.com/dvcrn/go-rekordbox

Usage

Create a new rekordbox.Client with rekordbox.NewClient and use it to query

You'll need the following:

  • path to options.json, on mac this is typically ~/Library/Application Support/Pioneer/rekordboxAgent/storage/options.json, on windows potentially in C:\Users\XXXX\AppData\Roaming\Pioneer\rekordboxAgent\storage
package main

import (
	"context"
	"fmt"

	"github.com/dvcrn/go-rekordbox/rekordbox"
)

func main() {
	ctx := context.Background()

	homeDir, err := os.UserHomeDir()
	if err != nil {
		panic(err)
	}

	optionsFilePath := filepath.Join(homeDir, "/Library/Application Support/Pioneer/rekordboxAgent/storage/", "options.json")

	client, err := rekordbox.NewClient(optionsFilePath)
	if err != nil {
		panic(err)
	}
	defer client.Close()

	// your amazing stuff here

Usage Example: Query all playlists

package main

import (
	"context"
	"fmt"

	"github.com/dvcrn/go-rekordbox/rekordbox"
)

func main() {
	ctx := context.Background()

	homeDir, err := os.UserHomeDir()
	if err != nil {
		panic(err)
	}

	optionsFilePath := filepath.Join(homeDir, "/Library/Application Support/Pioneer/rekordboxAgent/storage/", "options.json")

	client, err := rekordbox.NewClient(optionsFilePath)
	if err != nil {
		panic(err)
	}
	defer client.Close()

    // query all playlists
	playlists, err := client.AllDjmdPlaylist(ctx)
	if err != nil {
		panic(err)
	}

	for _, playlist := range playlists {
		fmt.Printf("%s\n", playlist.Name.String)

        // query all songs that are in the playlist
		playlistSongs, err := client.DjmdSongPlaylistByPlaylistID(ctx, playlist.ID)
		if err != nil {
			panic(err)
		}

        // get all songs within the playlist
		for _, playlistSong := range playlistSongs {
			content, err := client.DjmdContentByID(ctx, playlistSong.ContentID)
			if err != nil {
				panic(err)
			}

			fmt.Printf("\t%s\n", content.Title.String)
		}
	}
}

Output:

Tech
        8A - 125 - La Candela Viva (Extended Mix)
        10A - 125 - Do It To It (Extended Mix)
        8B - 125 - Milk Shake (Original Mix)
        6A - 126 - You Give Me A Feeling (Original Mix)
        12B - 129 - My Humps (Original Mix)
        4A - 126 - Want To Love (Extended Mix)
        12B - 126 - Technologic (Original Mix)
        9B - 126 - Disconnected (Extended Mix)
        (...and so on)

Usage Example: Get the most recent plays/songs to use in overlays

	ctx := context.Background()

	homeDir, err := os.UserHomeDir()
	if err != nil {
		panic(err)
	}

	optionsFilePath := filepath.Join(homeDir, "/Library/Application Support/Pioneer/rekordboxAgent/storage/", "options.json")

	client, err := rekordbox.NewClient(optionsFilePath)
	if err != nil {
		panic(err)
	}
	defer client.Close()

	songs, err := client.RecentDjmdSongHistory(ctx, 10)
	if err != nil {
		panic(err)
	}

	fmt.Println("Your recent songs")
	for _, song := range songs {
		content, err := client.DjmdContentByID(ctx, song.ContentID)
		if err != nil {
			panic(err)
		}

		fmt.Printf("\t%s (%s)\n", content.Title.String, song.ContentID.String)
	}

Output:

Your recent songs
        9A - 127 - Intense (Original Mix) (123557411)
        7A - 127 - Gradual Disruption (Original Mix) (154120490)
        7A - 126 - In Chicago (Extended Mix) (175359939)
        7A - 126 - What I Need (Extended) (59136374)
        10A - 127 - Romeo (Wh0 Extended Festival Remix) (59823290)
        9A - 126 - Higher (Original Mix) (183964619)
        9A - 125 - Gimme Some Keys (Original Mix) (247948942)
        10A - 125 - Do It To It (Extended Mix) (150109009)
        1B - 126 - Bad Habits (MEDUZA Extended Remix) (173015998)
        12B - 130 - Last Step (A*S*Y*S Remix) (246529)

Intelligent / Smart playlists

Smart playlists are not fully supported by this library yet, but work has started in djmdplaylist.go. The gist is - there is no built-in support for filtering smartlists. Those are not stored in the DB so you'll have to do the querying yourself.

If a playlist is a smartlist, the field SmartList will be set with an XML of all conditions of the filter like so:

<NODE Id="1820824522" LogicalOperator="2" AutomaticUpdate="0"><CONDITION PropertyName="myTag" Operator="9" ValueUnit="" ValueLeft="-1758883739" ValueRight=""/><CONDITION PropertyName="artist" Operator="11" ValueUnit="" ValueLeft="hoge" ValueRight=""/></NODE>

Note here: Rekordbox stores value as 32bit integer, so if you see a negative value under ValueLeft, this value likely overflowed by exceeding max int32, and you need to reverse the overflow process to get the original value back.

This library comes with helpers to parse the XML content and all sub-conditions. There are also helpers to parse the value and fix the overflow mentioned in the line above.

Here is an example that parses an intelligent playlist with 2 MyTag filters, one by MyTag "Genre" (this overflows and is stored as '-215641850' but the actual ID is '4079325446'), one by MyTag "Energy".

playlist, err := client.DjmdPlaylistByID(ctx, nulltype.NullStringOf("397309610"))
if err != nil {
	panic(err)
}

if playlist.IsSmartlist() {
	fmt.Println("playlist is a smartlist")
	fmt.Println("title: ", playlist.Name)

	node := playlist.SmartlistNode()

	fmt.Println("conditions in this smartlist:")
	for _, condition := range node.Conditions {
		fmt.Println(condition.PropertyName)
		fmt.Println(condition.ParseValueLeft())
	}
}

Outputs:

playlist is a smartlist
title:  Techno
conditions in this smartlist:
myTag
4079325446
myTag
22788202

Usage without generated helper functions

You can also directly access the sqlx.DB object by calling client.GetDB(), and use that to issue queries directly

Contributing

How to regenerate the models (after rekordbox upgrades)

There's a handy Makefile command that does all of this for you (works only on Mac):

make genmodels

You need to have xo (run make deps and sqlcipher installed (through brew))

Extending the default generated code with custom code

To add custom functions ontop of the existin generated code, just create a new file under rekordbox/ and name it accordingly, without xo in the name.

For example, the recent song history implementation is done in djmdsonghistory.go which extends djmdsonghistory.xo.go (the auto-generated one)

Extending code generation

Code generation is handled by xo. To extend the generated code, update the xo template in tpl/schema.xo.go.tpl. Once you updated that, re-generate the models with make genmodels to see your changes

Prior art

  • Initial database connection code taken from supbox

Directories

Path Synopsis
cmd
examples
internal
Package rekordbox contains generated code for schema 'plaintext.db'.
Package rekordbox contains generated code for schema 'plaintext.db'.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL