cinesync 0.9.6
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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.markdown +53 -0
- data/Rakefile +59 -0
- data/Samples/Export Notes to CSV.rb +33 -0
- data/VERSION +1 -0
- data/cineSync Session v3 Schema.rnc +164 -0
- data/cinesync.gemspec +76 -0
- data/lib/cinesync/color_grading.rb +70 -0
- data/lib/cinesync/event_handler.rb +67 -0
- data/lib/cinesync/frame_annotation.rb +19 -0
- data/lib/cinesync/mask.rb +41 -0
- data/lib/cinesync/media_file.rb +122 -0
- data/lib/cinesync/pixel_ratio.rb +22 -0
- data/lib/cinesync/play_range.rb +21 -0
- data/lib/cinesync/session.rb +24 -0
- data/lib/cinesync/ui/standard_additions.rb +473 -0
- data/lib/cinesync/ui/win32_save_file_dialog.rb +95 -0
- data/lib/cinesync/ui.rb +68 -0
- data/lib/cinesync/xml.rb +436 -0
- data/lib/cinesync/zoom_state.rb +18 -0
- data/lib/cinesync.rb +46 -0
- data/test/helper.rb +10 -0
- metadata +128 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jonathon Mah
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# cineSync RubyGem
|
2
|
+
|
3
|
+
## Quick Start
|
4
|
+
### Installing the Gem
|
5
|
+
|
6
|
+
gem install cinesync
|
7
|
+
|
8
|
+
### Creating a session file
|
9
|
+
|
10
|
+
#!/usr/bin/ruby
|
11
|
+
require 'rubygems'
|
12
|
+
require 'cinesync'
|
13
|
+
|
14
|
+
s = CineSync::Session.new
|
15
|
+
s.media << CineSync::MediaFile.new("http://cinesync.com/files/sample_qt.mov")
|
16
|
+
s.media << CineSync::MediaFile.new("/System/Library/Compositions/Fish.mov")
|
17
|
+
File.open("/tmp/session.csc", "w") {|f| f << s.to_xml }
|
18
|
+
|
19
|
+
|
20
|
+
### Running in response to an event
|
21
|
+
|
22
|
+
#!/usr/bin/ruby
|
23
|
+
require 'rubygems'
|
24
|
+
require 'cinesync'
|
25
|
+
|
26
|
+
CineSync.event_handler do |evt|
|
27
|
+
puts "cineSync online with key #{evt.session_key}" unless evt.offline?
|
28
|
+
puts "Playlist has #{evt.session.media.length} files"
|
29
|
+
active_file = evt.session.media.find {|m| m.active? }
|
30
|
+
puts "Currently viewing #{active_file.name}" if active_file
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
## Scripting Overview
|
35
|
+
|
36
|
+
cineSync 3.0 has new support for calling user-defined scripts. (Scripting requires a cineSync Pro account.) These are configured in cineSync's preferences. Scripts can be run from the Session > Run Script menu, and can also be set to automatically trigger on certain events. When triggered, the script will be passed some arguments about the current environment (the current session key, where frames are being saved, and the save frame format), and the current session will be serialized and sent to it through standard I/O.
|
37
|
+
|
38
|
+
Additionally, a script can be run from a different application, set up a session, and send it to cineSync.
|
39
|
+
|
40
|
+
(More info to come: URLs etc)
|
41
|
+
|
42
|
+
|
43
|
+
## Links
|
44
|
+
|
45
|
+
* [cineSync homepage](http://cinesync.com/)
|
46
|
+
|
47
|
+
## Files
|
48
|
+
|
49
|
+
* `cineSync Session v3 Schema.rnc`: Session XML schema in [RELAX NG](http://relaxng.org/) Compact syntax
|
50
|
+
|
51
|
+
## Copyright
|
52
|
+
|
53
|
+
Copyright (c) 2010 Rising Sun Research Pty Ltd. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "cinesync"
|
8
|
+
gem.summary = %Q{Library for scripting the cineSync collaborative video review tool}
|
9
|
+
gem.description = <<-EOF
|
10
|
+
This gem provides a Ruby interface to the cineSync session file format,
|
11
|
+
which is used by cineSync's scripting system. Use it to integrate
|
12
|
+
cineSync into your workflow.
|
13
|
+
EOF
|
14
|
+
gem.email = ["jmah@cinesync.com", "info@cinesync.com"]
|
15
|
+
gem.homepage = "http://github.com/jmah/cinesync"
|
16
|
+
gem.authors = ["Jonathon Mah", "Rising Sun Research"]
|
17
|
+
gem.add_dependency "activesupport", ">= 2.3"
|
18
|
+
gem.add_dependency "andand", ">= 1.3.1"
|
19
|
+
gem.add_dependency "builder", ">= 2.1"
|
20
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
21
|
+
end
|
22
|
+
Jeweler::GemcutterTasks.new
|
23
|
+
rescue LoadError
|
24
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'test'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'test'
|
38
|
+
test.pattern = 'test/**/test_*.rb'
|
39
|
+
test.verbose = true
|
40
|
+
end
|
41
|
+
rescue LoadError
|
42
|
+
task :rcov do
|
43
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
task :test => :check_dependencies
|
48
|
+
|
49
|
+
task :default => :test
|
50
|
+
|
51
|
+
require 'rake/rdoctask'
|
52
|
+
Rake::RDocTask.new do |rdoc|
|
53
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
54
|
+
|
55
|
+
rdoc.rdoc_dir = 'rdoc'
|
56
|
+
rdoc.title = "cinesync #{version}"
|
57
|
+
rdoc.rdoc_files.include('README*')
|
58
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
59
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# This script reads the current session from cineSync and converts all notes
|
4
|
+
# made on the session, each media file, and each frame to a CSV file. This file
|
5
|
+
# can then be processed by other scripts or opened in a spreadsheet program
|
6
|
+
# (Microsoft Excel, OpenOffice.org Calc, Apple Numbers).
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'cinesync'
|
11
|
+
require 'csv'
|
12
|
+
|
13
|
+
|
14
|
+
CineSync.event_handler do |evt|
|
15
|
+
name = "Notes from #{evt.offline? ? "offline session" : evt.session_key}.csv"
|
16
|
+
path = CineSync::UI.prompt_to_save("Save CSV file as:", name)
|
17
|
+
exit if path.nil? # User cancelled
|
18
|
+
|
19
|
+
CSV.open(path, "w") do |csv|
|
20
|
+
csv << ["Media File", "Frame", "Notes"] # Header row
|
21
|
+
|
22
|
+
csv << ["", "[Session]", evt.session.notes] unless evt.session.notes.empty?
|
23
|
+
|
24
|
+
evt.session.media.each do |media|
|
25
|
+
csv << [media.name, "[Media File]", media.notes] unless media.notes.empty?
|
26
|
+
media.annotations.keys.sort.each do |frame|
|
27
|
+
ann = media.annotations[frame]
|
28
|
+
next if ann.notes.empty?
|
29
|
+
csv << [media.name, frame, ann.notes]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.9.6
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# ==== Namespace ====
|
2
|
+
default namespace csc = "http://www.cinesync.com/ns/session/3.0"
|
3
|
+
|
4
|
+
# ==== Data types ====
|
5
|
+
tBool = "true" | "false"
|
6
|
+
tPositiveFloat = xsd:float { minExclusive = "0" }
|
7
|
+
tFrameNumber = xsd:integer { minInclusive = "1" }
|
8
|
+
tAlpha = xsd:float { minInclusive = "0" maxInclusive = "1" }
|
9
|
+
tColorOff = xsd:float { minInclusive = "-0.2" maxInclusive = "0.2" }
|
10
|
+
tColorExp = xsd:float { minInclusive = "0.367879" maxInclusive = "2.718282" } # exp(-1) .. exp(1)
|
11
|
+
tUnitFloat = xsd:float { minInclusive = "-1" maxInclusive = "1" }
|
12
|
+
tColor = xsd:string { pattern = "#[0-9a-fA-F]{6}" }
|
13
|
+
tShortHash = xsd:string { length = "40" }
|
14
|
+
tFilePath = xsd:string { minLength = "1" }
|
15
|
+
tURL = xsd:string { minLength = "1" }
|
16
|
+
tStereoType = "side-by-side" | "composite" | "interlaced"
|
17
|
+
tPoint = xsd:float
|
18
|
+
|
19
|
+
|
20
|
+
# ==== Session format ====
|
21
|
+
start = eSession
|
22
|
+
|
23
|
+
eSession = element session {
|
24
|
+
attribute version { xsd:integer { minInclusive = "3" } } &
|
25
|
+
attribute sessionFeatures { "standard" | "pro" } &
|
26
|
+
attribute hashToken { string }? &
|
27
|
+
aUserData? &
|
28
|
+
eGroup* &
|
29
|
+
eNotes? &
|
30
|
+
eChat? &
|
31
|
+
eStereo? &
|
32
|
+
eMedia* }
|
33
|
+
|
34
|
+
MediaBase =
|
35
|
+
aUserData? &
|
36
|
+
attribute active { tBool }? &
|
37
|
+
attribute currentFrame { tFrameNumber }? &
|
38
|
+
eGroup* &
|
39
|
+
ePlayRange?
|
40
|
+
|
41
|
+
eMedia |= element media {
|
42
|
+
# Normal media file
|
43
|
+
MediaBase &
|
44
|
+
element name { xsd:string { minLength = "1" } } &
|
45
|
+
element locators { eLocator+ } &
|
46
|
+
eNotes? &
|
47
|
+
eZoomState? &
|
48
|
+
ePixelRatio? &
|
49
|
+
eMask? &
|
50
|
+
eColorGrading? &
|
51
|
+
eFrameAnnotation* }
|
52
|
+
|
53
|
+
eMedia |= element media {
|
54
|
+
# Group movie
|
55
|
+
MediaBase &
|
56
|
+
element groupMovie { eGroup } }
|
57
|
+
|
58
|
+
eLocator |= element path { tFilePath }
|
59
|
+
eLocator |= element shortHash { tShortHash }
|
60
|
+
eLocator |= element url { tURL }
|
61
|
+
|
62
|
+
ePlayRange = element playRange {
|
63
|
+
element inFrame { attribute value { tFrameNumber } } &
|
64
|
+
element outFrame { attribute value { tFrameNumber } } &
|
65
|
+
element playOnlyRange { aBoolValue } }
|
66
|
+
|
67
|
+
eZoomState = element zoomState {
|
68
|
+
element center { aXY } &
|
69
|
+
eScaleFactor }
|
70
|
+
|
71
|
+
ePixelRatio = element pixelRatio {
|
72
|
+
element source { aRatio } &
|
73
|
+
element target { aRatio } }
|
74
|
+
|
75
|
+
eMask = element mask {
|
76
|
+
aAlpha &
|
77
|
+
element center { aXY } &
|
78
|
+
element ratio { aRatio } &
|
79
|
+
eScaleFactor }
|
80
|
+
|
81
|
+
eNotes = element notes { text }
|
82
|
+
|
83
|
+
eChat = element chat { text }
|
84
|
+
|
85
|
+
eStereo = element stereo {
|
86
|
+
attribute enabled { tBool } &
|
87
|
+
attribute type { tStereoType } &
|
88
|
+
element anamorphic { aBoolValue }? &
|
89
|
+
element anaglyph { aBoolValue }? &
|
90
|
+
element grayscale { aBoolValue }? &
|
91
|
+
element separation {
|
92
|
+
attribute annotation { tUnitFloat } &
|
93
|
+
attribute image { tUnitFloat } }? }
|
94
|
+
|
95
|
+
eColorGrading = element colorGrading {
|
96
|
+
element offset {
|
97
|
+
attribute red { tColorOff } &
|
98
|
+
attribute green { tColorOff } &
|
99
|
+
attribute blue { tColorOff } }? &
|
100
|
+
|
101
|
+
element brightness {
|
102
|
+
attribute rgb { tColorExp } &
|
103
|
+
attribute red { tColorExp } &
|
104
|
+
attribute green { tColorExp } &
|
105
|
+
attribute blue { tColorExp } }? &
|
106
|
+
|
107
|
+
element saturation { attribute value { tColorExp } }? &
|
108
|
+
element gamma { attribute value { tColorExp } }? &
|
109
|
+
element contrast { attribute value { tColorExp } }? &
|
110
|
+
|
111
|
+
element linearToLog { aBoolValue }? &
|
112
|
+
element lutPath { attribute value { tFilePath } }? }
|
113
|
+
|
114
|
+
|
115
|
+
# ==== Frame annotations ====
|
116
|
+
eFrameAnnotation = element annotation {
|
117
|
+
attribute frame { tFrameNumber } &
|
118
|
+
eNotes? &
|
119
|
+
eObject* }
|
120
|
+
|
121
|
+
eObject |= element line { aObjectID? & aAlphaColor & LinePath }
|
122
|
+
eObject |= element erase { aObjectID? & ErasePath }
|
123
|
+
eObject |= element circle { aObjectID? & aAlphaColor & Rect }
|
124
|
+
eObject |= element arrow {
|
125
|
+
aObjectID? &
|
126
|
+
aAlphaColor &
|
127
|
+
Rect &
|
128
|
+
element special { aBoolValue }? &
|
129
|
+
element flipSide { aBoolValue }? &
|
130
|
+
element reverse { aBoolValue }? }
|
131
|
+
|
132
|
+
eObject |= element text {
|
133
|
+
aObjectID? &
|
134
|
+
Rect &
|
135
|
+
element background { aAlphaColor } &
|
136
|
+
element foreground { aAlphaColor } &
|
137
|
+
element p { text } }
|
138
|
+
|
139
|
+
|
140
|
+
# ==== Components ====
|
141
|
+
aAlpha = attribute alpha { tAlpha }
|
142
|
+
aAlphaColor = attribute color { tColor } & aAlpha
|
143
|
+
aBoolValue = attribute value { tBool }
|
144
|
+
aObjectID = attribute id { xsd:string { minLength = "1" } }
|
145
|
+
aUserData = attribute userData { text }
|
146
|
+
eGroup = element group { xsd:string { minLength = "1" } }
|
147
|
+
eScaleFactor = element scaleFactor { attribute value { tPositiveFloat } }
|
148
|
+
|
149
|
+
aRatio &= attribute width { tPositiveFloat }
|
150
|
+
aRatio &= attribute height { tPositiveFloat }
|
151
|
+
|
152
|
+
aXY &= attribute x { tPoint }
|
153
|
+
aXY &= attribute y { tPoint }
|
154
|
+
|
155
|
+
|
156
|
+
# ==== Object components ====
|
157
|
+
LinePath &= attribute thickness { tPositiveFloat }
|
158
|
+
LinePath &= element point { aXY & aAlpha? }+
|
159
|
+
|
160
|
+
ErasePath &= attribute thickness { tPositiveFloat }
|
161
|
+
ErasePath &= element point { aXY }+
|
162
|
+
|
163
|
+
Rect &= element startPoint { aXY }
|
164
|
+
Rect &= element endPoint { aXY }
|
data/cinesync.gemspec
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{cinesync}
|
8
|
+
s.version = "0.9.6"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Jonathon Mah", "Rising Sun Research"]
|
12
|
+
s.date = %q{2010-05-08}
|
13
|
+
s.description = %q{ This gem provides a Ruby interface to the cineSync session file format,
|
14
|
+
which is used by cineSync's scripting system. Use it to integrate
|
15
|
+
cineSync into your workflow.
|
16
|
+
}
|
17
|
+
s.email = ["jmah@cinesync.com", "info@cinesync.com"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"LICENSE",
|
20
|
+
"README.markdown"
|
21
|
+
]
|
22
|
+
s.files = [
|
23
|
+
".document",
|
24
|
+
".gitignore",
|
25
|
+
"LICENSE",
|
26
|
+
"README.markdown",
|
27
|
+
"Rakefile",
|
28
|
+
"Samples/Export Notes to CSV.rb",
|
29
|
+
"VERSION",
|
30
|
+
"cineSync Session v3 Schema.rnc",
|
31
|
+
"cinesync.gemspec",
|
32
|
+
"lib/cinesync.rb",
|
33
|
+
"lib/cinesync/color_grading.rb",
|
34
|
+
"lib/cinesync/event_handler.rb",
|
35
|
+
"lib/cinesync/frame_annotation.rb",
|
36
|
+
"lib/cinesync/mask.rb",
|
37
|
+
"lib/cinesync/media_file.rb",
|
38
|
+
"lib/cinesync/pixel_ratio.rb",
|
39
|
+
"lib/cinesync/play_range.rb",
|
40
|
+
"lib/cinesync/session.rb",
|
41
|
+
"lib/cinesync/ui.rb",
|
42
|
+
"lib/cinesync/ui/standard_additions.rb",
|
43
|
+
"lib/cinesync/ui/win32_save_file_dialog.rb",
|
44
|
+
"lib/cinesync/xml.rb",
|
45
|
+
"lib/cinesync/zoom_state.rb",
|
46
|
+
"test/helper.rb"
|
47
|
+
]
|
48
|
+
s.homepage = %q{http://github.com/jmah/cinesync}
|
49
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
50
|
+
s.require_paths = ["lib"]
|
51
|
+
s.rubygems_version = %q{1.3.6}
|
52
|
+
s.summary = %q{Library for scripting the cineSync collaborative video review tool}
|
53
|
+
s.test_files = [
|
54
|
+
"test/helper.rb"
|
55
|
+
]
|
56
|
+
|
57
|
+
if s.respond_to? :specification_version then
|
58
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
59
|
+
s.specification_version = 3
|
60
|
+
|
61
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
62
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.3"])
|
63
|
+
s.add_runtime_dependency(%q<andand>, [">= 1.3.1"])
|
64
|
+
s.add_runtime_dependency(%q<builder>, [">= 2.1"])
|
65
|
+
else
|
66
|
+
s.add_dependency(%q<activesupport>, [">= 2.3"])
|
67
|
+
s.add_dependency(%q<andand>, [">= 1.3.1"])
|
68
|
+
s.add_dependency(%q<builder>, [">= 2.1"])
|
69
|
+
end
|
70
|
+
else
|
71
|
+
s.add_dependency(%q<activesupport>, [">= 2.3"])
|
72
|
+
s.add_dependency(%q<andand>, [">= 1.3.1"])
|
73
|
+
s.add_dependency(%q<builder>, [">= 2.1"])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module CineSync
|
2
|
+
class ColorGrading
|
3
|
+
attr_reader :offset, :brightness
|
4
|
+
attr_accessor :saturation, :gamma, :contrast, :linear_to_log, :lut_path
|
5
|
+
|
6
|
+
alias_method :linear_to_log?, :linear_to_log
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@offset = RGBArray.new([0.0] * 3)
|
10
|
+
@brightness = BrightnessArray.new([1.0] * 4)
|
11
|
+
@saturation = 1.0
|
12
|
+
@gamma = 1.0
|
13
|
+
@contrast = 1.0
|
14
|
+
@linear_to_log = false
|
15
|
+
@lut_path = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def default?
|
19
|
+
offset == [0.0]*3 and brightness == [1.0]*4 and
|
20
|
+
[saturation, gamma, contrast].all? {|f| f == 1.0 } and
|
21
|
+
not linear_to_log? and lut_path.nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid?
|
25
|
+
exp_range = Math.exp(-1)..Math.exp(1)
|
26
|
+
default? or (offset.length == 3 and brightness.length == 4 and
|
27
|
+
offset.all? {|o| (-0.2..0.2) === o } and
|
28
|
+
[saturation, gamma, contrast, *brightness].all? {|b| exp_range === b }) rescue false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
class RGBArray < Array
|
34
|
+
# Implements an array of three values (corresponding to red, green, and
|
35
|
+
# blue) that can be indexed by position or string-convertible object:
|
36
|
+
# r,g,b = rgbary[0], rgbary[1], rgbary[2]
|
37
|
+
# r,g,b = rgbary['red'], rgbary[:green], rgbary[:b]
|
38
|
+
|
39
|
+
def [](key)
|
40
|
+
index = index_for_key(key)
|
41
|
+
fail "Unknown key used to index #{self.class}: #{key.inspect}" unless index
|
42
|
+
self.at(index)
|
43
|
+
end
|
44
|
+
|
45
|
+
def []=(key, value)
|
46
|
+
index = index_for_key(key)
|
47
|
+
fail "Unknown key used to index #{self.class}: #{key.inspect}" unless index
|
48
|
+
super(index, value)
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
def index_for_key(key)
|
53
|
+
if Fixnum === key then key
|
54
|
+
elsif %w[red r].include?(key.to_s.downcase) then 0
|
55
|
+
elsif %w[green g].include?(key.to_s.downcase) then 1
|
56
|
+
elsif %w[blue b].include?(key.to_s.downcase) then 2
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
class BrightnessArray < RGBArray
|
63
|
+
protected
|
64
|
+
def index_for_key(key)
|
65
|
+
if %w[all rgb].include?(key.to_s.downcase) then 3
|
66
|
+
else super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
|
4
|
+
module CineSync
|
5
|
+
class EventHandler
|
6
|
+
attr_reader :save_format, :save_ext, :save_parent, :url, :session_key, :session
|
7
|
+
|
8
|
+
def initialize(argv, session)
|
9
|
+
@save_format = arg_value(argv, :save_fmt).downcase.to_sym
|
10
|
+
@save_ext = { :jpeg => 'jpg', :png => 'png' }[@save_format]
|
11
|
+
sp = arg_value(argv, :save_path, false)
|
12
|
+
@save_parent = Pathname.new(sp) if sp
|
13
|
+
|
14
|
+
@url = arg_value(argv, :url, false)
|
15
|
+
|
16
|
+
key_val = arg_value(argv, :key)
|
17
|
+
@session_key = key_val if key_val != OfflineKey
|
18
|
+
|
19
|
+
@session = session
|
20
|
+
end
|
21
|
+
|
22
|
+
def offline?
|
23
|
+
@session_key.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def saved_frame_path(media_file, frame)
|
27
|
+
return nil unless save_parent
|
28
|
+
|
29
|
+
# This algorithm is ugly and will be improved before release
|
30
|
+
base = ('%s-%05d' % [media_file.name, frame])
|
31
|
+
i = 1; p2 = nil
|
32
|
+
begin
|
33
|
+
p = p2
|
34
|
+
p2, i = saved_frame_ver_path(base, i)
|
35
|
+
end while p2.exist?
|
36
|
+
p
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
ArgMatchers = { :key => /^--key=(\w+)$/,
|
43
|
+
:save_fmt => /^--save-format=(.+)$/,
|
44
|
+
:save_path => /^--save-dir=(.+)$/,
|
45
|
+
:url => /^--url=(.+)$/ }
|
46
|
+
|
47
|
+
def arg_value(argv, arg_key, required = true)
|
48
|
+
re = ArgMatchers[arg_key]
|
49
|
+
fail "Unknown symbolic argument key: #{arg_key.inspect}" unless re
|
50
|
+
args = argv.select {|a| a =~ re }
|
51
|
+
if args.empty?
|
52
|
+
if required
|
53
|
+
fail "Unable to find argument matching #{re.inspect} (argument key: #{arg_key.inspect})"
|
54
|
+
else
|
55
|
+
return nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
fail "Found multiple arguments matching #{re.inspect}: #{args.inspect}" if args.size > 1
|
59
|
+
args[0].match(re)[1]
|
60
|
+
end
|
61
|
+
|
62
|
+
def saved_frame_ver_path(base, version)
|
63
|
+
basename = base + (version == 1 ? '' : " (#{version})") + '.' + save_ext
|
64
|
+
[save_parent + basename, version + 1]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module CineSync
|
2
|
+
class FrameAnnotation
|
3
|
+
attr_accessor :frame, :notes, :drawing_objects
|
4
|
+
|
5
|
+
def initialize(frame_num)
|
6
|
+
@frame = frame_num
|
7
|
+
@notes = ''
|
8
|
+
@drawing_objects = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def default?
|
12
|
+
notes.empty? and drawing_objects.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
frame >= 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module CineSync
|
2
|
+
class Mask
|
3
|
+
attr_accessor :alpha, :center, :scale_factor
|
4
|
+
attr_accessor :width, :height
|
5
|
+
|
6
|
+
def initialize(ratio_or_width = nil, height = nil)
|
7
|
+
@alpha = 1.0
|
8
|
+
@center = [0.5, 0.5]
|
9
|
+
@scale_factor = 1.0
|
10
|
+
if ratio_or_width and height.nil?
|
11
|
+
self.ratio = ratio_or_width
|
12
|
+
elsif ratio_or_width and height
|
13
|
+
@width = Float(ratio_or_width)
|
14
|
+
@height = Float(height)
|
15
|
+
else
|
16
|
+
@width = 1
|
17
|
+
@height = 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def ratio
|
22
|
+
wh = [width, height]
|
23
|
+
# Convert to ints if the float represents an int
|
24
|
+
wh.map {|f| (f == f.floor) ? f.to_i : f }.map {|num| num.to_s }.join(':')
|
25
|
+
end
|
26
|
+
|
27
|
+
def ratio=(rat)
|
28
|
+
@width, @height = rat.split(':', 2).map(&:to_f)
|
29
|
+
end
|
30
|
+
|
31
|
+
def default?
|
32
|
+
alpha == 1.0 and center == [0.5, 0.5] and scale_factor == 1.0 and
|
33
|
+
width > 0.0 and width == height
|
34
|
+
end
|
35
|
+
|
36
|
+
def valid?
|
37
|
+
default? or (width > 0.0 and height > 0.0 and center.length == 2 and
|
38
|
+
[alpha, scale_factor, *center].all? {|f| (0.0..1.0) === f }) rescue false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|