Skip to content

Commit

Permalink
+ monster commit (sry): moved to specs, added a wrapping pure ruby
Browse files Browse the repository at this point in the history
                        class to handle charset issues, more specs, ...
  • Loading branch information
niko committed Jan 1, 2011
1 parent 831f695 commit 8c5afe1
Show file tree
Hide file tree
Showing 14 changed files with 311 additions and 149 deletions.
51 changes: 49 additions & 2 deletions README.textile
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -16,11 +16,56 @@ h2. Usage


See example.rb. Run it like See example.rb. Run it like


./example.rb test/test.mp3 ./example.rb spec/test.mp3

h2. Ruby 1.9 and encoding

Ruby 1.9 features really good encoding support: Each string has it's own explicit encoding. The default encoding for Icecast is ISO-8859-1 for station data and meta data.

In order to make ruby-shout play nice with ruby 1.9 all I added a Shout#charset setter and all string accessors for station data are wrapped:

* setters convert the given string to the given charset
* and store the original string, available under #original_..., for example #original_description. This is done because converting may alter your string (characters incompatible with conversion are just swallowed). That way you are able to get your original string back (should you need it) and the getter is enabled to:
* getters convert the value back to the original encoding, determined by the #original_... method.

The idea is: We want to guaranty that strings keep their encoding when assigning them via a Shout accessor. In case strings are not compatible with Icecasts charset we want to degrade as gracefully as possible. IMHO just dropping incompatible characters does this best (when being listed in an external YP directory these "???br?" things just look awful). If you want to make sure this doesn't happen you have to compare the value before and after assigning it (or just compare for example #description with #original_description).

h3. Synopsis:

s = Shout.new
s.charset = 'UTF-8'
s.description = 'dikşîne MUSłϾ'
puts s.description # => 'dikşîne MUSłϾ'

s = Shout.new
s.charset = 'ISO-8859-1' # this is the default
s.description = 'dikşîne MUSłϾ'
puts s.description # => 'dikîne MUSłϾ'
puts s.original_description # => 'dikşîne MUSłϾ'

h3. Icecast charset config

If you use s/th else than ISO-8859-1 you should configure your icecast accordingly: In the mount section set the charset:

<mount>
<mount-name>/your_station</mount-name>
<charset>UTF-8</charset>
...
</mount>

or (in the latest icecast builds)

