Class: Id3Taginator::Frames::Id3v2Frame

Inherits:
Object
  • Object
show all
Includes:
Extensions::ArgumentCheck, Extensions::Encodable
Defined in:
lib/id3taginator/frames/id3v2_frame.rb

Direct Known Subclasses

Buffer::RecommendedBufferSizeFrame, Comment::CommentFrame, Count::PlayCounterFrame, Count::PopularityFrame, CustomFrame, Encryption::AudioEncryptionFrame, Encryption::EncryptionMethodFrame, Geo::GeneralEncapsulatedObjectFrame, Grouping::GroupIdentificationFrame, Grouping::GroupingFrame, Id3Taginator::Frames::Ipl::InvolvedPeopleFrame, Lyrics::UnsyncLyricsFrame, Mcdi::MusicCDIdentifierFrame, Picture::PictureFrame, Private::PrivateFrame, Text::AlbumArtistFrame, Text::AlbumFrame, Text::AlbumSortOrderFrame, Text::ArtistsFrame, Text::BPMFrame, Text::ComposerFrame, Text::ConductorFrame, Text::ContentGroupDescriptionFrame, Text::CopyrightFrame, Text::DateFrame, Text::EncodedByFrame, Text::EncoderFrame, Text::FileOwnerFrame, Text::FileTypeFrame, Text::GenreFrame, Text::ISRCFrame, Text::InitialKeyFrame, Text::InternetRadioStationFrame, Text::LanguageFrame, Text::LengthFrame, Text::MediaTypeFrame, Text::ModifiedByFrame, Text::OriginalAlbumFrame, Text::OriginalArtistsFrame, Text::OriginalFilenameFrame, Text::OriginalReleaseYearFrame, Text::OriginalWritersFrame, Text::PartOfSetFrame, Text::PerformerSortOrderFrame, Text::PlaylistDelayFrame, Text::PublisherFrame, Text::RecordingDatesFrame, Text::SizeFrame, Text::SubtitleFrame, Text::TimeFrame, Text::TitleFrame, Text::TitleSortOrderFrame, Text::TrackNumberFrame, Text::UserTextInfoFrame, Text::WritersFrame, Text::YearFrame, Tos::OwnershipFrame, Tos::TermsOfUseFrame, Ufid::UniqueFileIdentifierFrame, Url::CommercialUrlFrame, Url::CopyrightUrlFrame, Url::OfficialArtistWebpageFrame, Url::OfficialAudioRadioStationHomepageFrame, Url::OfficialFileWebpageFrame, Url::OfficialPublisherWebpageFrame, Url::OfficialSourceWebpageFrame, Url::PaymentUrlFrame, Url::UserUrlLinkFrame

Constant Summary collapse

HEADER_SIZE_V_3_4 =
10
HEADER_SIZE_V_2 =
6

Constants included from Extensions::Encodable

Extensions::Encodable::ISO8859_1, Extensions::Encodable::UNICODE_ZERO, Extensions::Encodable::UTF_16, Extensions::Encodable::UTF_16BE, Extensions::Encodable::UTF_8, Extensions::Encodable::ZERO

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Extensions::ArgumentCheck

#argument_between_num, #argument_boolean, #argument_exactly_chars, #argument_less_than_chars, #argument_more_than_chars, #argument_not_empty, #argument_not_nil, #argument_sym, included

Methods included from Extensions::Encodable

#decode, #decode_using_encoding_byte, #default_encoding_destination_byte, #encode, #encode_and_add_encoding_byte, #encode_string, #find_encoding, #find_encoding_byte, included, #merge, #pad_left, #pad_right, #read_stream_until, #remove_trailing_zeros, #zero_byte

Constructor Details

#initialize(frame_id, payload_size, flags, frame_payload, header_size = 10, group_identify = nil) ⇒ Id3v2Frame

Constructor

Parameters:

  • frame_id (String)

    the frame id

  • payload_size (Integer)

    the payload size (excludes header)

  • flags (Id3v23FrameFlags, Id3v24FrameFlags, nil)

    the frame flags

  • frame_payload (String)

    the decompressed and unsynchronized payload

  • header_size (Integer) (defaults to: 10)

    the frame header size, 6 vor v2, 10 otherwise

  • group_identify (String, nil) (defaults to: nil)

    the group identify if present



