taglib-simple 0.1.1

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.
@@ -0,0 +1,133 @@
1
+ #include "conversions.h"
2
+ #include <ruby/encoding.h>
3
+
4
+ using namespace Rice;
5
+
6
+ namespace TagLib {
7
+ namespace Simple {
8
+
9
+ Object tagLibStringToNonEmptyRubyUTF8String(TagLib::String string) {
10
+ if (string.length() == 0) {
11
+ return {Qnil};
12
+ }
13
+ return { tagLibStringToRubyUTF8String(string) };
14
+ }
15
+
16
+ Object uintToNonZeroRubyInteger(unsigned integer) {
17
+ if (integer == 0) {
18
+ return {Qnil};
19
+ }
20
+ return { UINT2NUM(integer) };
21
+ }
22
+
23
+ Rice::String tagLibStringToRubyUTF8String(const TagLib::String& str) {
24
+ Rice::String rb_str(str.to8Bit(true)); // true ensures UTF-8
25
+ rb_enc_associate(rb_str.value(), rb_utf8_encoding());
26
+ return rb_str;
27
+ }
28
+
29
+ Array tagLibStringListToRuby(const TagLib::StringList& list) {
30
+ Array result;
31
+ for (const auto& str : list) {
32
+ result.push(tagLibStringToRubyUTF8String(str));
33
+ }
34
+ return result;
35
+ }
36
+
37
+ Rice::String tagLibByteVectorToRuby(const TagLib::ByteVector& byteVector) {
38
+ // Convert ByteVector to Ruby String with binary encoding
39
+ Rice::String rb_str(std::string(byteVector.data(), byteVector.size()));
40
+ rb_enc_associate(rb_str.value(), rb_ascii8bit_encoding());
41
+ return rb_str;
42
+ }
43
+
44
+ Array tagLibByteVectorListToRuby(const TagLib::ByteVectorList& list) {
45
+ Array result;
46
+ for (const auto& byteVector : list) {
47
+ result.push(tagLibByteVectorToRuby((byteVector)));
48
+ }
49
+ return result;
50
+ }
51
+
52
+ Hash tagLibPropertyMapToRubyHash(const TagLib::PropertyMap& properties) {
53
+ Hash result;
54
+ // Iterate through the PropertyMap
55
+ for(auto & property : properties) {
56
+
57
+ Rice::String key = tagLibStringToRubyUTF8String(property.first);
58
+
59
+ // Convert the StringList to Ruby Array
60
+ Array values;
61
+ for(const auto& item : property.second) {
62
+ values.push(tagLibStringToRubyUTF8String(item));
63
+ }
64
+ // Add to result hash
65
+ values.freeze();
66
+ result[key] = values;
67
+ }
68
+
69
+ result.freeze();
70
+ return result;
71
+ }
72
+ #if (TAGLIB_MAJOR_VERSION >= 2)
73
+ Object taglibVariantToRuby(const TagLib::Variant &value);
74
+
75
+ Hash taglibVariantMapToRuby(const TagLib::Map<TagLib::String, TagLib::Variant> & map) {
76
+ Hash result;
77
+ for (const auto& pair : map) {
78
+ Rice::String key(pair.first.toCString());
79
+ const TagLib::Variant& value = pair.second;
80
+ result[key] = taglibVariantToRuby(value);
81
+ }
82
+ return result;
83
+ }
84
+
85
+ Array taglibVariantListToRuby(const TagLib::List<TagLib::Variant> & list) {
86
+ Array result;
87
+ for (const auto& item : list) {
88
+ result.push(taglibVariantToRuby(item));
89
+ }
90
+ return result;
91
+ }
92
+
93
+ Object taglibVariantToRuby(const TagLib::Variant &value) {
94
+ switch (value.type()) {
95
+ case TagLib::Variant::Bool:
96
+ return value.toBool() ? Qtrue : Qfalse;
97
+ case TagLib::Variant::Int:
98
+ return INT2NUM(value.toInt());
99
+ case TagLib::Variant::UInt:
100
+ return UINT2NUM(value.toUInt());
101
+ case TagLib::Variant::LongLong:
102
+ return LL2NUM(value.toLongLong());
103
+ case TagLib::Variant::ULongLong:
104
+ return ULL2NUM(value.toULongLong());
105
+ case TagLib::Variant::String:
106
+ return { tagLibStringToRubyUTF8String(value.toString()) };
107
+ case TagLib::Variant::StringList:
108
+ return { tagLibStringListToRuby(value.toStringList()) };
109
+ case TagLib::Variant::ByteVector:
110
+ return { tagLibByteVectorToRuby(value.toByteVector()) };
111
+ case TagLib::Variant::ByteVectorList:
112
+ return { tagLibByteVectorListToRuby(value.toByteVectorList()) };
113
+ case TagLib::Variant::VariantList:
114
+ return { taglibVariantListToRuby(value.toList()) };
115
+ case TagLib::Variant::VariantMap:
116
+ return { taglibVariantMapToRuby(value.toMap()) };
117
+ default:
118
+ return Qnil;
119
+ }
120
+ }
121
+
122
+ Array tagLibComplexPropertyToRuby(const TagLib::List<TagLib::VariantMap>& list) {
123
+ Array result;
124
+
125
+ for (const auto& variantMap : list) {
126
+ result.push(taglibVariantMapToRuby(variantMap));
127
+ }
128
+
129
+ return result;
130
+ }
131
+ #endif
132
+ }
133
+ }
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mkmf-rice'
4
+
5
+ # --with-taglib-dir - the install dir that a custom build of taglib has been deployed to.
6
+ dir = with_config('taglib-dir')
7
+ if dir && !dir.empty?
8
+ dir = Pathname.new(dir)
9
+ dir = dir.expand_path if dir.to_s.start_with?('~') # ~/.local
10
+ # treat relative paths as relative to the project root
11
+ dir = Pathname.new(__FILE__).dirname.parent.parent.expand_path / dir if dir.relative?
12
+ raise "TAGLIB_DIR does not exist: #{dir}" unless dir.directory?
13
+
14
+ dir_config('tag', dir.to_path)
15
+ end
16
+
17
+ have_library('tag') || abort('TagLib is required')
18
+
19
+ append_cppflags('-g,-DDEBUG') if enable_config('debug')
20
+
21
+ # Add to existing flags
22
+ append_ldflags('-Wl,--no-undefined')
23
+ create_makefile('taglib_simple_fileref')
@@ -0,0 +1,57 @@
1
+
2
+ #include "FileRef.hpp"
3
+ #if TAGLIB_MAJOR_VERSION > 1
4
+ #include <taglib/tversionnumber.h>
5
+ #endif
6
+
7
+ extern "C"
8
+ void Init_taglib_simple_fileref() {
9
+ Module rb_mTagLib = define_module("TagLib");
10
+ Module rb_mTagLibExt = define_module_under({rb_mTagLib},"Simple");
11
+
12
+ define_taglib_simple_fileref(rb_mTagLibExt);
13
+
14
+ uint major;
15
+ uint minor;
16
+ uint patch;
17
+ Rice::String version;
18
+
19
+ #if TAGLIB_MAJOR_VERSION == 1
20
+ // Taglib 1 does not have runtime version information
21
+ major = TAGLIB_MAJOR_VERSION;
22
+ minor = TAGLIB_MINOR_VERSION;
23
+ patch = TAGLIB_PATCH_VERSION;
24
+
25
+ version = Rice::String::format("%d.%d.%d", TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION, TAGLIB_PATCH_VERSION);
26
+ #else
27
+
28
+ TagLib::VersionNumber runtime_version = TagLib::runtimeVersion();
29
+
30
+ major = runtime_version.majorVersion();
31
+ minor = runtime_version.minorVersion();
32
+ patch = runtime_version.patchVersion();
33
+ version = runtime_version.toString().toCString();
34
+
35
+ // Fatal error on major version mismatch
36
+ if (TAGLIB_MAJOR_VERSION != runtime_version.majorVersion()) {
37
+ rb_raise(rb_eLoadError,
38
+ "Incompatible TagLib version. Compiled with %d.%d.%d but loaded %s",
39
+ TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION, TAGLIB_PATCH_VERSION, version.c_str());
40
+
41
+ }
42
+
43
+ // Warning if compiled against a minor version that is newer than the runtime library
44
+ if (TAGLIB_MINOR_VERSION > runtime_version.minorVersion()) {
45
+ rb_warn(
46
+ "TagLib runtime version %s is older than compile-time version %d.%d.%d",
47
+ version.c_str(), TAGLIB_MAJOR_VERSION, TAGLIB_MINOR_VERSION, TAGLIB_PATCH_VERSION
48
+ );
49
+ }
50
+ #endif
51
+
52
+ rb_mTagLib.const_set("MAJOR_VERSION", UINT2NUM(major));
53
+ rb_mTagLib.const_set("MINOR_VERSION", UINT2NUM(minor));
54
+ rb_mTagLib.const_set("PATCH_VERSION", UINT2NUM(patch));
55
+ rb_mTagLib.const_set("LIBRARY_VERSION", {version});
56
+
57
+ }
@@ -0,0 +1,5 @@
1
+
2
+ #pragma once
3
+
4
+ // this is just a placeholder in case we need to define things before including taglib
5
+
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TagLib
4
+ AudioProperties = Data.define(:audio_length, :bitrate, :sample_rate, :channels)
5
+
6
+ # Represents the audio properties of a media file
7
+ # @see https://taglib.org/api/classTagLib_1_1AudioProperties.html`
8
+ class AudioProperties < Data
9
+ # @!attribute [r] audio_length
10
+ # @return [Integer] The length of the audio in milliseconds
11
+
12
+ # @!attribute [r] bitrate
13
+ # @return [Integer] The bitrate of the audio in kb/s
14
+
15
+ # @!attribute [r] sample_rate
16
+ # @return [Integer] The sample rate in Hz
17
+
18
+ # @!attribute [r] channels
19
+ # @return [Integer] The number of audio channels
20
+
21
+ class << self
22
+ # @param [String|:to_path|IO] filename
23
+ # @param [Symbol<:average, :fast, :accurate>] audio_properties read style
24
+ # @return [AudioProperties]
25
+ def read(filename, audio_properties: true)
26
+ raise ArgumentError, 'audio_properties must be one of :average, :fast, :accurate' unless audio_properties
27
+
28
+ Taglib::MediaFile.open(filename, audio_properties:, &:audio_properties)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TagLib
4
+ AudioTag = Data.define :title, :artist, :album, :genre, :year, :track, :comment
5
+
6
+ # Represents a normalised subset of tags
7
+ # @see https://taglib.org/api/classTagLib_1_1Tag.html
8
+ class AudioTag < Data
9
+ # @!attribute [r] title
10
+ # @return [String] The title of the track
11
+
12
+ # @!attribute [r] artist
13
+ # @return [String] The artist name
14
+
15
+ # @!attribute [r] album
16
+ # @return [String] The album name
17
+
18
+ # @!attribute [r] genre
19
+ # @return [String] The genre of the track
20
+
21
+ # @!attribute [r] year
22
+ # @return [Integer] The release year
23
+
24
+ # @!attribute [r] track
25
+ # @return [Integer] The track number
26
+
27
+ # @!attribute [r] comment
28
+ # @return [String] Additional comments about the track
29
+
30
+ class << self
31
+ # @param [String|:to_path|IO] filename
32
+ # @return [AudioTag]
33
+ def read(filename)
34
+ MediaFile.open(filename, all: false, tag: true, &:tag)
35
+ end
36
+
37
+ # @!visibility private
38
+ def check_value(member, value)
39
+ return value if value.nil?
40
+
41
+ return check_int_value(member, value) if %i[year track].include?(member)
42
+
43
+ check_string_value(member, value)
44
+ end
45
+
46
+ # @!visibility private
47
+ def check_int_value(member, value)
48
+ raise TypeError, "#{member} must be a +ve integer" unless value.is_a?(Integer) && value >= 0
49
+
50
+ value.zero? ? nil : value
51
+ end
52
+
53
+ # @!visibility private
54
+ def check_string_value(member, value)
55
+ raise TypeError, "#{member} must be a string" unless value.is_a?(String)
56
+
57
+ value.empty? ? nil : value
58
+ end
59
+ end
60
+
61
+ # @return [Hash<Symbol, <String,Integer>>] the tag values as a hash, excluding entries with nil values
62
+ def to_h
63
+ # do not expose nil entries
64
+ super.compact
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TagLib
4
+ class Error < StandardError; end
5
+ end