id3tag 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/Gemfile.lock +49 -15
- data/VERSION +1 -1
- data/id3tag.gemspec +10 -3
- data/lib/id3tag.rb +1 -0
- data/lib/id3tag/frame_id_advisor.rb +5 -1
- data/lib/id3tag/frames/v2/basic_frame.rb +99 -5
- data/lib/id3tag/frames/v2/frame_flags.rb +140 -0
- data/lib/id3tag/frames/v2/genre_frame.rb +1 -1
- data/lib/id3tag/frames/v2/text_frame.rb +3 -3
- data/lib/id3tag/frames/v2/unique_file_id_frame.rb +5 -5
- data/spec/lib/id3tag/frames/v2/basic_frame_spec.rb +75 -0
- data/spec/lib/id3tag/frames/v2/frame_flags_spec.rb +157 -0
- data/spec/lib/id3tag/tag_spec.rb +1 -3
- data/standard_documents/id3v2-00.txt +1657 -0
- data/standard_documents/id3v2.3.0.txt +2022 -0
- data/standard_documents/id3v2.4.0-frames.txt +1732 -0
- data/standard_documents/id3v2.4.0-structure.txt +732 -0
- data/tags +288 -0
- metadata +19 -12
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
NTc3ZDYzY2FiYWEwNTgzOGU4MjM1NDYxYTU1NDgyNzZlYWVhNGRjOTAzZDk1
|
10
|
-
YmVhMTViOThlYmMxZDRkNzVmMDRkMGViZGU2MWY5ZjViY2IxMTMyOTIwYjUy
|
11
|
-
NmRhZWNkNDliMjA2MDliMGFmNDM3MzI5NWVmZTM0OTNjY2QzMmM=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
MGE3NWQwMGM4MmQ3M2E3ZTkxNWMxYzkzODk2MTAxOThjYTc4ODllMGI1Yjgy
|
14
|
-
MGU1ZTdhZTMyYjdlYmFmMzFlMDZiZjIyN2IzMzk0NjYxZTJiNzk3YzdjMzg0
|
15
|
-
OTNmN2EwMWY5NGM3MzZkMmEwZDkwZmI0MmZkYTY2NzNiNjkzNTk=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d7e015abc718273110bec0cb6fcdb8036de50f37
|
4
|
+
data.tar.gz: 0b3b5d6b9acdd5cf5393fc9a97ef594f393e0101
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1db6cc3bd2407e2bfc6a172e508adc8b11907bd00903b9006aea4b94d0d1c8c620351dc6f22de95405ea6fb16e0784b42456bbde45121fdc56b1e0b44bb0f6e3
|
7
|
+
data.tar.gz: 3357247645ce3d9c07be8b0633c95a782361471297bc9bfaefc2d1566db2f4f584adba69c0678dab721e2597986fb9aa11e45149e3c4a13fb2df8cd678da6fa5
|
data/Gemfile.lock
CHANGED
@@ -1,32 +1,63 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
-
|
4
|
+
addressable (2.3.5)
|
5
|
+
builder (3.2.2)
|
5
6
|
columnize (0.3.6)
|
6
|
-
coveralls (0.
|
7
|
-
colorize
|
7
|
+
coveralls (0.7.0)
|
8
8
|
multi_json (~> 1.3)
|
9
9
|
rest-client
|
10
10
|
simplecov (>= 0.7)
|
11
|
+
term-ansicolor
|
11
12
|
thor
|
12
|
-
debugger (1.
|
13
|
+
debugger (1.6.2)
|
13
14
|
columnize (>= 0.3.1)
|
14
15
|
debugger-linecache (~> 1.2.0)
|
15
|
-
debugger-ruby_core_source (~> 1.2.
|
16
|
+
debugger-ruby_core_source (~> 1.2.3)
|
16
17
|
debugger-linecache (1.2.0)
|
17
|
-
debugger-ruby_core_source (1.2.
|
18
|
-
diff-lcs (1.2.
|
19
|
-
|
20
|
-
|
18
|
+
debugger-ruby_core_source (1.2.3)
|
19
|
+
diff-lcs (1.2.4)
|
20
|
+
faraday (0.8.8)
|
21
|
+
multipart-post (~> 1.2.0)
|
22
|
+
git (1.2.6)
|
23
|
+
github_api (0.10.1)
|
24
|
+
addressable
|
25
|
+
faraday (~> 0.8.1)
|
26
|
+
hashie (>= 1.2)
|
27
|
+
multi_json (~> 1.4)
|
28
|
+
nokogiri (~> 1.5.2)
|
29
|
+
oauth2
|
30
|
+
hashie (2.0.5)
|
31
|
+
highline (1.6.19)
|
32
|
+
httpauth (0.2.0)
|
33
|
+
jeweler (1.8.8)
|
34
|
+
builder
|
21
35
|
bundler (~> 1.0)
|
22
36
|
git (>= 1.2.5)
|
37
|
+
github_api (= 0.10.1)
|
38
|
+
highline (>= 1.6.15)
|
39
|
+
nokogiri (= 1.5.10)
|
23
40
|
rake
|
24
41
|
rdoc
|
25
|
-
json (1.
|
26
|
-
json (1.
|
27
|
-
|
28
|
-
|
29
|
-
|
42
|
+
json (1.8.0)
|
43
|
+
json (1.8.0-java)
|
44
|
+
jwt (0.1.8)
|
45
|
+
multi_json (>= 1.5)
|
46
|
+
mime-types (1.25)
|
47
|
+
multi_json (1.8.2)
|
48
|
+
multi_xml (0.5.5)
|
49
|
+
multipart-post (1.2.0)
|
50
|
+
nokogiri (1.5.10)
|
51
|
+
nokogiri (1.5.10-java)
|
52
|
+
oauth2 (0.9.2)
|
53
|
+
faraday (~> 0.8)
|
54
|
+
httpauth (~> 0.2)
|
55
|
+
jwt (~> 0.1.4)
|
56
|
+
multi_json (~> 1.0)
|
57
|
+
multi_xml (~> 0.5)
|
58
|
+
rack (~> 1.2)
|
59
|
+
rack (1.5.2)
|
60
|
+
rake (10.1.0)
|
30
61
|
rdoc (3.12.2)
|
31
62
|
json (~> 1.4)
|
32
63
|
rest-client (1.6.7)
|
@@ -38,12 +69,15 @@ GEM
|
|
38
69
|
rspec-core (2.13.1)
|
39
70
|
rspec-expectations (2.13.0)
|
40
71
|
diff-lcs (>= 1.1.3, < 2.0)
|
41
|
-
rspec-mocks (2.13.
|
72
|
+
rspec-mocks (2.13.1)
|
42
73
|
simplecov (0.7.1)
|
43
74
|
multi_json (~> 1.0)
|
44
75
|
simplecov-html (~> 0.7.1)
|
45
76
|
simplecov-html (0.7.1)
|
77
|
+
term-ansicolor (1.2.2)
|
78
|
+
tins (~> 0.8)
|
46
79
|
thor (0.18.1)
|
80
|
+
tins (0.12.0)
|
47
81
|
|
48
82
|
PLATFORMS
|
49
83
|
java
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/id3tag.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "id3tag"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Krists Ozols"]
|
12
|
-
s.date = "2013-
|
12
|
+
s.date = "2013-10-19"
|
13
13
|
s.description = "Native Ruby ID3 tag reader that aims for 100% covarage of ID3v2.x and ID3v1.x standards"
|
14
14
|
s.email = "krists@iesals.lv"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -38,6 +38,7 @@ Gem::Specification.new do |s|
|
|
38
38
|
"lib/id3tag/frames/v2/basic_frame.rb",
|
39
39
|
"lib/id3tag/frames/v2/comments_frame.rb",
|
40
40
|
"lib/id3tag/frames/v2/frame_fabricator.rb",
|
41
|
+
"lib/id3tag/frames/v2/frame_flags.rb",
|
41
42
|
"lib/id3tag/frames/v2/genre_frame.rb",
|
42
43
|
"lib/id3tag/frames/v2/genre_frame/genre_parser.rb",
|
43
44
|
"lib/id3tag/frames/v2/genre_frame/genre_parser_24.rb",
|
@@ -67,6 +68,7 @@ Gem::Specification.new do |s|
|
|
67
68
|
"spec/lib/id3tag/frames/v2/basic_frame_spec.rb",
|
68
69
|
"spec/lib/id3tag/frames/v2/comments_frame_spec.rb",
|
69
70
|
"spec/lib/id3tag/frames/v2/frame_fabricator_spec.rb",
|
71
|
+
"spec/lib/id3tag/frames/v2/frame_flags_spec.rb",
|
70
72
|
"spec/lib/id3tag/frames/v2/genre_frame/genre_parser_24_spec.rb",
|
71
73
|
"spec/lib/id3tag/frames/v2/genre_frame/genre_parser_pre_24_spec.rb",
|
72
74
|
"spec/lib/id3tag/frames/v2/genre_frame/genre_parser_spec.rb",
|
@@ -82,7 +84,12 @@ Gem::Specification.new do |s|
|
|
82
84
|
"spec/lib/id3tag/synchsafe_integer_spec.rb",
|
83
85
|
"spec/lib/id3tag/tag_spec.rb",
|
84
86
|
"spec/spec_helper.rb",
|
85
|
-
"spec/support/mp3_fixtures.rb"
|
87
|
+
"spec/support/mp3_fixtures.rb",
|
88
|
+
"standard_documents/id3v2-00.txt",
|
89
|
+
"standard_documents/id3v2.3.0.txt",
|
90
|
+
"standard_documents/id3v2.4.0-frames.txt",
|
91
|
+
"standard_documents/id3v2.4.0-structure.txt",
|
92
|
+
"tags"
|
86
93
|
]
|
87
94
|
s.homepage = "http://github.com/krists/id3tag"
|
88
95
|
s.licenses = ["MIT"]
|
data/lib/id3tag.rb
CHANGED
@@ -23,6 +23,7 @@ require "id3tag/frames/v2/genre_frame/genre_parser"
|
|
23
23
|
require "id3tag/frames/v2/genre_frame/genre_parser_pre_24"
|
24
24
|
require "id3tag/frames/v2/genre_frame/genre_parser_24"
|
25
25
|
require "id3tag/frames/v2/frame_fabricator"
|
26
|
+
require "id3tag/frames/v2/frame_flags"
|
26
27
|
|
27
28
|
module ID3Tag
|
28
29
|
def self.read(source, version = :all)
|
@@ -47,10 +47,14 @@ module ID3Tag
|
|
47
47
|
version_ids && version_ids[frame_name]
|
48
48
|
end
|
49
49
|
|
50
|
+
def version_of_interest
|
51
|
+
"v#{@version}.#{@major_version}"
|
52
|
+
end
|
53
|
+
|
50
54
|
private
|
51
55
|
|
52
56
|
def version_ids
|
53
|
-
COMMON_FRAME_IDS_BY_VERSION
|
57
|
+
COMMON_FRAME_IDS_BY_VERSION.fetch(version_of_interest)
|
54
58
|
end
|
55
59
|
end
|
56
60
|
end
|
@@ -2,23 +2,117 @@ module ID3Tag
|
|
2
2
|
module Frames
|
3
3
|
module V2
|
4
4
|
class BasicFrame
|
5
|
-
|
5
|
+
DECOMPRESSED_SIZE_BYTE_COUNT = 4
|
6
|
+
GROUP_BYTE_COUNT = 1
|
7
|
+
|
8
|
+
attr_reader :id, :raw_content
|
9
|
+
|
6
10
|
def initialize(id, content, flags, major_version_number)
|
7
11
|
@id, @raw_content, @flags, @major_version_number = id.to_sym, content, flags, major_version_number
|
8
12
|
end
|
9
13
|
|
10
14
|
def content
|
11
|
-
|
15
|
+
usable_content
|
16
|
+
end
|
17
|
+
|
18
|
+
def usable_content
|
19
|
+
raw_content_io.seek(additional_info_byte_count)
|
20
|
+
if unsynchronised?
|
21
|
+
StringUtil.undo_unsynchronization(raw_content_io.read)
|
22
|
+
else
|
23
|
+
raw_content_io.read
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def group_id
|
28
|
+
read_additional_info_byte(*position_and_count_of_group_id_bytes) if grouped?
|
29
|
+
end
|
30
|
+
|
31
|
+
def encryption_id
|
32
|
+
read_additional_info_byte(*position_and_count_of_encryption_id_bytes) if encrypted?
|
33
|
+
end
|
34
|
+
|
35
|
+
def read_additional_info_byte(position, byte_count)
|
36
|
+
if position && byte_count
|
37
|
+
raw_content_io.seek(position)
|
38
|
+
raw_content_io.read(byte_count).unpack("C").first
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def final_size
|
43
|
+
pos, count = position_and_count_of_data_length_bytes
|
44
|
+
if (compressed? || data_length_indicator?) && pos && count
|
45
|
+
raw_content_io.seek(pos)
|
46
|
+
SynchsafeInteger.decode(NumberUtil.convert_string_to_32bit_integer(raw_content_io.read(count)))
|
47
|
+
else
|
48
|
+
raw_content_io.size
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def preserve_on_file_alteration?
|
53
|
+
frame_flags.preserve_on_file_alteration?
|
54
|
+
end
|
55
|
+
|
56
|
+
def preserve_on_tag_alteration?
|
57
|
+
frame_flags.preserve_on_tag_alteration?
|
58
|
+
end
|
59
|
+
|
60
|
+
def read_only?
|
61
|
+
frame_flags.read_only?
|
62
|
+
end
|
63
|
+
|
64
|
+
def compressed?
|
65
|
+
frame_flags.compressed?
|
66
|
+
end
|
67
|
+
|
68
|
+
def encrypted?
|
69
|
+
frame_flags.encrypted?
|
70
|
+
end
|
71
|
+
|
72
|
+
def grouped?
|
73
|
+
frame_flags.grouped?
|
74
|
+
end
|
75
|
+
|
76
|
+
def unsynchronised?
|
77
|
+
frame_flags.unsynchronised?
|
78
|
+
end
|
79
|
+
|
80
|
+
def data_length_indicator?
|
81
|
+
frame_flags.data_length_indicator?
|
12
82
|
end
|
13
83
|
|
14
84
|
def inspect
|
15
|
-
"<#{self.class.name} #{id}: #{
|
85
|
+
"<#{self.class.name} #{id}: #{inspectable_content}>"
|
86
|
+
end
|
87
|
+
|
88
|
+
def inspectable_content
|
89
|
+
content
|
16
90
|
end
|
17
91
|
|
18
92
|
private
|
19
93
|
|
20
|
-
def
|
21
|
-
|
94
|
+
def additional_info_byte_count
|
95
|
+
frame_flags.additional_info_byte_count
|
96
|
+
end
|
97
|
+
|
98
|
+
def position_and_count_of_data_length_bytes
|
99
|
+
frame_flags.position_and_count_of_data_length_bytes
|
100
|
+
end
|
101
|
+
|
102
|
+
def position_and_count_of_group_id_bytes
|
103
|
+
frame_flags.position_and_count_of_group_id_bytes
|
104
|
+
end
|
105
|
+
|
106
|
+
def position_and_count_of_encryption_id_bytes
|
107
|
+
frame_flags.position_and_count_of_encryption_id_bytes
|
108
|
+
end
|
109
|
+
|
110
|
+
def frame_flags
|
111
|
+
@frame_flags ||= FrameFlags.new(@flags, @major_version_number)
|
112
|
+
end
|
113
|
+
|
114
|
+
def raw_content_io
|
115
|
+
@raw_content_io ||= StringIO.new(raw_content)
|
22
116
|
end
|
23
117
|
end
|
24
118
|
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module ID3Tag
|
2
|
+
module Frames
|
3
|
+
module V2
|
4
|
+
class FrameFlags
|
5
|
+
FLAG_MAP_IN_APPEARANCE_ORDER_BY_VERSION = {
|
6
|
+
3 => {
|
7
|
+
:status_flags => [ :preserve_on_tag_alteration, :preserve_on_file_alteration, :read_only, nil, nil, nil, nil, nil ],
|
8
|
+
:format_flags => [ :compressed, :encrypted, :grouped, nil, nil, nil, nil, nil ]
|
9
|
+
},
|
10
|
+
4 => {
|
11
|
+
:status_flags => [ nil, :preserve_on_tag_alteration, :preserve_on_file_alteration, :read_only, nil, nil, nil, nil ],
|
12
|
+
:format_flags => [ nil, :grouped, nil, nil, :compressed, :encrypted, :unsynchronised, :data_length_indicator ]
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
ADDITIONAL_INFO_BYTES_IN_APPEARANCE_ORDER_BY_VERSION = {
|
17
|
+
3 => [
|
18
|
+
[:compressed?, 4], [:encrypted?, 1], [:grouped?, 1]
|
19
|
+
],
|
20
|
+
4 => [
|
21
|
+
[:grouped?, 1], [:encrypted?, 1], [:data_length_indicator?, 4]
|
22
|
+
]
|
23
|
+
}
|
24
|
+
|
25
|
+
def initialize(flag_bytes, major_version_number)
|
26
|
+
@flag_bytes = flag_bytes
|
27
|
+
@major_version_number = major_version_number
|
28
|
+
end
|
29
|
+
|
30
|
+
def preserve_on_tag_alteration?
|
31
|
+
flag(:status_flags, :preserve_on_tag_alteration) != 1
|
32
|
+
end
|
33
|
+
|
34
|
+
def preserve_on_file_alteration?
|
35
|
+
flag(:status_flags, :preserve_on_file_alteration) != 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def read_only?
|
39
|
+
flag(:status_flags, :read_only) == 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def compressed?
|
43
|
+
flag(:format_flags, :compressed) == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def encrypted?
|
47
|
+
flag(:format_flags, :encrypted) == 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def grouped?
|
51
|
+
flag(:format_flags, :grouped) == 1
|
52
|
+
end
|
53
|
+
|
54
|
+
def unsynchronised?
|
55
|
+
flag(:format_flags, :unsynchronised) == 1
|
56
|
+
end
|
57
|
+
|
58
|
+
def data_length_indicator?
|
59
|
+
flag(:format_flags, :data_length_indicator) == 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def additional_info_byte_count
|
63
|
+
additional_info_flags_in_effect.inject(0) do |total, query|
|
64
|
+
total += query.last
|
65
|
+
total
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def position_and_count_of_data_length_bytes
|
70
|
+
if data_length_indicator? || compressed?
|
71
|
+
find_position_and_length_for_additional_info(:data_length_indicator?, :compressed?)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def position_and_count_of_group_id_bytes
|
76
|
+
if grouped?
|
77
|
+
find_position_and_length_for_additional_info(:grouped?)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def position_and_count_of_encryption_id_bytes
|
82
|
+
if encrypted?
|
83
|
+
find_position_and_length_for_additional_info(:encrypted?)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def find_position_and_length_for_additional_info(*methods_of_interest)
|
88
|
+
start_position = 0
|
89
|
+
target_info_byte_count = nil
|
90
|
+
additional_info_flags_in_effect.map do |query_method, byte_count|
|
91
|
+
start_position += (byte_count) unless methods_of_interest.include?(query_method)
|
92
|
+
if methods_of_interest.include?(query_method)
|
93
|
+
target_info_byte_count = byte_count
|
94
|
+
break
|
95
|
+
end
|
96
|
+
end
|
97
|
+
[start_position, target_info_byte_count]
|
98
|
+
end
|
99
|
+
|
100
|
+
def additional_info_flags_in_effect
|
101
|
+
current_additional_info_map.reject { |q| !self.send(q.first) }
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def flag(scope, name)
|
107
|
+
index = index_of_flag(scope, name)
|
108
|
+
if index
|
109
|
+
flags = flags_for_scope(scope)
|
110
|
+
flags && flags[7 - index]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def flags_for_scope(scope)
|
115
|
+
pair = @flag_bytes.to_s.unpack("C2")
|
116
|
+
case scope
|
117
|
+
when :status_flags
|
118
|
+
pair.first
|
119
|
+
when :format_flags
|
120
|
+
pair.last
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def index_of_flag(scope, name)
|
125
|
+
current_flag_map[scope].find_index(name)
|
126
|
+
end
|
127
|
+
|
128
|
+
def current_flag_map
|
129
|
+
FLAG_MAP_IN_APPEARANCE_ORDER_BY_VERSION.fetch(@major_version_number) do
|
130
|
+
{ :status_flags => [], :format_flags => [] }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def current_additional_info_map
|
135
|
+
ADDITIONAL_INFO_BYTES_IN_APPEARANCE_ORDER_BY_VERSION.fetch(@major_version_number) { [] }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|