gimdb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/src/gimdb.rb ADDED
@@ -0,0 +1,379 @@
1
+ begin
2
+ require 'gtk2'
3
+ rescue LoadError => e
4
+ puts "Error: #{e.to_s}"
5
+ puts ""
6
+ puts "You must install 'gtk2' to run this program."
7
+ puts "If you are using Debian/GNU Linux you can install it with:"
8
+ puts ""
9
+ puts " apt-get install libgnome2-ruby"
10
+ puts ""
11
+ exit -1
12
+ end
13
+ begin
14
+ require 'libglade2'
15
+ rescue LoadError => e
16
+ puts "Error: #{e.to_s}"
17
+ puts ""
18
+ puts "You must install 'libglade2' to run this program."
19
+ puts "If you are using Debian/GNU Linux you can install it with:"
20
+ puts ""
21
+ puts " apt-get install libglade2-ruby"
22
+ puts ""
23
+ exit -1
24
+ end
25
+ require 'lib/imdb'
26
+ require 'src/controller'
27
+ require 'src/movie_box'
28
+
29
+
30
+ class GimdbGlade
31
+ include GetText
32
+
33
+ attr :glade
34
+
35
+ def initialize(path_or_data, root = nil, domain = $DOMAIN, localedir = $LOCALEDIR, flag = GladeXML::FILE)
36
+ bindtextdomain(domain, localedir, nil, 'UTF-8')
37
+ @glade = GladeXML.new(path_or_data, root, domain, localedir, flag) { |handler| method(handler) }
38
+ @searcher = IMDB.new
39
+ @movies = []
40
+ setting_up
41
+ end
42
+
43
+
44
+ private
45
+
46
+
47
+ def setting_up
48
+ # Get widgets from glade xml file
49
+ @window = @glade.get_widget('window')
50
+ @sidebar = @glade.get_widget('sidebar')
51
+ @users_menu_item = @glade.get_widget('users_menu_item')
52
+ @entry_title = @glade.get_widget('entry_title')
53
+ @spin_year_from = @glade.get_widget('spin_year_from')
54
+ @spin_year_to = @glade.get_widget('spin_year_to')
55
+ @combo_rating_from = @glade.get_widget('combo_rating_from')
56
+ @combo_rating_to = @glade.get_widget('combo_rating_to')
57
+ @b_search = @glade.get_widget('b_search')
58
+ @combo_sort = @glade.get_widget('combo_sort')
59
+ @toggle_sort = @glade.get_widget('toggle_sort')
60
+ @check_hide_seen = @glade.get_widget('check_hide_seen')
61
+ @check_only_see = @glade.get_widget('check_only_see')
62
+ @label_status = @glade.get_widget('label_status')
63
+ @progress = @glade.get_widget('progress')
64
+ @image_connection = @glade.get_widget('image_connection')
65
+ @image_spinner = @glade.get_widget('image_spinner')
66
+ @scrolled = @glade.get_widget('scrolled')
67
+ @vbox_movies = Gtk::VBox.new
68
+ @dialog_users = @glade.get_widget('dialog_users')
69
+ @entry_user = @glade.get_widget('entry_user')
70
+ @combo_del_users = @glade.get_widget('combo_del_users')
71
+ @table_combo = @glade.get_widget('table_combo')
72
+ @check_genres_all = @glade.get_widget('check_genres_all')
73
+
74
+ @genres = [
75
+ :action,:adventure,:animation,:biography,:comedy,
76
+ :crime,:documentary,:drama,:family,:fantasy,:film_noir,
77
+ :game_show,:history,:horror,:music,:musical,:mystery,:news,
78
+ :romance,:sci_fi,:sport,:thriller,:war,:western
79
+ ]
80
+ @genres.each do |genre|
81
+ instance_variable_set("@check_genres_#{genre}", @glade.get_widget("check_genres_#{genre}")).signal_connect('clicked') do
82
+ @check_genres_all.active = false
83
+ end
84
+ end
85
+
86
+ # Some stuffs
87
+ @users = User.find(:all, :conditions => 'selected = 1')
88
+ @all_users = User.all
89
+ build_users_menu
90
+ @spin_year_to.value = Time.now.year.to_i
91
+ @combo_rating_from.active = 0
92
+ @combo_rating_to.active = 9
93
+ @combo_sort.active = 0
94
+ @image_spinner.pixbuf_animation = Gdk::PixbufAnimation.new('data/icons/spinner16x16.gif')
95
+ @scrolled.add_with_viewport(@vbox_movies)
96
+ @scrolled.vscrollbar.signal_connect('value-changed') do |s|
97
+ x = (s.adjustment.upper * 90.0)/100.0
98
+ vadj = s.value + s.adjustment.page_size
99
+ if (vadj > x && vadj > @vadj && @b_search.sensitive?)
100
+ Thread.new{get_more_movies} if @b_search.sensitive?
101
+ #get_more_movies if @b_search.sensitive?
102
+ end
103
+ @vadj = vadj
104
+ end
105
+ @vbox_movies.border_width = 10
106
+ @vbox_movies.spacing = 10
107
+
108
+ # Window startup
109
+ @window.signal_connect('delete_event') { Gtk.main_quit }
110
+ @window.show_all
111
+ @progress.hide
112
+ @label_status.hide
113
+ @image_spinner.hide
114
+ @image_connection.hide
115
+ end
116
+
117
+
118
+ def build_options
119
+ options = {}
120
+ options[:offline] = @offline
121
+ options[:title] = @entry_title.text unless @entry_title.text.blank?
122
+ options[:release_date] = "#{@spin_year_from.value.to_i},#{@spin_year_to.value.to_i}"
123
+ rating_from = @combo_rating_from.active_text
124
+ rating_to = @combo_rating_to.active_text
125
+ if rating_from != '1' && rating_to != '10'
126
+ options[:user_rating] = "#{rating_from},#{rating_to}"
127
+ end
128
+ unless @check_genres_all.active?
129
+ options[:genres] = ''
130
+ @genres.each do |genre|
131
+ options[:genres] += "#{genre}," if instance_variable_get("@check_genres_#{genre}").active?
132
+ end
133
+ options[:genres].chop!
134
+ end
135
+ case @combo_sort.active
136
+ when 0
137
+ sort = 'moviemeter'
138
+ when 1
139
+ sort = 'alpha'
140
+ when 2
141
+ sort = 'user_rating'
142
+ when 3
143
+ sort = 'num_votes'
144
+ when 4
145
+ sort = 'runtime'
146
+ when 5
147
+ sort = 'year'
148
+ end
149
+ options[:sort] = sort + (@toggle_sort.active? ? ',DESC' : '')
150
+ return options
151
+ end
152
+
153
+
154
+ def searching(state)
155
+ if state
156
+ @b_search.sensitive = false
157
+ update_progress_bar(0.0, 0.0, _('Searching'))
158
+ @progress.show
159
+ @label_status.show
160
+ @image_spinner.show
161
+ else
162
+ @b_search.sensitive = true
163
+ @progress.hide
164
+ @label_status.hide
165
+ @image_spinner.hide
166
+ end
167
+ end
168
+
169
+
170
+ def update_progress_bar(step, max, text = nil)
171
+ max = 80000 if max.nil? || max == 0
172
+ fraction = step.to_f / max.to_f
173
+ fraction = 1.0 if fraction > 1.0
174
+ @progress.fraction = fraction
175
+ @label_status.text = _(text) + '...' unless text.nil?
176
+ end
177
+
178
+
179
+ def get_movies(kind = nil)
180
+ if @b_search.sensitive?
181
+ clear_movies_list
182
+ searching(true)
183
+ if kind.nil?
184
+ @movies = Controller::process_info(@searcher, build_options) do |step, max, text|
185
+ update_progress_bar(step, max, text)
186
+ end
187
+ else
188
+ @movies = Movie.get_kind(@users, kind)
189
+ end
190
+ @index = 0
191
+ update_movies_list
192
+ searching(false)
193
+ end
194
+ end
195
+
196
+
197
+ def get_more_movies
198
+ if @b_search.sensitive?
199
+ searching(true)
200
+ @movies += Controller::process_info(@searcher, :next => true, :offline => @offline) do |step, max, text|
201
+ update_progress_bar(step, max, text)
202
+ end
203
+ update_movies_list
204
+ searching(false)
205
+ end
206
+ end
207
+
208
+
209
+ def clear_movies_list
210
+ @scrolled.each { |child| @scrolled.remove(child) }
211
+ @vbox_movies = Gtk::VBox.new
212
+ @vbox_movies.border_width = 10
213
+ @vbox_movies.spacing = 10
214
+ @scrolled.add_with_viewport(@vbox_movies)
215
+ @scrolled.vscrollbar.adjustment.value = 0
216
+ end
217
+
218
+
219
+ def update_movies_list
220
+ @progress.fraction = 0
221
+ unless @index.nil?
222
+ display_movies = @movies[@index..-1]
223
+ display_movies.each_with_index do |m, i|
224
+ if ((!@check_hide_seen.active? || (m.get_users(:seen) & @users).empty?) &&
225
+ (!@check_only_see.active? || !(m.get_users(:to_see) & @users).empty?))
226
+ @vbox_movies.pack_start(GtkGimdb::MovieBox.new(m, @users), false)
227
+ @vbox_movies.pack_start(Gtk::HSeparator.new, false)
228
+ end
229
+ update_progress_bar(i, display_movies.size - 1, 'Building movie boxes')
230
+ end
231
+ @index = @movies.size
232
+ @vbox_movies.show_all
233
+ end
234
+ end
235
+
236
+
237
+ def build_users_menu
238
+ submenu = Gtk::Menu.new
239
+ @all_users.sort{|x,y| x.name <=> y.name}.each do |u|
240
+ m = Gtk::CheckMenuItem.new(u.name)
241
+ m.active = true if @users.include?(u)
242
+ submenu.append(m)
243
+ m.signal_connect('toggled') do
244
+ if m.active?
245
+ u.selected = 1
246
+ u.save!
247
+ @users << u
248
+ else
249
+ u.selected = 0
250
+ u.save!
251
+ @users.delete(u)
252
+ end
253
+ @users.sort!{|x,y| x.name <=> y.name}
254
+ end
255
+ end
256
+ @users_menu_item.submenu = submenu
257
+ @users_menu_item.show_all
258
+ end
259
+
260
+
261
+ ############
262
+ ### Events ###
263
+ ############
264
+
265
+
266
+ def on_search_clicked(widget, arg = nil)
267
+ Thread.new{get_movies} if @b_search.sensitive?
268
+ #get_movies if @b_search.sensitive?
269
+ end
270
+
271
+ def on_key_press(widget, arg = nil)
272
+ on_search_clicked(widget) if arg.keyval == Gdk::Keyval::GDK_Return
273
+ end
274
+
275
+ def on_get_more_movies_clicked(widget, arg = nil)
276
+ Thread.new{get_more_movies} if @b_search.sensitive?
277
+ #get_more_movies if @b_search.sensitive?
278
+ end
279
+
280
+ def on_show_to_see_clicked(widget, arg = nil)
281
+ Thread.new{get_movies(:to_see)}
282
+ end
283
+
284
+ def on_show_seen_clicked(widget, arg = nil)
285
+ Thread.new{get_movies(:seen)}
286
+ end
287
+
288
+ def on_show_favourites_clicked(widget, arg = nil)
289
+ Thread.new{get_movies(:favourites)}
290
+ end
291
+
292
+ def on_clean_clicked(widget, arg = nil)
293
+ @entry_title.text = ''
294
+ @spin_year_from.value = 1970
295
+ @spin_year_to.value = Time.now.year.to_i
296
+ @combo_rating_from.active = 0
297
+ @combo_rating_to.active = 9
298
+ @combo_sort.active = 0
299
+ @genres.each do |genre|
300
+ instance_variable_get("@check_genres_#{genre}").active = false
301
+ @check_genres_all.active = true
302
+ end
303
+ end
304
+
305
+ def on_work_offline_clicked(widget, arg = nil)
306
+ @offline = !widget.active?
307
+ if @offline
308
+ @window.title += ' (offline)'
309
+ @image_connection.show
310
+ else
311
+ @window.title = @window.title.gsub(' (offline)', '')
312
+ @image_connection.hide
313
+ end
314
+ end
315
+
316
+ def on_quit_clicked(widget, arg = nil)
317
+ Gtk.main_quit
318
+ end
319
+
320
+ def on_show_sidebar(widget, arg = nil)
321
+ widget.active? ? @sidebar.show : @sidebar.hide
322
+ end
323
+
324
+ def on_manage_users_clicked(widget, arg = nil)
325
+ @combo_del_users = Gtk::ComboBox.new
326
+ @table_combo.attach(@combo_del_users, 0,1, 1,2)
327
+ @all_users.sort{|x,y| x.name <=> y.name}.each do |u|
328
+ @combo_del_users.append_text(u.name)
329
+ end
330
+ @combo_del_users.active = 0
331
+ @dialog_users.show_all
332
+ end
333
+
334
+ def on_add_users_clicked(widget, arg = nil)
335
+ unless @entry_user.text.empty?
336
+ u = User.new(:name => @entry_user.text)
337
+ begin
338
+ u.save
339
+ @all_users << u
340
+ build_users_menu
341
+ @combo_del_users.append_text(u.name)
342
+ @combo_del_users.active = 0
343
+ @entry_user.text = ''
344
+ @label_status.text = _('New user added')
345
+ @label_status.show
346
+ # @dialog_users.hide
347
+ rescue
348
+ nil
349
+ end
350
+ end
351
+ end
352
+
353
+ def on_del_users_clicked(widget, arg = nil)
354
+ name = @combo_del_users.active_text
355
+ u = User.find_by_name(name)
356
+ unless u.nil?
357
+ u.destroy
358
+ @all_users.delete(u)
359
+ build_users_menu
360
+ @combo_del_users.remove_text(@combo_del_users.active)
361
+ @label_status.text = _('User deleted')
362
+ @label_status.show
363
+ # @combo_del_users.active = 0
364
+ end
365
+ end
366
+
367
+ def on_select_all_users_clicked(widget, arg = nil)
368
+ @users_menu_item.submenu.children.each do |c|
369
+ c.active = !c.active?
370
+ end
371
+ @dialog_users.hide
372
+ end
373
+
374
+ def on_close_manage_users_clicked(widget, arg = nil)
375
+ @dialog_users.hide
376
+ @label_status.hide
377
+ end
378
+
379
+ end
data/src/model.rb ADDED
@@ -0,0 +1,135 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'etc'
4
+
5
+
6
+ $GIMDB_PATH = "#{Etc.getpwuid.dir}/.gimdb"
7
+ Dir.mkdir($GIMDB_PATH) unless File.exist?($GIMDB_PATH)
8
+ Dir.mkdir("#{$GIMDB_PATH}/posters") unless File.exist?("#{$GIMDB_PATH}/posters")
9
+
10
+
11
+ ActiveRecord::Base.establish_connection(
12
+ :adapter => "sqlite3",
13
+ :database => "#{$GIMDB_PATH}/db.sqlite3"
14
+ )
15
+
16
+
17
+ if(!ActiveRecord::Base.connection.tables.include?('movies') ||
18
+ !ActiveRecord::Base.connection.tables.include?('users') ||
19
+ !ActiveRecord::Base.connection.tables.include?('populars'))
20
+ ActiveRecord::Schema.define do
21
+ create_table :movies do |t|
22
+ t.string :code, :unique => true, :null => false
23
+ t.string :title, :null => false
24
+ t.string :image_url
25
+ t.string :image_path
26
+ t.integer :year, :limit => 4
27
+ t.integer :votes
28
+ t.float :rating
29
+ t.text :outline
30
+ t.string :credit
31
+ t.string :genre
32
+ t.float :runtime
33
+ t.datetime :updated_at, :null => false, :default => Time.now
34
+ end
35
+
36
+ create_table :users do |t|
37
+ t.string :name, :unique => true, :null => :false
38
+ t.integer :selected, :limit => 1, :default => 0
39
+ end
40
+
41
+ create_table :populars, :id => false do |t|
42
+ t.references :movie
43
+ t.references :user
44
+ t.integer :kind, :null => false, :limit => 3
45
+ end
46
+
47
+ add_index :movies, :id, :unique
48
+ add_index :populars, [:movie_id, :user_id, :kind], :unique
49
+ end
50
+ end
51
+
52
+
53
+ class Popular < ActiveRecord::Base
54
+ belongs_to :movie
55
+ belongs_to :user
56
+ end
57
+
58
+
59
+ class Movie < ActiveRecord::Base
60
+ has_many :populars, :dependent => :delete_all
61
+ has_many :users, :through => :populars
62
+
63
+ def get_users(what = :to_see)
64
+ code = Movie.get_code(what)
65
+ unless code.nil?
66
+ pops = Popular.find(:all, :conditions => "movie_id = #{self.id} AND kind = #{code}")
67
+ return [] if pops.nil?
68
+ return pops.collect{|pop| pop.user}
69
+ else
70
+ return []
71
+ end
72
+ end
73
+
74
+ def set_user(user, what = :to_see)
75
+ code = Movie.get_code(what)
76
+ unless code.nil?
77
+ pop = Popular.new(:movie => self, :user => user, :kind => code)
78
+ self.populars << pop
79
+ end
80
+ end
81
+
82
+ def remove_user(user, what = :to_see)
83
+ code = Movie.get_code(what)
84
+ unless code.nil?
85
+ sql = "delete from populars where movie_id = #{self.id} AND user_id = #{user.id} AND kind = #{code}"
86
+ ActiveRecord::Base.connection.execute(sql)
87
+ end
88
+ end
89
+
90
+ def self.get_list(options)
91
+ @start = options[:start] || 1
92
+ c = ''
93
+ c += " AND title LIKE '%#{options[:title]}%'" if options[:title]
94
+ if options[:release_data] && options[:release_data].include?(',')
95
+ c += " AND year >= #{options[:release_data].split(',')[0]}"
96
+ c += " AND year <= #{options[:release_data].split(',')[1]}"
97
+ end
98
+ if options[:user_rating] && options[:user_rating].include?(',')
99
+ c += " AND rating >= #{options[:user_rating].split(',')[0]}"
100
+ c += " AND rating <= #{options[:user_rating].split(',')[1]}"
101
+ end
102
+ c += " AND genre LIKE '%#{options[:genre]}%'" if options[:genre]
103
+ c = c[5..-1]
104
+ @movies = Movie.find(:all, :conditions => c, :order => 'title ASC')#, :limit => 50)
105
+ return @movies[@start-1..@start+48]
106
+ end
107
+
108
+ def self.next
109
+ @start = @start + 50
110
+ return @movies[@start-1..@start+48] || []
111
+ end
112
+
113
+ def self.get_kind(users, kind)
114
+ c = ''
115
+ users.each { |u| c += "populars.user_id = #{u.id} OR " }
116
+ c = '(' + c[0..-5] + ') AND ' unless c.empty?
117
+ c += "populars.kind = #{Movie.get_code(kind)}"
118
+ Movie.find(:all, :joins => :populars, :conditions => c, :order => 'title ASC', :group => 'movies.id')
119
+ end
120
+
121
+ def self.get_code(what)
122
+ case what.to_sym
123
+ when :to_see: 0
124
+ when :seen: 1
125
+ when :favourites: 2
126
+ else nil
127
+ end
128
+ end
129
+ end
130
+
131
+
132
+ class User < ActiveRecord::Base
133
+ has_many :populars, :dependent => :delete_all
134
+ has_many :movies, :through => :populars
135
+ end
data/src/movie_box.rb ADDED
@@ -0,0 +1,110 @@
1
+ module GtkGimdb
2
+
3
+ class MovieBox < Gtk::HBox
4
+ include GetText
5
+
6
+
7
+ def initialize(movie, users = [])
8
+ bindtextdomain($DOMAIN, $LOCALEDIR, nil, 'UTF-8')
9
+ super()
10
+ @movie = movie
11
+ @users = users
12
+ setting_up
13
+ end
14
+
15
+
16
+ private
17
+
18
+
19
+ def setting_up
20
+ img = Gtk::Image.new((@movie.image_path.nil? || !File.exists?(@movie.image_path)) ? 'data/icons/no_poster.png' : @movie.image_path)
21
+ img.set_tooltip_text("Code: #{@movie.code}")
22
+ self.pack_start(img, false)
23
+
24
+ vbox = Gtk::VBox.new
25
+ vbox.spacing = 10
26
+ hbox1 = Gtk::HBox.new
27
+ hbox1.spacing = 50
28
+ hbox2 = Gtk::HBox.new
29
+ hbox2.spacing = 50
30
+
31
+ title = Gtk::Label.new
32
+ year = (@movie.year.nil? || @movie.year == 0) ? '' : "(#{@movie.year})"
33
+ title.markup = "<a href='http://www.imdb.it/title/#{@movie.code}/'><span color='#000' underline='none' size='large' weight='ultrabold'>#{@movie.title}</span></a> #{year}"
34
+ hbox1.pack_start(title, false)
35
+
36
+ rating = Gtk::Label.new
37
+ rating.markup = "#{@movie.rating}/10" unless @movie.rating.nil? || @movie.rating == 0
38
+ rating.set_tooltip_text(@movie.votes.to_s + ' votes') unless @movie.votes.nil? || @movie.votes == 0
39
+ hbox1.pack_end(rating, false)
40
+
41
+ vbox.pack_start(hbox1, false)
42
+
43
+ outline = Gtk::TextView.new
44
+ outline.buffer.text = @movie.outline || ''
45
+ outline.editable = false
46
+ outline.wrap_mode = Gtk::TextTag::WRAP_WORD_CHAR
47
+ outline.cursor_visible = false
48
+ outline.modify_base(Gtk::STATE_NORMAL, Gdk::Color.parse('#edeceb'))
49
+ vbox.pack_start(outline, false)
50
+
51
+ credit = Gtk::Label.new
52
+ credit.markup = @movie.credit || ''
53
+ credit.set_alignment(0.0, 0.0)
54
+ vbox.pack_start(credit, false)
55
+
56
+ genre = Gtk::Label.new
57
+ genre.text = @movie.genre.nil? ? '' : _('Genres') + ': ' + @movie.genre.gsub('|', ' | ')
58
+ hbox2.pack_start(genre, false)
59
+
60
+ runtime = Gtk::Label.new
61
+ unless @movie.runtime.nil? || @movie.runtime == 0
62
+ runtime.text = @movie.runtime.to_i.to_s + ' mins'
63
+ time = Time.now.midnight + @movie.runtime * 60
64
+ runtime.set_tooltip_text("#{time.hour}:#{"%02d" % time.min}")
65
+ end
66
+ hbox2.pack_end(runtime, false)
67
+
68
+ vbox.pack_start(hbox2, false)
69
+
70
+ vbox.pack_start(add_users_info, false) if @users.size > 0
71
+
72
+ self.pack_start(vbox)
73
+ self.spacing = 10
74
+ end
75
+
76
+
77
+ def add_users_info
78
+ table = Gtk::Table.new(@users.size + 1, 4)
79
+ table.attach(Gtk::Label.new, 0,1, 0,1, Gtk::SHRINK)
80
+ table.attach(Gtk::Label.new.set_markup(_('<b>To see</b>')), 1,2, 0,1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 10)
81
+ table.attach(Gtk::Label.new.set_markup(_('<b>Seen</b>')), 2,3, 0,1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 10)
82
+ table.attach(Gtk::Label.new.set_markup(_('<b>Favourites</b>')), 3,4, 0,1, Gtk::SHRINK)
83
+ @users.each_with_index do |u, i|
84
+ row = i + 1
85
+ table.attach(Gtk::Label.new(u.name), 0,1, row,row+1, Gtk::SHRINK)
86
+ table.attach(UserCheckButton.new(u, @movie, :to_see), 1,2, row,row+1, Gtk::SHRINK)
87
+ table.attach(UserCheckButton.new(u, @movie, :seen), 2,3, row,row+1, Gtk::SHRINK)
88
+ table.attach(UserCheckButton.new(u, @movie, :favourites), 3,4, row,row+1, Gtk::SHRINK)
89
+ end
90
+ return table
91
+ end
92
+
93
+
94
+ class UserCheckButton < Gtk::CheckButton
95
+ def initialize(user, movie, what)
96
+ super()
97
+ self.active = movie.get_users(what).include?(user)
98
+ self.signal_connect('toggled') do |b|
99
+ if b.active?
100
+ movie.set_user(user, what)
101
+ else
102
+ movie.remove_user(user, what)
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ end