Skip to content
This repository has been archived by the owner on Nov 28, 2022. It is now read-only.

Updates #4

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1fa09b4
Reorganising to current standards.
Mar 8, 2018
f908512
Ignore more things. It's probably a good motto for life.
Mar 11, 2018
d602c8c
Don't ignore everything in fixtures.
Mar 11, 2018
5516370
Specs and set up.
Mar 11, 2018
34dde91
All the things I like to have for development.
Mar 11, 2018
c594586
Reorganisation and rewrite.
Mar 11, 2018
8c6ebba
Had Timecop in the gemspec. I don't like dev dependencies in the gems…
Mar 11, 2018
fb619a2
Added the dependency on Tilt and made it look pretty.
Mar 11, 2018
f532c60
Pkgsrc supports taglib.
Mar 11, 2018
77bb9ab
Location should've been a URI, fixed.
Mar 11, 2018
4fe6b3f
You are free to format RSpec as you wish.
Mar 12, 2018
06b39a8
Clean up the opts a bit; fixed potential bug.
Mar 12, 2018
c1dc890
The --no- options work again.
Mar 12, 2018
c1c51fb
Max tracks option working.
Mar 12, 2018
83d814b
Didn't replace the option with the variable and it caused a bug, fixed.
Mar 12, 2018
2260697
Needed the exit or errors on calling help.
Mar 12, 2018
3633652
Added a --use-tidy and a --force option.
Mar 13, 2018
8d50c10
Updated the instructions.
Mar 13, 2018
99e3673
gem build complains about this line, fixed.
Mar 13, 2018
6d021d7
Version wasn't picked up, fixed; full path looks messy, hence Pathname.
Mar 13, 2018
1a54194
No longer using this parser.
Mar 13, 2018
2228420
Using yard for docs and yardstick to help improve the docs.
Mar 13, 2018
4df5955
Improved the yard docs based on yardstick's pedantry.
Mar 13, 2018
2ed317a
Fixed a typo, kind of fixed formatting of the table for markdown. Kin…
Mar 13, 2018
ff76b44
Added CHANGELOG, in the style of Keep a Changelog.
Mar 13, 2018
ce08f26
Output errors.
Mar 14, 2018
541ef78
Bail early with error if no tracks found; warn if path(s) not exist; …
Mar 14, 2018
9856880
Merge branch 'handling-errors' into develop
Mar 14, 2018
5d649e6
Forgot to rename var, and make the dir for the empty directory test.
Mar 14, 2018
cce5de6
Ignore the generated gem files.
Mar 14, 2018
6493c6c
RSpec listed twice, fixed.
Mar 14, 2018
2027a44
Use Addressable, it escapes paths better than stdlib URI.
Mar 14, 2018
44bc9c5
Make sure extra whitespace isn't introduced, it has a habit of muckin…
Mar 14, 2018
e1a3ed4
Properly encode text in XML, decoders don't like stray ampersands and…
Mar 14, 2018
5298c0f
Wasn't conforming properly to XSPF schema, fixed and updated specs. S…
Mar 15, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.idea
.project
.loadpath
.DS_Store
pkg
doc
vendor/
vendor.noindex/
.bundle/
Gemfile.lock
bin/
coverage/
config/
backup/
scratch/
.yardoc/
spec/support/fixtures/albums
.rspec
*.gem
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased

### Added

