id3taginator 0.8

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.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.idea/.gitignore +8 -0
  4. data/.idea/ID3Taginator.iml +73 -0
  5. data/.idea/misc.xml +4 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/vcs.xml +6 -0
  8. data/.rspec +3 -0
  9. data/.rubocop.yml +26 -0
  10. data/CHANGELOG.md +5 -0
  11. data/CODE_OF_CONDUCT.md +84 -0
  12. data/Gemfile +12 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +408 -0
  15. data/Rakefile +12 -0
  16. data/bin/console +15 -0
  17. data/bin/setup +8 -0
  18. data/id3taginator.gemspec +37 -0
  19. data/lib/id3taginator/audio_file.rb +136 -0
  20. data/lib/id3taginator/errors/id3_tag_error.rb +12 -0
  21. data/lib/id3taginator/extensions/argument_check.rb +88 -0
  22. data/lib/id3taginator/extensions/comparable.rb +28 -0
  23. data/lib/id3taginator/extensions/encodable.rb +215 -0
  24. data/lib/id3taginator/extensions/optionable.rb +73 -0
  25. data/lib/id3taginator/frames/buffer/entities/buffer.rb +26 -0
  26. data/lib/id3taginator/frames/buffer/rbuf_recommended_buffer_size_frame.rb +59 -0
  27. data/lib/id3taginator/frames/buffer_frames.rb +32 -0
  28. data/lib/id3taginator/frames/comment/comm_comment_frame.rb +56 -0
  29. data/lib/id3taginator/frames/comment/entities/comment.rb +26 -0
  30. data/lib/id3taginator/frames/comment_frames.rb +42 -0
  31. data/lib/id3taginator/frames/count/entities/popularimeter.rb +26 -0
  32. data/lib/id3taginator/frames/count/pcnt_play_counter_frame.rb +40 -0
  33. data/lib/id3taginator/frames/count/popm_popularimeter_frame.rb +52 -0
  34. data/lib/id3taginator/frames/count_frames.rb +51 -0
  35. data/lib/id3taginator/frames/custom_frame.rb +39 -0
  36. data/lib/id3taginator/frames/custom_frames.rb +54 -0
  37. data/lib/id3taginator/frames/encryption/aenc_audio_encryption.rb +54 -0
  38. data/lib/id3taginator/frames/encryption/encr_encryption_method_frame.rb +49 -0
  39. data/lib/id3taginator/frames/encryption/entities/audio_encryption.rb +28 -0
  40. data/lib/id3taginator/frames/encryption/entities/encryption_method.rb +26 -0
  41. data/lib/id3taginator/frames/encryption_frames.rb +77 -0
  42. data/lib/id3taginator/frames/frameable.rb +75 -0
  43. data/lib/id3taginator/frames/geo/entities/encapsulated_object.rb +28 -0
  44. data/lib/id3taginator/frames/geo/geob_general_encapsulated_object_frame.rb +59 -0
  45. data/lib/id3taginator/frames/geo_frames.rb +41 -0
  46. data/lib/id3taginator/frames/grouping/entities/group_identification.rb +26 -0
  47. data/lib/id3taginator/frames/grouping/grid_group_identification_frame.rb +49 -0
  48. data/lib/id3taginator/frames/grouping/grp1_grouping_frame.rb +40 -0
  49. data/lib/id3taginator/frames/grouping_frames.rb +61 -0
  50. data/lib/id3taginator/frames/has_id.rb +51 -0
  51. data/lib/id3taginator/frames/id3v23_frame_flags.rb +64 -0
  52. data/lib/id3taginator/frames/id3v24_frame_flags.rb +80 -0
  53. data/lib/id3taginator/frames/id3v2_frame.rb +249 -0
  54. data/lib/id3taginator/frames/id3v2_frame_factory.rb +98 -0
  55. data/lib/id3taginator/frames/ipl/entities/involved_person.rb +24 -0
  56. data/lib/id3taginator/frames/ipl/ipls_involved_people_frame.rb +48 -0
  57. data/lib/id3taginator/frames/ipl_frames.rb +31 -0
  58. data/lib/id3taginator/frames/lyrics/entities/unsync_lyrics.rb +26 -0
  59. data/lib/id3taginator/frames/lyrics/uslt_unsync_lyrics_frame.rb +56 -0
  60. data/lib/id3taginator/frames/lyrics_frames.rb +42 -0
  61. data/lib/id3taginator/frames/mcdi/mcdi_music_cd_identifier_frame.rb +40 -0
  62. data/lib/id3taginator/frames/mcdi_frames.rb +28 -0
  63. data/lib/id3taginator/frames/picture/apic_picture_frame.rb +106 -0
  64. data/lib/id3taginator/frames/picture/entities/picture.rb +32 -0
  65. data/lib/id3taginator/frames/picture_frames.rb +51 -0
  66. data/lib/id3taginator/frames/private/entities/private_frame.rb +24 -0
  67. data/lib/id3taginator/frames/private/priv_private_frame.rb +45 -0
  68. data/lib/id3taginator/frames/private_frames.rb +40 -0
  69. data/lib/id3taginator/frames/text/entities/copyright.rb +24 -0
  70. data/lib/id3taginator/frames/text/entities/date.rb +24 -0
  71. data/lib/id3taginator/frames/text/entities/part_of_set.rb +24 -0
  72. data/lib/id3taginator/frames/text/entities/time.rb +24 -0
  73. data/lib/id3taginator/frames/text/entities/track_number.rb +24 -0
  74. data/lib/id3taginator/frames/text/entities/user_info.rb +24 -0
  75. data/lib/id3taginator/frames/text/talb_album_frame.rb +40 -0
  76. data/lib/id3taginator/frames/text/tbpm_bpm_frame.rb +40 -0
  77. data/lib/id3taginator/frames/text/tcom_composer_frame.rb +42 -0
  78. data/lib/id3taginator/frames/text/tcon_genre_frame.rb +104 -0
  79. data/lib/id3taginator/frames/text/tcop_copyright_frame.rb +55 -0
  80. data/lib/id3taginator/frames/text/tdat_date_frame.rb +60 -0
  81. data/lib/id3taginator/frames/text/tdly_playlist_delay_frame.rb +40 -0
  82. data/lib/id3taginator/frames/text/tenc_encoded_by_frame.rb +40 -0
  83. data/lib/id3taginator/frames/text/text_writers_frame.rb +43 -0
  84. data/lib/id3taginator/frames/text/tflt_file_type_frame.rb +71 -0
  85. data/lib/id3taginator/frames/text/time_time_frame.rb +60 -0
  86. data/lib/id3taginator/frames/text/tit1_content_group_description_frame.rb +40 -0
  87. data/lib/id3taginator/frames/text/tit2_title_frame.rb +40 -0
  88. data/lib/id3taginator/frames/text/tit3_subtitle_frame.rb +40 -0
  89. data/lib/id3taginator/frames/text/tkey_initial_key_frame.rb +40 -0
  90. data/lib/id3taginator/frames/text/tlan_language_frame.rb +48 -0
  91. data/lib/id3taginator/frames/text/tlen_length_frame.rb +40 -0
  92. data/lib/id3taginator/frames/text/tmed_media_type_frame.rb +40 -0
  93. data/lib/id3taginator/frames/text/toal_original_album_frame.rb +40 -0
  94. data/lib/id3taginator/frames/text/tofn_original_filename_frame.rb +40 -0
  95. data/lib/id3taginator/frames/text/toly_original_writers_frame.rb +43 -0
  96. data/lib/id3taginator/frames/text/tope_original_artists_frame.rb +43 -0
  97. data/lib/id3taginator/frames/text/tory_original_release_year_frame.rb +42 -0
  98. data/lib/id3taginator/frames/text/town_file_owner_frame.rb +40 -0
  99. data/lib/id3taginator/frames/text/tpe1_artist_frame.rb +43 -0
  100. data/lib/id3taginator/frames/text/tpe2_album_artist_frame.rb +40 -0
  101. data/lib/id3taginator/frames/text/tpe3_conductor_frame.rb +40 -0
  102. data/lib/id3taginator/frames/text/tpe4_modified_by_frame.rb +40 -0
  103. data/lib/id3taginator/frames/text/tpos_part_of_set_frame.rb +50 -0
  104. data/lib/id3taginator/frames/text/tpub_publisher_frame.rb +40 -0
  105. data/lib/id3taginator/frames/text/trck_track_number_frame.rb +50 -0
  106. data/lib/id3taginator/frames/text/trda_recording_dates_frame.rb +42 -0
  107. data/lib/id3taginator/frames/text/trsn_internet_radio_station_frame.rb +40 -0
  108. data/lib/id3taginator/frames/text/tsiz_size_frame.rb +41 -0
  109. data/lib/id3taginator/frames/text/tsoa_album_sort_order_frame.rb +40 -0
  110. data/lib/id3taginator/frames/text/tsop_performer_sort_order_frame.rb +40 -0
  111. data/lib/id3taginator/frames/text/tsot_title_sort_order_frame.rb +40 -0
  112. data/lib/id3taginator/frames/text/tsrc_isrc_frame.rb +40 -0
  113. data/lib/id3taginator/frames/text/tsse_encoder_frame.rb +40 -0
  114. data/lib/id3taginator/frames/text/txxx_user_text_info_frame.rb +51 -0
  115. data/lib/id3taginator/frames/text/tyer_year_frame.rb +42 -0
  116. data/lib/id3taginator/frames/text_frames.rb +840 -0
  117. data/lib/id3taginator/frames/tos/entities/ownership.rb +26 -0
  118. data/lib/id3taginator/frames/tos/entities/terms_of_use.rb +24 -0
  119. data/lib/id3taginator/frames/tos/owne_ownership_frame.rb +53 -0
  120. data/lib/id3taginator/frames/tos/user_terms_of_use_frame.rb +49 -0
  121. data/lib/id3taginator/frames/tos_frames.rb +54 -0
  122. data/lib/id3taginator/frames/ufid/entities/ufid_info.rb +24 -0
  123. data/lib/id3taginator/frames/ufid/ufid_unique_file_identifier_frame.rb +47 -0
  124. data/lib/id3taginator/frames/ufid_frames.rb +40 -0
  125. data/lib/id3taginator/frames/url/entities/user_info.rb +24 -0
  126. data/lib/id3taginator/frames/url/wcom_commercial_url_frame.rb +40 -0
  127. data/lib/id3taginator/frames/url/wcop_copyright_url_frame.rb +40 -0
  128. data/lib/id3taginator/frames/url/woaf_official_file_webpage_frame.rb +40 -0
  129. data/lib/id3taginator/frames/url/woar_official_artist_webpage_frame.rb +40 -0
  130. data/lib/id3taginator/frames/url/woas_official_source_webpage_frame.rb +40 -0
  131. data/lib/id3taginator/frames/url/wors_official_radio_station_homepage_frame.rb +40 -0
  132. data/lib/id3taginator/frames/url/wpay_payment_url_frame.rb +40 -0
  133. data/lib/id3taginator/frames/url/wpub_official_publisher_webpage_frame.rb +40 -0
  134. data/lib/id3taginator/frames/url/wxxx_user_url_link_frame.rb +50 -0
  135. data/lib/id3taginator/frames/url_frames.rb +195 -0
  136. data/lib/id3taginator/genres.rb +168 -0
  137. data/lib/id3taginator/header/id3v23_extended_header.rb +37 -0
  138. data/lib/id3taginator/header/id3v24_extended_header.rb +100 -0
  139. data/lib/id3taginator/header/id3v2_flags.rb +64 -0
  140. data/lib/id3taginator/id3v1_tag.rb +156 -0
  141. data/lib/id3taginator/id3v22_tag.rb +30 -0
  142. data/lib/id3taginator/id3v23_tag.rb +63 -0
  143. data/lib/id3taginator/id3v24_tag.rb +75 -0
  144. data/lib/id3taginator/id3v2_tag.rb +241 -0
  145. data/lib/id3taginator/options/options.rb +33 -0
  146. data/lib/id3taginator/util/compress_util.rb +25 -0
  147. data/lib/id3taginator/util/math_util.rb +68 -0
  148. data/lib/id3taginator/util/sync_util.rb +65 -0
  149. data/lib/id3taginator.rb +449 -0
  150. metadata +198 -0
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module PrivateFrames
6
+ include Frames::Frameable
7
+
8
+ # extracts the private frames (PRIV)
9
+ #
10
+ # @return [Array<Frames::Private::Entities::PrivateFrame>] returns the Private Frames
11
+ def private_frames
12
+ frame = find_frames(Private::PrivateFrame.frame_id(@major_version, @options))
13
+ return [] if frame.nil? || frame.empty?
14
+
15
+ frame.map { |f| Private::Entities::PrivateFrame.new(f.owner_id, f.private_data) }
16
+ end
17
+
18
+ # adds a private frame (PRIV)
19
+ # Multiple ones can be added, as long as they have a different owner_id
20
+ #
21
+ # @param priv [Frames::Private::Entities::PrivateFrame] the private frame to add
22
+ def private_frames=(priv)
23
+ set_frame_fields_by_selector(Private::PrivateFrame, %i[@owner_id @private_data],
24
+ ->(f) { f.owner_id == priv.owner_id },
25
+ priv.owner_id, priv.private_data)
26
+ end
27
+
28
+ alias add_private_frame private_frames=
29
+
30
+ # removes a pictures for the specific owner_id
31
+ #
32
+ # @param owner_id [String] the owner_id
33
+ def remove_private_frame(owner_id)
34
+ @frames.delete_if do |f|
35
+ f.frame_id == Private::PrivateFrame.frame_id(@major_version, @options) && f.owner_id == owner_id
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ module Entities
7
+ class Copyright
8
+ include Extensions::Comparable
9
+
10
+ attr_accessor :year, :holder
11
+
12
+ # constructor
13
+ #
14
+ # @param year [String] the 4 character year e.g. 2020
15
+ # @param holder [String] the copyright holder
16
+ def initialize(year, holder)
17
+ @year = year
18
+ @holder = holder
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ module Entities
7
+ class Date
8
+ include Extensions::Comparable
9
+
10
+ attr_accessor :month, :day
11
+
12
+ # constructor
13
+ #
14
+ # @param day [String, Integer] the day
15
+ # @param month [String, Integer] the month
16
+ def initialize(month, day)
17
+ @month = month
18
+ @day = day
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ module Entities
7
+ class PartOfSet
8
+ include Extensions::Comparable
9
+
10
+ attr_accessor :part, :total
11
+
12
+ # constructor
13
+ #
14
+ # @param part [String, Integer] part of the set
15
+ # @param total [String, Integer, nil] total number of parts
16
+ def initialize(part, total)
17
+ @part = part
18
+ @total = total
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ module Entities
7
+ class Time
8
+ include Extensions::Comparable
9
+
10
+ attr_accessor :hours, :minutes
11
+
12
+ # constructor
13
+ #
14
+ # @param hours [String, Integer] the hours
15
+ # @param minutes [String, Integer] the minutes
16
+ def initialize(hours, minutes)
17
+ @hours = hours
18
+ @minutes = minutes
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ module Entities
7
+ class TrackNumber
8
+ include Extensions::Comparable
9
+
10
+ attr_accessor :track_number, :total
11
+
12
+ # constructor
13
+ #
14
+ # @param track_number [String, Integer] the track number
15
+ # @param total [String, Integer, nil] the total number of tracks
16
+ def initialize(track_number, total)
17
+ @track_number = track_number
18
+ @total = total
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ module Entities
7
+ class UserInfo
8
+ include Extensions::Comparable
9
+
10
+ attr_accessor :description, :content
11
+
12
+ # constructor
13
+ #
14
+ # @param description [String] the description
15
+ # @param content [String] the content
16
+ def initialize(description, content)
17
+ @description = description
18
+ @content = content
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class AlbumFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TAL, :TALB, :TALB
10
+
11
+ attr_accessor :album
12
+
13
+ # builds the album frame
14
+ #
15
+ # @param album [String] the album name
16
+ # @param options [Options::Options] options to use
17
+ # @param id3_version [Integer] the id3 version to build the frame for
18
+ #
19
+ # @return [Id3v2Frame] the resulting id3v2 frame
20
+ def self.build_frame(album, options = nil, id3_version: 3)
21
+ supported?('TALB', id3_version, options)
22
+
23
+ argument_not_nil(album, 'album')
24
+
25
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
26
+ frame.album = album
27
+ frame
28
+ end
29
+
30
+ def process_content(content)
31
+ @album = decode_using_encoding_byte(content)
32
+ end
33
+
34
+ def content_to_bytes
35
+ encode_and_add_encoding_byte(@album)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class BPMFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TBP, :TBPM, :TBPM
10
+
11
+ attr_accessor :bpm
12
+
13
+ # builds the bpm frame
14
+ #
15
+ # @param album [String, Integer] the bpm
16
+ # @param options [Options::Options] options to use
17
+ # @param id3_version [Integer] the id3 version to build the frame for
18
+ #
19
+ # @return [Id3v2Frame] the resulting id3v2 frame
20
+ def self.build_frame(bpm, options = nil, id3_version: 3)
21
+ supported?('TBPM', id3_version, options)
22
+
23
+ argument_not_nil(bpm, 'bpm')
24
+
25
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
26
+ frame.bpm = bpm.to_s
27
+ frame
28
+ end
29
+
30
+ def process_content(content)
31
+ @bpm = decode_using_encoding_byte(content)
32
+ end
33
+
34
+ def content_to_bytes
35
+ encode_and_add_encoding_byte(@bpm)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class ComposerFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TCM, :TCOM, :TCOM
10
+
11
+ attr_accessor :composers
12
+
13
+ # builds the composer frame
14
+ #
15
+ # @param composers [Array<String>, String] the composer(s)
16
+ # @param options [Options::Options] options to use
17
+ # @param id3_version [Integer] the id3 version to build the frame for
18
+ #
19
+ # @return [Id3v2Frame] the resulting id3v2 frame
20
+ def self.build_frame(composers, options = nil, id3_version: 3)
21
+ supported?('TCOM', id3_version, options)
22
+
23
+ argument_not_empty(composers, 'composers')
24
+
25
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
26
+ frame.composers = composers.is_a?(Array) ? composers : [composers]
27
+ frame
28
+ end
29
+
30
+ def process_content(content)
31
+ content = decode_using_encoding_byte(content)
32
+ @composers = content.split('/').map(&:strip)
33
+ end
34
+
35
+ def content_to_bytes
36
+ content = @composers.join('/')
37
+ encode_and_add_encoding_byte(content)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class GenreFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TCO, :TCON, :TCON
10
+
11
+ attr_accessor :genres
12
+
13
+ # builds the genre frame
14
+ #
15
+ # @param genres [Array<String, Symbol>, Symbol, String] the genre(s)
16
+ # @param options [Options::Options] options to use
17
+ # @param id3_version [Integer] the id3 version to build the frame for
18
+ #
19
+ # @return [Id3v2Frame] the resulting id3v2 frame
20
+ def self.build_frame(genres, options = nil, id3_version: 3)
21
+ supported?('TCON', id3_version, options)
22
+
23
+ argument_not_empty(genres, 'genres')
24
+
25
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
26
+ frame.genres = genres.is_a?(Array) ? genres : [genres]
27
+ frame
28
+ end
29
+
30
+ def process_content(content)
31
+ genre_str = decode_using_encoding_byte(content)
32
+ genre_ids = parse_genres(genre_str)
33
+
34
+ @genres = []
35
+ genre_ids.each do |g|
36
+ next @genres << Genres.genre(g.to_i) if g.match(/^(\d)+$/)
37
+ next @genres << 'Remix' if g == 'RX'
38
+ next @genres << 'Cover' if g == 'CR'
39
+
40
+ @genres << g
41
+ end
42
+ end
43
+
44
+ def content_to_bytes
45
+ raise Errors::Id3TagError, 'Genres can\'t be empty' if @genres.nil? || @genres.empty?
46
+
47
+ encode_and_add_encoding_byte(genres_to_string(@genres))
48
+ end
49
+
50
+ def genres_to_string(genres)
51
+ result = ''
52
+ genres.each do |genre|
53
+ g = Genres.genre_by_name(genre)
54
+ g = g.is_a?(Integer) ? "(#{g})" : "((#{g})"
55
+ g = '(RX)' if g == '((Remix)'
56
+ g = '(CR)' if g == '((Cover)'
57
+
58
+ result += g
59
+ end
60
+
61
+ result
62
+ end
63
+
64
+ def parse_genres(genres)
65
+ result = []
66
+ curr = ''
67
+ is_started = false
68
+ if genres[0] == '('
69
+ is_started = true
70
+ genres = genres[1..-1]
71
+ end
72
+ genres.each_char do |c|
73
+ # genre has started, but nothing has parsed, so ( is opening for custom genre like ((my genre)
74
+ next if c == '(' && curr.empty? && is_started
75
+
76
+ if c == '(' && !curr.empty? && is_started
77
+ is_started = false
78
+ result << curr
79
+ curr = ''
80
+ next
81
+ elsif c == ')' && is_started
82
+ is_started = false
83
+ result << curr
84
+ curr = ''
85
+ next
86
+ elsif c == '(' && !is_started
87
+ is_started = true
88
+ next
89
+ else
90
+ is_started = true
91
+ curr += c
92
+ end
93
+ end
94
+
95
+ result << curr unless curr.empty?
96
+
97
+ result.reject(&:empty?)
98
+ end
99
+
100
+ private :parse_genres
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class CopyrightFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TCR, :TCOP, :TCOP
10
+
11
+ attr_accessor :holder, :year
12
+
13
+ # builds the copyright frame
14
+ #
15
+ # @param year [String] the 4 character year e.g. 2020
16
+ # @param holder [String] the copyright holder
17
+ # @param options [Options::Options] options to use
18
+ # @param id3_version [Integer] the id3 version to build the frame for
19
+ #
20
+ # @return [Id3v2Frame] the resulting id3v2 frame
21
+ def self.build_frame(year, holder, options = nil, id3_version: 3)
22
+ supported?('TCOP', id3_version, options)
23
+
24
+ year = year.to_s unless year.nil?
25
+
26
+ argument_exactly_chars(year, 'year', 4)
27
+ argument_not_nil(holder, 'holder')
28
+
29
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
30
+ frame.year = year
31
+ frame.holder = holder
32
+ frame
33
+ end
34
+
35
+ def process_content(content)
36
+ content = decode_using_encoding_byte(content)
37
+ if content[4] == ' '
38
+ @year = content[0..3]
39
+ content = content[4..-1]
40
+ else
41
+ @year = 1970
42
+ end
43
+
44
+ content = content.gsub('Copyright ©', '')
45
+ @holder = content.strip
46
+ end
47
+
48
+ def content_to_bytes
49
+ content = "#{@year} #{@holder} Copyright ©"
50
+ encode_and_add_encoding_byte(content)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class DateFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TDA, :TDAT, :TDAT
10
+
11
+ attr_accessor :day, :month
12
+
13
+ # builds the date frame
14
+ #
15
+ # @param day [String, Integer] the day
16
+ # @param month [String, Integer] the month
17
+ # @param options [Options::Options] options to use
18
+ # @param id3_version [Integer] the id3 version to build the frame for
19
+ #
20
+ # @return [Id3v2Frame] the resulting id3v2 frame
21
+ def self.build_frame(day, month, options = nil, id3_version: 3)
22
+ supported?('TDAT', id3_version, options)
23
+
24
+ argument_not_nil(month, 'month')
25
+ argument_not_nil(day, 'day')
26
+ month = pad_right(month.to_s, 2, '0')
27
+ day = pad_right(day.to_s, 2, '0')
28
+ argument_more_than_chars(day, 'day', 2)
29
+ argument_more_than_chars(month, 'month', 2)
30
+
31
+ month = pad_right(month, 2, '0')
32
+ day = pad_right(day, 2, '0')
33
+
34
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
35
+ frame.day = day
36
+ frame.month = month
37
+ frame
38
+ end
39
+
40
+ def process_content(content)
41
+ content = decode_using_encoding_byte(content)
42
+ if content.length != 4
43
+ @day = '1'
44
+ @month = '1'
45
+ else
46
+ @day = content[0..1]
47
+ @month = content[2..3]
48
+ end
49
+ end
50
+
51
+ def content_to_bytes
52
+ @day ||= '1'
53
+ @month ||= '1'
54
+ content = "#{pad_right(@day.to_s, 2, '0')}#{pad_right(@month.to_s, 2, '0')}"
55
+ encode_and_add_encoding_byte(content)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class PlaylistDelayFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TDY, :TDLY, :TDLY
10
+
11
+ attr_accessor :delay
12
+
13
+ # builds the playlist delay frame
14
+ #
15
+ # @param delay [String, Integer] the playlist delay in ms
16
+ # @param options [Options::Options] options to use
17
+ # @param id3_version [Integer] the id3 version to build the frame for
18
+ #
19
+ # @return [Id3v2Frame] the resulting id3v2 frame
20
+ def self.build_frame(delay, options = nil, id3_version: 3)
21
+ supported?('TDLY', id3_version, options)
22
+
23
+ argument_not_nil(delay, 'delay')
24
+
25
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
26
+ frame.delay = delay.to_s
27
+ frame
28
+ end
29
+
30
+ def process_content(content)
31
+ @delay = decode_using_encoding_byte(content)
32
+ end
33
+
34
+ def content_to_bytes
35
+ encode_and_add_encoding_byte(@delay)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class EncodedByFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TEN, :TENC, :TENC
10
+
11
+ attr_accessor :encoded_by
12
+
13
+ # builds the encoded by frame
14
+ #
15
+ # @param encoded_by [String] the encoded by
16
+ # @param options [Options::Options] options to use
17
+ # @param id3_version [Integer] the id3 version to build the frame for
18
+ #
19
+ # @return [Id3v2Frame] the resulting id3v2 frame
20
+ def self.build_frame(encoded_by, options = nil, id3_version: 3)
21
+ supported?('TENC', id3_version, options)
22
+
23
+ argument_not_nil(encoded_by, 'encoded_by')
24
+
25
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
26
+ frame.encoded_by = encoded_by
27
+ frame
28
+ end
29
+
30
+ def process_content(content)
31
+ @encoded_by = decode_using_encoding_byte(content)
32
+ end
33
+
34
+ def content_to_bytes
35
+ encode_and_add_encoding_byte(@encoded_by)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Id3Taginator
4
+ module Frames
5
+ module Text
6
+ class WritersFrame < Id3v2Frame
7
+ include HasId
8
+
9
+ frame_info :TXT, :TEXT, :TEXT
10
+
11
+ attr_accessor :writers
12
+
13
+ # builds the writers frame
14
+ #
15
+ # @param encoded_by [Array<String>, String] the writer(s)
16
+ # @param options [Options::Options] options to use
17
+ # @param id3_version [Integer] the id3 version to build the frame for
18
+ #
19
+ # @return [Id3v2Frame] the resulting id3v2 frame
20
+ def self.build_frame(writers, options = nil, id3_version: 3)
21
+ supported?('TEXT', id3_version, options)
22
+
23
+ argument_not_empty(writers, 'writers')
24
+
25
+ frame = new(frame_id(id3_version, options), 0, build_id3_flags(id3_version), '')
26
+ frame.writers = writers.is_a?(Array) ? writers : [writers]
27
+ frame
28
+ end
29
+
30
+ def process_content(content)
31
+ writers_str = decode_using_encoding_byte(content)
32
+ @writers = writers_str.split('/').map(&:strip)
33
+ end
34
+
35
+ def content_to_bytes
36
+ @writers ||= ['[unset]']
37
+ content = @writers.join('/')
38
+ encode_and_add_encoding_byte(content)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end