musicbox 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9a8a6574c510c69dd25263a2bc0f70ffc791d70dfbd384525b2377a6761250e9
4
+ data.tar.gz: 6d7ac478b52e5ce9ae490df0062e1184ff26f1f49138b9b79efb70e8f2340e05
5
+ SHA512:
6
+ metadata.gz: 6538ba594a30af05bc5844162c9d61c567a4bc428ef9ee41346e7d0d6a10e2677448150009e0f5026dc15cfcc93cdfbf6bf0c96a4abe689266761591487c6922
7
+ data.tar.gz: f88d27a2c821cbfcdcaa0b497da9a92fe6eaeb45036b6617de1e436032cefb8c01bb2168d076c6ca21adf6346aa2bbbc0139f93178e03721a1632e3a9e7111ac
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ *.gem
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'path', github: 'eregon/path'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 John Labovitz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # MusicBox
2
+
3
+ MusicBox is an opinionated little music management system. It does the following:
4
+
5
+ - uses the [Discogs](https://discogs.com) platform as a source of metadata
6
+ - stores and plays digital audio files corresponding to the tracks of albums
7
+ - manages album cover art, downloading replacement cover images where necessary
8
+ - allows manipulating of metadata so audio tracks correspond to the actual release
9
+ - prints custom labels for sleeves and cases
10
+ - prints covers for CDs that don’t have an insert (ie, digipaks)
11
+ - allows extensive searching, not just for artist/titles, but also whether an album is missing cover art, hasn't been ripped, etc.
12
+ - stores all data locally, so no internet connection required
13
+ - exports to iTunes to be able to sync collection with iPhones, etc.
14
+
15
+
16
+ ## Conceptual overview
17
+
18
+ It will be helpful to understand some basic concepts and common structures used in MusicBox. These concepts guide the forward development of MusicBox, as well as acting as design constraints to keep the scope of MusicBox reasonably limited.
19
+
20
+
21
+ ### Collection oriented, album/artist oriented
22
+
23
+ MusicBox is designed by and for people who collect music, specifically in the form of albums, in a certain sequence as created by the artist and their producers. MusicBox treats an artist's creation of an album as a sacred object. Hence, features generally revolve around working with albums as a whole. While there is the occasional random-shuffle feature, the MusicBox user will find themselves naturally led towards appreciating albums from start to finish.
24
+
25
+ While MusicBox plays audio files imported into its catalog, it does not leave the physical media behind. Rather, the user is encouraged to maintain an organized library that can be appreciated along with the digital representations.
26
+
27
+ When maintaining a physical library, one of the challenges is keeping the items well-organized. MusicBox helps by allowing the collector to create adhesive paper labels (printed using a standard label printer) with sufficient information to allow the items to be kept in order, generally by artist and release year.
28
+
29
+ Artist names is a particularly tricky part of the organizational process. MusicBox's simple but effective identification system generates and uses _artist keys._ These are a short but unique representation of an artist's name that is easily alphabetized. For example, Bruce Springsteen is `SprB`, Modest Mouse is `ModM`, and Parliament is `Par`. Configuration options allow for proper sorting of personal names, as well as aliases.
30
+
31
+
32
+ ### Highest quality
33
+
34
+ While MusicBox is generally agnostic about the source of audio files, it can handle sources that consist of downloaded files, tracks ripped from CD, or digitized from LP or tape. There is no specific limit or constraint to resolution, sample rate, or file format. If you have high quality audio files, and high quality playback equipment, you should be able to utilize the maximum quality.
35
+
36
+ Furthermore, while listening to music, you can easily specify equalization filters, using sources such as [AutoEq](https://github.com/jaakkopasanen/AutoEq)'s database of neutral headphone adjustment curves.
37
+
38
+ MusicBox uses the excellent [MPV](https://mpv.io) media player to play audio files. When configured appropriately, MPV can play any audio files that MusicBox can store, in bit-perfect quality.
39
+
40
+
41
+ ### Local storage
42
+
43
+ MusicBox is defiantly non-streaming. All audio files and related metadata reside in a real directory of real files. While these files can easily be synchronized to other devices, there is no use or dependency on cloud-based or proprietary services (besides Discogs; see below). Once MusicBox has the needed metadata, playing albums or tracks requires absolutely no internet connection.
44
+
45
+ By design, there is little or no functionality commonly found in other platforms and players, such as playlists, favorites, recommendations, popularity lists, lyrics, and so on.
46
+
47
+ Generally, all data is in open formats, such as JSON and YAML, to encourage the creation of tools to manipulate and maintain data. For example, MusicBox's catalog can easily be version-controlled with [Git](https://git-scm.com).
48
+
49
+
50
+ ### Discogs as the 'truth'
51
+
52
+ Instead of re-inventing the wheel to store the extremely complex schema of music releases, MusicBox delegates most of that to the very competent [Discogs](https://discogs.com) platform. Much work has been done to avoid duplicating Discogs' breadth of data, and instead to use Discogs as the authoritative 'truth' wherever possible.
53
+
54
+ Discogs has several databases that MusicBox 'borrows' (generally by downloading and caching locally), including:
55
+
56
+ - _release_ -- Represents a specific issuance of a record, such as a CD or LP. You could call it an 'album,' but in MusicBox, an album is something else (see below).
57
+
58
+ - _master_ -- Represents more than one version of a release. Not all releases have a master. For example, if a given record is issued only on CD, in a single pressing, there may not be a master. But if the record is issued on vinyl & CD, there will be one _master_ record and two _release_ records.
59
+
60
+ - _collection_ -- The list of releases actually owned by the user (ie, you). MusicBox generally uses the collection to synchronize associated releases, masters, and artists. If you update your collection on Discogs, you can issue a MusicBox command to download the changed collection, and update the related records.
61
+
62
+ Data structures in Discogs are identified with an ID, which is an integer. You will sometimes see this shown in MusicBox, and it is useful to identify a specific release, master, or album.
63
+
64
+ In general, you must keep your Discogs collection updated according to your actual music collection. MusicBox itself has no way of editing your collection; you must edit on Discogs' site, and then update to synchronize. (In the future, MusicBox may support non-released albums -- like personal mixtapes -- but for now, an Discogs account with an accurate collection is required.)
65
+
66
+
67
+ ### Albums as 'real' instances
68
+
69
+ In addition to the Discogs structures described above, MusicBox maintains a database of _albums._ An album can be understood as a specific instance of a release (which may be one of several releases, under a master). In MusicBox, an album is where the actual audio tracks and other associated files are stored. In general, the ID of an album is equal to the ID of its associated release (but this may change).
70
+
71
+ An album has one or more tracks, which are the actual song files for the release tracks.
72
+
73
+ Occasionally, the tracks of a digitized album do not match up with the tracks listed under a release. For example, a ripped CD might combine several sub-tracks into a single track. The `import` command allows you to customize the matching between album tracks and release tracks.
74
+
75
+
76
+ ### Queries
77
+
78
+ Most commands support a generalized query language. The simplest form is a series of words, which will search the text of album titles and artists:
79
+
80
+ ```
81
+ musicbox show smith
82
+ ```
83
+
84
+ Further _selectors_ (prefixed with a colon) modify a query, such as:
85
+
86
+ - `:recently-added` -- added to collection in last 30 days
87
+ - `:cd` -- releases that are in CD format
88
+ - `:unripped` -- releases that do not yet have any audio files
89
+ - `:no-cover` -- releases without a cover image
90
+
91
+
92
+ ### Command-line first
93
+
94
+ MusicBox is controlled entirely through a command-line interface. The main tool is called `musicbox`, with successive sub-commands to perform certain actions. For example, to play Holger Czukay's [Moving Pictures](https://www.discogs.com/Holger-Czukay-Moving-Pictures/release/159821), applying equalization adjustment for Apple's Beats X headphones:
95
+
96
+ ```
97
+ musicbox play --eq='Beats X' 'Moving Pictures'
98
+ ```
99
+
100
+ Future versions of MusicBox may incorporate standalone/headless instances, or platform-specific players.
101
+
102
+
103
+ ## Installation
104
+
105
+ 1. Confirm that you are using a Mac running macOS, preferably with [Homebrew](https://brew.sh) installed and working. Although MusicBox does not explicitly require macOS, there are a few places where other systems will likely break at the moment. MusicBox was developed using Ruby 3.0. It may not be compatible with earlier versions.
106
+
107
+ 2. Install this gem, and dependent gems:
108
+
109
+ ```
110
+ gem install musicbox
111
+ ```
112
+
113
+ 3. Install required Homebrew packages:
114
+
115
+ ```
116
+ brew install mpv mp4v ffmpeg
117
+ ```
118
+
119
+ 4. If you want MusicBox to store its files other than the default location of `~/Music/MusicBox`, then set the `MUSICBOX_ROOT` environment variable in your shell:
120
+
121
+ ```
122
+ export MUSICBOX_ROOT=somewhere_else
123
+ ```
124
+
125
+ 5. Create the directory:
126
+
127
+ ```
128
+ mkdir ~/Music/MusicBox
129
+ ```
130
+
131
+ 6. Create a Discogs account, if you haven't already, and add at least one release to your collection.
132
+
133
+ 7. Create a personal access token on Discogs by going to [this page](https://www.discogs.com/settings/developers) and clicking the blue button labeled 'Generate new token'. Copy the generated token for the next step.
134
+
135
+ 8. Create a simple configuration file named `config.yaml` in your main MusicBox directory:
136
+
137
+ ```
138
+ # $MUSICBOX_ROOT/config.yaml
139
+ user: YOUR_DISCOGS_USERNAME
140
+ token: YOUR_DISCOGS_TOKEN
141
+ ```
142
+
143
+
144
+ ## Operation
145
+
146
+ Here are a few common commands. There are more uncommon commands, as yet undocumented.
147
+
148
+
149
+ ### Update your collection
150
+
151
+ ```
152
+ musicbox update
153
+ ```
154
+
155
+ This will connect to Discogs, download your collection, and download any further information for the releases in your collection.
156
+
157
+
158
+ ### Search and show
159
+
160
+ ```
161
+ musicbox show [QUERY]
162
+ ```
163
+
164
+ This will search your collection for `QUERY` and show them in a summary view. By adding `--details` after `show`, MusicBox will show all the details of the found releases. If no query is specified, all releases are shown.
165
+
166
+
167
+ ### Import
168
+
169
+ ```
170
+ musicbox import [DIR ...]
171
+ ```
172
+
173
+ This will import albums into your catalog. You can specify the directories to import, or create a directory called `import` inside your MusicBox directory containing directories to import.
174
+
175
+ Each directory should contain an entire album, with each track represented as an `.mp4` file. MusicBox will attempt to find the correct release for the given album, as well as the tracks. If the track info does not line up with the information in the related Discogs release, MusicBox will begin an interactive prompting session. (If this doesn't work, you'll have to edit the JSON file by hand.)
176
+
177
+
178
+ ### Export
179
+
180
+ ```
181
+ musicbox export --dir=DIR [QUERY]
182
+ ```
183
+
184
+ This will export the tracks for the albums of selected releases to the directory `DIR`. Albums will be saved to named subdirectories. By default, tracks are compressed in lossy AAC format; specify `--compress=false` to export tracks in lossless ALAC.
185
+
186
+
187
+ ### Make covers and labels
188
+
189
+ ```
190
+ musicbox label [QUERY]
191
+ musicbox cover [QUERY]
192
+ ```
193
+
194
+ This will generate PDF files with labels or cover images, respectively, for the specified releases.
195
+
196
+ Labels will be formatted for 1x3" (approximately) labels.
197
+
198
+ Covers are sized as typical CD covers, a bit less than 5" square. Cut with scissors or a sharp knife along the black border lines.
199
+
200
+ If a cover does not yet exist, one is attempted to be found from the following sources:
201
+
202
+ - the cover (aka box) image found within the audio track, usually derived from a CD ripping program that looks up releases on a service like CDDB
203
+
204
+ - the 'primary' or 'secondary' image specified in the Discogs release page or master page
205
+
206
+ Covers will be displayed, and a prompt will allow selection of the appropriate image.
207
+
208
+
209
+ ## Questions? Comments? Suggestions?
210
+
211
+ Email me at johnl@johnlabovitz.com, or file an issue here.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ Bundler.require
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.test_files = FileList['test/*_test.rb']
7
+ end
8
+
9
+ task :default => :test
data/TODO.txt ADDED
@@ -0,0 +1,145 @@
1
+ # TODO
2
+
3
+ - make image class instead of hash
4
+
5
+ - ensure any audio file is treated the same
6
+ - FLAC, MP3, etc.
7
+
8
+ - create better way of making sub-commands
9
+ - as easy as shell script
10
+ - no class or methods needed
11
+ - one command -> one file
12
+ - auto-loaded from commands/ directory
13
+ - no need to modify bin/musicbox or lib/musicbox.rb
14
+ - predefined variables:
15
+ - $catalog (based on --root)
16
+ - $prompt
17
+
18
+ - split up Catalog
19
+ - Discogs-related classes under Discogs
20
+ - Album, AlbumTrack, Artist (new) under Catalog
21
+
22
+ - re-implement file-based Group?
23
+ - convert all Discogs structures
24
+
25
+ - remove 'extract' logic?
26
+ - presume that ripping is handled externally
27
+ - only concerned with importing
28
+ - NOT: ripping, downloading, digitizing, converting, etc.
29
+
30
+ - make covers 2-up
31
+
32
+ - center cover horizontally on page
33
+
34
+ - make crop marks on covers
35
+
36
+ - only use #cd? to determine need for ripping
37
+ - add default find selector to reject releases with non-rippable formats
38
+ https://www.discogs.com/help/formatslist
39
+ - can override to show full collection, find dups, or make LP/etc. labels
40
+
41
+ - rework use of #album != nil to has_album?
42
+
43
+ - fix 'dup' command to not treat multidisc release as duplicate
44
+
45
+ - import: handle multiple formats/discs better
46
+ - consider case where digitized LP becomes album
47
+ - if more than one format, prompt for choice
48
+ - but how to handle multidisc releases
49
+ - some have formats of [CD, CD] ('expanded')
50
+ - others have [CD x2] ('compact')
51
+ - save selected format into album info
52
+ - in general:
53
+ - goal is to create list of tracks with minimal metadata:
54
+ - track #
55
+ - disc # (optional)
56
+ - title
57
+ - artist (optional)
58
+ - (others inherited from album)
59
+ - use Discogs data as a guide/template
60
+ - but allow assigning, collapsing, expanding, reordering as needed
61
+
62
+ - allow creation of standalone album
63
+ - for: unreleased, mixtape, etc.
64
+
65
+ - allow label font to be configured
66
+
67
+ - save artist ID into album
68
+
69
+ - consider using Album/AlbumTrack as master/truth?
70
+ - keep in SQLite?
71
+ - created/updated from Discogs, but treated separately
72
+
73
+ - keep separate list of artists
74
+ - integrate key, first/last name order
75
+
76
+ - save @cover_file in album info.json file?
77
+ - just read cover_file (as path) instead of deriving via glob
78
+
79
+ - reimplement player state checkpointing, to resume exactly where left off
80
+
81
+ - use curses or tty for full-screen player TTY mode?
82
+
83
+ - use TTY::ProgressBar to show track playing progress?
84
+
85
+ - move config handling into main class
86
+ - also definition of import/extract dir
87
+ - use TTY::Config?
88
+
89
+ - sort #find results by relevance, not by artist/title
90
+
91
+ - move #find into Group
92
+ - move hard-coded selectors into Group subclasses
93
+ - call dynamically
94
+ - eg, ':cd' => send("select_cd")
95
+
96
+ - improve 'select' tool
97
+ - save to $MUSIC_ROOT/selection.json?
98
+ - optionally name
99
+ - can be referred to in query
100
+
101
+ - when updating collection, don't simply delete previous collection
102
+ - determine additions/removals/changes
103
+ - handle case where Discogs has merged/moved releases
104
+ - ie, release_id has changed
105
+ - need to move related album
106
+
107
+ - when finding unripped CDs, also look for dups
108
+ - to avoid needing to rip dup CDs
109
+
110
+ - allow artist key to be user-defined
111
+ - save in group database
112
+ - allow for modification as needed
113
+ - ensure uniqueness
114
+ - allow override of primary artist
115
+ - eg, to file release under main artist instead of artist-group of collaboration
116
+ - do artist sort/rename using artist ID
117
+ - instead of CanonicalArtists/PersonalNames hash maps
118
+
119
+ - use exiftool instead of ffprobe/mp4tags
120
+ $ exiftool -json -recurse . -ext m4a
121
+
122
+ - handle importing logs like tracks
123
+ - create Log class with attributes:
124
+ - timestamp
125
+ - media type
126
+ - accurip details
127
+ - file:
128
+ - disc: (can be nil)
129
+ - status: (overall)
130
+ - tracks status: (tracks)
131
+ - add command to show:
132
+ - missing rips (no tracks or log files)
133
+ - questionable rips (tracks, but no log files)
134
+ - unconfirmed rips (no record in online databases)
135
+ - bad rips (actual read errors)
136
+ - dubious rips (inconsistent checksums)
137
+ - successful rips
138
+
139
+ - improve validation
140
+ - validate logs
141
+ - ensure cover exists
142
+ - ensure track files exist
143
+
144
+ - calculate loudness
145
+ - see: info/replay-gain.md
data/bin/musicbox ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'simple-command'
4
+ require 'musicbox'
5
+
6
+ SimpleCommand.run do
7
+
8
+ global root: nil do
9
+ @musicbox = MusicBox.new(root: @root)
10
+ end
11
+
12
+ command 'extract' do |args|
13
+ @musicbox.extract(args)
14
+ end
15
+
16
+ command 'export', dir: nil, compress: false, force: false, parallel: true do |args|
17
+ @musicbox.export(args,
18
+ dir: @dir,
19
+ compress: @compress,
20
+ force: @force,
21
+ parallel: @parallel)
22
+ end
23
+
24
+ command 'fix' do |args|
25
+ @musicbox.fix(args)
26
+ end
27
+
28
+ command 'formats' do |args|
29
+ @musicbox.formats(args)
30
+ end
31
+
32
+ command 'download-images' do |args|
33
+ @musicbox.download_images(args)
34
+ end
35
+
36
+ command 'extract-cover' do |args|
37
+ @musicbox.extract_cover(args)
38
+ end
39
+
40
+ command 'cover', prompt: false do |args|
41
+ @musicbox.cover(args,
42
+ prompt: @prompt)
43
+ end
44
+
45
+ command 'import' do |args|
46
+ @musicbox.import(args)
47
+ end
48
+
49
+ command 'label' do |args|
50
+ @musicbox.label(args)
51
+ end
52
+
53
+ command 'dir', group: nil do |args|
54
+ @musicbox.dir(args,
55
+ group: @group)
56
+ end
57
+
58
+ command 'open', group: nil do |args|
59
+ @musicbox.open(args,
60
+ group: @group)
61
+ end
62
+
63
+ command 'select-cover' do |args|
64
+ @musicbox.select_cover(args)
65
+ end
66
+
67
+ command 'orphaned' do
68
+ @musicbox.orphaned
69
+ end
70
+
71
+ command 'show', group: nil, details: false, cover: false, prompt: false do |args|
72
+ if @details
73
+ mode = :details
74
+ elsif @cover
75
+ mode = :cover
76
+ else
77
+ mode = :summary
78
+ end
79
+ @musicbox.show(args,
80
+ group: @group,
81
+ mode: mode,
82
+ prompt: @prompt)
83
+ end
84
+
85
+ command 'csv' do |args|
86
+ @musicbox.csv(args)
87
+ end
88
+
89
+ command 'dups' do |args|
90
+ @musicbox.dups(args)
91
+ end
92
+
93
+ command 'artist-keys' do |args|
94
+ @musicbox.artist_keys(args)
95
+ end
96
+
97
+ command 'play', prompt: nil, device: nil, log_level: nil, eq: nil do |args|
98
+ @musicbox.play(args,
99
+ prompt: @prompt,
100
+ audio_device: @device,
101
+ mpv_log_level: @log_level,
102
+ equalizer_name: @eq)
103
+ end
104
+
105
+ command 'select' do |args|
106
+ @musicbox.select(args)
107
+ end
108
+
109
+ command 'update' do |args|
110
+ @musicbox.update
111
+ end
112
+
113
+ command 'update-tags', force: false do |args|
114
+ @musicbox.update_tags(args,
115
+ force: @force)
116
+ end
117
+
118
+ end