- Start using [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- A separate version file, lib/spfy/version.rb
- Lots of Yard docs.
- Specs.
- Command line options to control the meta-data of a playlist.
- Option to prettify the output via `tidy`

### Changed

- Start following [SemVer](http://semver.org) properly.
- Updated gemspec to current standards (2018) e.g. a separate version file, git ls-files etc
- Moved executable from bin/ to exe/ so as not to get caught up with binstubs.
- Using docopt for parsing, it's so much easier to work with than other opt-parsers.
- Moved from building up the XML line by line, tag by tag, to running it through ERB templates.
- More object orientated design.
- Some of the options for suppressing output.

## [1.0.0]

- A working product at this point.
26 changes: 26 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gemspec

group :development do
gem "rake"
gem "pry-byebug"
gem "pry-state"
gem "rb-readline"
gem "awesome_print"
gem "yard"
gem "yardstick"
end


group :test do
gem "rspec"
gem "rspec-its"
gem "simplecov"
gem "rspec-given"
gem "timecop", ">=0.9.1"
end
69 changes: 52 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,41 @@
**Spfy** is a command-line tool for generating [XSPF](http://xspf.org/) playlists from metadata stored in several popular audio formats and is developed entirely in [Ruby](http://www.ruby-lang.org/). It takes one or more local directory paths as input, extracts metadata tags from any audio files that it encounters, and generates a valid XSPF playlist.

### Prerequisites
A working Ruby installation (version 1.9 or greater) is required for Spfy to work, but this is outside the scope of this guide. For more information refer to the [official installation procedure](http://www.ruby-lang.org/en/downloads/).
A working Ruby installation (version 2.0 or greater) is required for Spfy to work, but this is outside the scope of this guide. For more information refer to the [official installation procedure](http://www.ruby-lang.org/en/downloads/).

[TagLib](http://developer.kde.org/~wheeler/taglib.html) is also required. Follow the steps below (taken from the [taglib-ruby installation guide](http://robinst.github.com/taglib-ruby/)) to install the necessary files for your respective system type:

| System: | Command: |
|---------------|------------------------------------|
| Debian/Ubuntu | `sudo apt-get install libtag1-dev` |
| Fedora/RHEL | `sudo yum install taglib-devel` |
| Brew | `brew install taglib` |
| MacPorts | `sudo port install taglib` |
| System: | Command: |
|---------------|------------------------------------|
| Debian/Ubuntu | `sudo apt-get install libtag1-dev` |
| Fedora/RHEL | `sudo yum install taglib-devel` |
| Brew | `brew install taglib` |
| MacPorts | `sudo port install taglib` |
| Pkgsrc | `(sudo) pkgin install taglib` |

### Installation
With the prerequisites above taken care of Spfy can be installed with the following command:

$ gem install spfy

### Using Spfy
By default, Spfy will output a formatted XSPF playlist to the standard output stream that will include _location_, _title_, _creator_, _album_, and _trackNum_ elements for each audio file where available.
By default, Spfy will output a formatted XSPF playlist to the standard output stream that will include _location_, _album_, _artist_, _comment_, _genre_, _title_, _trackNum_, and _year_ elements for each audio file where available.

The general syntax for Spfy is `spfy [options] dir1 ... dirN`, where _dir1 ... dirN_ is one or more paths to directories containing audio files.

For example:

$ spfy ~/music
$ spfy ~/music/"Smashing Pumpkins"

..will produce the following output (where ~/music contains one audio file with valid metadata):
..will produce the following output (where ~/music/Smashing\ Pumpkins contains one audio file with valid metadata):

```xml
<?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<title>Smashing Pumpkins</title>
<creator>bobby (or whatever your username is)</creator>
<date>2018-03-11T06:49:16+00:00</date>
<annotation>Created with Spfy.rb</annotation>
<trackList>
<track>
<location>file:///Users/spfy/music/03%20A%20Stitch%20In%20Time.mp3</location>
Expand All @@ -46,25 +51,55 @@ For example:
</playlist>
```

Spfy supports multiple directory paths (e.g. `spfy /dir1 /dir2`) and traverses each directory recursively by default. Unsupported files and empty directories in a directory tree are silently ignored and will not impact Spfy's output.
Spfy supports multiple directory paths (e.g. `spfy /dir1 /dir2`) and traverses each directory recursively by default. Unsupported files and empty directories in a directory tree are silently ignored and will not impact Spfy's output. It will even take a single file as a target, or directories with sub-directories that hold files (no max depth has been set on the recursion so don't start too far up the directory tree;)

Command-line arguments allow you to control which elements Spfy outputs:

-f, --no-location Suppress file location output
-t, --no-title Suppress track title in output
-a, --no-artist Suppress artist name in output
-l, --no-album Suppress album name in output
-n, --no-tracknum Suppress track number in output
--no-location Suppress file location output
--no-title Suppress track title in output
--no-artist Suppress artist name in output
--no-album Suppress album name in output
--no-trackNum Suppress track number in output - CASE SENSITIVE!

You can also control the metadata for the playlist:

-t TITLE --title=TITLE Playlist title
-c CREATOR --creator=CREATOR Playlist creator, defaults to env $USER
-d DATE --date=DATE Playlist creation date, defaults to now
-a NOTE --annotation=NOTE Playlist annotation, default: "Created with Spfy.rb"

Specify an output file:

-o FILE --output=FILE File to write to, otherwise output to STDOUT
--force Allow the overwriting of a file.

Limit the number of tracks:

--max-tracks NUM Limit the output to NUM tracks

And prettify the output:

--use-tidy Run the tidy command to prettify the output.
Uses `/usr/bin/command -v tidy` to find tidy and
`tidy -q -i -xml` to filter through.

Although you could simply not specify an outfile and pipe through a filter of your choice to get the same effect.

For additional options use `spfy --help`.

### License
Spfy is free software, and you are welcome to redistribute it under certain conditions. See the [GNU General Public License](http://www.gnu.org/licenses/gpl.html) for more details.


### Development

Use Bundler to install the development dependencies and then run the Rake task to copy the audio files for the specs to run against.


### Acknowledgements
Spfy uses the following third party software components:

* [taglib-ruby](http://robinst.github.com/taglib-ruby/) by Robin Stocker

### Contact
Email me at [[email protected]](mailto:marc[email protected]) or tweet [@marcransome](http://www.twitter.com/marcransome).
Email me at [[email protected]](mailto_marc_[email protected]) or tweet [@marcransome](http://www.twitter.com/marcransome).
26 changes: 26 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'pathname'

desc "Install the spec fixtures"
task :fixtures do
gem_home = Pathname(ENV["GEM_HOME"])
files = gem_home.join("gems").glob("taglib-ruby-*/test/data/*").reject{|x| x.extname.to_s == ".cpp" }
# mk dir tree
albums_dir = Pathname(__dir__).join("spec/support/fixtures/albums")
albums_dir.mkpath
%w{flac mp3 mp4 oga wav}.each do |ext|
album = albums_dir.join(ext)
album.mkpath
# find taglib and copy files
# And yes, this is a horrible hack
if ext == "mp4"
exts = ["m4a","aiff"]
else
exts = [ext]
end
exts.each do |ext|
FileUtils.cp files.select{|x| x.extname.to_s == ".#{ext}" }, album
end
end
puts "Audio files copied from taglib to spec/support/fixtures/albums"
albums_dir.join("empty").mkpath # for the empty directory test
end
6 changes: 0 additions & 6 deletions bin/spfy

This file was deleted.

90 changes: 90 additions & 0 deletions exe/spfy
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env ruby

require 'spfy'
require 'spfy/version'
require 'docopt'
require 'pathname'

EXE = Pathname(__FILE__).basename

doc = <<DOCOPT
Grab and output XSPF data for media files. See http://xspf.org/ for the data definition.

Usage:
#{EXE} [options] PATHS...
#{EXE} --version

Options:
-h --help Show this help message and exit
-v --version Display version information
-o FILE --output=FILE File to write to, otherwise output to STDOUT
-t TITLE --title=TITLE Playlist title
-c CREATOR --creator=CREATOR Playlist creator, defaults to env $USER
-d DATE --date=DATE Playlist creation date, defaults to now
-a NOTE --annotation=NOTE Playlist annotation, default: "Created with Spfy.rb"
--no-location Suppress file location output
--no-title Suppress track title in output
--no-artist Suppress artist/creator name in track output
--no-album Suppress album name in output
--no-trackNum Suppress track number in output - CASE SENSITIVE!
--max-tracks NUM Limit the output to NUM tracks
--use-tidy Run the tidy command to prettify the output.
Uses `/usr/bin/command -v tidy` to find tidy and
`tidy -q -i -xml` to filter through.
--force Allow the overwriting of a file.
DOCOPT

begin
options = Docopt::docopt(doc, version: Spfy::VERSION)
rescue Docopt::Exit => e
puts e.message
exit 0
end

output = options.delete "--output"

# Remove extraneous
options.delete "--help"
options.delete "--version"
use_tidy = options.delete "--use-tidy"
force_ok = options.delete "--force"
WARNING_RED="tput setaf 1"
BACK_TO_NORMAL="tput sgr0"

playlist = Spfy::Playlist.new options

COMMAND = "/usr/bin/command"
DEFAULT_TIDY_FINDER="#{COMMAND} -v tidy"

def filter xml, use_tidy=false
if use_tidy
require 'open3'
path = Pathname(`#{DEFAULT_TIDY_FINDER}`.chomp)
if path.exist?
stdout, _= Open3.capture2(path.to_path,"-q", "-i", "-xml", stdin_data: xml)
stdout
else
warn `#{WARNING_RED}` + "tidy not found at #{path.to_path}" + `#{BACK_TO_NORMAL}` + "\nRunning without tidying…\n\n"
xml
end
else
xml
end
end

blk = ->(f){ f.puts filter(playlist.to_xml, use_tidy) }

begin
if output
path = Pathname(output)
if path.exist? and not force_ok
warn `#{WARNING_RED}` + "#{path.to_path} exists. Use the --force option to overwrite" + `#{BACK_TO_NORMAL}`
else
File.open(path, "w", &blk)
end
else
blk.call(Kernel)
end
rescue => e
warn e.message
end
Loading