playlist 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b3afe56ad81f3b8f00d9132b64f654b14c60c977
4
- data.tar.gz: 0d7fd4cf379196ecbac561a8471d8011bb7d3682
3
+ metadata.gz: e0abdf1a10cd890a9d446a6617ed5bf12856dba0
4
+ data.tar.gz: 0719e57569fed6a8798e58db21eb50dd6c7dfc34
5
5
  SHA512:
6
- metadata.gz: 28a5a2a72c16ffd5f19fbd97adf9317bcdb026f7e537623cafdb160ef88f1ee08cd427dc7b00bf6a81b3aa0d38f235f75febb6137479d15a75a3bc4372c2f938
7
- data.tar.gz: 1f5c2c2abd8874c200005a2b2a9e70a126683dc0a7d9890166a3c67b59ca0e5e7c50701619438450339d3fd0b9ad1cfa786b2b185fa1591e0e6ea791639941b0
6
+ metadata.gz: 44aa5aff176b61c1ad739765882e4c7a7dde57f828a9f4eee95bf19112362d8bcb4b55fe24145e0b435e48f56746ff6bba83086bf810862ffe584f253821cbdc
7
+ data.tar.gz: e71116d7a8906db2cea15f117a4de683945e47b7fb285a36c8043635491c94979653be85adb0b90e4fd947d7fe6626baf4823fd128e32a58414a22ee78836c6b
data/README.md CHANGED
@@ -9,7 +9,6 @@ It supports parsing and generating playlists in the following formats:
9
9
  * M3U
10
10
  * XSPF
11
11
  * A simple human readable format
12
- * SciSys dira XML
13
12
 
14
13
  ## Installation
15
14
 
@@ -14,6 +14,11 @@ class Playlist
14
14
  # @return [String]
15
15
  attr_accessor :annotation
16
16
 
17
+ # A URI (or filename) to the location of the media
18
+ # Use this if the playlist is a single file
19
+ # @return [String]
20
+ attr_accessor :media_location
21
+
17
22
  # A URL to get more inforamtion about this playlist
18
23
  # @return [String]
19
24
  attr_accessor :info_url
@@ -22,6 +27,11 @@ class Playlist
22
27
  # @return [String]
23
28
  attr_accessor :license
24
29
 
30
+ # Get a hash of identifier for this playlist
31
+ # Identifiers can either be Strings or URIs
32
+ # @return [Hash] an hash of identifiers
33
+ attr_reader :identifiers
34
+
25
35
  # Get the array that contains the list of track for this playlist
26
36
  # @return [Array<Track>] an array of tracks in the playlist
27
37
  attr_reader :tracks
@@ -51,6 +61,17 @@ class Playlist
51
61
  @tracks.map(&:duration).compact.inject(:+)
52
62
  end
53
63
 
64
+ # Calculate the start track times based on the duration.
65
+ # This method will only overwrite the start times, if not set
66
+ def calculate_start_times
67
+ time = tracks.first.start_time || 0
68
+ tracks.each do |track|
69
+ break if track.duration.nil?
70
+ time = (track.start_time ||= time)
71
+ time += track.duration
72
+ end
73
+ end
74
+
54
75
  autoload :Contributor, 'playlist/contributor'
55
76
  autoload :Track, 'playlist/track'
56
77
  autoload :Format, 'playlist/format'
@@ -6,6 +6,11 @@ class Playlist::Contributor
6
6
  # @return [String]
7
7
  attr_accessor :name
8
8
 
9
+ # Get a hash of identifier for this Contributor
10
+ # Identifiers can either be Strings or URIs
11
+ # @return [Hash] an hash of identifiers
12
+ attr_reader :identifiers
13
+
9
14
  # The role of the contribrition to the track
10
15
  #
11
16
  # Recommended values for role:
@@ -18,6 +23,7 @@ class Playlist::Contributor
18
23
 
19
24
  # Create a new Contributor
