1eaa33f9ed97f307d68e3098dcd58cbad26602b9
[pompage.git] / src / film.rb
1 require 'rexml/document'
2 require 'net/http'
3 require 'thread'
4 require 'thwait'
5 require 'cgi'
6
7 require 'pays.rb'
8 require 'genre.rb'
9 require 'personne.rb'
10
11 require 'constantes.rb'
12
13 class Film
14 attr_accessor :id, :titre, :fichiers, :annee, :realisateurs, :acteurs, :pays, :duree, :critiquePresse, :critiqueSpectateur, :genres, :synopsis, :budget
15
16 # Les films indexés par leur titre
17 @@films = {}
18
19 # Les films indexés par leur nom de fichier, deux fichiers différents peuvent pointer sur le même film
20 @@filmsFichier = {}
21
22 # Les films qui ont plusieurs réponses lors de la recherche, traités à la fin
23 @@filmsPlusieursReponses = []
24
25 @@mutex = Mutex::new
26 @@threadsWait = ThreadsWait::new
27 @@nbConn = 0
28
29 # le prochain id disponible
30 @@idDisponible = 1
31
32 # retourne un nouvel id, utilisé lors de la création d'un nouveau film
33 def Film::getNewId
34 id = @@idDisponible
35 @@idDisponible += 1
36 return id
37 end
38
39 # Lit un repertoire de manière recursive
40 def Film::litRepertoire(r)
41 Film::litRepertoireR(r)
42 # on attends que les threads se terminent
43 @@threadsWait.all_waits
44
45 # traite les films qui avaient plusieurs réponses lors de la recherche
46 # l'utilisateur doit faire un choix
47 @@filmsPlusieursReponses.each{|f|
48 if f.reglerConflitPlusieursReponses
49 Film::ajouterFilm(f)
50 end
51 }
52 end
53
54 # Charge les films contenus dans un fichier XML.
55 def Film::loadFilmsXml(xmlFile)
56 # si le fichier n'existe pas il n'y a rien à charger
57 if !File.exists?(xmlFile)
58 return
59 end
60
61 racine = REXML::Document::new(File::new(xmlFile)).root
62 racine.each_element{|e|
63 id = e.attribute('id').to_s().to_i()
64
65 @@idDisponible = id + 1
66 titre = e.get_elements('titre')[0].get_text
67
68 fichiers = e.get_elements('fichiers')[0].get_elements('fichier')
69 annee = e.get_elements('annee')[0].get_text
70 duree = e.get_elements('duree')[0].get_text
71 critiquePresse = e.get_elements('critiquePresse')[0].get_text
72 critiqueSpectateur = e.get_elements('critiqueSpectateur')[0].get_text
73 synopsis = e.get_elements('synopsis')[0].get_text
74 budget = e.get_elements('budget')[0].get_text
75 realisateurs = e.get_elements('realisateurs')[0].get_elements('realisateur')
76 acteurs = e.get_elements('acteurs')[0].get_elements('acteur')
77 pays = e.get_elements('lespays')[0].get_elements('pays')
78 genres = e.get_elements('genres')[0].get_elements('genre')
79
80 film = Film::new(fichiers[0].get_text.value)
81
82 film.titre = titre.value unless titre.nil?
83 film.id = id
84 fichiers.each{|e|
85 film.addFichier(e.get_text.value)
86 @@filmsFichier[e.get_text.value] = film
87 }
88 film.annee = annee.value unless annee.nil?
89 acteurs.each{|e|
90 film.acteurs << Personne::ajouter(e.get_text.value)
91 }
92 pays.each{|e|
93 film.pays << Pays::ajouter(e.get_text.value)
94 }
95 film.duree = duree.value unless duree.nil?
96 film.critiquePresse = critiquePresse.value unless critiquePresse.nil?
97 film.critiqueSpectateur = critiqueSpectateur.value unless critiqueSpectateur.nil?
98 genres.each{|e|
99 film.genres << Genre::ajouter(e.get_text.value) if e.get_text != nil
100 }
101 film.synopsis = synopsis.value unless synopsis.nil?
102 film.budget = budget.value unless budget.nil?
103 @@films[film.titre] = film
104 }
105 end
106
107 # Renvoie tous les films sous la forme d'un document XML.
108 def Film::getFilmsXml
109 # le document
110 docXml = REXML::Document::new
111 docXml.xml_decl().encoding = "UTF-8" # normalement UTF-8
112 docXml.xml_decl().dowrite
113
114 # la racine du document
115 racine = REXML::Element::new('filmographie')
116 docXml.add(racine)
117 pi = REXML::Instruction.new("xml-stylesheet", "type=\"text/xsl\" href=\"../xsl/yopyop.xsl\"")
118 racine.previous_sibling = pi
119
120 # on ajoute chaque film à la racine
121 @@films.each{|nom, f|
122 racine.add(f.getXml)
123 }
124
125 # revoie le document
126 docXml
127 end
128
129 private
130
131 def Film::filmsFactory(fichier)
132 /^.*?\.(.{3,4})$/ =~ fichier
133 if FILMS_EXTENSIONS.include?($1)
134 #convertit le code latin-1 en UTF8
135 Film::new(fichier.unpack("C*").pack("U*")).loadData
136 end
137 end
138
139 def Film::litRepertoireR(r)
140 Dir::foreach(r){|f|
141 next if f == '.' or f == '..'
142 fichier = r + "/" + f
143 if File::directory?(fichier)
144 litRepertoireR(fichier)
145 else
146
147 #si le film n'existe pas déjà dans la liste
148 if film = @@filmsFichier[fichier]
149 puts "[i] Already exists in DB : #{film.titre} (#{film.fichiers[0]})"
150 next
151 end
152
153 film = nil
154
155 @@nbConn += 1
156 @@threadsWait.join_nowait(
157 Thread::new{
158 begin
159 @@mutex.lock if @@nbConn >= NB_CONN_MAX
160 film = Film::filmsFactory(fichier)
161 unless film.nil? # le film a été correctement construit
162 Film::ajouterFilm(film)
163 end
164 @@nbConn -= 1
165 @@mutex.unlock
166 rescue Exception => e
167 puts e.message
168 puts e.backtrace
169 end
170 }
171 )
172 end
173 }
174 end
175
176 def Film::ajouterFilm(film)
177 if film.plusieursReponses?
178 @@filmsPlusieursReponses << film
179 return
180 end
181
182 # le film existe déjà
183 if @@films.has_key?(film.titre)
184 # le fichier n'est pas connu -> nième partie d'un film
185 if !@@filmsFichier.has_key?(film.fichiers[0])
186 puts "[i] movie #{film.titre} has a another file part : #{film.fichiers[0]}"
187 @@films[film.titre].addFichier(film.fichiers[0])
188 @@filmsFichier[film.fichiers[0]] = @@films[film.titre]
189 else
190 puts "[!] Duplicate movie : #{film.titre} (#{film.fichier})"
191 end
192 else
193 puts "[i] movie added : #{film.titre} (#{film.fichiers[0]})"
194 @@films[film.titre] = film
195 @@filmsFichier[film.fichiers[0]] = film
196 end
197 end
198
199
200 def initialize(fichier)
201 @fichiers = [fichier]
202
203 @id = 0
204 @titre = ''
205 @annee = nil
206 @realisateurs = []
207 @acteurs = []
208 @pays = []
209 @duree = nil
210 @critiquePresse = nil
211 @critiqueSpectateur = nil
212 @genres = []
213 @synopsis = nil
214 @budget = nil
215 @budgetUnite = 'euro'
216 @url
217
218 @aPlusieursReponses = false
219 # mémorise les tuples {nom => id} dans le cas ou il y a plusieurs réponses
220 @idsAllocine = {}
221 end
222
223 public
224
225 def plusieursReponses?
226 return @aPlusieursReponses
227 end
228
229 # demande à l'utilisateur de faire un choix
230 # ret : true si le conflit à été résolu sinon false
231 def reglerConflitPlusieursReponses
232 puts "prout"
233
234 return false
235 end
236
237 def addFichier(fichier)
238 if (!@fichiers.include?(fichier))
239 @fichiers << fichier
240 end
241 end
242
243 # Charge les informations du films à partir d'allocine.fr
244 # ret [Film]
245 def loadData
246 unless LOAD_DATA
247 @titre = @fichiers[0]
248 return self
249 end
250
251 @id = Film::getNewId
252
253 connexionHttp = Net::HTTP::new('www.allocine.fr')
254
255 #extrait le nom à partir du nom du fichier
256 /^.*?([^\/]*?)\.(.{3,4})$/ =~ @fichiers[0]
257 #remplace undescores et points par des espaces
258 titre = $1.gsub(/[_\.]/, ' ')
259 #remplace les suites d'espaces par un seul
260 titre.gsub!(/ {2,}/,' ')
261 titre.gsub!(/\[.*?\]/,'')
262 titre.gsub!(/\(.*?\)/,'')
263 titre.gsub!(/\{.*?\}/,'')
264 #vire les espaces au début et à la fin
265 titre.strip!
266
267 @titre = titre.dup
268
269 donneesHtml = nil
270 begin
271 begin
272 reponse, donneesHtml = connexionHttp.get("/recherche/?motcle=#{CGI::escape(titre)}")
273 rescue Exception => e
274 p e
275 puts "[!] Connexion lost, retry.."
276 retry
277 end
278
279 #convertit le code latin-1 en UTF8
280 donneesHtml = donneesHtml.unpack("C*").pack("U*")
281
282 #si pas trouvé alors on enlève un mot à la fin
283 if /.*?Pas de résultats.*?/ =~ donneesHtml || ! donneesHtml.include?("<h3><b>Films <h4>")
284 /(.*?)[^ ]+?$/ =~ titre.strip
285 titre = $1
286 titre.strip!
287 else
288 break;
289 end
290 end while not titre.nil? and not titre.empty?
291
292 unless titre.nil? or titre.empty?
293
294 #/<a href="\/film\/fichefilm_gen_cfilm=(\d+)\.html" class="link1">/ =~ donneesHtml
295 r = donneesHtml.scan(/<a href="\/film\/fichefilm_gen_cfilm=(\d+)\.html" class="link1">(.*?)<\/a>/)
296
297 if r.length > 1
298 @aPlusieursReponses = true
299 r.each{|f|
300 @idsAllocine[f[1].gsub(/<(.*?)>/, '')] = f[0]
301 }
302 elsif r.length == 1
303 loadDepuisIdAllocine(r[0][0], connexionHttp)
304 else
305 puts "[!] Movie not found : #{@titre} (#{@fichier})"
306 end
307 end
308 self
309 end
310
311 private
312 def loadDepuisIdAllocine(id, connexionHttp)
313 r, ficheHtml = connexionHttp.get("/film/fichefilm_gen_cfilm=#{id}.html")
314
315 #convertit le code latin-1 en UTF8
316 ficheHtml = ficheHtml.unpack("C*").pack("U*")
317
318 #url
319 @url = "http://www.allocine.fr/film/fichefilm_gen_cfilm=#{id}.html"
320
321 # Titre
322 /<title>(.*?)<\/title>/ =~ ficheHtml
323 @titre = $1 unless $1.nil?
324
325 puts "Movie found : #{@titre} (#{@fichier})"
326
327 # Année
328 /<h4>Année de production : (\d+)<\/h4>/ =~ ficheHtml
329 @annee = $1.to_i unless $1.nil?
330
331 # Réalisateurs
332 /<h4>Réalisé par(.*?)<\/h4>/ =~ ficheHtml
333 $1.scan(/<a class="link1" href=".*?">(.*?)<\/a>/m){|a|
334 @realisateurs << Personne::ajouter(a[0]) unless a[0].nil?
335 } unless $1.nil?
336
337 # Acteurs
338 /<h4>Avec(.*?)<\/h4>/ =~ ficheHtml
339 $1.scan(/<a class="link1" href="\/personne\/fichepersonne_gen_cpersonne=\d+\.html">(.+?)<\/a>/m){|a|
340 @acteurs << Personne::ajouter(a[0]) unless a[0].nil?
341 } unless $1.nil?
342
343
344 # Pays
345 /<h4>Film (.*?)\.&nbsp;<\/h4>/ =~ ficheHtml
346 $1.split(',').each{|pays|
347 @pays << Pays::ajouter(pays) unless pays.nil?
348 } unless $1.nil?
349
350 # Duree
351 /<h4>Durée : (\d+)h (\d+)min./ =~ ficheHtml
352 @duree = $1.nil? ? $2.to_i : $1.to_i * 60 + $2.to_i
353
354 # Critiques presse et spectateur
355 /Presse.*etoile_([012345]).*Spectateurs.*etoile_([012345])"/m =~ ficheHtml
356 @critiquePresse = $1.to_i unless $1.nil?
357 @critiqueSpectateur = $2.to_i unless $2.nil?
358
359 # Genre
360 /<h4>Genre : (.*?)<\/h4>/ =~ ficheHtml
361 $1.scan(/<a href="\/film\/alaffiche_genre_gen_genre=.*?" class="link1">(.+?)<\/a>/m){|g|
362 @genres << Genre::ajouter(g[0]) unless g[0].nil?
363 } unless $1.nil?
364
365 # Synopsis
366 /Synopsis.*?<h4>(.+?)<\/h4>/m =~ ficheHtml
367 @synopsis = $1 unless $1.nil?
368
369 # Budget
370 /Budget<\/b> : (.+?) millions d'euros<\/h4>/ =~ ficheHtml
371 @budget = $1.to_i unless $1.nil?
372 end
373
374 public
375
376 # Renvoie un film sous la forme d'un élément XML de type REXML
377 def getXml
378
379 racine = REXML::Element::new('film')
380 racine.add_attribute('id', @id.to_s)
381
382 fichiers = REXML::Element::new('fichiers')
383 @fichiers.each{|f|
384 fichiers.add(REXML::Element::new('fichier').add_text(f))
385 }
386 racine.add(fichiers)
387
388 racine.add(REXML::Element::new('titre').add_text(@titre))
389 racine.add(REXML::Element::new('annee').add_text(@annee.to_s))
390
391 realisateurs = REXML::Element::new('realisateurs')
392 @realisateurs.each{|r|
393 realisateurs.add(REXML::Element::new('realisateur').add_text(r.nom))
394 }
395 racine.add(realisateurs)
396
397 acteurs = REXML::Element::new('acteurs')
398 @acteurs.each{|a|
399 acteurs.add(REXML::Element::new('acteur').add_text(a.nom))
400 }
401 racine.add(acteurs)
402
403 lespays = REXML::Element::new('lespays')
404 @pays.each{|p|
405 lespays.add(REXML::Element::new('pays').add_text(p.nom))
406 }
407 racine.add(lespays)
408
409 racine.add(REXML::Element::new('duree').add_text(@duree.to_s))
410
411 racine.add(REXML::Element::new('critiquePresse').add_text(@critiquePresse.to_s))
412 racine.add(REXML::Element::new('critiqueSpectateur').add_text(@critiqueSpectateur.to_s))
413
414 genres = REXML::Element::new('genres')
415 @genres.each{|g|
416 genres.add(REXML::Element::new('genre').add_text(g.nom))
417 }
418 racine.add(genres)
419
420 racine.add(REXML::Element::new('synopsis').add_text(@synopsis))
421 budgetElement = REXML::Element::new('budget')
422 budgetElement.add_text(@budget.to_s)
423 budgetElement.add_attribute('unite', @budgetUnite)
424 racine.add(budgetElement)
425
426 racine.add(REXML::Element::new('url').add_text(@url))
427
428 racine
429 end
430 end
431