<mount>
<mount-name>/*</mount-name>
<charset>UTF-8</charset>
...
</mount>

to set UTF-8 for all mountpoints.


h2. Todo h2. Todo


* proper unit tests (blush) * proper unit tests (blush) ; working on that


h2. History & Credits h2. History & Credits


Expand All @@ -39,6 +84,8 @@ h2. Recent Changes


rake test:integration rake test:integration


* Added Ruby 1.9 encoding support.

h2. State h2. State


I tested this version of ruby-shout to build and run on I tested this version of ruby-shout to build and run on
Expand Down
15 changes: 8 additions & 7 deletions Rakefile
Original file line number Original file line Diff line number Diff line change
@@ -1,13 +1,14 @@
require 'rubygems' require 'rubygems'
require 'rake/gempackagetask' require 'rake/gempackagetask'
require 'spec/rake/spectask'


task :default => [:test] $TESTING_MINIUNIT = true


namespace :test do desc "Run spec with specdoc output"
desc "run the build integration test" Spec::Rake::SpecTask.new do |t|
task :integration do spec_files = Dir.glob('spec/*_spec.rb')
ruby "test/integration-test.rb" t.spec_files = spec_files
end t.spec_opts << '--format specdoc -c'
end end


begin begin
Expand All @@ -21,7 +22,7 @@ begin
s.authors = ["Jared Jennings", "Niko Dittmann"] s.authors = ["Jared Jennings", "Niko Dittmann"]
s.require_paths = ["lib"] s.require_paths = ["lib"]
s.rubyforge_project = 'ruby-shout' s.rubyforge_project = 'ruby-shout'
s.files = FileList["[A-Z]*", "{ext}/*"] s.files = FileList["[A-Z]*", "{ext}/*", "{lib}/*"]


end end
rescue LoadError rescue LoadError
Expand Down
18 changes: 12 additions & 6 deletions example.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,33 +1,39 @@
# -*- encoding : utf-8 -*-

#!/usr/bin/env ruby #!/usr/bin/env ruby


# Stream all the files given on the commandline to the Icecast server on localhost. # Stream all the files given on the commandline to the Icecast server on localhost.
# #
# Use the mp3 in the test directory: # Use the mp3 in the test directory:
# #
# ./example.rb test/test.mp3 # ./example.rb spec/test.mp3


require 'rubygems' BASE_DIR = File.expand_path File.dirname(__FILE__)
require 'shout' VERSION = File.open(File.join(BASE_DIR, 'VERSION')).readline.strip
require File.join(BASE_DIR, "spec/test_gem_installation/gems/ruby-shout-#{VERSION}/lib/shout")


BLOCKSIZE = 16384 BLOCKSIZE = 16384


s = Shout.new s = Shout.new
s.host = "localhost"
s.port = 8000
s.mount = "/example" s.mount = "/example"
# s.charset = "UTF-8"
# s.mount = "/utf8"
s.host = "192.168.178.34"
s.user = "source" s.user = "source"
s.pass = "hackme" s.pass = "hackme"
s.format = Shout::MP3 s.format = Shout::MP3
s.description ='çaffé düdeldø … dikşîne ΞŁΞϾТЯФЛłϾ MUSłϾ ☼ ☺'


s.connect s.connect


puts "open VLC and open network -> http://localhost:8000/example" puts "open VLC and open network -> http://#{s.host}:#{s.port}/example"


ARGV.each do |filename| ARGV.each do |filename|
File.open(filename) do |file| File.open(filename) do |file|
puts "sending data from #{filename}" puts "sending data from #{filename}"
m = ShoutMetadata.new m = ShoutMetadata.new
m.add 'filename', filename m.add 'filename', filename
m.add 'title', 'title ☼ ☺'
s.metadata = m s.metadata = m


while data = file.read(BLOCKSIZE) while data = file.read(BLOCKSIZE)
Expand Down
2 changes: 1 addition & 1 deletion ext/extconf.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
have_library("vorbis", "vorbis_dsp_clear") have_library("vorbis", "vorbis_dsp_clear")
have_library("pthread", "pthread_create") have_library("pthread", "pthread_create")
if find_library("shout", "shout_init","/usr","/usr/local") and have_header("shout/shout.h") if find_library("shout", "shout_init","/usr","/usr/local") and have_header("shout/shout.h")
create_makefile("shout") create_makefile("shout_ext")
else else
print "*** ERROR: need to have libshout and shout/shout.h to compile this module\n" print "*** ERROR: need to have libshout and shout/shout.h to compile this module\n"
end end
2 changes: 1 addition & 1 deletion ext/shout.c → ext/shout_ext.c
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ VALUE _sh_metadata_eq(VALUE self, VALUE meta) {
---------------------------------------------------------------- ----------------------------------------------------------------
*/ */


