ffi-shout 0.1.0

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,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The integer and string constants from shout.h.
4
+ module Shout
5
+ SHOUTERR_SUCCESS = 0
6
+ SHOUTERR_INSANE = -1
7
+ SHOUTERR_NOCONNECT = -2
8
+ SHOUTERR_NOLOGIN = -3
9
+ SHOUTERR_SOCKET = -4
10
+ SHOUTERR_MALLOC = -5
11
+ SHOUTERR_METADATA = -6
12
+ SHOUTERR_CONNECTED = -7
13
+ SHOUTERR_UNCONNECTED = -8
14
+ SHOUTERR_UNSUPPORTED = -9
15
+ SHOUTERR_BUSY = -10
16
+ SHOUTERR_NOTLS = -11
17
+ SHOUTERR_TLSBADCERT = -12
18
+ SHOUTERR_RETRY = -13
19
+
20
+ SHOUT_FORMAT_OGG = 0
21
+ SHOUT_FORMAT_MP3 = 1
22
+ SHOUT_FORMAT_WEBM = 2
23
+ SHOUT_FORMAT_WEBMAUDIO = 3
24
+ SHOUT_FORMAT_MATROSKA = 4
25
+ SHOUT_FORMAT_TEXT = 5
26
+ SHOUT_FORMAT_VORBIS = SHOUT_FORMAT_OGG
27
+
28
+ SHOUT_USAGE_AUDIO = 0x0001
29
+ SHOUT_USAGE_VISUAL = 0x0002
30
+ SHOUT_USAGE_TEXT = 0x0004
31
+ SHOUT_USAGE_SUBTITLE = 0x0008
32
+ SHOUT_USAGE_LIGHT = 0x0010
33
+ SHOUT_USAGE_UI = 0x0020
34
+ SHOUT_USAGE_METADATA = 0x0040
35
+ SHOUT_USAGE_APPLICATION = 0x0080
36
+ SHOUT_USAGE_CONTROL = 0x0100
37
+ SHOUT_USAGE_COMPLEX = 0x0200
38
+ SHOUT_USAGE_OTHER = 0x0400
39
+ SHOUT_USAGE_UNKNOWN = 0x0800
40
+ SHOUT_USAGE_3D = 0x1000
41
+ SHOUT_USAGE_4D = 0x2000
42
+
43
+ SHOUT_PROTOCOL_HTTP = 0
44
+ SHOUT_PROTOCOL_XAUDIOCAST = 1
45
+ SHOUT_PROTOCOL_ICY = 2
46
+ SHOUT_PROTOCOL_ROARAUDIO = 3
47
+
48
+ SHOUT_TLS_DISABLED = 0
49
+ SHOUT_TLS_AUTO = 1
50
+ SHOUT_TLS_AUTO_NO_PLAIN = 2
51
+ SHOUT_TLS_RFC2818 = 11
52
+ SHOUT_TLS_RFC2817 = 12
53
+
54
+ SHOUT_BLOCKING_DEFAULT = 255
55
+ SHOUT_BLOCKING_FULL = 0
56
+ SHOUT_BLOCKING_NONE = 1
57
+
58
+ SHOUT_AI_BITRATE = "bitrate"
59
+ SHOUT_AI_SAMPLERATE = "samplerate"
60
+ SHOUT_AI_CHANNELS = "channels"
61
+ SHOUT_AI_QUALITY = "quality"
62
+
63
+ SHOUT_META_NAME = "name"
64
+ SHOUT_META_URL = "url"
65
+ SHOUT_META_GENRE = "genre"
66
+ SHOUT_META_DESCRIPTION = "description"
67
+ SHOUT_META_IRC = "irc"
68
+ SHOUT_META_AIM = "aim"
69
+ SHOUT_META_ICQ = "icq"
70
+
71
+ SHOUT_CALLBACK_PASS = 1
72
+
73
+ SHOUT_THREADSAFE = 1
74
+ SHOUT_TLS = 1
75
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shout
4
+ # Class macros that define attribute readers and writers delegating to
5
+ # libshout's shout_get_<name> / shout_set_<name> functions.
6
+ module Delegation
7
+ # Defines <name> and <name>= for each name. With predicate: true, also
8
+ # defines <name>? returning a boolean (for on/off flags such as :public).
9
+ def delegate_to_shout(*delegations, predicate: false)
10
+ delegations.each do |name|
11
+ define_setter_delegator(name, name)
12
+ define_getter_delegator(name, name)
13
+ define_predicate_delegator(name) if predicate
14
+ end
15
+ end
16
+
17
+ def delegate_alias_to_shout(delegations)
18
+ delegations.each_pair do |from, to|
19
+ define_setter_delegator(from, to)
20
+ define_getter_delegator(from, to)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def define_setter_delegator(from, to, preset = "shout")
27
+ define_method(:"#{from}=") do |value|
28
+ converted_value = TypeConvertor.convert(value)
29
+ ret = Native.public_send(:"#{preset}_set_#{to}", shout, converted_value)
30
+ Error.check_error(ret, shout)
31
+ value
32
+ end
33
+ end
34
+
35
+ def define_getter_delegator(from, to, preset = "shout")
36
+ define_method(:"#{from}") do
37
+ Native.public_send(:"#{preset}_get_#{to}", shout)
38
+ end
39
+ end
40
+
41
+ def define_predicate_delegator(name)
42
+ define_method(:"#{name}?") do
43
+ TypeConvertor.convert_return(public_send(name))
44
+ end
45
+ end
46
+
47
+ # Converts Ruby values to what libshout's setters expect. Enum symbols
48
+ # such as :mp3 are left to the FFI enums in Shout::Native.
49
+ class TypeConvertor
50
+ # Symbols that libshout treats as another name for the same value.
51
+ ALIASES = { vorbis: :ogg }.freeze
52
+
53
+ def self.convert(value)
54
+ case value
55
+ when true then 1
56
+ when false then 0
57
+ else ALIASES.fetch(value, value)
58
+ end
59
+ end
60
+
61
+ def self.convert_return(value)
62
+ case value
63
+ when 0, false, nil
64
+ false
65
+ else
66
+ true
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Exception classes for libshout errors.
4
+ module Shout
5
+ class ShoutError < StandardError; end
6
+
7
+ class InsaneError < ShoutError; end
8
+ class ConnectionError < ShoutError; end
9
+ class LoginError < ShoutError; end
10
+ class SocketIOError < ShoutError; end
11
+ class MallocError < ShoutError; end
12
+ class MetadataError < ShoutError; end
13
+ class ConnectedError < ShoutError; end
14
+ class UnconnectedError < ShoutError; end
15
+ class UnsupportedError < ShoutError; end
16
+ class BusyError < ShoutError; end
17
+ class NoTLSError < ShoutError; end
18
+ class TLSBadCertError < ShoutError; end
19
+ class RetryError < ShoutError; end
20
+
21
+ # Maps libshout's SHOUTERR_* return codes to exception classes.
22
+ module Error
23
+ ERROR_CLASSES = {
24
+ SHOUTERR_INSANE => InsaneError,
25
+ SHOUTERR_NOCONNECT => ConnectionError,
26
+ SHOUTERR_NOLOGIN => LoginError,
27
+ SHOUTERR_SOCKET => SocketIOError,
28
+ SHOUTERR_MALLOC => MallocError,
29
+ SHOUTERR_METADATA => MetadataError,
30
+ SHOUTERR_CONNECTED => ConnectedError,
31
+ SHOUTERR_UNCONNECTED => UnconnectedError,
32
+ SHOUTERR_UNSUPPORTED => UnsupportedError,
33
+ SHOUTERR_BUSY => BusyError,
34
+ SHOUTERR_NOTLS => NoTLSError,
35
+ SHOUTERR_TLSBADCERT => TLSBadCertError,
36
+ SHOUTERR_RETRY => RetryError
37
+ }.freeze
38
+
39
+ # Raises the matching ShoutError for a negative libshout return code.
40
+ def self.check_error(return_code, shout_ptr = nil)
41
+ return unless return_code.is_a?(Integer) && return_code.negative?
42
+
43
+ error_class = ERROR_CLASSES.fetch(return_code, ShoutError)
44
+ raise error_class, libshout_message(shout_ptr) || "libshout error code #{return_code}"
45
+ end
46
+
47
+ def self.libshout_message(shout_ptr)
48
+ ptr = shout_ptr.respond_to?(:to_ptr) ? shout_ptr.to_ptr : shout_ptr
49
+ return if ptr.nil? || ptr.null?
50
+
51
+ message = Native.shout_get_error(ptr)
52
+ message unless message.nil? || message.empty?
53
+ rescue StandardError
54
+ nil
55
+ end
56
+ private_class_method :libshout_message
57
+ end
58
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "openssl"
5
+ require "uri"
6
+
7
+ module Shout
8
+ # Updates a mount's metadata through Icecast's +/admin/metadata+ endpoint,
9
+ # sending the source credentials with the very first request.
10
+ #
11
+ # This replaces +shout_set_metadata_utf8+ for the HTTP protocol. libshout
12
+ # 2.4.6 first sends the request without credentials and then reuses a
13
+ # socket that Icecast 2.5 has already closed after its 401. The empty reply
14
+ # starts libshout's "GET /admin/!POKE" TLS probe, Icecast answers 400 and
15
+ # closes again, and libshout retries forever without ever returning. See
16
+ # https://gitlab.xiph.org/xiph/icecast-libshout/-/issues/2341
17
+ class HttpMetadata
18
+ PATH = "/admin/metadata"
19
+ DEFAULT_TIMEOUT = 5
20
+
21
+ PLAIN_TLS_MODES = %i[disabled auto].freeze
22
+
23
+ # Timeout::Error covers Net::OpenTimeout, ReadTimeout and WriteTimeout;
24
+ # SystemCallError covers the Errno::* socket errors; IOError covers EOFError.
25
+ NETWORK_ERRORS = [
26
+ SystemCallError, IOError, Timeout::Error, ::SocketError, OpenSSL::SSL::SSLError
27
+ ].freeze
28
+
29
+ attr_reader :configuration, :timeout
30
+
31
+ def initialize(configuration, timeout: DEFAULT_TIMEOUT)
32
+ @configuration = configuration
33
+ @timeout = timeout
34
+ end
35
+
36
+ # Sends +metadata+ (a Shout::Metadata) for the configured mount. Returns
37
+ # the server's response message, or raises a Shout::ShoutError.
38
+ def update(metadata)
39
+ handle_response(http.request(build_request(metadata)))
40
+ rescue *NETWORK_ERRORS => e
41
+ raise ConnectionError, "Metadata update failed: #{e.class}: #{e.message}"
42
+ end
43
+
44
+ private
45
+
46
+ def build_request(metadata)
47
+ request = Net::HTTP::Get.new("#{PATH}?#{query(metadata)}")
48
+ request.basic_auth(configuration.user.to_s, configuration.password.to_s)
49
+ request["User-Agent"] = configuration.agent if configuration.agent
50
+ request
51
+ end
52
+
53
+ def query(metadata)
54
+ params = [%w[mode updinfo], %w[charset UTF-8], ["mount", configuration.mount.to_s]]
55
+ params.concat(metadata.to_h.to_a)
56
+ params.map { |key, value| "#{encode(key)}=#{encode(value)}" }.join("&")
57
+ end
58
+
59
+ def encode(value)
60
+ URI.encode_uri_component(value.to_s)
61
+ end
62
+
63
+ def http
64
+ http = Net::HTTP.new(configuration.host, configuration.port)
65
+ http.open_timeout = timeout
66
+ http.read_timeout = timeout
67
+ http.write_timeout = timeout
68
+ configure_tls(http)
69
+ http
70
+ end
71
+
72
+ def configure_tls(http)
73
+ mode = configuration.tls
74
+ return if PLAIN_TLS_MODES.include?(mode)
75
+
76
+ unless mode == :rfc2818
77
+ raise UnsupportedError,
78
+ "TLS mode #{mode.inspect} is not supported for HTTP metadata updates; use :rfc2818 or :disabled"
79
+ end
80
+
81
+ http.use_ssl = true
82
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
83
+ configure_ca(http)
84
+ end
85
+
86
+ def configure_ca(http)
87
+ http.ca_file = configuration.ca_file if configuration.ca_file
88
+ http.ca_path = configuration.ca_directory if configuration.ca_directory
89
+ end
90
+
91
+ def handle_response(response)
92
+ body = response.body.to_s
93
+ message = body[%r{<message>(.*?)</message>}m, 1]
94
+ detail = "HTTP #{response.code}#{": #{message}" if message}"
95
+
96
+ if response.is_a?(Net::HTTPUnauthorized) || response.is_a?(Net::HTTPForbidden)
97
+ raise LoginError, "Metadata update rejected (#{detail})"
98
+ end
99
+ return message if response.is_a?(Net::HTTPSuccess) && body.include?("<return>1</return>")
100
+
101
+ raise MetadataError, "Metadata update failed (#{detail})"
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shout
4
+ # The version of the loaded libshout library (shout_version), as returned
5
+ # by Shout.version.
6
+ class LibraryVersion
7
+ include Comparable
8
+
9
+ # The oldest libshout with every function these bindings attach
10
+ # (shout_set_metadata_utf8 was added in 2.4.6).
11
+ MINIMUM = "2.4.6"
12
+
13
+ attr_reader :string, :major, :minor, :patch
14
+
15
+ # Raises LoadError when the loaded libshout is older than MINIMUM.
16
+ def self.ensure_supported!
17
+ version = new
18
+ return version if version >= Gem::Version.new(MINIMUM)
19
+
20
+ raise LoadError, "ffi-shout needs libshout #{MINIMUM} or newer, but found #{version.number}"
21
+ end
22
+
23
+ def initialize
24
+ major_ptr = FFI::MemoryPointer.new(:int)
25
+ minor_ptr = FFI::MemoryPointer.new(:int)
26
+ patch_ptr = FFI::MemoryPointer.new(:int)
27
+
28
+ @string = Native.shout_version(major_ptr, minor_ptr, patch_ptr)
29
+ @major = major_ptr.read_int
30
+ @minor = minor_ptr.read_int
31
+ @patch = patch_ptr.read_int
32
+ end
33
+
34
+ # "major.minor.patch", e.g. "2.4.6".
35
+ def number
36
+ [major, minor, patch].join(".")
37
+ end
38
+
39
+ def <=>(other)
40
+ Gem::Version.new(number) <=> Gem::Version.new(other.to_s)
41
+ end
42
+
43
+ def to_s
44
+ @string
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shout
4
+ # Song metadata to send to the server, such as "song" => "Artist - Title".
5
+ # Keys and values are stored as UTF-8 strings.
6
+ class Metadata
7
+ include Enumerable
8
+
9
+ # The libshout shout_metadata_t handle (a Native::MetadataHandle).
10
+ attr_reader :handle
11
+
12
+ def initialize(attributes = {})
13
+ @handle = Native.shout_metadata_new
14
+ @attributes = {}
15
+
16
+ attributes.each do |key, value|
17
+ add(key, value)
18
+ end
19
+ end
20
+
21
+ def add(name, value)
22
+ key_str = name.to_s.encode("UTF-8")
23
+ val_str = value.to_s.encode("UTF-8")
24
+
25
+ Error.check_error(Native.shout_metadata_add(@handle, key_str, val_str))
26
+
27
+ @attributes[key_str] = val_str
28
+ self
29
+ end
30
+ alias []= add
31
+
32
+ def [](key)
33
+ @attributes[key.to_s]
34
+ end
35
+
36
+ def each(&)
37
+ return enum_for(:each) { @attributes.size } unless block_given?
38
+
39
+ @attributes.each(&)
40
+ self
41
+ end
42
+
43
+ def to_h
44
+ @attributes.dup
45
+ end
46
+
47
+ def to_ptr
48
+ @handle.to_ptr
49
+ end
50
+
51
+ def ==(other)
52
+ other.is_a?(Metadata) && to_h == other.to_h
53
+ end
54
+ alias eql? ==
55
+
56
+ def hash
57
+ [self.class, @attributes].hash
58
+ end
59
+
60
+ def inspect
61
+ "#<#{self.class.name} #{@attributes.inspect}>"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shout
4
+ # FFI enum types for the libshout constants in Shout (see constants.rb).
5
+ # Functions that take or return these values are attached with the enum
6
+ # type, so they accept and return symbols such as :mp3 or :http.
7
+ module Native
8
+ enum :shout_error, [
9
+ :success, SHOUTERR_SUCCESS,
10
+ :insane, SHOUTERR_INSANE,
11
+ :noconnect, SHOUTERR_NOCONNECT,
12
+ :nologin, SHOUTERR_NOLOGIN,
13
+ :socket, SHOUTERR_SOCKET,
14
+ :malloc, SHOUTERR_MALLOC,
15
+ :metadata, SHOUTERR_METADATA,
16
+ :connected, SHOUTERR_CONNECTED,
17
+ :unconnected, SHOUTERR_UNCONNECTED,
18
+ :unsupported, SHOUTERR_UNSUPPORTED,
19
+ :busy, SHOUTERR_BUSY,
20
+ :notls, SHOUTERR_NOTLS,
21
+ :tlsbadcert, SHOUTERR_TLSBADCERT,
22
+ :retry, SHOUTERR_RETRY
23
+ ]
24
+
25
+ enum :shout_format, [
26
+ :ogg, SHOUT_FORMAT_OGG,
27
+ :mp3, SHOUT_FORMAT_MP3,
28
+ :webm, SHOUT_FORMAT_WEBM,
29
+ :webmaudio, SHOUT_FORMAT_WEBMAUDIO,
30
+ :matroska, SHOUT_FORMAT_MATROSKA,
31
+ :text, SHOUT_FORMAT_TEXT
32
+ ]
33
+
34
+ enum :shout_protocol, [
35
+ :http, SHOUT_PROTOCOL_HTTP,
36
+ :xaudiocast, SHOUT_PROTOCOL_XAUDIOCAST,
37
+ :icy, SHOUT_PROTOCOL_ICY,
38
+ :roaraudio, SHOUT_PROTOCOL_ROARAUDIO
39
+ ]
40
+
41
+ enum :shout_tls, [
42
+ :disabled, SHOUT_TLS_DISABLED,
43
+ :auto, SHOUT_TLS_AUTO,
44
+ :auto_no_plain, SHOUT_TLS_AUTO_NO_PLAIN,
45
+ :rfc2818, SHOUT_TLS_RFC2818,
46
+ :rfc2817, SHOUT_TLS_RFC2817
47
+ ]
48
+
49
+ enum :shout_blocking, [
50
+ :full, SHOUT_BLOCKING_FULL,
51
+ :none, SHOUT_BLOCKING_NONE,
52
+ :default, SHOUT_BLOCKING_DEFAULT
53
+ ]
54
+
55
+ enum :shout_control_t, [
56
+ :control_min, 0,
57
+ :control_get_server_certificate_as_pem, 1,
58
+ :control_get_server_certificate_chain_as_pem, 2,
59
+ :control_max, 32_767
60
+ ]
61
+
62
+ enum :shout_event_t, [
63
+ :event_min, 0,
64
+ :event_tls_check_peer_certificate, 1,
65
+ :event_max, 32_767
66
+ ]
67
+ end
68
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shout
4
+ # attach_function declarations for every function in shout.h.
5
+ module Native
6
+ typedef :pointer, :shout_t
7
+ typedef :pointer, :shout_metadata_t
8
+
9
+ callback :shout_callback_t, %i[pointer int pointer pointer], :int
10
+
11
+ # These `attach_function` declarations are in the order of shout.h.
12
+ # Parameters and return values that take SHOUT_TLS_*, SHOUT_FORMAT_*,
13
+ # SHOUT_PROTOCOL_* or SHOUT_BLOCKING_* values use the FFI enums from
14
+ # enums.rb, so they accept and return symbols such as :mp3 or :http.
15
+ # shout_new and shout_metadata_new return a Handle / MetadataHandle,
16
+ # which frees itself when it is garbage collected.
17
+
18
+ # ----------------[ Generic ]----------------
19
+
20
+ # initializes the shout library. Must be called before anything else
21
+ attach_function :shout_init, [], :void
22
+
23
+ # shuts down the shout library, deallocating any global storage. Don't call
24
+ # anything afterwards
25
+ attach_function :shout_shutdown, [], :void
26
+
27
+ # returns a static version string. Non-null parameters will be set to the
28
+ # value of the library major, minor, and patch levels, respectively
29
+ # (shout_version is attached in native.rb, before the version check)
30
+
31
+ # Allocates and sets up a new shout_t. Returns NULL if it can't get enough
32
+ # memory. The returns shout_t must be disposed of with shout_free.
33
+ attach_function :shout_new, [], Handle
34
+
35
+ # Free all memory allocated by a shout_t
36
+ attach_function :shout_free, [:shout_t], :void
37
+
38
+ # Returns a statically allocated string describing the last shout error
39
+ # to occur. Only valid until the next libshout call on this shout_t
40
+ attach_function :shout_get_error, [:shout_t], :string
41
+
42
+ # Return the error code (e.g. SHOUTERR_SOCKET) for this shout instance
43
+ attach_function :shout_get_errno, [:shout_t], :int
44
+
45
+ # returns SHOUTERR_CONNECTED or SHOUTERR_UNCONNECTED
46
+ attach_function :shout_get_connected, [:shout_t], :int
47
+
48
+ # ----------------[ Configuration ]----------------
49
+ # Parameter manipulation functions. libshout makes copies of all parameters,
50
+ # the caller may free its copies after giving them to libshout. May return
51
+ # SHOUTERR_MALLOC
52
+
53
+ # Connection parameters
54
+ attach_function :shout_set_host, %i[shout_t string], :int
55
+ attach_function :shout_get_host, [:shout_t], :string
56
+
57
+ attach_function :shout_set_port, %i[shout_t ushort], :int
58
+ attach_function :shout_get_port, [:shout_t], :ushort
59
+
60
+ attach_function :shout_set_agent, %i[shout_t string], :int
61
+ attach_function :shout_get_agent, [:shout_t], :string
62
+
63
+ # See SHOUT_TLS_*
64
+ attach_function :shout_set_tls, %i[shout_t shout_tls], :int
65
+ attach_function :shout_get_tls, [:shout_t], :shout_tls
66
+
67
+ # Set the directory for CA certs. Default: operating system's default
68
+ attach_function :shout_set_ca_directory, %i[shout_t string], :int
69
+ attach_function :shout_get_ca_directory, [:shout_t], :string
70
+
71
+ # Set a CA cert file for checking. If you use a self signed server cert
72
+ # you can pass this cert using this function for verification.
73
+ # Default: operating system's default
74
+ attach_function :shout_set_ca_file, %i[shout_t string], :int
75
+ attach_function :shout_get_ca_file, [:shout_t], :string
76
+
77
+ # Set list of allowed ciphers.
78
+ attach_function :shout_set_allowed_ciphers, %i[shout_t string], :int
79
+ attach_function :shout_get_allowed_ciphers, [:shout_t], :string
80
+
81
+ # Authentication parameters
82
+ attach_function :shout_set_user, %i[shout_t string], :int
83
+ attach_function :shout_get_user, [:shout_t], :string
84
+
85
+ attach_function :shout_set_password, %i[shout_t string], :int
86
+ attach_function :shout_get_password, [:shout_t], :string
87
+
88
+ # Set a client certificate for TLS connections.
89
+ attach_function :shout_set_client_certificate, %i[shout_t string], :int
90
+ attach_function :shout_get_client_certificate, [:shout_t], :string
91
+
92
+ # Mount parameters
93
+ attach_function :shout_set_mount, %i[shout_t string], :int
94
+ attach_function :shout_get_mount, [:shout_t], :string
95
+
96
+ # Other parameters
97
+ # takes a SHOUT_AI_xxxx argument
98
+ attach_function :shout_set_audio_info, %i[shout_t string string], :int
99
+ attach_function :shout_get_audio_info, %i[shout_t string], :string
100
+
101
+ # takes a SHOUT_META_xxxx argument
102
+ attach_function :shout_set_meta, %i[shout_t string string], :int
103
+ attach_function :shout_get_meta, %i[shout_t string], :string
104
+
105
+ attach_function :shout_set_public, %i[shout_t uint], :int
106
+ attach_function :shout_get_public, [:shout_t], :uint
107
+
108
+ # Set the content language.
109
+ attach_function :shout_set_content_language, %i[shout_t string], :int
110
+ attach_function :shout_get_content_language, [:shout_t], :string
111
+
112
+ # Set the format of the content to send.
113
+ attach_function :shout_set_content_format, %i[shout_t shout_format uint string], :int
114
+ attach_function :shout_get_content_format, %i[shout_t pointer pointer pointer], :int
115
+
116
+ # takes a SHOUT_PROTOCOL_xxxxx argument
117
+ attach_function :shout_set_protocol, %i[shout_t shout_protocol], :int
118
+ attach_function :shout_get_protocol, [:shout_t], :shout_protocol
119
+
120
+ # Instructs libshout to use nonblocking I/O. Must be called before shout_open
121
+ attach_function :shout_set_nonblocking, %i[shout_t shout_blocking], :int
122
+ attach_function :shout_get_nonblocking, [:shout_t], :shout_blocking
123
+
124
+ # ----------------[ Actions ]----------------
125
+ # Calls that may wait on the network are attached with `blocking: true`
126
+ # so they release Ruby's GVL and don't freeze other threads.
127
+
128
+ # Opens a connection to the server. All parameters must already be set
129
+ attach_function :shout_open, [:shout_t], :int, blocking: true
130
+
131
+ # Closes a connection to the server
132
+ attach_function :shout_close, [:shout_t], :int, blocking: true
133
+
134
+ # Send data to the server, parsing it for format specific timing info
135
+ attach_function :shout_send, %i[shout_t pointer size_t], :int, blocking: true
136
+
137
+ # Send unparsed data to the server.
138
+ # Returns the number of bytes written, or < 0 on error.
139
+ attach_function :shout_send_raw, %i[shout_t pointer size_t], :ssize_t, blocking: true
140
+
141
+ # return the number of bytes currently on the write queue (only makes sense in
142
+ # nonblocking mode).
143
+ attach_function :shout_queuelen, [:shout_t], :ssize_t
144
+
145
+ # Puts caller to sleep until it is time to send more data to the server
146
+ attach_function :shout_sync, [:shout_t], :void, blocking: true
147
+
148
+ # Amount of time in ms caller should wait before sending again
149
+ attach_function :shout_delay, [:shout_t], :int
150
+
151
+ # ----------------[ MP3/AAC ONLY ]----------------
152
+
153
+ # Sets MP3 metadata. Returns one of SHOUTERR_XXX.
154
+ attach_function :shout_set_metadata_utf8, %i[shout_t shout_metadata_t], :int, blocking: true
155
+
156
+ # Allocates a new metadata structure. Must be freed by shout_metadata_free.
157
+ attach_function :shout_metadata_new, [], MetadataHandle
158
+
159
+ # Free resources allocated by shout_metadata_t
160
+ attach_function :shout_metadata_free, [:shout_metadata_t], :void
161
+
162
+ # Add a parameter to the metadata structure.
163
+ attach_function :shout_metadata_add, %i[shout_metadata_t string string], :int
164
+
165
+ # ----------------[ Advanced ]----------------
166
+
167
+ attach_function :shout_control, %i[shout_t int varargs], :int
168
+ attach_function :shout_set_callback, %i[shout_t shout_callback_t pointer], :int
169
+
170
+ # ----------------[ Obsolete ]----------------
171
+
172
+ attach_function :shout_set_name, %i[shout_t string], :int
173
+ attach_function :shout_get_name, [:shout_t], :string
174
+
175
+ attach_function :shout_set_url, %i[shout_t string], :int
176
+ attach_function :shout_get_url, [:shout_t], :string
177
+
178
+ attach_function :shout_set_genre, %i[shout_t string], :int
179
+ attach_function :shout_get_genre, [:shout_t], :string
180
+
181
+ attach_function :shout_set_description, %i[shout_t string], :int
182
+ attach_function :shout_get_description, [:shout_t], :string
183
+
184
+ attach_function :shout_set_format, %i[shout_t shout_format], :int
185
+ attach_function :shout_get_format, [:shout_t], :shout_format
186
+
187
+ attach_function :shout_set_dumpfile, %i[shout_t string], :int
188
+ attach_function :shout_get_dumpfile, [:shout_t], :string
189
+
190
+ attach_function :shout_set_metadata, %i[shout_t shout_metadata_t], :int, blocking: true
191
+ end
192
+ end