stupeflix-client 0.0.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,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NjhmNGM1ZmE1MzBjYTNhYjBjZDM3ZDA4YmEzMjc3ZDdlNjljNzIwMw==
5
+ data.tar.gz: !binary |-
6
+ YzMyMjlmNDk1MjYwMDU1NzAyYTk2OWUzODU2MDg3NDVmMjM5YjczZA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NjlkYzY0YjIwNDNhNDk2NTM3M2NmNDRmMzJjNDFmYjU2Mzc0YmI1ZWFkNzVj
10
+ NWYyMzVmNzcxN2ZjYWQzYzQ3ODRhMDcyN2VlYTAwY2E1NDI4ZDY3YWVkYWVj
11
+ NDBmNGJkOTVmOGViNzY5YmM2YjUzY2RiZGIxMzU0M2Y1NWZiOTY=
12
+ data.tar.gz: !binary |-
13
+ MDYyNzhkMzgwYjZiNzQ2NzNmOWFkOTVlZmYwY2IwN2I2ZDczZWNiMzZhNTAw
14
+ NzFiMzk2ZjFjMzNiOWUxYWI2MGY0MTc5NzBlZDFmOTI3OGUyOTBkMWJiZWRi
15
+ ZDFkYzc1Zjc0MjkwZWZjYTBiNDA5MjlkNjM4YzljYjAyYjA2OGY=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in stupeflix.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Andrea Pavoni
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ # Stupeflix API Ruby client
2
+
3
+ This is a gem-ified version of the [Official Stupeflix API Client](https://github.com/Stupeflix/Stupeflix-API-Client).
4
+ At the moment, I've only took the Ruby code and packed it _as is_ into a gem to get it more usable inside a Ruby/Rails project.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'stupeflix'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install stupeflix
19
+
20
+ ## Usage
21
+
22
+ See the [examples/](https://github.com/apeacox/stupeflix-client/tree/master/examples) folder.
23
+
24
+ ## TODO
25
+
26
+ * add tests (perhaps rspec or minitest/spec would be perfect)
27
+ * refactor code to get closer to the _ruby way_
28
+ * add better builder (I'd use 'builder' gem to keep dependencies simple)
29
+
30
+ ## Contributing
31
+
32
+ 1. Fork it
33
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
34
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
35
+ 4. Push to the branch (`git push origin my-new-feature`)
36
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,223 @@
1
+ require_relative "conf"
2
+ require "stupeflix"
3
+ require "Time"
4
+
5
+ class StupeflixTest
6
+ # including the Stupeflix module here, to avoid to specify namespece
7
+ include Stupeflix
8
+
9
+ def initialize()
10
+ # Create the client to access the API
11
+ # You can safely assume that $stupeflixHost is nil
12
+ @client = StupeflixClient.new($stupeflixAccessKey, $stupeflixSecretKey)
13
+
14
+ # Set the name for the resource to be created
15
+ # These names are alphanumerical, and can be set to whatever you want
16
+ @user = "test"
17
+ @resource = "resource" + $dateString
18
+
19
+ # Configuration for s3 retries
20
+ @s3Retries = 5
21
+ @s3Wait = 1
22
+ # Set this to true if you want to see the status of videos being generated
23
+ @debug = false
24
+ end
25
+
26
+ def uploadsCreate(profileName)
27
+ user = @user
28
+ resource = @resource
29
+ # Array of uploads to be filled in
30
+ uploads = []
31
+
32
+ # Default upload creation : will store to the stupeflix s3 bucket
33
+ # This is not mandatory, just a easy way to store temporarily the result of the video generation.
34
+ uploads += [StupeflixDefaultUpload.new()]
35
+
36
+ # YouTube upload creation, if correct information was entered in conf.rb
37
+ if $youtubeLogin != nil
38
+ # Create sample youtube information
39
+ tags = ["these","are","my","tags"].join(",")
40
+ youtubeInfo = {"title" => "Upload test " + $dateString,
41
+ "description"=> "Upload test description" + $dateString,
42
+ "tags"=>tags,
43
+ "channels"=>"Tech",
44
+ "acl"=>"public",
45
+ "location"=>"49,-3"}
46
+
47
+ youtubeMeta = StupeflixMeta.new(youtubeInfo)
48
+
49
+ # There is no currently notification
50
+ youtubeNotify = nil
51
+ uploads += [StupeflixYoutubeUpload.new($youtubeLogin, $youtubePassword, youtubeMeta, youtubeNotify)]
52
+ end
53
+
54
+ # S3 Upload creation : upload to your own S3 bucket
55
+ if $s3AccessKey != nil
56
+ s3resource = "%s/%s/%s" % [user, resource, "iphone"]
57
+ # Create s3 upload settings
58
+ uploads += [StupeflixS3Upload.new(bucket=$s3Bucket, s3resource, $s3AccessKey, $s3SecretKey)]
59
+ end
60
+
61
+ # HTTP Uploads creation : POST and PUT
62
+ if $httpUploadPrefix != nil
63
+ # Create http POST upload settings
64
+ postURL = $httpUploadPrefix + "post/%s/%s/%s" % [user, resource, profileName]
65
+ uploads += [StupeflixHttpPOSTUpload.new(postURL)]
66
+
67
+ # Create http PUT upload settings
68
+ putURL = $httpUploadPrefix + "put/%s/%s/%s" % [user, resource, profileName]
69
+ uploads += [StupeflixHttpPUTUpload.new(putURL)]
70
+ end
71
+ return uploads
72
+ end
73
+
74
+ def availableKey(rank, suffix = "id")
75
+ return "available-" + rank.to_s() + "-" + suffix
76
+ end
77
+
78
+ # Check that all went fine, until every upoads is finished (or went on error)
79
+ def waitForCompletion(uploadCount)
80
+ error = false
81
+ # Then wait for the generation to complete
82
+ available = false
83
+ status = nil
84
+ error = false
85
+
86
+ while not available and not error
87
+ # Retrieve an array of status for every profiles for user and resource
88
+ status = @client.getStatus(@user, @resource, nil)
89
+ # Variable to test if every profile is available
90
+ for s in status
91
+ if @debug
92
+ puts s
93
+ end
94
+ available = true
95
+ for id in 0..uploadCount - 1
96
+ availableKey = availableKey(id)
97
+ availableType = availableKey(id, "type")
98
+ if not s["status"].has_key?(availableKey)
99
+ if @debug
100
+ puts "upload #" + id.to_s() + " not yet ready for profile " + s["profile"]
101
+ end
102
+ available = false
103
+ break
104
+ else
105
+ if @debug
106
+ puts "upload #" + id.to_s() + " '" + s["status"][availableType] + "' ready for profile " + s["profile"]
107
+ end
108
+ end
109
+ end
110
+ if s["status"]["status"] == "error"
111
+ error = true
112
+ break
113
+ end
114
+ end
115
+ sleep(5)
116
+ end
117
+ # if available if false, that means that an error occurred
118
+ return available, status
119
+ end
120
+
121
+ #Sometimes we have to wait for s3 to make the content available (this is in the Amazon S3 spec). This function is built to do just that.
122
+ def s3WaitLoop()
123
+ s3Wait = @s3Wait
124
+ for i in 0..@s3Retries - 1
125
+ begin
126
+ yield nil
127
+ rescue
128
+ if (i + 1) == @s3Retries
129
+ raise $!
130
+ else
131
+ sleep(s3Wait)
132
+ s3Wait *= 2
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ #This is the main function for creating videos, with main calls to the Stupeflix API.
139
+ def run()
140
+ profileNames = ["iphone"] # You can add profiles there : "flash-small, quicktime, dvd ..."
141
+ profileArray = []
142
+ for profileName in profileNames
143
+ uploads = uploadsCreate(profileName)
144
+ # Create a new profile.
145
+ profileArray += [StupeflixProfile.new(profileName, uploads = uploads)]
146
+ end
147
+
148
+ # Notification is not configured there : this consists in a series of HTTP POST ping requests to your own server.
149
+ # This is much more powerful than polling the API as demonstrated in function waitForCompletion.
150
+ notify = nil
151
+ # Uncomment this line if you want to test notification. StatusRegexp is used to filter notification sent to your server.
152
+ # Here, only final "available message" would be sent
153
+ # notify = StupeflixNotify.new(url = "http://myserver.com/mypath", statusRegexp = "available")
154
+
155
+ # Create the set of profiles to be created
156
+ profiles = StupeflixProfileSet.new(profileArray, meta = nil, notify = notify)
157
+
158
+ # This is only used to give proper names to output files (file names are appended with a proper extension)
159
+ extensions = ["mp4"] # flv ...
160
+
161
+ # Calls to the API start there
162
+
163
+ # First send the movie definition file to the service. (see sample movie.xml in this directory)
164
+ @client.sendDefinition(@user, @resource, $filename)
165
+
166
+ # Then launch the generation, using the configuration we have built earlier
167
+ @client.createProfiles(@user, @resource, profiles)
168
+
169
+ # Poll the API, waiting for completion
170
+ available, status = waitForCompletion(uploadCount = uploads.length)
171
+
172
+ # Check if everything went fine
173
+ if not available
174
+ # Something went bad: at least some part of the task was not complete, but some may still have or even was uploaded.
175
+ # This may happen for example if upload to youtube failed but upload to your own server succeeded.
176
+ # an error occured, the status will give more information
177
+ s = ""
178
+ status.each {|element|
179
+ s += [element["accesskey"], element["user"], element["resource"], element["profile"]].join(",")
180
+ s += ":\n "
181
+ element["status"].sort.each { |key, value|
182
+ s += "#{key} => #{value}, \n "
183
+ }
184
+ s += "\n"
185
+ }
186
+ raise s
187
+ end
188
+
189
+ # Download all profiles
190
+ i = 0
191
+ profileNames.each do |p|
192
+ # Print the profile url were the video can be found
193
+ puts "movie url= " + @client.getProfileUrl(@user, @resource, p)
194
+ movieName = "%smovie.%s" % [p, extensions[i]]
195
+ puts "Download movie to file " + movieName
196
+ s3WaitLoop { |n|
197
+ @client.getProfile(@user, @resource, p, movieName)
198
+ }
199
+
200
+ # Download the profile thumb url
201
+ thumbName = "thumb_%s.jpg" % p
202
+ puts "Download movie thumb to file " + thumbName
203
+ s3WaitLoop { |n|
204
+ @client.getProfileThumb(@user, @resource, p, thumbName)
205
+ }
206
+
207
+ i += 1
208
+ end
209
+
210
+ puts "Test succeeded."
211
+ end
212
+ end
213
+
214
+
215
+ # Test if the keys were set
216
+ if $stupeflixAccessKey == nil
217
+ puts "ERROR : Please fill in key information in conf.rb"
218
+ exit(0)
219
+ end
220
+
221
+ test = StupeflixTest.new()
222
+ test.run()
223
+
@@ -0,0 +1,25 @@
1
+ # required API keys
2
+ $stupeflixAccessKey = ENV["STUPEFLIX_KEY"]
3
+ $stupeflixSecretKey = ENV["STUPEFLIX_SECRET"]
4
+
5
+ # These are optional variables, by default read from the environement variables,
6
+ # but you can too override them with your own credentials directly
7
+
8
+ $youtubeLogin = ENV["YOUTUBE_LOGIN"]
9
+ $youtubePassword = ENV["YOUTUBE_PASSWORD"]
10
+ $s3AccessKey = ENV["S3_ACCESS_KEY"]
11
+ $s3SecretKey = ENV["S3_SECRET_KEY"]
12
+ $s3Bucket = ENV["S3_BUCKET"]
13
+ $httpUploadPrefix = ENV["HTTP_UPLOAD_PREFIX"]
14
+
15
+ if ENV["STUPEFLIX_TEST_TIME"] == nil
16
+ t = Time.now
17
+ $dateString = t.strftime("%Ya%ma%da%Ha%Ma%S")
18
+ else
19
+ $dateString = ENV["STUPEFLIX_TEST_TIME"]
20
+ end
21
+
22
+ $filename = ENV["STUPEFLIX_MOVIE"]
23
+ if $filename == nil
24
+ $filename = "movie.xml"
25
+ end
@@ -0,0 +1,348 @@
1
+ <movie service="craftsman-1.0">
2
+ <body>
3
+ <!-- Top level stack: contains the soundtrack and the video itself-->
4
+ <stack>
5
+ <!-- Add a movie wide soundtrack: duration of the soundtrack is dependant on the up node (stack) using duration =".." -->
6
+ <!-- Silence, L'autre endroit: http://www.jamendo.com/fr/album/830 -->
7
+ <audio filename="http://assets.stupeflix.com/code/ebusinessvideo/soundtracks/Realite.mp3" volume="0.5" fadeout="4.0" duration=".."/>
8
+ <!-- Top level sequence -->
9
+ <sequence>
10
+ <!-- First part: sequence of two background images with a single text overlay -->
11
+ <stack duration="6.0">
12
+ <!-- Sequence of images -->
13
+ <sequence>
14
+ <!-- Classical kenburns effect -->
15
+ <effect type="kenburns" duration="3.5">
16
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/file_11.jpg">
17
+ <!-- Small trick: Add a small white frame with no transparency to the image to reduce it at rendering time => no edit of the image is needed -->
18
+ <filter type="frame" width="0.1" color="#FFFFFFFF"/>
19
+ </image>
20
+ </effect>
21
+ <!-- Transition between the two background images -->
22
+ <transition type="move" duration="1.0"/>
23
+ <!-- Same effect for the second background image -->
24
+ <effect type="kenburns" duration="3.5">
25
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/file_10.jpg">
26
+ <filter type="frame" width="0.1" color="#FFFFFFFF"/>
27
+ </image>
28
+ </effect>
29
+ </sequence>
30
+ <!-- Text overlay with shadow: shadow is currently created duplicating the text in black with some blur added-->
31
+ <!-- NB: newlines are currently ignored at the start and end of text, so this xml is indented to ease the explanations, but you should not add extra newlines in
32
+ your own xml. -->
33
+
34
+ <!-- Text like placement using left, bottom and height. Apply black font color as this is the shadow. -->
35
+ <text type="zone" left="0.09" bottom="0.13" height="0.18" fontcolor="#000000">
36
+ EOS 50D:
37
+ <!-- Animator: grow will slowly increase text size (implemented by moving the text near the camera in 3D)-->
38
+ <animator type="grow" />
39
+ <!-- Make the text appear in 1 second, sliding from the left -->
40
+ <animator type="slide-in" direction="left" duration="1.0"/>
41
+ <!-- Make the text disappear in 1 second, sliding from the left -->
42
+ <animator type="slide-out" direction="left" duration="1.0"/>
43
+ <!-- Make the text slowly move to improve attractiveness -->
44
+ <animator type="slide" direction="left" duration="6.0"/>
45
+ <!-- Text will appear in 1.5s from totally transparent, to totally opaque (in addition to the slide-in effect) -->
46
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.5"/>
47
+ <!-- Text will disappear in 1.5s from totally opaque, to totally transparent (in addition to the slide-out effect) -->
48
+ <!-- Note the margin-end that will fix the effect to the end of text effect. (just like a CSS property, but for time) -->
49
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.5"/>
50
+ <!-- Add a blur effect to soften shadow edges -->
51
+ <filter type="blur" diameter="2.0"/>
52
+ </text>
53
+ <!-- Text itself: same xml, with slight variations on position and color-->
54
+ <!-- Text color is almost white -->
55
+ <text type="zone" left="0.1" bottom="0.12" height="0.18" fontcolor="#DDDDDD">
56
+ EOS 50D:
57
+ <animator type="grow" duration="6.0"/>
58
+ <animator type="slide-in" direction="left" duration="1.0"/>
59
+ <animator type="slide-out" direction="left" duration="1.0"/>
60
+ <animator type="slide" direction="left" duration="6.0"/>
61
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.5"/>
62
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.5"/>
63
+ </text>
64
+ <!-- Same tricks for the rest of text -->
65
+ <text type="zone" left="0.09" bottom="0.01" height="0.18" fontcolor="#000000">
66
+ Explore photography
67
+ <animator type="grow" duration="6.0"/>
68
+ <animator type="slide-in" direction="right" duration="1.0"/>
69
+ <animator type="slide-out" direction="right" duration="1.0"/>
70
+ <animator type="slide" direction="right" duration="6.0"/>
71
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.5"/>
72
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.5"/>
73
+ <filter type="blur" diameter="2.0"/>
74
+ </text>
75
+ <text type="zone" left="0.1" bottom="0.0" height="0.18" fontcolor="#DDDDDD">
76
+ Explore photography
77
+ <animator type="grow" duration="6.0"/>
78
+ <animator type="slide-in" direction="right" duration="1.0"/>
79
+ <animator type="slide-out" direction="right" duration="1.0"/>
80
+ <animator type="slide" direction="right" duration="6.0"/>
81
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.5"/>
82
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.5"/>
83
+ </text>
84
+ </stack>
85
+ <!-- Transition -->
86
+ <transition type="cube" duration="1.0"/>
87
+ <!-- Second camera image set: basically same set of effects-->
88
+ <stack duration="6.0">
89
+ <sequence>
90
+ <effect type="kenburns" duration="3.5">
91
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/file_13.jpg">
92
+ <filter type="frame" width="0.1" color="#FFFFFFFF"/>
93
+ </image>
94
+ </effect>
95
+ <transition type="under" duration="1.0"/>
96
+ <effect type="kenburns" duration="3.5">
97
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/file_07.jpg">
98
+ <filter type="frame" width="0.1" color="#FFFFFFFF"/>
99
+ </image>
100
+ </effect>
101
+ </sequence>
102
+ <text type="zone" left="0.09" bottom="0.13" height="0.18" fontcolor="#000000">
103
+ 15.1 Megapixels,
104
+ <animator type="grow" duration="6.0"/>
105
+ <animator type="slide-in" direction="left" duration="1.0"/>
106
+ <animator type="slide-out" direction="left" duration="1.0"/>
107
+ <animator type="slide" direction="left" duration="6.0"/>
108
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.5"/>
109
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.5"/>
110
+ <filter type="blur" diameter="2.0"/>
111
+ </text>
112
+ <text type="zone" left="0.1" bottom="0.12" height="0.18" fontcolor="#DDDDDD">
113
+ 15.1 Megapixels,
114
+ <animator type="grow" duration="6.0"/>
115
+ <animator type="slide-in" direction="left" duration="1.0"/>
116
+ <animator type="slide-out" direction="left" duration="1.0"/>
117
+ <animator type="slide" direction="left" duration="6.0"/>
118
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.5"/>
119
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.5"/>
120
+ </text>
121
+ <text type="zone" left="0.09" bottom="0.01" height="0.18" fontcolor="#000000">
122
+ Bright 3" LCD
123
+ <animator type="grow" duration="6.0"/>
124
+ <animator type="slide-in" direction="right" duration="1.0"/>
125
+ <animator type="slide-out" direction="right" duration="1.0"/>
126
+ <animator type="slide" direction="right" duration="6.0"/>
127
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.5"/>
128
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.5"/>
129
+ <filter type="blur" diameter="2.0"/>
130
+ </text>
131
+ <text type="zone" left="0.1" bottom="0.0" height="0.18" fontcolor="#DDDDDD">
132
+ Bright 3" LCD
133
+ <animator type="grow" duration="6.0"/>
134
+ <animator type="slide-in" direction="right" duration="1.0"/>
135
+ <animator type="slide-out" direction="right" duration="1.0"/>
136
+ <animator type="slide" direction="right" duration="6.0"/>
137
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.5"/>
138
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.5"/>
139
+ </text>
140
+ </stack>
141
+ <transition type="move" duration="1.0"/>
142
+ <!-- Third Image Set : single image with a set of logo added to the right -->
143
+ <stack duration="6.0">
144
+ <stack duration="6.0">
145
+ <sequence>
146
+ <effect type="kenburns" duration="6.0">
147
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/file_12.jpg">
148
+ <filter type="frame" width="0.1" color="#FFFFFFFF"/>
149
+ </image>
150
+ </effect>
151
+ </sequence>
152
+ </stack>
153
+ <!-- Sets of small feature logo on the right: number of megapixels, lcd size, digic logo etc ...-->
154
+ <!-- overlay position is CSS like properties, just like for text: top and right margin + height, in fraction of height / width -->
155
+ <overlay top="0.05" right="0.02" height="0.16">
156
+ <!-- Base Image -->
157
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/megapixels.gif"/>
158
+ <!-- Apparition animation : slide-in, and time margin -->
159
+ <animator duration="1.0" type="slide-in" margin-start="0.0"/>
160
+ <!-- Disparition animation : slide-out, time margin, and direction -->
161
+ <animator duration="1.0" type="slide-out" margin-end="0.8" direction="right" />
162
+ </overlay>
163
+ <overlay top="0.23" right="0.02" height="0.16">
164
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/lcd_size.gif"/>
165
+ <!-- margin-start is increased from an image to the next one, to shift apparition of logos -->
166
+ <animator duration="1.0" type="slide-in" margin-start="0.5"/>
167
+ <!-- margin-end is dereased from an image to the next one, to shift disparition of logos -->
168
+ <animator duration="1.0" type="slide-out" margin-end="0.6" direction="right"/>
169
+ </overlay>
170
+ <!-- So on ... -->
171
+ <overlay top="0.41" right="0.02" height="0.16">
172
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/digic4.gif"/>
173
+ <animator duration="1.0" type="slide-in" margin-start="1.0"/>
174
+ <animator duration="1.0" type="slide-out" margin-end="0.4" direction="right"/>
175
+ </overlay>
176
+ <overlay top="0.59" right="0.02" height="0.16">
177
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/liveview.gif"/>
178
+ <animator duration="1.0" type="slide-in" margin-start="1.5"/>
179
+ <animator duration="1.0" type="slide-out" margin-end="0.2" direction="right" />
180
+ </overlay>
181
+ <overlay top="0.77" right="0.02" height="0.16">
182
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/pictbridge.gif"/>
183
+ <animator duration="1.0" type="slide-in" margin-start="2.0"/>
184
+ <animator duration="1.0" type="slide-out" margin-end="0.0" direction="right"/>
185
+ </overlay>
186
+ </stack>
187
+ <transition type="crossfade" duration="0.3"/>
188
+ <!-- Fourth part: slideshow of photos take with the camera -->
189
+ <stack duration="12.0">
190
+ <!-- Multi image effect : several images on the same scene, with rotation between each couple.-->
191
+ <effect type="rotator" duration="12.0">
192
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/2008-10-14_14-09-18.jpg">
193
+ <filter type="borderBlur" transWidth="0.05" width="0.0"/>
194
+ </image>
195
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/2008-10-10_17-08-27.jpg">
196
+ <filter type="borderBlur" transWidth="0.05" width="0.0"/>
197
+ </image>
198
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/2008-10-10_15-55-14.jpg">
199
+ <filter type="borderBlur" transWidth="0.05" width="0.0"/>
200
+ </image>
201
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/2008-10-10_14-03-50.jpg">
202
+ <filter type="borderBlur" transWidth="0.05" width="0.0"/>
203
+ </image>
204
+ </effect>
205
+ <!-- Same shadow / text effects as previously -->
206
+ <text type="zone" left="0.09" bottom="0.11" height="0.13" fontcolor="#000000">
207
+ Improved noise reduction:
208
+ <animator type="grow" duration="12.0"/>
209
+ <animator type="slide-in" direction="left" duration="2.0"/>
210
+ <animator type="slide-out" direction="left" duration="2.0"/>
211
+ <animator type="slide" direction="left" duration="12.0"/>
212
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="3.0"/>
213
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="3.0"/>
214
+ <filter type="blur" diameter="2.0"/>
215
+ </text>
216
+ <text type="zone" left="0.1" bottom="0.1" height="0.13" fontcolor="#FFFFFF">
217
+ Improved noise reduction:
218
+ <animator type="grow" duration="12.0"/>
219
+ <animator type="slide-in" direction="left" duration="2.0"/>
220
+ <animator type="slide-out" direction="left" duration="2.0"/>
221
+ <animator type="slide" direction="left" duration="12.0"/>
222
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="3.0"/>
223
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="3.0"/>
224
+ </text>
225
+ <text type="zone" left="0.09" bottom="0.03" height="0.13" fontcolor="#000000">
226
+ exceptional image quality
227
+ <animator type="grow" duration="12.0"/>
228
+ <animator type="slide-in" direction="right" duration="2.0"/>
229
+ <animator type="slide-out" direction="right" duration="2.0"/>
230
+ <animator type="slide" direction="right" duration="12.0"/>
231
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="3.0"/>
232
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="3.0"/>
233
+ <filter type="blur" diameter="2.0"/>
234
+ </text>
235
+ <text type="zone" left="0.1" bottom="0.02" height="0.13" fontcolor="#FFFFFF">
236
+ exceptional image quality
237
+ <animator type="grow" duration="12.0"/>
238
+ <animator type="slide-in" direction="right" duration="2.0"/>
239
+ <animator type="slide-out" direction="right" duration="2.0"/>
240
+ <animator type="slide" direction="right" duration="12.0"/>
241
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="3.0"/>
242
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="3.0"/>
243
+ </text>
244
+ </stack>
245
+ <transition type="crossfade" duration="1.0"/>
246
+ <!-- Fifth part : first journalist comment -->
247
+ <stack duration="4.0">
248
+ <text type="zone" left="0.09" bottom="0.53" height="0.18" fontcolor="#000000">
249
+ "High-speed and perfect quality"
250
+ <animator type="grow" duration="4.0"/>
251
+ <animator type="slide-in" direction="left" duration="0.666666666667"/>
252
+ <animator type="slide-out" direction="left" duration="0.666666666667"/>
253
+ <animator type="slide" direction="left" duration="4.0"/>
254
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.0"/>
255
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.0"/>
256
+ <filter type="blur" diameter="2.0"/>
257
+ </text>
258
+ <text type="zone" left="0.1" bottom="0.52" height="0.18" fontcolor="#DDDDDD">
259
+ "High-speed and perfect quality"
260
+ <animator type="grow" duration="4.0"/>
261
+ <animator type="slide-in" direction="left" duration="0.666666666667"/>
262
+ <animator type="slide-out" direction="left" duration="0.666666666667"/>
263
+ <animator type="slide" direction="left" duration="4.0"/>
264
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.0"/>
265
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.0"/>
266
+ </text>
267
+ <text type="zone" left="0.09" bottom="0.41" height="0.18" fontcolor="#000000">
268
+ The Photographer
269
+ <animator type="grow" duration="4.0"/>
270
+ <animator type="slide-in" direction="right" duration="0.666666666667"/>
271
+ <animator type="slide-out" direction="right" duration="0.666666666667"/>
272
+ <animator type="slide" direction="right" duration="4.0"/>
273
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.0"/>
274
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.0"/>
275
+ <filter type="blur" diameter="2.0"/>
276
+ </text>
277
+ <text type="zone" left="0.1" bottom="0.4" height="0.18" fontcolor="#DDDDDD">
278
+ The Photographer
279
+ <animator type="grow" duration="4.0"/>
280
+ <animator type="slide-in" direction="right" duration="0.666666666667"/>
281
+ <animator type="slide-out" direction="right" duration="0.666666666667"/>
282
+ <animator type="slide" direction="right" duration="4.0"/>
283
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.0"/>
284
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.0"/>
285
+ </text>
286
+ </stack>
287
+ <transition type="crossfade" duration="1.0"/>
288
+ <!-- Sixth part : second journalist comment -->
289
+ <stack duration="4.0">
290
+ <text type="zone" left="0.09" bottom="0.33" height="0.18" fontcolor="#000000">
291
+ "Feature-packed and affordable."
292
+ <animator type="grow" duration="4.0"/>
293
+ <animator type="slide-in" direction="left" duration="0.666666666667"/>
294
+ <animator type="slide-out" direction="left" duration="0.666666666667"/>
295
+ <animator type="slide" direction="left" duration="4.0"/>
296
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.0"/>
297
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.0"/>
298
+ <filter type="blur" diameter="2.0"/>
299
+ </text>
300
+ <text type="zone" left="0.1" bottom="0.32" height="0.18" fontcolor="#DDDDDD">
301
+ "Feature-packed and affordable."
302
+ <animator type="grow" duration="4.0"/>
303
+ <animator type="slide-in" direction="left" duration="0.666666666667"/>
304
+ <animator type="slide-out" direction="left" duration="0.666666666667"/>
305
+ <animator type="slide" direction="left" duration="4.0"/>
306
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.0"/>
307
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.0"/>
308
+ </text>
309
+ <text type="zone" left="0.09" bottom="0.21" height="0.18" fontcolor="#000000">
310
+ Camera online
311
+ <animator type="grow" duration="4.0"/>
312
+ <animator type="slide-in" direction="right" duration="0.666666666667"/>
313
+ <animator type="slide-out" direction="right" duration="0.666666666667"/>
314
+ <animator type="slide" direction="right" duration="4.0"/>
315
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.0"/>
316
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.0"/>
317
+ <filter type="blur" diameter="2.0"/>
318
+ </text>
319
+ <text type="zone" left="0.1" bottom="0.2" height="0.18" fontcolor="#DDDDDD">
320
+ Camera online
321
+ <animator type="grow" duration="4.0"/>
322
+ <animator type="slide-in" direction="right" duration="0.666666666667"/>
323
+ <animator type="slide-out" direction="right" duration="0.666666666667"/>
324
+ <animator type="slide" direction="right" duration="4.0"/>
325
+ <filter type="alpha" alphaStart="0.0" alphaEnd="1.0" duration="1.0"/>
326
+ <filter type="alpha" margin-end="0.0" alphaStart="1.0" alphaEnd="0.0" duration="1.0"/>
327
+ </text>
328
+ </stack>
329
+ <transition type="crossfade" duration="0.5"/>
330
+ <!-- Finally the Stupeflix end animation -->
331
+ <effect duration="1.6" type="none">
332
+ <!-- A single blank image -->
333
+ <image color="#FFFFFF"/>
334
+ </effect>
335
+ <transition type="move" duration="1" direction="up"/>
336
+ <!-- First logo, without subtitle-->
337
+ <effect duration="2.5" type="none">
338
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/logo1.jpg"/>
339
+ </effect>
340
+ <transition type="circle" duration="1"/>
341
+ <!-- Identical logo, with subtitile => transition will make it appear -->
342
+ <effect duration="3" type="none">
343
+ <image filename="http://assets.stupeflix.com/code/ebusinessvideo/images/logo2.jpg"/>
344
+ </effect>
345
+ </sequence>
346
+ </stack>
347
+ </body>
348
+ </movie>