20
25
  def initialize(attr = nil)
26
+ @identifiers = {}
21
27
  if attr.is_a?(Hash)
22
28
  attr.each_pair do |key, value|
23
29
  send("#{key}=", value)
@@ -1,6 +1,6 @@
1
1
  # Base module for the various playlist formats
2
2
  module Playlist::Format
3
- autoload :Dira, 'playlist/format/dira'
3
+ autoload :Cue, 'playlist/format/cue'
4
4
  autoload :M3U, 'playlist/format/m3u'
5
5
  autoload :SimpleText, 'playlist/format/simple_text'
6
6
  autoload :XSPF, 'playlist/format/xspf'
@@ -0,0 +1,117 @@
1
+ # Module to parse and generate Cue Sheet file format
2
+ module Playlist::Format::Cue
3
+ class << self
4
+ # Parse a Cue Sheet file into a [Playlist]
5
+ # @param input any object that responds to #each_line
6
+ # (either a String or a IO object)
7
+ # @return [Playlist] a new Playlist object
8
+ def parse(input)
9
+ Playlist.new do |playlist|
10
+ track = nil
11
+ input.each_line do |line|
12
+ if line =~ /^\s*REM\s/
13
+ next
14
+ elsif line =~ /^\s*TRACK (\d+) AUDIO/
15
+ track = Playlist::Track.new(
16
+ :track_number => Regexp.last_match(1).to_i
17
+ )
18
+ playlist.add_track(track)
19
+ elsif !track.nil?
20
+ parse_track_line(track, line.strip)
21
+ else
22
+ parse_playlist_line(playlist, line.strip)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # Generate a Cue Sheet from a Playlist
29
+ # @param playlist [Playlist] the playlist
30
+ # @return [String] the playlist in Cue Sheet format
31
+ def generate(playlist)
32
+ text = ''
33
+ playlist.calculate_start_times
34
+ text += generate_line(0, 'TITLE', playlist.title)
35
+ text += generate_line(
36
+ 0, 'FILE', format_filename(playlist.media_location), false
37
+ )
38
+ playlist.tracks.each_with_index do |track, index|
39
+ text += generate_track(track, index + 1)
40
+ end
41
+ text
42
+ end
43
+
44
+ protected
45
+
46
+ def parse_track_line(track, line)
47
+ if line =~ /^\s*TITLE \"?(.+?)\"?$/
48
+ track.title = Regexp.last_match(1)
49
+ elsif line =~ /^\s*PERFORMER \"?(.+?)\"?$/
50
+ track.performer = Regexp.last_match(1)
51
+ elsif line =~ /^\s*ISRC \"?(\w+?)\"?$/
52
+ track.isrc = Regexp.last_match(1)
53
+ elsif line =~ /^\s*INDEX /
54
+ track.start_time = parse_time(line)
55
+ else
56
+ warn "Unknown command: #{line}"
57
+ end
58
+ end
59
+
60
+ def parse_playlist_line(playlist, line)
61
+ if line =~ /^\s*TITLE \"?(.+?)\"?$/
62
+ playlist.title = Regexp.last_match(1)
63
+ elsif line =~ /^\s*FILE \"?(.+?)\" (\w+)?$/
64
+ playlist.media_location = Regexp.last_match(1)
65
+ end
66
+ end
67
+
68
+ def generate_track(track, track_num)
69
+ text = ''
70
+ text += generate_line(1, 'TRACK', format('%2.2d AUDIO', track_num), false)
71
+ text += generate_line(2, 'TITLE', track.title)
72
+ text += generate_line(2, 'PERFORMER', track.performer)
73
+ text += generate_line(2, 'ISRC', track.isrc)
74
+ text += generate_line(2, 'INDEX 01', format_time(track.start_time), false)
75
+ text
76
+ end
77
+
78
+ def generate_line(depth, name, value, escape = true)
79
+ if value.nil?
80
+ ''
81
+ else
82
+ space = ' ' * depth
83
+ if escape
84
+ escaped = value.tr('"', "'")
85
+ "#{space}#{name} \"#{escaped}\"\n"
86
+ else
87
+ "#{space}#{name} #{value}\n"
88
+ end
89
+ end
90
+ end
91
+
92
+ def parse_time(timestamp)
93
+ if timestamp =~ /INDEX (\d+) (\d+):(\d+):(\d+)/
94
+ if Regexp.last_match(1).to_i == 1
95
+ mins = Regexp.last_match(2).to_f
96
+ secs = Regexp.last_match(3).to_f
97
+ frames = Regexp.last_match(4).to_f
98
+ (mins * 60) + secs + (frames / 75)
99
+ end
100
+ end
101
+ end
102
+
103
+ def format_time(duration)
104
+ duration = 0 if duration.nil?
105
+ mins = (duration / 60).floor
106
+ secs = (duration % 60).floor
107
+ frames = ((duration - duration.floor) * 75).round
108
+ format('%2.2d:%2.2d:%2.2d', mins, secs, frames)
109
+ end
110
+
111
+ def format_filename(location)
112
+ filename = File.basename(location)
113
+ fmt = File.extname(filename)[1..-1].upcase
114
+ "\"#{filename}\" #{fmt}"
115
+ end
116
+ end
117
+ end
@@ -11,6 +11,11 @@ class Playlist::Track
11
11
  # @return [String]
12
12
  attr_accessor :album
13
13
 
14
+ # The catalogue number of the album that the track came from
15
+ # Also known as the UPC/EAN code
16
+ # @return [String]
17
+ attr_accessor :catalogue_number
18
+
14
19
  # The number of the track on the album it came from
15
20
  # @return [Integer]
16
21
  attr_accessor :track_number
@@ -37,6 +42,11 @@ class Playlist::Track
37
42
  # @return [Integer, Float]
38
43
  attr_reader :duration
39
44
 
45
+ # Get a hash of identifier for this Track
46
+ # Identifiers can either be Strings or URIs
47
+ # @return [Hash] an hash of identifiers
48
+ attr_reader :identifiers
49
+
40
50
  # Get the array of the contributors to this Track
41
51
  # @return [Array<Contributor>] an array of tracks in the playlist
42
52
  attr_reader :contributors
@@ -45,6 +55,7 @@ class Playlist::Track
45
55
  # @param attr [Hash] a hash of attibute values to set
46
56
  def initialize(attr = {})
47
57
  @contributors = []
58
+ @identifiers = {}
48
59
  attr.each_pair do |key, value|
49
60
  send("#{key}=", value)
50
61
  end
@@ -52,6 +63,18 @@ class Playlist::Track
52
63
  yield(self) if block_given?
53
64
  end
54
65
 
66
+ # Get the International Standard Recording Code for this track
67
+ # @return [String] the ISRC for the track
68
+ def isrc
69
+ @identifiers[:isrc]
70
+ end
71
+
72
+ # Set the International Standard Recording Code for this track
73
+ # @param isrc [String] the ISRC for the track
74
+ def isrc=(isrc)
75
+ @identifiers[:isrc] = isrc
76
+ end
77
+
55
78
  # Get a concatinated list of contributors names with no role
56
79
  # @return [String]
57
80
  def creator
@@ -81,6 +104,32 @@ class Playlist::Track
81
104
  end
82
105
  alias artist= performer=
83
106
 
107
+ # Get a conactinated list of composers for this track
108
+ # @return [String] the name of the composer or nil
109
+ def composer
110
+ contributor_names(:composer)
111
+ end
112
+
113
+ # Set the name of the composer for the track
114
+ # Removes any existing composers
115
+ # @param name [String] the name the composer
116
+ def composer=(name)
117
+ replace_contributor(:composer, name)
118
+ end
119
+
120
+ # Get a conactinated list of arrangers for this track
121
+ # @return [String] the name of the arranger or nil
122
+ def arranger
123
+ contributor_names(:arranger)
124
+ end
125
+
126
+ # Set the name of the arranger for the track
127
+ # Removes any existing arrangers
128
+ # @param name [String] the name the arranger
129
+ def arranger=(name)
130
+ replace_contributor(:arranger, name)
131
+ end
132
+
84
133
  # Set the duration of the track
