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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.idea/.gitignore +8 -0
- data/.idea/ID3Taginator.iml +73 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/vcs.xml +6 -0
- data/.rspec +3 -0
- data/.rubocop.yml +26 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +408 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/id3taginator.gemspec +37 -0
- data/lib/id3taginator/audio_file.rb +136 -0
- data/lib/id3taginator/errors/id3_tag_error.rb +12 -0
- data/lib/id3taginator/extensions/argument_check.rb +88 -0
- data/lib/id3taginator/extensions/comparable.rb +28 -0
- data/lib/id3taginator/extensions/encodable.rb +215 -0
- data/lib/id3taginator/extensions/optionable.rb +73 -0
- data/lib/id3taginator/frames/buffer/entities/buffer.rb +26 -0
- data/lib/id3taginator/frames/buffer/rbuf_recommended_buffer_size_frame.rb +59 -0
- data/lib/id3taginator/frames/buffer_frames.rb +32 -0
- data/lib/id3taginator/frames/comment/comm_comment_frame.rb +56 -0
- data/lib/id3taginator/frames/comment/entities/comment.rb +26 -0
- data/lib/id3taginator/frames/comment_frames.rb +42 -0
- data/lib/id3taginator/frames/count/entities/popularimeter.rb +26 -0
- data/lib/id3taginator/frames/count/pcnt_play_counter_frame.rb +40 -0
- data/lib/id3taginator/frames/count/popm_popularimeter_frame.rb +52 -0
- data/lib/id3taginator/frames/count_frames.rb +51 -0
- data/lib/id3taginator/frames/custom_frame.rb +39 -0
- data/lib/id3taginator/frames/custom_frames.rb +54 -0
- data/lib/id3taginator/frames/encryption/aenc_audio_encryption.rb +54 -0
- data/lib/id3taginator/frames/encryption/encr_encryption_method_frame.rb +49 -0
- data/lib/id3taginator/frames/encryption/entities/audio_encryption.rb +28 -0
- data/lib/id3taginator/frames/encryption/entities/encryption_method.rb +26 -0
- data/lib/id3taginator/frames/encryption_frames.rb +77 -0
- data/lib/id3taginator/frames/frameable.rb +75 -0
- data/lib/id3taginator/frames/geo/entities/encapsulated_object.rb +28 -0
- data/lib/id3taginator/frames/geo/geob_general_encapsulated_object_frame.rb +59 -0
- data/lib/id3taginator/frames/geo_frames.rb +41 -0
- data/lib/id3taginator/frames/grouping/entities/group_identification.rb +26 -0
- data/lib/id3taginator/frames/grouping/grid_group_identification_frame.rb +49 -0
- data/lib/id3taginator/frames/grouping/grp1_grouping_frame.rb +40 -0
- data/lib/id3taginator/frames/grouping_frames.rb +61 -0
- data/lib/id3taginator/frames/has_id.rb +51 -0
- data/lib/id3taginator/frames/id3v23_frame_flags.rb +64 -0
- data/lib/id3taginator/frames/id3v24_frame_flags.rb +80 -0
- data/lib/id3taginator/frames/id3v2_frame.rb +249 -0
- data/lib/id3taginator/frames/id3v2_frame_factory.rb +98 -0
- data/lib/id3taginator/frames/ipl/entities/involved_person.rb +24 -0
- data/lib/id3taginator/frames/ipl/ipls_involved_people_frame.rb +48 -0
- data/lib/id3taginator/frames/ipl_frames.rb +31 -0
- data/lib/id3taginator/frames/lyrics/entities/unsync_lyrics.rb +26 -0
- data/lib/id3taginator/frames/lyrics/uslt_unsync_lyrics_frame.rb +56 -0
- data/lib/id3taginator/frames/lyrics_frames.rb +42 -0
- data/lib/id3taginator/frames/mcdi/mcdi_music_cd_identifier_frame.rb +40 -0
- data/lib/id3taginator/frames/mcdi_frames.rb +28 -0
- data/lib/id3taginator/frames/picture/apic_picture_frame.rb +106 -0
- data/lib/id3taginator/frames/picture/entities/picture.rb +32 -0
- data/lib/id3taginator/frames/picture_frames.rb +51 -0
- data/lib/id3taginator/frames/private/entities/private_frame.rb +24 -0
- data/lib/id3taginator/frames/private/priv_private_frame.rb +45 -0
- data/lib/id3taginator/frames/private_frames.rb +40 -0
- data/lib/id3taginator/frames/text/entities/copyright.rb +24 -0
- data/lib/id3taginator/frames/text/entities/date.rb +24 -0
- data/lib/id3taginator/frames/text/entities/part_of_set.rb +24 -0
- data/lib/id3taginator/frames/text/entities/time.rb +24 -0
- data/lib/id3taginator/frames/text/entities/track_number.rb +24 -0
- data/lib/id3taginator/frames/text/entities/user_info.rb +24 -0
- data/lib/id3taginator/frames/text/talb_album_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tbpm_bpm_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tcom_composer_frame.rb +42 -0
- data/lib/id3taginator/frames/text/tcon_genre_frame.rb +104 -0
- data/lib/id3taginator/frames/text/tcop_copyright_frame.rb +55 -0
- data/lib/id3taginator/frames/text/tdat_date_frame.rb +60 -0
- data/lib/id3taginator/frames/text/tdly_playlist_delay_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tenc_encoded_by_frame.rb +40 -0
- data/lib/id3taginator/frames/text/text_writers_frame.rb +43 -0
- data/lib/id3taginator/frames/text/tflt_file_type_frame.rb +71 -0
- data/lib/id3taginator/frames/text/time_time_frame.rb +60 -0
- data/lib/id3taginator/frames/text/tit1_content_group_description_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tit2_title_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tit3_subtitle_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tkey_initial_key_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tlan_language_frame.rb +48 -0
- data/lib/id3taginator/frames/text/tlen_length_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tmed_media_type_frame.rb +40 -0
- data/lib/id3taginator/frames/text/toal_original_album_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tofn_original_filename_frame.rb +40 -0
- data/lib/id3taginator/frames/text/toly_original_writers_frame.rb +43 -0
- data/lib/id3taginator/frames/text/tope_original_artists_frame.rb +43 -0
- data/lib/id3taginator/frames/text/tory_original_release_year_frame.rb +42 -0
- data/lib/id3taginator/frames/text/town_file_owner_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tpe1_artist_frame.rb +43 -0
- data/lib/id3taginator/frames/text/tpe2_album_artist_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tpe3_conductor_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tpe4_modified_by_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tpos_part_of_set_frame.rb +50 -0
- data/lib/id3taginator/frames/text/tpub_publisher_frame.rb +40 -0
- data/lib/id3taginator/frames/text/trck_track_number_frame.rb +50 -0
- data/lib/id3taginator/frames/text/trda_recording_dates_frame.rb +42 -0
- data/lib/id3taginator/frames/text/trsn_internet_radio_station_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tsiz_size_frame.rb +41 -0
- data/lib/id3taginator/frames/text/tsoa_album_sort_order_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tsop_performer_sort_order_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tsot_title_sort_order_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tsrc_isrc_frame.rb +40 -0
- data/lib/id3taginator/frames/text/tsse_encoder_frame.rb +40 -0
- data/lib/id3taginator/frames/text/txxx_user_text_info_frame.rb +51 -0
- data/lib/id3taginator/frames/text/tyer_year_frame.rb +42 -0
- data/lib/id3taginator/frames/text_frames.rb +840 -0
- data/lib/id3taginator/frames/tos/entities/ownership.rb +26 -0
- data/lib/id3taginator/frames/tos/entities/terms_of_use.rb +24 -0
- data/lib/id3taginator/frames/tos/owne_ownership_frame.rb +53 -0
- data/lib/id3taginator/frames/tos/user_terms_of_use_frame.rb +49 -0
- data/lib/id3taginator/frames/tos_frames.rb +54 -0
- data/lib/id3taginator/frames/ufid/entities/ufid_info.rb +24 -0
- data/lib/id3taginator/frames/ufid/ufid_unique_file_identifier_frame.rb +47 -0
- data/lib/id3taginator/frames/ufid_frames.rb +40 -0
- data/lib/id3taginator/frames/url/entities/user_info.rb +24 -0
- data/lib/id3taginator/frames/url/wcom_commercial_url_frame.rb +40 -0
- data/lib/id3taginator/frames/url/wcop_copyright_url_frame.rb +40 -0
- data/lib/id3taginator/frames/url/woaf_official_file_webpage_frame.rb +40 -0
- data/lib/id3taginator/frames/url/woar_official_artist_webpage_frame.rb +40 -0
- data/lib/id3taginator/frames/url/woas_official_source_webpage_frame.rb +40 -0
- data/lib/id3taginator/frames/url/wors_official_radio_station_homepage_frame.rb +40 -0
- data/lib/id3taginator/frames/url/wpay_payment_url_frame.rb +40 -0
- data/lib/id3taginator/frames/url/wpub_official_publisher_webpage_frame.rb +40 -0
- data/lib/id3taginator/frames/url/wxxx_user_url_link_frame.rb +50 -0
- data/lib/id3taginator/frames/url_frames.rb +195 -0
- data/lib/id3taginator/genres.rb +168 -0
- data/lib/id3taginator/header/id3v23_extended_header.rb +37 -0
- data/lib/id3taginator/header/id3v24_extended_header.rb +100 -0
- data/lib/id3taginator/header/id3v2_flags.rb +64 -0
- data/lib/id3taginator/id3v1_tag.rb +156 -0
- data/lib/id3taginator/id3v22_tag.rb +30 -0
- data/lib/id3taginator/id3v23_tag.rb +63 -0
- data/lib/id3taginator/id3v24_tag.rb +75 -0
- data/lib/id3taginator/id3v2_tag.rb +241 -0
- data/lib/id3taginator/options/options.rb +33 -0
- data/lib/id3taginator/util/compress_util.rb +25 -0
- data/lib/id3taginator/util/math_util.rb +68 -0
- data/lib/id3taginator/util/sync_util.rb +65 -0
- data/lib/id3taginator.rb +449 -0
- 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
|