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