85
134
  # If the duration is 0 or -1, then the duration is set to nil
86
135
  # @param seconds [Numeric] the duration of the track in seconds
@@ -1,4 +1,4 @@
1
1
  class Playlist
2
2
  # The version number of the Playlist Ruby gem
3
- VERSION = '0.1.3'
3
+ VERSION = '0.2.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: playlist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicholas Humfrey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-16 00:00:00.000000000 Z
11
+ date: 2018-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -140,7 +140,7 @@ files:
140
140
  - lib/playlist/contributor.rb
141
141
  - lib/playlist/ext/content_at.rb
142
142
  - lib/playlist/format.rb
143
- - lib/playlist/format/dira.rb
143
+ - lib/playlist/format/cue.rb
144
144
  - lib/playlist/format/m3u.rb
145
145
  - lib/playlist/format/simple_text.rb
146
146
  - lib/playlist/format/xspf.rb
@@ -1,57 +0,0 @@
1
- require 'nokogiri'
2
-
3
- unless Nokogiri::XML::Node.respond_to?(:content_at)
4
- require 'playlist/ext/content_at.rb'
5
- end
6
-
7
- # Module to parse Scisys dira XML genealogy files
8
- module Playlist::Format::Dira
9
- class << self
10
- # Parse a Dira XML document into a new Playlist object
11
- # @param input [String, IO] the source of the Dira XML
12
- # @return [Playlist] a new Playlist object
13
- def parse(input)
14
- Playlist.new do |playlist|
15
- doc = Nokogiri::XML(input)
16
- playlist.title = doc.content_at(
17
- '//TAKE/GENERIC/GENE_MULTIMEDIA_TITLE'
18
- ) || doc.content_at(
19
- '//TAKE/GENERIC/GENE_TITLE'
20
- )
21
- doc.xpath('//TAKE/GENEALOGY/GENEALOGY_ITEM').each do |item|
22
- playlist.tracks << parse_genealogy_item(item)
23
- end
24
- end
25
- end
26
-
27
- protected
28
-
29
- # Parse a single Genealogy Item from a Dira XML document
30
- def parse_genealogy_item(doc)
31
- Playlist::Track.new do |track|
32
- track.title = doc.content_at('./CLIP/CLIP_INFO/GENE_TITLE')
33
- track.album = doc.content_at('./CLIP/CLIP_INFO/GENE_ALBUM')
34
- track.track_number = doc.content_at('./CLIP/CLIP_INFO/GENE_TRACK')
35
- track.side = doc.content_at('./CLIP/CLIP_INFO/GENE_SIDE')
36
- track.record_label = doc.content_at('./CLIP/CLIP_INFO/GENE_LABEL')
37
- track.publisher = doc.content_at('./CLIP/CLIP_INFO/GENE_PUBLISHER')
38
- if (duration = doc.content_at('./GENY_CLIP_LENGTH'))
39
- track.duration = duration.to_f / 1000
40
- end
41
- doc.xpath('./CLIP/CLIP_PERSONS/PERSON').each do |person|
42
- track.contributors << parse_person(person)
43
- end
44
- end
45
- end
46
-
47
- # Parse a single Person from a dira XML document
48
- def parse_person(doc)
49
- Playlist::Contributor.new do |c|
50
- c.name = doc.content_at('PERSON_NAME')
51
- if doc.content_at('PMAP_FUNC') =~ /^PERSON_FUNC\$(.+)\#(.*)$/
52
- c.role = Regexp.last_match(1).downcase.to_sym
53
- end
54
- end
55
- end
56
- end
57
- end