void Init_shout() void Init_shout_ext()
{ {
cShout = rb_define_class("Shout", rb_cObject); cShout = rb_define_class("Shout", rb_cObject);


Expand Down
49 changes: 49 additions & 0 deletions lib/shout.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,49 @@
$LOAD_PATH << File.expand_path(File.dirname(__FILE__))
require 'shout_ext'
require 'shout/string_extension_rb_18'

class Shout
attr_accessor :charset

INT_ACCESSORS = :port, :format
STRING_ACCESSORS = :host, :user, :username, :pass, :password, :protocol, :mount, :dumpfile,
:agent, :user_agent, :public, :name, :url, :genre, :description

STRING_ACCESSORS.each do |accessor|
attr_accessor :"original_#{accessor}"

alias :"raw_#{accessor}" :"#{accessor}"
define_method accessor do
return nil unless orig_acc = self.__send__("original_#{accessor}")

decode self.__send__(:"raw_#{accessor}"), orig_acc.encoding.name
end

alias :"raw_#{accessor}=" :"#{accessor}="
define_method :"#{accessor}=" do |value|
self.__send__ "original_#{accessor}=", value

self.__send__ :"raw_#{accessor}=", encode(value)
end
end

alias :ext_initialize :initialize
def initialize(opts={})
ext_initialize

self.charset = opts[:charset] || 'ISO-8859-1'

accessors = STRING_ACCESSORS + INT_ACCESSORS + [:charset]
a_opts = opts.select{ |k,v| accessors.include? k }

a_opts.each{ |k,v| self.__send__ :"#{k}=", v }
end

private
def encode(s)
s.encode(charset, :invalid => :replace, :undef => :replace, :replace => '')
end
def decode(s, orig_charset)
s.encode(orig_charset, charset, :invalid => :replace, :undef => :replace, :replace => '')
end
end
7 changes: 7 additions & 0 deletions lib/shout/string_extension_rb_18.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,7 @@
class String

def encode(*)
self
end

end
10 changes: 7 additions & 3 deletions ruby-shout.gemspec
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Gem::Specification.new do |s|


s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Jared Jennings", "Niko Dittmann"] s.authors = ["Jared Jennings", "Niko Dittmann"]
s.date = %q{2010-12-20} s.date = %q{2011-01-01}
s.description = %q{Ruby bindings for libshout 2, a "Library which can be used to write a source client like ices" for Icecast (http://www.icecast.org/download.php).} s.description = %q{Ruby bindings for libshout 2, a "Library which can be used to write a source client like ices" for Icecast (http://www.icecast.org/download.php).}
s.email = %q{mail@niko-dittmann.com} s.email = %q{mail@niko-dittmann.com}
s.extensions = ["ext/extconf.rb"] s.extensions = ["ext/extconf.rb"]
Expand All @@ -21,7 +21,8 @@ Gem::Specification.new do |s|
"Rakefile", "Rakefile",
"VERSION", "VERSION",
"ext/extconf.rb", "ext/extconf.rb",
"ext/shout.c" "ext/shout_ext.c",
"lib/shout.rb"
] ]
s.homepage = %q{http://github.com/niko/ruby-shout} s.homepage = %q{http://github.com/niko/ruby-shout}
s.rdoc_options = ["--charset=UTF-8"] s.rdoc_options = ["--charset=UTF-8"]
Expand All @@ -30,7 +31,10 @@ Gem::Specification.new do |s|
s.rubygems_version = %q{1.3.7} s.rubygems_version = %q{1.3.7}
s.summary = %q{Send audio over the network to an Icecast server} s.summary = %q{Send audio over the network to an Icecast server}
s.test_files = [ s.test_files = [
"test/integration-test.rb" "spec/accessors_spec.rb",
"spec/build_spec.rb",
"spec/integration_spec.rb",
"spec/spec_helper.rb"
] ]


if s.respond_to? :specification_version then if s.respond_to? :specification_version then
Expand Down
24 changes: 24 additions & 0 deletions spec/accessors_spec.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- encoding : utf-8 -*-

require File.join(File.expand_path(File.dirname(__FILE__)), 'spec_helper')

describe "Accessors" do
before(:each) do
@shout = Shout.new
@description = 'çaffé düdeldø'
@genre = 'foobar'.encode('ISO-8859-1')
@shout.genre = @genre
@shout.description = @description
end
it "should not change the original encoding" do
@shout.description.encoding.should == @description.encoding
@shout.genre.encoding.should == @genre.encoding
end
describe "#original_..." do
it "should store the original value" do
@shout.original_description.should == @description
@shout.original_genre.should == @genre
end
end
end

44 changes: 44 additions & 0 deletions spec/build_spec.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,44 @@
require 'spec'

BASE_DIR = File.expand_path File.join(File.dirname(__FILE__), '..')
VERSION = File.open(File.join(BASE_DIR, 'VERSION')).readline.strip

describe "Build process" do
def remove_pkg
command = %Q{
cd #{BASE_DIR}
rm -rf pkg
}
c = `#{command}`
puts c
return c
end

def clean_test_gem
command = %Q{
cd #{BASE_DIR}
rm -rf spec/test_gem_installation
}
c = `#{command}`
puts c
return c
end

def install_gem
command = %Q{
cd #{BASE_DIR}
rake build
gem install --no-test --no-rdoc --no-ri --install-dir spec/test_gem_installation --bindir spec/test_gem_installation pkg/ruby-shout-#{VERSION}.gem
}
c = `#{command}`
puts c
return c
end

it "should build" do
clean_test_gem.should be_true
install_gem.should be_true
remove_pkg.should be_true
end

end
Loading

0 comments on commit 8c5afe1

Please sign in to comment.