radiovis-generator 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,109 @@
1
+ ## RadioVIS Generator
2
+
3
+ This is a small gem which allows you to quickly and simply generate slides (comprising of a complex, rendered and composited image, and some text) customised for your station from an SVG source file, a set of substitutions (which can be dynamically driven) and optionally an image to composite behind the SVG output.
4
+
5
+ The generator also includes a framework for specifying slide durations and intelligently pushing them to your Stomp broker.
6
+
7
+ It's designed to let any station plug it directly into their infrastructure with a minimal amount of coding and a simple process for non-technical content creators.
8
+
9
+ ### What is RadioVIS?
10
+
11
+ RadioVIS is a [RadioDNS](http://radiodns.org/) application. It's designed for hybrid radio receivers (Broadcast audio plus IP back channel) but can be used on other platforms such as the internet in addition. A [much better description](http://radiodns.org/about-radiodns/) can be found on the RadioDNS website.
12
+
13
+ Simply put (though if you want the spec, [it's here](http://radiodns.org/documentation/)) RadioVIS lets receivers and audio player software show an image and a line of text next to your station, and allows for the use of push technologies to update which image and text the receiver is displaying at a given point in time. This allows for content mapped to the output of your station (such as 'Now Playing' information or information on the current show) to be generated and displayed to your users.
14
+
15
+ ## Dependencies
16
+
17
+ RadioVIS Generator depends on Inkscape (which does not require a GUI) and the ImageMagick project (the 'convert' and 'composite' binaries).
18
+
19
+ Aside from those two tools, Ruby is required, along with RubyGems. Recent versions are recommended.
20
+
21
+ You'll also need a Stomp broker, though this can be on another system. You'll probably need a web server like nginx or Apache on your system, though.
22
+
23
+ RadioVIS Generator is tested on Linux. Other operating systems are supported in principle, but not tested.
24
+
25
+ ## Installation and Usage
26
+
27
+ Installing is simple. Install the dependencies (on Ubuntu, that's `sudo apt-get install imagemagick inkscape` - add `ruby1.9-full` if you're missing Ruby), and then simply install the gem with `gem install radiovis-generator`. Congratulations, you're good to go!
28
+
29
+
30
+ You can test the installation out by using the simple command line program bundled with the generator - see `radiovis-generator --help` for options. This will cycle between the slides provided by the gem, and is intended as a really simple starting point for your own runner.
31
+
32
+
33
+ ### Simple Usage
34
+
35
+ RadioVIS Generator comes bundled with a couple of basic templates - one which shows two pieces of information (intended for Now Playing slides), and one which shows just a large title. These will get you off the ground, or at least show you how to customise your slides.
36
+
37
+ To run RadioVIS Generator is very simple. I'm going to assume you can set up a folder on your system and point your web server at it, and that you've got a Stomp broker running. If this is all correct, then you can run a simple demo by creating a file like this:
38
+
39
+ ```
40
+ require 'rubygems'
41
+ require 'radiovis-generator'
42
+ gen = RadioVISGenerator::Generator.new
43
+ options = {
44
+ slides: [RadioVISGenerator::BrandingSlide.new, RadioVISGenerator::NowPlayingSlide.new],
45
+ url: 'http://localhost/radiovis/', # The path on your web server pointing at..
46
+ path: '/tmp/radiovis-output' # This path on your filesystem!
47
+ broadcast_parameters: 'fm/ecc/pi/freq', # See the RadioVIS spec for how to generate this.
48
+ username: 'system', # These details are for your Stomp broker.
49
+ password: 'manager',
50
+ host: 'localhost',
51
+ port: 61313
52
+ }
53
+ gen.run(options)
54
+ ```
55
+
56
+ Running this file will perpetually serve up RadioVIS images and text, and send out Stomp messages. You can use a tool like CasterPlay's RadioVIS Monitor to check everything is working. The console output will also tell you more or less what it's doing, or at least let you know if it's obviously not working!
57
+
58
+ ### Customising
59
+
60
+ Customisation should simply be a matter of making a new subclass of `RadioVISGenerator::Slide`, making a new SVG file and if required a background image, and then adding the new subclass to your runner rotation. So let's say we want a simple slide which is going to have a rotating title and a background image.
61
+
62
+ ```
63
+ class MyCustomSlide < RadioVISGenerator::Slide
64
+ def generate
65
+ return {
66
+ '$$TITLE$$' => ['Some cool text!', 'Some other cool text!'].shuffle.first
67
+ }
68
+ end
69
+ # We don't want this one displayed so often.
70
+ def redisplay_delay
71
+ 20
72
+ end
73
+ # Specify our filename - this is messy because it's relative to the gem.
74
+ def svg_filename
75
+ return "/some/path/to/my-custom-slide.svg"
76
+ end
77
+ # We want a background image!
78
+ def background_image
79
+ return "/some/path/to/my-background.png"
80
+ end
81
+ end
82
+ ```
83
+
84
+ Now all we need to do is pass `MyCustomSlide.new` to our runner as a slide to consider in the rotation and we're done! We can customise the SVG file or the background image, adjust the text we're dynamically adding in our slide class, or add new dynamic elements through the same mechanism. The only limit is the lengths to which you're willing to figure out how to do things with SVG.
85
+
86
+
87
+ Loads more information on how to make your own custom slides can be found in the RDoc documentation or by simply reading the `slide.rb` file.
88
+
89
+
90
+ ## Credits and Acknowledgements
91
+
92
+ * This software package was developed by James Harrison, originally as part of the OCD Broadcast Content Management System for [Insanity Radio](http://insanityradio.com).
93
+ * RadioDNS is supported by a [lot of people](http://radiodns.org/supporters/).
94
+ * The [Inkscape](http://inkscape.org/) project provides the kick-ass SVG to PNG converter and a really slick SVG editor.
95
+ * [ImageMagick](http://www.imagemagick.org/script/index.php) has yet to not appear in anything I've ever written. All the contributors rock.
96
+
97
+
98
+ ## License
99
+
100
+ Copyright (c) 2012, James Harrison
101
+ All rights reserved.
102
+
103
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
104
+
105
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
106
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
107
+ * Neither the name of Insanity Radio nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
108
+
109
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JAMES HARRISON OR INSANITY RADIO BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'radiovis-generator'
4
+ require 'trollop'
5
+ opts = Trollop::options do
6
+ opt :url, "Base Image URL", type: String, default: 'http://localhost/radiovis/'
7
+ opt :path, "Base Image Path", type: String, default: '/tmp/radiovis-output'
8
+ opt :broadcast_parameters, "Broadcast Parameters", type: String
9
+ opt :username, "Stomp username", type: String, default: 'system'
10
+ opt :password, "Stomp password", type: String, default: 'manager'
11
+ opt :host, "Stomp host", type: String, default: 'localhost'
12
+ opt :port, "Stomp port", type: :int, default: 61613
13
+ end
14
+ gen = RadioVISGenerator::Generator.new
15
+ options = {
16
+ slides: [RadioVISGenerator::BrandingSlide.new, RadioVISGenerator::NowPlayingSlide.new]
17
+ }.merge(opts)
18
+ gen.run(options)
@@ -0,0 +1,116 @@
1
+ require 'stomp'
2
+
3
+ module RadioVISGenerator
4
+ class Generator
5
+ def initialize
6
+ Generator.has_dependencies?
7
+ end
8
+
9
+ # Runs the generator!
10
+ # This does all the tying together of things and Stomp interaction.
11
+ # The generator accepts a hash of options:
12
+ # slides - An array of Slide instances.
13
+ # url - The root URL at which the pictures can be found from the outside world, ie 'http://blah.com/system/radiovis-images/'.
14
+ # path - The root path where we should write the images, ie '/opt/www/website/public/system/radiovis-images'
15
+ # broadcast_parameters - The broadcast parameters for your station in RadioVIS topic format, ie 'fm/ECC/PI/FREQUENCY' for FM (like 'fm/ce1/c08f/10320' for Insanity Radio 103.2 FM)
16
+ # username - The Stomp username
17
+ # password - The Stomp password
18
+ # host - The Stomp host
19
+ # port - The Stomp port
20
+ # Sensible defaults are provided for Stomp, but you should set them explicitly.
21
+ def run(options)
22
+ defaults = {
23
+ slides: [],
24
+ url: 'http://localhost/radiovis/',
25
+ path: '/tmp/radiovis-output',
26
+ broadcast_parameters: 'fm/ecc/pi/freq',
27
+ username: 'system',
28
+ password: 'manager',
29
+ host: 'localhost',
30
+ port: 61313
31
+ }
32
+ options = defaults.merge(options)
33
+ raise ArgumentsError, "No slide instances provided!" unless options[:slides].size > 0
34
+ puts "Generator starting - parameters #{options.inspect}"
35
+ begin
36
+ conn = Stomp::Connection.open options[:username], options[:password], options[:host], options[:port], false
37
+ while true do
38
+ # Okay - so here's the plan.
39
+ # We have a bunch of Slide subclasses which contain what we want.
40
+ # Every so often we check to see if any need rendering and render whichever has the highest priority.
41
+ slides_to_render = []
42
+ options[:slides].each do |slide|
43
+ slides_to_render.push(slide) if slide.changed?
44
+ end
45
+ # If we've got nothing that's changed, pick a redisplay instead.
46
+ if slides_to_render.size == 0
47
+ options[:slides].each do |slide|
48
+ slides_to_render.push(slide) if slide.redisplay?
49
+ end
50
+ end
51
+ # Pick the lowest priority.
52
+ slides_to_render.sort_by!{|slide|slide.priority}
53
+ slide = slides_to_render.reverse[0] rescue nil
54
+ if slide
55
+ # Go for it!
56
+ puts "Rendering #{slide.inspect}"
57
+ puts "Chosen from #{slides_to_render.inspect}"
58
+ slide_output = slide.render(options[:path])
59
+
60
+ # Now we go ahead and send those messages.
61
+ image_topic = "/topic/#{options[:broadcast_parameters]}/image"
62
+ text_topic = "/topic/#{options[:broadcast_parameters]}/text"
63
+ image_message = "SHOW "+options[:url]+slide_output[:image_small]
64
+ text_message = "TEXT "+slide_output[:text]
65
+ puts "Publishing Stomp messages"
66
+ puts " - #{image_topic} <= #{image_message}"
67
+ puts " - #{text_topic} <= #{text_message}"
68
+ conn.publish(image_topic, image_message, {'persistent'=>'false'})
69
+ conn.publish(text_topic, text_message, {'persistent'=>'false'})
70
+ puts "Done, waiting #{slide.display_time} seconds to let people read this slide."
71
+ # Sleep for this long before we display anything else.
72
+ sleep slide.display_time
73
+ else
74
+ puts "No slide to render, sleeping a second."
75
+ sleep 1
76
+ end
77
+ end
78
+ #rescue Exception => e
79
+ # puts "Got exception #{e}!"
80
+ # conn.disconnect rescue nil
81
+ # conn = nil
82
+ # sleep 5
83
+ # retry
84
+ end
85
+ end
86
+
87
+ def self.has_dependencies?
88
+ unless self.which('inkscape')
89
+ raise SystemCallError, "Inkscape is required to use this library.\nYou can install it on Ubuntu/Debian with 'sudo apt-get install inkscape'. (And don't worry, it works headlessly and won't install a full GUI stack).\nSpecifically I need 'inkscape' in my PATH."
90
+ end
91
+ unless self.which('convert')
92
+ raise SystemCallError, "ImageMagick is required to use this library.\nYou can install it on Ubuntu/Debian with 'sudo apt-get install imagemagick'.\nSpecifically I need 'convert' in my PATH."
93
+ end
94
+ unless self.which('composite')
95
+ raise SystemCallError, "ImageMagick is required to use this library.\nYou can install it on Ubuntu/Debian with 'sudo apt-get install imagemagick'.\nSpecifically I need 'composite' in my PATH."
96
+ end
97
+ end
98
+ private
99
+ # Utility method to identify if a program is available and executable.
100
+ def self.which(cmd)
101
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
102
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
103
+ exts.each { |ext|
104
+ exe = "#{path}/#{cmd}#{ext}"
105
+ return exe if File.executable? exe
106
+ }
107
+ end
108
+ return nil
109
+ end
110
+ end
111
+ end
112
+
113
+ require 'radiovis-generator/slide'
114
+ require 'radiovis-generator/slides/branding_slide'
115
+ require 'radiovis-generator/slides/now_playing_slide'
116
+
@@ -0,0 +1,132 @@
1
+ require 'fileutils'
2
+ class RadioVISGenerator::Slide
3
+ # Generate the substitution hash to process on this run. This is where we add our dynamic content!
4
+ # Returns a hash where "$$TARGET$$" => "Content", where $$TARGET$$ is the target string to replace in the SVG and Content is the substitute.
5
+ # This is passed to RadioVISGenerator::Slide.rewrite_svg
6
+ def generate
7
+ return {}
8
+ end
9
+
10
+ # Generate the text to display alongside this slide.
11
+ def text
12
+ return "Default slide text - RadioVIS Generator"
13
+ end
14
+
15
+ # Has this slide changed since last rendered?
16
+ # Reset anything you need to in the Slide#generate method.
17
+ # For instance, if I'm showing the current show or now playing, I'd check if the current track on air
18
+ # was the same as the one I'd stored last time I'd rendered. If it were different I'd return true.
19
+ # Then in Slide#generate, I'd store the current track on air.
20
+ # This means we can get very prompt updates on realtime events - maximum delay defined by your largest
21
+ # display_time of all the slides, ie 5 seconds with nothing overridden.
22
+ def changed?
23
+ return false
24
+ end
25
+
26
+ # What is the priority of this slide? Lower numbers takes precedence. Default 50.
27
+ def priority
28
+ return 50
29
+ end
30
+
31
+ # How long should we display this slide for at a minimum? Values under 5 are rarely good.
32
+ def display_time
33
+ return 5
34
+ end
35
+
36
+ # How long should we wait between redisplays if we've got nothing better to do (ie no content changed)?
37
+ def redisplay_delay
38
+ return 15
39
+ end
40
+
41
+ # What image makes the background of this slide, and should be composited over it?
42
+ # Return nil to skip the composition stage and just display the SVG output.
43
+ # Defaults to nil.
44
+ def background_image
45
+ return nil
46
+ end
47
+
48
+ # What SVG file are we going to render?
49
+ # Defaults to an empty slide.
50
+ def svg_filename
51
+ return "#{File.expand_path(File.dirname(File.dirname(__FILE__)))}/templates/empty-slide.svg"
52
+ end
53
+
54
+ # Okay, show's over!
55
+
56
+ # That's it. Everything up there, define in your subclasses if you want to override them.
57
+
58
+ # Set up the slide. Just sets the last time this slide was rendered up.
59
+ def initialize
60
+ @last_render_time = Time.now
61
+ end
62
+
63
+ # Have we gotten bored enough to just show this again, assuming it's not been too little time
64
+ # (as defined in Slide#redisplay_delay)
65
+ def redisplay?
66
+ if @last_render_time < Time.now - 15
67
+ return true
68
+ end
69
+ return false
70
+ end
71
+
72
+
73
+ # Render this slide, returns a hash with image_big, image_small, output_path, name and text. Paths returned are relative to the provided output_path.
74
+ # Will create the output path if needed.
75
+ # Shouldn't be overriden unless you know what you're doing.
76
+ # Calls Slide#generate and Slide#text to get dynamic content and then spits out some images.
77
+ def render(output_path)
78
+ # Reset time-based cycling
79
+ @last_render_time = Time.now
80
+ # Make sure our output path exists
81
+ FileUtils.mkdir_p(output_path) rescue nil
82
+ Dir.mkdir('/tmp/radiovis-generator') rescue nil # Make our temporary storage
83
+ RadioVISGenerator::Slide.rewrite_svg(svg_filename, "/tmp/#{self.name}.svg", self.generate)
84
+ if background_image
85
+ RadioVISGenerator::Slide.render_svg("/tmp/#{self.name}.svg", "/tmp/#{self.name}-precomp.png")
86
+ RadioVISGenerator::Slide.composite("/tmp/#{self.name}-precomp.png", background_image, File.join(output_path, "#{self.name}-640x480.png"))
87
+ else
88
+ RadioVISGenerator::Slide.render_svg("/tmp/#{self.name}.svg", File.join(output_path, "#{self.name}-640x480.png"))
89
+ end
90
+ FileUtils.rm_rf('/tmp/radiovis-generator') rescue nil # Clean up after ourselves
91
+ # Make our smaller-res version
92
+ RadioVISGenerator::Slide.resize_to_fit(File.join(output_path, "#{self.name}-640x480.png"), File.join(output_path, "#{self.name}-320x240.png"), '320x240')
93
+ return {
94
+ image_big: "#{self.name}-640x480.png",
95
+ image_small: "#{self.name}-320x240.png",
96
+ text: self.text,
97
+ output_path: output_path,
98
+ name: self.name
99
+ }
100
+ end
101
+
102
+ # Returns the name of this slide as a friendlyish string to be used in output image names.
103
+ def name
104
+ self.class.to_s.downcase.gsub("radiovisgenerator::","")
105
+ end
106
+
107
+ # Rewrites an SVG at a given input path with the information in the given hash to the given output path.
108
+ # Hash is of the format "$$TARGET$$" => "Content", where $$TARGET$$ is the target string to replace in the SVG and Content is the substitute.
109
+ def self.rewrite_svg(in_path, out_path, sub_hash)
110
+ svg_input = File.open(in_path).read()
111
+ sub_hash.each_pair do |k,v|
112
+ svg_input = svg_input.gsub(k,v)
113
+ end
114
+ File.open(out_path, 'w'){|f|f<<svg_input}
115
+ end
116
+
117
+ # Renders an SVG to a PNG
118
+ def self.render_svg(in_path, out_path)
119
+ `inkscape -f #{in_path} -e #{out_path}`
120
+ end
121
+
122
+ # Resize and (if needed) crop to the size of a slide (640x480 by default), using center gravity.
123
+ def self.resize_to_fit(in_path, out_path, size='640x480')
124
+ `convert #{in_path} -resize #{size}^ -gravity center -extent #{size} #{out_path}`
125
+ end
126
+
127
+ # Composite two images to another image. Center gravity.
128
+ def self.composite(top_in_path, bottom_in_path, out_path)
129
+ `composite -gravity center #{top_in_path} #{bottom_in_path} #{out_path}`
130
+ end
131
+
132
+ end
@@ -0,0 +1,26 @@
1
+ class RadioVISGenerator::BrandingSlide < RadioVISGenerator::Slide
2
+ # Man, NowPlayingSlide thinks he's got problems. I'm a complete idiot compared to that guy.
3
+ # Hey, want to help me out? Maybe add some functionality to me?
4
+ def generate
5
+ return {
6
+ '$$STATIONNAME$$' => 'Your Radio Station'
7
+ }
8
+ end
9
+ # I'm just an example slide so I don't actually have a background image bundled.
10
+ # But here I'd return a full path to whatever I wanted behind my SVG with all the SVG's shiny opacity.
11
+ def background_image
12
+ return nil
13
+ end
14
+ # We don't want this one displayed so often.
15
+ def redisplay_delay
16
+ 20
17
+ end
18
+ # And it's lower priority.
19
+ def priority
20
+ 25
21
+ end
22
+ # Specify our filename - this is messy because it's relative to the gem.
23
+ def svg_filename
24
+ return "#{File.expand_path(File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))))}/templates/branding-slide.svg"
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ class RadioVISGenerator::NowPlayingSlide < RadioVISGenerator::Slide
2
+ # In here I'd do some clever stuff if I were being clever. But I'm not.
3
+ # I'm just an example slide. You're clever, right? You can help me out here!
4
+ def generate
5
+ return {
6
+ '$$STATIONNAME$$' => 'Your Radio Station',
7
+ '$$NOWPLAYING$$' => 'Some Track is Playing'
8
+ }
9
+ end
10
+ # Specify our filename - this is messy because it's relative to the gem.
11
+ def svg_filename
12
+ return "#{File.expand_path(File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))))}/templates/now-playing-slide.svg"
13
+ end
14
+ end
@@ -0,0 +1,153 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6
+ xmlns:cc="http://creativecommons.org/ns#"
7
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8
+ xmlns:svg="http://www.w3.org/2000/svg"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ xmlns:xlink="http://www.w3.org/1999/xlink"
11
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
+ width="640"
14
+ height="480"
15
+ id="svg4824"
16
+ version="1.1"
17
+ inkscape:version="0.48.2 r9819"
18
+ sodipodi:docname="now-playing-slide.svg">
19
+ <title
20
+ id="title4836">RadioVIS Generator Default Slide</title>
21
+ <defs
22
+ id="defs4826">
23
+ <linearGradient
24
+ id="linearGradient6807">
25
+ <stop
26
+ style="stop-color:#333333;stop-opacity:1;"
27
+ offset="0"
28
+ id="stop6809" />
29
+ <stop
30
+ style="stop-color:#333333;stop-opacity:0.71428573;"
31
+ offset="1"
32
+ id="stop6811" />
33
+ </linearGradient>
34
+ <linearGradient
35
+ inkscape:collect="always"
36
+ xlink:href="#linearGradient6807"
37
+ id="linearGradient6815"
38
+ x1="19.112589"
39
+ y1="972.36218"
40
+ x2="620.88739"
41
+ y2="972.36218"
42
+ gradientUnits="userSpaceOnUse"
43
+ spreadMethod="pad"
44
+ gradientTransform="matrix(0.2,0,0,5.0000001,908.36218,-5181.8111)" />
45
+ <linearGradient
46
+ inkscape:collect="always"
47
+ xlink:href="#linearGradient6807-5"
48
+ id="linearGradient6815-7"
49
+ x1="19.112589"
50
+ y1="972.36218"
51
+ x2="620.88739"
52
+ y2="972.36218"
53
+ gradientUnits="userSpaceOnUse"
54
+ spreadMethod="pad"
55
+ gradientTransform="matrix(0.2,0,0,5.0000001,908.36218,-5181.8111)" />
56
+ <linearGradient
57
+ id="linearGradient6807-5">
58
+ <stop
59
+ style="stop-color:#333333;stop-opacity:1;"
60
+ offset="0"
61
+ id="stop6809-7" />
62
+ <stop
63
+ style="stop-color:#333333;stop-opacity:0.71428573;"
64
+ offset="1"
65
+ id="stop6811-8" />
66
+ </linearGradient>
67
+ <linearGradient
68
+ y2="972.36218"
69
+ x2="620.88739"
70
+ y1="972.36218"
71
+ x1="19.112589"
72
+ spreadMethod="pad"
73
+ gradientTransform="matrix(0.13333332,0,0,5.0000001,589.6955,-5181.8111)"
74
+ gradientUnits="userSpaceOnUse"
75
+ id="linearGradient6834"
76
+ xlink:href="#linearGradient6807-5"
77
+ inkscape:collect="always" />
78
+ </defs>
79
+ <sodipodi:namedview
80
+ id="base"
81
+ pagecolor="#ffffff"
82
+ bordercolor="#666666"
83
+ borderopacity="1.0"
84
+ inkscape:pageopacity="0.0"
85
+ inkscape:pageshadow="2"
86
+ inkscape:zoom="0.7"
87
+ inkscape:cx="197.53061"
88
+ inkscape:cy="151.88693"
89
+ inkscape:document-units="px"
90
+ inkscape:current-layer="layer1"
91
+ showgrid="true"
92
+ inkscape:window-width="1122"
93
+ inkscape:window-height="722"
94
+ inkscape:window-x="25"
95
+ inkscape:window-y="25"
96
+ inkscape:window-maximized="0">
97
+ <inkscape:grid
98
+ type="xygrid"
99
+ id="grid4834"
100
+ empspacing="5"
101
+ visible="true"
102
+ enabled="true"
103
+ snapvisiblegridlinesonly="true" />
104
+ </sodipodi:namedview>
105
+ <metadata
106
+ id="metadata4829">
107
+ <rdf:RDF>
108
+ <cc:Work
109
+ rdf:about="">
110
+ <dc:format>image/svg+xml</dc:format>
111
+ <dc:type
112
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
113
+ <dc:title>RadioVIS Generator Default Slide</dc:title>
114
+ <dc:creator>
115
+ <cc:Agent>
116
+ <dc:title>James Harrison</dc:title>
117
+ </cc:Agent>
118
+ </dc:creator>
119
+ <dc:rights>
120
+ <cc:Agent>
121
+ <dc:title>Creative Commons C0</dc:title>
122
+ </cc:Agent>
123
+ </dc:rights>
124
+ </cc:Work>
125
+ </rdf:RDF>
126
+ </metadata>
127
+ <g
128
+ inkscape:label="Layer 1"
129
+ inkscape:groupmode="layer"
130
+ id="layer1"
131
+ transform="translate(0,-572.36218)">
132
+ <rect
133
+ style="fill:url(#linearGradient6834);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1.44928122;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
134
+ id="rect4838-2"
135
+ width="80"
136
+ height="600"
137
+ x="592.36218"
138
+ y="-620"
139
+ transform="matrix(0,1,-1,0,0,0)" />
140
+ <text
141
+ xml:space="preserve"
142
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff8000;fill-opacity:1;stroke:none;font-family:Sans"
143
+ x="40"
144
+ y="652.36218"
145
+ id="text5348-7"
146
+ sodipodi:linespacing="125%"><tspan
147
+ sodipodi:role="line"
148
+ id="tspan5350-8"
149
+ x="40"
150
+ y="652.36218"
151
+ style="font-size:48px;fill:#ff8000;fill-opacity:1">$$STATIONNAME$$</tspan></text>
152
+ </g>
153
+ </svg>
@@ -0,0 +1,75 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6
+ xmlns:cc="http://creativecommons.org/ns#"
7
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8
+ xmlns:svg="http://www.w3.org/2000/svg"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12
+ width="640"
13
+ height="480"
14
+ id="svg4824"
15
+ version="1.1"
16
+ inkscape:version="0.48.2 r9819"
17
+ sodipodi:docname="New document 23">
18
+ <title
19
+ id="title4836">RadioVIS Generator Default Slide</title>
20
+ <defs
21
+ id="defs4826" />
22
+ <sodipodi:namedview
23
+ id="base"
24
+ pagecolor="#ffffff"
25
+ bordercolor="#666666"
26
+ borderopacity="1.0"
27
+ inkscape:pageopacity="0.0"
28
+ inkscape:pageshadow="2"
29
+ inkscape:zoom="0.35"
30
+ inkscape:cx="-66.428571"
31
+ inkscape:cy="520"
32
+ inkscape:document-units="px"
33
+ inkscape:current-layer="layer1"
34
+ showgrid="true"
35
+ inkscape:window-width="1122"
36
+ inkscape:window-height="722"
37
+ inkscape:window-x="25"
38
+ inkscape:window-y="25"
39
+ inkscape:window-maximized="0">
40
+ <inkscape:grid
41
+ type="xygrid"
42
+ id="grid4834"
43
+ empspacing="5"
44
+ visible="true"
45
+ enabled="true"
46
+ snapvisiblegridlinesonly="true" />
47
+ </sodipodi:namedview>
48
+ <metadata
49
+ id="metadata4829">
50
+ <rdf:RDF>
51
+ <cc:Work
52
+ rdf:about="">
53
+ <dc:format>image/svg+xml</dc:format>
54
+ <dc:type
55
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
56
+ <dc:title>RadioVIS Generator Default Slide</dc:title>
57
+ <dc:creator>
58
+ <cc:Agent>
59
+ <dc:title>James Harrison</dc:title>
60
+ </cc:Agent>
61
+ </dc:creator>
62
+ <dc:rights>
63
+ <cc:Agent>
64
+ <dc:title>Creative Commons C0</dc:title>
65
+ </cc:Agent>
66
+ </dc:rights>
67
+ </cc:Work>
68
+ </rdf:RDF>
69
+ </metadata>
70
+ <g
71
+ inkscape:label="Layer 1"
72
+ inkscape:groupmode="layer"
73
+ id="layer1"
74
+ transform="translate(0,-572.36218)" />
75
+ </svg>
@@ -0,0 +1,173 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6
+ xmlns:cc="http://creativecommons.org/ns#"
7
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8
+ xmlns:svg="http://www.w3.org/2000/svg"
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ xmlns:xlink="http://www.w3.org/1999/xlink"
11
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
+ width="640"
14
+ height="480"
15
+ id="svg4824"
16
+ version="1.1"
17
+ inkscape:version="0.48.2 r9819"
18
+ sodipodi:docname="now-playing-slide.svg">
19
+ <title
20
+ id="title4836">RadioVIS Generator Default Slide</title>
21
+ <defs
22
+ id="defs4826">
23
+ <linearGradient
24
+ id="linearGradient6807">
25
+ <stop
26
+ style="stop-color:#333333;stop-opacity:1;"
27
+ offset="0"
28
+ id="stop6809" />
29
+ <stop
30
+ style="stop-color:#333333;stop-opacity:0.71428573;"
31
+ offset="1"
32
+ id="stop6811" />
33
+ </linearGradient>
34
+ <linearGradient
35
+ inkscape:collect="always"
36
+ xlink:href="#linearGradient6807"
37
+ id="linearGradient6815"
38
+ x1="19.112589"
39
+ y1="972.36218"
40
+ x2="620.88739"
41
+ y2="972.36218"
42
+ gradientUnits="userSpaceOnUse"
43
+ spreadMethod="pad"
44
+ gradientTransform="matrix(0.2,0,0,5.0000001,908.36218,-5181.8111)" />
45
+ <linearGradient
46
+ inkscape:collect="always"
47
+ xlink:href="#linearGradient6807-5"
48
+ id="linearGradient6815-7"
49
+ x1="19.112589"
50
+ y1="972.36218"
51
+ x2="620.88739"
52
+ y2="972.36218"
53
+ gradientUnits="userSpaceOnUse"
54
+ spreadMethod="pad"
55
+ gradientTransform="matrix(0.2,0,0,5.0000001,908.36218,-5181.8111)" />
56
+ <linearGradient
57
+ id="linearGradient6807-5">
58
+ <stop
59
+ style="stop-color:#333333;stop-opacity:1;"
60
+ offset="0"
61
+ id="stop6809-7" />
62
+ <stop
63
+ style="stop-color:#333333;stop-opacity:0.71428573;"
64
+ offset="1"
65
+ id="stop6811-8" />
66
+ </linearGradient>
67
+ <linearGradient
68
+ y2="972.36218"
69
+ x2="620.88739"
70
+ y1="972.36218"
71
+ x1="19.112589"
72
+ spreadMethod="pad"
73
+ gradientTransform="matrix(0.13333332,0,0,5.0000001,589.6955,-5181.8111)"
74
+ gradientUnits="userSpaceOnUse"
75
+ id="linearGradient6834"
76
+ xlink:href="#linearGradient6807-5"
77
+ inkscape:collect="always" />
78
+ </defs>
79
+ <sodipodi:namedview
80
+ id="base"
81
+ pagecolor="#ffffff"
82
+ bordercolor="#666666"
83
+ borderopacity="1.0"
84
+ inkscape:pageopacity="0.0"
85
+ inkscape:pageshadow="2"
86
+ inkscape:zoom="0.7"
87
+ inkscape:cx="197.53061"
88
+ inkscape:cy="151.88693"
89
+ inkscape:document-units="px"
90
+ inkscape:current-layer="layer1"
91
+ showgrid="true"
92
+ inkscape:window-width="1122"
93
+ inkscape:window-height="722"
94
+ inkscape:window-x="25"
95
+ inkscape:window-y="25"
96
+ inkscape:window-maximized="0">
97
+ <inkscape:grid
98
+ type="xygrid"
99
+ id="grid4834"
100
+ empspacing="5"
101
+ visible="true"
102
+ enabled="true"
103
+ snapvisiblegridlinesonly="true" />
104
+ </sodipodi:namedview>
105
+ <metadata
106
+ id="metadata4829">
107
+ <rdf:RDF>
108
+ <cc:Work
109
+ rdf:about="">
110
+ <dc:format>image/svg+xml</dc:format>
111
+ <dc:type
112
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
113
+ <dc:title>RadioVIS Generator Default Slide</dc:title>
114
+ <dc:creator>
115
+ <cc:Agent>
116
+ <dc:title>James Harrison</dc:title>
117
+ </cc:Agent>
118
+ </dc:creator>
119
+ <dc:rights>
120
+ <cc:Agent>
121
+ <dc:title>Creative Commons C0</dc:title>
122
+ </cc:Agent>
123
+ </dc:rights>
124
+ </cc:Work>
125
+ </rdf:RDF>
126
+ </metadata>
127
+ <g
128
+ inkscape:label="Layer 1"
129
+ inkscape:groupmode="layer"
130
+ id="layer1"
131
+ transform="translate(0,-572.36218)">
132
+ <rect
133
+ style="fill:url(#linearGradient6815);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1.77499986;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
134
+ id="rect4838"
135
+ width="120"
136
+ height="600"
137
+ x="912.36218"
138
+ y="-620"
139
+ transform="matrix(0,1,-1,0,0,0)" />
140
+ <text
141
+ xml:space="preserve"
142
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
143
+ x="40"
144
+ y="952.36218"
145
+ id="text5348"
146
+ sodipodi:linespacing="125%"><tspan
147
+ sodipodi:role="line"
148
+ id="tspan5350"
149
+ x="40"
150
+ y="952.36218"
151
+ style="font-size:32px;fill:#ffffff">$$NOWPLAYING$$</tspan></text>
152
+ <rect
153
+ style="fill:url(#linearGradient6834);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:1.44928122;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
154
+ id="rect4838-2"
155
+ width="80"
156
+ height="600"
157
+ x="592.36218"
158
+ y="-620"
159
+ transform="matrix(0,1,-1,0,0,0)" />
160
+ <text
161
+ xml:space="preserve"
162
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff8000;fill-opacity:1;stroke:none;font-family:Sans"
163
+ x="40"
164
+ y="652.36218"
165
+ id="text5348-7"
166
+ sodipodi:linespacing="125%"><tspan
167
+ sodipodi:role="line"
168
+ id="tspan5350-8"
169
+ x="40"
170
+ y="652.36218"
171
+ style="font-size:48px;fill:#ff8000;fill-opacity:1">$$STATIONNAME$$</tspan></text>
172
+ </g>
173
+ </svg>
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: radiovis-generator
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - James Harrison
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-04-09 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: trollop
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: stomp
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ description: An extendable gem to generate slide images and text for RadioVIS systems and publish them in an intelligent rotation
47
+ email: james@talkunafraid.co.uk
48
+ executables:
49
+ - radiovis-generator
50
+ extensions: []
51
+
52
+ extra_rdoc_files: []
53
+
54
+ files:
55
+ - lib/radiovis-generator/slide.rb
56
+ - lib/radiovis-generator/slides/branding_slide.rb
57
+ - lib/radiovis-generator/slides/now_playing_slide.rb
58
+ - lib/radiovis-generator.rb
59
+ - templates/empty-slide.svg
60
+ - templates/now-playing-slide.svg
61
+ - templates/branding-slide.svg
62
+ - README.md
63
+ - bin/radiovis-generator
64
+ has_rdoc: true
65
+ homepage: http://jamesharrison.github.com/radiovis-generator
66
+ licenses:
67
+ - Modified BSD
68
+ post_install_message: Thanks for installing! Please do visit http://jamesharrison.github.com/radiovis-generator and carefully read the documentation.
69
+ rdoc_options: []
70
+
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirements:
90
+ - ImageMagick v6 or better, Inkscape
91
+ rubyforge_project:
92
+ rubygems_version: 1.3.7
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: A RadioVIS slide generator
96
+ test_files: []
97
+