126
127
128
129
130
131
132
133
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 126

def initialize(frame_id, payload_size, flags, frame_payload, header_size = 10, group_identify = nil)
  @header_size = header_size
  @frame_id = frame_id.to_sym
  @payload_size = payload_size
  @flags = flags
  @frame_payload = frame_payload
  @group_identity = group_identify
end

Instance Attribute Details

#frame_idObject (readonly)

Returns the value of attribute frame_id.



13
14
15
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 13

def frame_id
  @frame_id
end

#optionsObject

Returns the value of attribute options.



12
13
14
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 12

def options
  @options
end

Class Method Details

.build_id3_flags(version, flags = "\x00\x00") ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 105

def self.build_id3_flags(version, flags = "\x00\x00")
  case version
  when 2
    nil
  when 3
    Id3v23FrameFlags.new(flags)
  when 4
    Id3v24FrameFlags.new(flags)
  else
    raise Errors::Id3TagError, "Id3v.2.#{version} is not supported."
  end
end

.build_v2_frame(frame_id, file, options) ⇒ Id3v2Frame

builds an id3v2.2 frame of the given frame id and the given data stream. The data stream(0) must be after the frame id

Parameters:

  • frame_id (String)

    the frame id

  • file (StringIO, IO, file)

    the data stream

  • options (Options::Options)

    the options to use

Returns:

Raises:



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 23

def self.build_v2_frame(frame_id, file, options)
  payload_size = Util::MathUtil.to_number(file.read(3)&.bytes)
  frame_payload = file.read(payload_size)

  raise Errors::Id3TagError, "Could not find any Frame data for #{frame_id}." if frame_payload.nil?

  instance = new(frame_id, payload_size, nil, frame_payload, HEADER_SIZE_V_2, nil)
  instance.options = options
  instance.process_content(frame_payload)
  instance
end

.build_v3_frame(frame_id, file, options) ⇒ Id3v2Frame

builds an id3v2.3 frame of the given frame id and the given data stream. The data stream(0) must be after the frame id

Parameters:

  • frame_id (String)

    the frame id

  • file (StringIO, IO, file)

    the data stream

  • options (Options::Options)

    the options to use

Returns:

Raises:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 43

def self.build_v3_frame(frame_id, file, options)
  payload_size = Util::MathUtil.to_number(file.read(4)&.bytes)
  flags = Id3v23FrameFlags.new(file.read(2))
  decompressed_size = Util::MathUtil.to_number(file.read(4)&.bytes) if flags.compression?

  if flags.compression?
    compressed_data = file.read(payload_size)
    raise Errors::Id3TagError, "Could not find any Frame data for #{frame_id}." if compressed_data.nil?

    frame_payload = Util::CompressUtil.decompress_data(compressed_data)
    # noinspection RubyScope
    payload_size = decompressed_size
  else
    frame_payload = file.read(payload_size)
  end

  group_identify = nil
  if flags.group_identity?
    group_identify = frame_payload[0]
    frame_payload = frame_payload[1..-1]
  end

  raise Errors::Id3TagError, "Could not find any Frame data for #{frame_id}." if frame_payload.nil?

  instance = new(frame_id, payload_size, flags, frame_payload, HEADER_SIZE_V_3_4, group_identify)
  instance.options = options
  instance.process_content(frame_payload)
  instance
end

.build_v4_frame(frame_id, file, options) ⇒ Id3v2Frame

builds an id3v2.4 frame of the given frame id and the given data stream. The data stream(0) must be after the frame id

Parameters:

  • frame_id (String)

    the frame id

  • file (StringIO, IO, file)

    the data stream

  • options (Options::Options)

    the options to use

Returns:

Raises:



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 81

