cue_breaker 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +43 -0
- data/bin/cue_break +7 -0
- data/lib/cue_breaker/album.rb +25 -0
- data/lib/cue_breaker/cli.rb +47 -0
- data/lib/cue_breaker/core.rb +68 -0
- data/lib/cue_breaker/dependencies.rb +17 -0
- data/lib/cue_breaker/ffmpeg_args.rb +50 -0
- data/lib/cue_breaker/track.rb +32 -0
- data/lib/cue_breaker/version.rb +3 -0
- data/lib/cue_breaker.rb +6 -0
- metadata +81 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bd9d8859514a4edf0131c5f6e9c01939df0ee6cfe0389ff909e77c7a3e0828a6
|
|
4
|
+
data.tar.gz: 18c39dd44c5e26ced3b3a232ed521a499453a4ded92d1fb92cdd6b6d303d2875
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b7e39bcf1aa41da85fb871a4e4f9975a9bdc9fd45f68c4fc0b5ea221b1fcc7211befb2be4bfcac66ef997c1123e0052bbe9a3f726a557b1708d69c81d1dfa2cf
|
|
7
|
+
data.tar.gz: ba7b880ae0588c66c32f445c82959e0b07741f22f300150f77e262f1495d56c050cf6872909a1d40a9043481c48d30a1474f0361afc4f8bda71bb71ac128846d
|
data/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# CueBreaker
|
|
2
|
+
|
|
3
|
+
CueBreaker is a Ruby script that parses CUE files and converts
|
|
4
|
+
associated WAV files into respective MP3 track files. It uses the
|
|
5
|
+
`rubycue` gem for parsing and FFmpeg for audio conversion and
|
|
6
|
+
metadata handling.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Parses CUE files to extract track information.
|
|
11
|
+
- Converts WAV segments to MP3 files.
|
|
12
|
+
- Adds metadata to the MP3 files (e.g. title, artist).
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
- Ruby
|
|
17
|
+
- FFmpeg
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
1. **Install Ruby:** Download and install Ruby from [ruby-lang.org](https://www.ruby-lang.org/).
|
|
22
|
+
2. **Install FFmpeg:** Download and install FFmpeg from [ffmpeg.org](https://ffmpeg.org/download.html) and ensure it is added to your system's PATH.
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
$ gem install cue_breaker
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Here's an example of how to use CueBreaker:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
$ cue_break --cue rockband.cue --wav rockband.wav --output $HOME/music/rockband
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## License
|
|
37
|
+
|
|
38
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
39
|
+
|
|
40
|
+
## Acknowledgments
|
|
41
|
+
|
|
42
|
+
- [rubycue](https://github.com/blakesmith/rubycue) for CUE file parsing.
|
|
43
|
+
- [FFmpeg](https://ffmpeg.org/) for audio processing.
|
data/bin/cue_break
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'delegate'
|
|
2
|
+
|
|
3
|
+
module CueBreaker
|
|
4
|
+
class Album
|
|
5
|
+
attr_reader :title, :performer, :genre, :total_tracks, :date
|
|
6
|
+
|
|
7
|
+
def initialize(title:, performer:, genre:, total_tracks:, date:)
|
|
8
|
+
@title, @performer, @genre, @total_tracks, @date = title, performer, genre, total_tracks, date
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def present
|
|
12
|
+
@_present ||= Presenter.new(self)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Presenter < SimpleDelegator
|
|
16
|
+
def track_number(track)
|
|
17
|
+
"#{track.number}/#{total_tracks}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def genre
|
|
21
|
+
super&.gsub(/[<>:"\/\\|?*]/, '').to_s
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
require 'delegate'
|
|
3
|
+
|
|
4
|
+
require 'cue_breaker/version'
|
|
5
|
+
require 'cue_breaker/core'
|
|
6
|
+
require 'cue_breaker/dependencies'
|
|
7
|
+
|
|
8
|
+
module CueBreaker
|
|
9
|
+
module CLI
|
|
10
|
+
extend self
|
|
11
|
+
|
|
12
|
+
def start
|
|
13
|
+
Dependencies.check!
|
|
14
|
+
|
|
15
|
+
duration = Core.get_audio_duration(options.wav)
|
|
16
|
+
|
|
17
|
+
Core.parse_cue(options.cue, duration: duration) do |album, song|
|
|
18
|
+
Core.convert_to_mp3(options.wav, album.present, song.present, options.output)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def options
|
|
25
|
+
@_options ||= Options.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class Options < SimpleDelegator
|
|
29
|
+
def initialize
|
|
30
|
+
options = {}
|
|
31
|
+
|
|
32
|
+
OptionParser.new do |parser|
|
|
33
|
+
parser.banner = <<~BANNER
|
|
34
|
+
cue_break version #{VERSION}
|
|
35
|
+
Usage: cue_break [options]
|
|
36
|
+
BANNER
|
|
37
|
+
|
|
38
|
+
parser.on('-c', '--cue CUEFILE') { |o| options[:cue] = o }
|
|
39
|
+
parser.on('-w', '--wav WAVFILE') { |o| options[:wav] = o }
|
|
40
|
+
parser.on('-o', '--output OUTPUT') { |o| options[:output] = o }
|
|
41
|
+
end.parse!
|
|
42
|
+
|
|
43
|
+
super Struct.new(*options.keys).new(*options.values)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'rubycue'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'open3'
|
|
4
|
+
|
|
5
|
+
require 'cue_breaker/album'
|
|
6
|
+
require 'cue_breaker/track'
|
|
7
|
+
require 'cue_breaker/ffmpeg_args'
|
|
8
|
+
|
|
9
|
+
module CueBreaker
|
|
10
|
+
module Core
|
|
11
|
+
extend self
|
|
12
|
+
|
|
13
|
+
def parse_cue(cue_file, duration:, &block)
|
|
14
|
+
cue_sheet = RubyCue::Cuesheet.new(File.read(cue_file), duration.to_i)
|
|
15
|
+
cue_sheet.parse!
|
|
16
|
+
|
|
17
|
+
album = as_album(cue_sheet)
|
|
18
|
+
|
|
19
|
+
cue_sheet.songs.each do |song|
|
|
20
|
+
block.call(album, as_track(song))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def convert_to_mp3(wav_file, album, track, output_dir)
|
|
25
|
+
output_path = File.join(output_dir, sanitize_file_name(album.title))
|
|
26
|
+
FileUtils.mkdir_p(output_path) unless Dir.exist?(output_path)
|
|
27
|
+
|
|
28
|
+
ffmpeger = FFmpegArgs.new(wav_file, album, track)
|
|
29
|
+
system('ffmpeg', *ffmpeger.options(output_path))
|
|
30
|
+
puts "Exported: #{ffmpeger.output_file_path(output_path)}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_audio_duration(file_path)
|
|
34
|
+
stdout, stderr, status = Open3.capture3(<<~CMD)
|
|
35
|
+
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"#{file_path}\"
|
|
36
|
+
CMD
|
|
37
|
+
|
|
38
|
+
raise "Error getting duration: #{stderr}" unless status.success?
|
|
39
|
+
|
|
40
|
+
stdout.strip.to_f
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def as_album(cue_sheet)
|
|
46
|
+
Album.new(
|
|
47
|
+
title: cue_sheet.title,
|
|
48
|
+
performer: cue_sheet.songs.first.fetch(:performer),
|
|
49
|
+
genre: cue_sheet.genre,
|
|
50
|
+
total_tracks: cue_sheet.songs.count,
|
|
51
|
+
date: cue_sheet.date
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def as_track(song)
|
|
56
|
+
Track.new(
|
|
57
|
+
title: song.fetch(:title),
|
|
58
|
+
number: song.fetch(:track),
|
|
59
|
+
start: song.fetch(:index).to_s,
|
|
60
|
+
finish: (song.fetch(:index) + song.fetch(:duration)).to_s
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def sanitize_file_name(name)
|
|
65
|
+
name.gsub(%r{[<>:"/\\|?*]}, '')
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module CueBreaker
|
|
2
|
+
module Dependencies
|
|
3
|
+
extend self
|
|
4
|
+
|
|
5
|
+
def check!
|
|
6
|
+
check_ffmpeg_installed!
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def check_ffmpeg_installed!
|
|
12
|
+
unless system('which ffmpeg > /dev/null 2>&1')
|
|
13
|
+
raise 'ffmpeg is not installed. Please install it to use this gem.'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module CueBreaker
|
|
2
|
+
class FFmpegArgs
|
|
3
|
+
def initialize(file, album, track)
|
|
4
|
+
@file, @album, @track = file, album, track
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def options(output_path)
|
|
8
|
+
[
|
|
9
|
+
file,
|
|
10
|
+
start,
|
|
11
|
+
finish,
|
|
12
|
+
metadata,
|
|
13
|
+
["-q:a", "0"],
|
|
14
|
+
output_file_path(output_path)
|
|
15
|
+
].reject(&:empty?).flatten
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def file
|
|
19
|
+
["-i", @file]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def start
|
|
23
|
+
["-ss", @track.start.to_s]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def finish
|
|
27
|
+
return [] unless @track.finish
|
|
28
|
+
|
|
29
|
+
["-to", @track.finish.to_s]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def metadata
|
|
33
|
+
[
|
|
34
|
+
"-metadata", "title=#{@track.title}",
|
|
35
|
+
"-metadata", "artist=#{@album.performer}",
|
|
36
|
+
"-metadata", "genre=#{@album.genre}",
|
|
37
|
+
"-metadata", "album=#{@album.title}",
|
|
38
|
+
"-metadata", "date=#{@album.date}",
|
|
39
|
+
"-metadata", "track=#{@album.track_number(@track)}",
|
|
40
|
+
]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def output_file_path(output_path)
|
|
44
|
+
File.join(
|
|
45
|
+
output_path,
|
|
46
|
+
"#{format('%02d', @track.number)} - #{@track.title}.mp3"
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'delegate'
|
|
2
|
+
|
|
3
|
+
module CueBreaker
|
|
4
|
+
class Track
|
|
5
|
+
attr_reader :title, :number, :start, :finish
|
|
6
|
+
|
|
7
|
+
def initialize(title:, number:, start:, finish:)
|
|
8
|
+
@title, @number, @start, @finish = title, number, start, finish
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def present
|
|
12
|
+
@_present ||= Presenter.new(self)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class Presenter < SimpleDelegator
|
|
16
|
+
def start
|
|
17
|
+
parse_time(super)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def finish
|
|
21
|
+
super ? parse_time(super) : nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def parse_time(time_str)
|
|
27
|
+
parts = time_str.split(':').map(&:to_f)
|
|
28
|
+
(parts[0] * 60) + parts[1] + (parts[2] / 75.0)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/cue_breaker.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cue_breaker
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Lukas Alexander
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2024-05-23 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: pry-nav
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
description: A simple hello world gem
|
|
42
|
+
email: the.lukelex@gmail.com
|
|
43
|
+
executables:
|
|
44
|
+
- cue_break
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- README.md
|
|
49
|
+
- bin/cue_break
|
|
50
|
+
- lib/cue_breaker.rb
|
|
51
|
+
- lib/cue_breaker/album.rb
|
|
52
|
+
- lib/cue_breaker/cli.rb
|
|
53
|
+
- lib/cue_breaker/core.rb
|
|
54
|
+
- lib/cue_breaker/dependencies.rb
|
|
55
|
+
- lib/cue_breaker/ffmpeg_args.rb
|
|
56
|
+
- lib/cue_breaker/track.rb
|
|
57
|
+
- lib/cue_breaker/version.rb
|
|
58
|
+
homepage: https://rubygems.org/gems/cue_breaker
|
|
59
|
+
licenses:
|
|
60
|
+
- MIT
|
|
61
|
+
metadata: {}
|
|
62
|
+
post_install_message:
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 3.5.9
|
|
78
|
+
signing_key:
|
|
79
|
+
specification_version: 4
|
|
80
|
+
summary: Breaks CUE files into mp3s
|
|
81
|
+
test_files: []
|