Skip to content

Commit

Permalink
Merge pull request #3 from adamjakab/devel
Browse files Browse the repository at this point in the history
Devel
  • Loading branch information
adamjakab committed Mar 23, 2020
2 parents 061a86e + 6434401 commit 68b6aad
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 114 deletions.
14 changes: 7 additions & 7 deletions BEETSDIR/config.yaml
Expand Up @@ -28,17 +28,17 @@ xtractor:
force: no
quiet: no
items_per_run: 0
keep_output: no
keep_output: yes
keep_profiles: no
output_path: /Users/jackisback/Documents/Projects/Python/BeetsPluginXtractor/BEETSDIR/xtraction
low_level_extractor: /Users/jackisback/Documents/Projects/Other/extractors/beta5/essentia_streaming_extractor_music
high_level_extractor: /Users/jackisback/Documents/Projects/Other/extractors/beta5/essentia_streaming_extractor_music_svm
low_level_profile:
outputFormat: yaml
outputFrames: 0
outputFormat: yaml
outputFrames: 0
high_level_profile:
outputFormat: json
highlevel:
outputFormat: json
highlevel:
compute: 1
svm_models:
- /Users/jackisback/Documents/Projects/Other/extractors/svm_models_beta5/danceability.history
Expand All @@ -52,6 +52,6 @@ xtractor:
- /Users/jackisback/Documents/Projects/Other/extractors/svm_models_beta5/mood_relaxed.history
- /Users/jackisback/Documents/Projects/Other/extractors/svm_models_beta5/mood_sad.history
- /Users/jackisback/Documents/Projects/Other/extractors/svm_models_beta5/voice_instrumental.history
chromaprint:
compute: 0
chromaprint:
compute: 0

2 changes: 2 additions & 0 deletions MANIFEST.in
@@ -1,3 +1,5 @@
prune test
include LICENSE.txt
include README.md
include beetsplug/xtractor/version.py
include beetsplug/xtractor/config_default.yml
9 changes: 5 additions & 4 deletions README.md
Expand Up @@ -5,19 +5,21 @@

# Xtractor (beets plugin)