def self.build_v4_frame(frame_id, file, options)
  payload_size = Util::MathUtil.to_32_synchsafe_integer(file.read(4)&.bytes)
  flags = Id3v24FrameFlags.new(file.read(2))

  frame_payload = file.read(payload_size)
  raise Errors::Id3TagError, "Could not find any Frame data for #{frame_id}." if frame_payload.nil?

  frame_payload = Util::SyncUtil.undo_synchronization(StringIO.new(frame_payload)) if flags.unsynchronisation?
  frame_payload = Util::CompressUtil.decompress_data(frame_payload) if flags.compression?

  group_identify = nil
  if flags.group_identity?
    group_identify = frame_payload[0]
    frame_payload = frame_payload[1..-1]
  end

  raise Errors::Id3TagError, "Could not find any Frame data for #{frame_id}." if frame_payload.nil?

  instance = new(frame_id, payload_size, flags, frame_payload, HEADER_SIZE_V_3_4, group_identify)
  instance.options = options
  instance.process_content(frame_payload)
  instance
end

Instance Method Details

#compression?Boolean

determined if the frame is compressed

Returns:

  • (Boolean)

    true if the frame is compressed, else false



224
225
226
227
228
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 224

def compression?
  return false if @flags.nil?

  @flags.compression?
end

#content_to_bytesString

dumps the frame content to the byte representation as a String

Returns:

  • (String)

    the byte array as String

Raises:

  • (NotImplementedError)


145
146
147
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 145

def content_to_bytes
  raise NotImplementedError, 'Implement this in the actual frame class.'
end

#encryption?Boolean

determined if the frame is encrypted

Returns:

  • (Boolean)

    true if the frame is encrypted, else false



233
234
235
236
237
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 233

def encryption?
  return false if @flags.nil?

  @flags.encryption?
end

#file_alter_preservation?Boolean

determined if the file is alter preserved

Returns:

  • (Boolean)

    true if the file is alter preserved, else false



206
207
208
209
210
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 206

def file_alter_preservation?
  return false if @flags.nil?

  @flags.file_alter_preservation?
end

#frame_sizeInteger

calculates the frame size including header and payload, takes compression, group identity and everything into effect

Returns:

  • (Integer)

    the frame size in bytes



186
187
188
189
190
191
192
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 186

def frame_size
  compression = compression? ? 4 : 0
  group_identity = group_identity? ? 1 : 0

  # noinspection RubyMismatchedReturnType
  @header_size + @payload_size + compression + group_identity
end

#group_identity?Boolean

determined if the frame has a group identity

Returns:

  • (Boolean)

    true if the frame has a group identity, else false



242
243
244
245
246
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 242

def group_identity?
  return false if @flags.nil?

  @flags.group_identity?
end

#process_content(_content) ⇒ Object

processes the frame payload for the specific frame

Parameters:

  • _content (String)

    the frame payload

Raises:

  • (NotImplementedError)


138
139
140
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 138

def process_content(_content)
  raise NotImplementedError, 'Implement this in a the actual frame class.'
end

#re_calc_payload_sizeObject

recalculates the payload size



178
179
180
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 178

def re_calc_payload_size
  @payload_size = content_to_bytes.length
end

#read_only?Boolean

determined if the file frame is read only

Returns:

  • (Boolean)

    true if frame is read only, else false



215
216
217
218
219
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 215

def read_only?
  return false if @flags.nil?

  @flags.read_only?
end

#tag_alter_preservation?Boolean

determined if the frame is alter preserved

Returns:

  • (Boolean)

    true if alter preserved, else false



197
198
199
200
201
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 197

def tag_alter_preservation?
  return false if @flags.nil?

  @flags.tag_alter_preservation?
end

#to_bytesString

dumps the frame to a byte string. This dump already takes unsynchronization, padding and all other options into effect

Returns:

  • (String)

    frame dump as a String. tag.bytes represents the byte array



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/id3taginator/frames/id3v2_frame.rb', line 153

def to_bytes
  size_padding = @frame_id.to_s.length == 3 ? 6 : 8
  payload = content_to_bytes
  payload_size_decompressed = Util::MathUtil.from_number(payload.length, size_padding)

  result = @frame_id.to_s
  if @flags&.compression?
    payload_compressed = Util::CompressUtil.compress_data(payload)
    payload_size_compressed = payload_compressed.size
    result += Util::MathUtil.from_number(payload_size_compressed, size_padding)
  else
    result += payload_size_decompressed
  end

  result += @flags.to_bytes unless @flags.nil?
  result += payload_size_decompressed if @flags&.compression?
  result += @group_identity if @flags&.group_identity?

  # noinspection RubyScope
  result += @flags&.compression? ? payload_compressed : payload

  result
end