*A [beets](https://github.com/beetbox/beets) plugin for insane obsessive-compulsive music geeks.*
The *beets-xtractor* plugin lets you use the extractors of the [Essentia](https://essentia.upf.edu/index.html) project developed by the Music Technology Group.

The *beets-xtractor* plugin lets you use the extractors of the Essentia project developed by (credits here) to...

*NOTE: This plugin is highly unstable and not at all documented! Use it at your own risk*


## Installation
The plugin can be installed via:

```shell script
$ pip install beets-xtractor (*not just yet!*)
$ pip install beets-xtractor
```


## References
[Essentia](https://essentia.upf.edu/index.html)

[SVM Models](https://essentia.upf.edu/svm_models/)
Expand All @@ -28,4 +30,3 @@ $ pip install beets-xtractor (*not just yet!*)

[Acousticbrainz Downloads](https://acousticbrainz.org/download)


17 changes: 8 additions & 9 deletions beetsplug/xtractor/__init__.py
Expand Up @@ -4,23 +4,22 @@
# Created: 3/13/20, 12:17 AM
# License: See LICENSE.txt

import os

from beets.plugins import BeetsPlugin
from beets.util import cpu_count
from beets.util.confit import ConfigSource, load_yaml

from beetsplug.xtractor.command import XtractorCommand


class XtractorPlugin(BeetsPlugin):
_default_plugin_config_file_name_ = 'config_default.yml'

def __init__(self):
super(XtractorPlugin, self).__init__()
self.config.add({
'auto': False,
'dry-run': False,
'write': True,
'threads': cpu_count(),
'force': False,
'quiet': False
})
config_file_path = os.path.join(os.path.dirname(__file__), self._default_plugin_config_file_name_)
source = ConfigSource(load_yaml(config_file_path) or {}, config_file_path)
self.config.add(source)

def commands(self):
return [XtractorCommand(self.config)]
45 changes: 33 additions & 12 deletions beetsplug/xtractor/command.py
Expand Up @@ -142,13 +142,32 @@ def xtract(self):
# Set up the query for unprocessed items
unprocessed_items_query = dbcore.query.OrQuery(
[
# LOW
# dbcore.query.NoneQuery(u'average_loudness', fast=False),
dbcore.query.MatchQuery(u'average_loudness', None, fast=False),
dbcore.query.NumericQuery(u'bpm', u'0'),
dbcore.query.MatchQuery(u'gender', u'', fast=False),
dbcore.query.MatchQuery(u'danceability', None, fast=False),
dbcore.query.MatchQuery(u'beats_count', None, fast=False),

# HIGH
dbcore.query.MatchQuery(u'danceable', None, fast=False),
dbcore.query.MatchQuery(u'gender', None, fast=False),
dbcore.query.MatchQuery(u'genre_rosamerica', None, fast=False),
dbcore.query.MatchQuery(u'voice_instrumental', None, fast=False),

dbcore.query.MatchQuery(u'mood_acoustic', None, fast=False),
dbcore.query.MatchQuery(u'mood_aggressive', None, fast=False),
dbcore.query.MatchQuery(u'mood_electronic', None, fast=False),
dbcore.query.MatchQuery(u'mood_happy', None, fast=False),
dbcore.query.MatchQuery(u'mood_party', None, fast=False),
dbcore.query.MatchQuery(u'mood_relaxed', None, fast=False),
dbcore.query.MatchQuery(u'mood_sad', None, fast=False),
]
)
combined_query = dbcore.query.AndQuery([parsed_query, unprocessed_items_query])

log.debug("Combined query: {}".format(combined_query))

# Get the library items
library_items = self.lib.items(combined_query, parsed_sort)
if len(library_items) == 0:
Expand Down Expand Up @@ -213,23 +232,22 @@ def _run_analysis_high_level(self, item):
self._say("Running high-level analysis: {0}".format(input_path))
self._run_essentia_extractor(extractor_path, input_path, output_path, profile_path)

# todo: allow failing individual attributes
try:
audiodata = bpmHelper.extract_high_level_data(output_path)
target_map = self.config["high_level_targets"]
audiodata = bpmHelper.extract_from_output(output_path, target_map)
except FileNotFoundError as e:
self._say("File not found: {0}".format(e))
return
except KeyError as e:
self._say("Attribute not present: {0}".format(e))
return

print(audiodata)

if not self.cfg_dry_run:
for attr in [
"danceable", "gender", "genre_rosamerica", "voice_instrumental",
"mood_acoustic", "mood_aggressive", "mood_electronic",
"mood_happy", "mood_party", "mood_relaxed", "mood_sad"
]:
setattr(item, attr, audiodata.get(attr))
for attr in audiodata.keys():
if audiodata.get(attr):
setattr(item, attr, audiodata.get(attr))
item.store()

def _run_analysis_low_level(self, item):
Expand All @@ -252,7 +270,9 @@ def _run_analysis_low_level(self, item):
self._run_essentia_extractor(extractor_path, input_path, output_path, profile_path)

try:
audiodata = bpmHelper.extract_low_level_data(output_path)
target_map = self.config["low_level_targets"]
audiodata = bpmHelper.extract_from_output(output_path, target_map)

except FileNotFoundError as e:
self._say("File not found: {0}".format(e))
return
Expand All @@ -261,8 +281,9 @@ def _run_analysis_low_level(self, item):
return

if not self.cfg_dry_run:
for attr in ["bpm"]:
setattr(item, attr, audiodata.get(attr))
for attr in audiodata.keys():
if audiodata.get(attr):
setattr(item, attr, audiodata.get(attr))
item.store()

def _run_essentia_extractor(self, extractor_path, input_path, output_path, profile_path):
Expand Down
65 changes: 65 additions & 0 deletions beetsplug/xtractor/config_default.yml
@@ -0,0 +1,65 @@
auto: no
dry-run: no
write: yes
threads: 1
force: no
quiet: no
low_level_targets:
average_loudness:
path: "lowlevel.average_loudness"
type: float
bpm:
path: "rhythm.bpm"
type: integer
danceability:
path: "rhythm.danceability"
type: float
beats_count:
path: "rhythm.beats_count"
type: integer
high_level_targets:
danceable:
path: "highlevel.danceability.all.danceable"
type: float
gender:
path: "highlevel.gender.value"
type: string
is_male:
path: "highlevel.gender.all.male"
type: float
is_female:
path: "highlevel.gender.all.female"
type: float
genre_rosamerica:
path: "highlevel.genre_rosamerica.value"
type: string
voice_instrumental:
path: "highlevel.voice_instrumental.value"
type: string
is_voice:
path: "highlevel.voice_instrumental.all.voice"
type: float
is_instrumental:
path: "highlevel.voice_instrumental.all.instrumental"
type: float
mood_acoustic:
path: "highlevel.mood_acoustic.all.acoustic"
type: float
mood_aggressive:
path: "highlevel.mood_aggressive.all.aggressive"
type: float
mood_electronic:
path: "highlevel.mood_electronic.all.electronic"
type: float
mood_happy:
path: "highlevel.mood_happy.all.happy"
type: float
mood_party:
path: "highlevel.mood_party.all.party"
type: float
mood_relaxed:
path: "highlevel.mood_relaxed.all.relaxed"
type: float
mood_sad:
path: "highlevel.mood_sad.all.sad"
type: float

0 comments on commit 68b6aad

Please sign in to comment.