8c915efb697c5d5de3f601d272db67bafcfdab53
[pompage.git] / src / films.rb
1 # coding: utf-8
2
3 require 'rexml/document'
4 require 'thwait'
5
6 require 'film.rb'
7 require 'pays.rb'
8 require 'genre.rb'
9 require 'personne.rb'
10 require 'constantes.rb'
11 require 'iconv'
12
13 # Représente un ensemble de films.
14 class Films
15 def initialize(xmlFile, modClasse)
16 @xmlFile = xmlFile
17
18 @modClasse = modClasse
19
20 # repertoire de base, par exemple C:/Divx/
21 @repBase = ''
22
23 # Les films indexé par leur titre
24 @films = {}
25
26 # Les films indexé par leur nom de fichier, deux fichiers différents peuvent pointer sur le même film
27 @filmsFichier = {}
28
29 # Les films qui ont plusieurs réponses lors de la recherche, traité à la fin
30 @filmsPlusieursReponses = []
31
32 # Les films qui n'ont aucune réponse après une recherche, traité à la fin
33 @filmsAucuneReponse = []
34
35 # permet de traiter facilement des groupes de thread
36 @threadsWait = ThreadsWait::new
37 @nbConn = 0 # le nombre de connexion
38
39 @mutexAjout = Mutex::new # mutex pour protéger l'ajout
40 @mutexId = Mutex::new # mutex pour protéger la génération d'id
41
42 # le prochain id disponible
43 @idDisponible = 1
44
45 chargerFilms
46 end
47
48 def each
49 @films.each{|t,f|
50 yield f
51 }
52 end
53
54 # Lit un repertoire de manière recursive et va chercher les informations concernant le film sur le net.
55 def pomper(r)
56 @repBase = r
57 repPrecedant = Dir::getwd
58 Dir::chdir(r)
59
60 t = Time::now
61
62 pomperR('.')
63
64 # on attends que les threads se terminent
65 @threadsWait.all_waits
66
67 puts "Pompage terminé, temps : #{Time::now - t} secondes"
68
69 # traite les films qui avaient plusieurs réponses lors de la recherche
70 # l'utilisateur doit faire un choix
71 i = 1
72 @filmsPlusieursReponses.each{|f|
73 puts
74 puts "Plop, ya un conflit #{i} / #{@filmsPlusieursReponses.length} :"
75 case f.reglerConflitPlusieursReponses
76 when 1
77 ajouterFilm(f)
78 when 3
79 break
80 end
81 i += 1
82 }
83
84 # traite les films qui n'avaient aucune réponse
85 i = 1
86 @filmsAucuneReponse.each{|f|
87 puts
88 puts "Plop, Ce film n'a pas été trouvé #{i} / #{@filmsAucuneReponse.length} :"
89 case f.reglerConflitPlusieursReponses
90 when 1
91 ajouterFilm(f)
92 when 3
93 break
94 end
95 i += 1
96 }
97
98 # annonce à chaque module d'importation que c'est fini
99 @films.each{|t,f|
100 f.mod.finish
101 }
102
103 Dir::chdir(repPrecedant)
104 end
105
106 # Mise à jour des films dans la BD.
107 # p1 string : un motif Regex correspondant à un ou plusieurs champs
108 def update(champ, titre)
109 @films.each{|t, f|
110 next if !f.titre.match(Regexp::new(titre, true))
111
112 ## si le film est complet on ne fait rien
113 if $force || f.url == nil || f.titre == '' || f.annee == nil ||
114 f.realisateurs.empty? || f.acteurs.empty? || f.pays.empty? ||
115 f.genres.empty? || f.synopsis == nil || !f.possedeImage?
116
117 f.update(champ)
118 end
119 }
120 end
121
122 # Sauve les films dans un fichier XML
123 def sauverFilms
124 # le document
125 docXml = REXML::Document::new
126 docXml.xml_decl().encoding = "UTF-8" # normalement UTF-8
127 docXml.xml_decl().dowrite
128
129 # la racine du document
130 racine = REXML::Element::new('filmographie')
131 docXml.add(racine)
132 pi = REXML::Instruction.new("xml-stylesheet", "type=\"text/xsl\" href=\"../xsl/yopyop.xsl\"")
133 racine.previous_sibling = pi
134
135 # on ajoute chaque film à la racine
136 @films.each{|nom, f|
137 racine.add(f.getXml)
138 }
139
140 # sauve le document
141 docXml.write(File::new(@xmlFile, 'w'), 0)
142 end
143
144 private
145
146 # Charge les films depuis le fichier XML
147 def chargerFilms
148 # si le fichier n'existe pas il n'y a rien à charger
149 if !File.exists?(@xmlFile)
150 return
151 end
152
153 racine = REXML::Document::new(File::new(@xmlFile)).root
154 racine.elements.each("film"){|e|
155 id = e.attribute('id').to_s.to_i
156
157 if id >= @idDisponible
158 @idDisponible = id + 1
159 end
160
161 film = Film::new(nil, @modClasse::new)
162 film.id = id
163
164 film.titre = e.get_text('titre')
165 film.titre = film.titre.value if film.titre != nil
166
167 print "#{film.titre}.. "
168
169
170 film.url = e.get_text('url')
171 film.url = film.url.value if film.url != nil
172
173 film.annee = e.get_text('annee')
174 film.annee = film.annee.value if film.annee != nil
175
176 film.duree = e.get_text('duree')
177 film.duree = film.duree.value if film.duree != nil
178
179 film.critiquePresse = e.get_text('critiquePresse')
180 film.critiquePresse = film.critiquePresse.value if film.critiquePresse != nil
181
182 film.critiqueSpectateur = e.get_text('critiqueSpectateur')
183 film.critiqueSpectateur = film.critiqueSpectateur.value if film.critiqueSpectateur != nil
184
185 film.budget = e.get_text('budget')
186 film.budget = film.budget.value if film.budget != nil
187
188 e.elements.each('fichiers/fichier'){|f|
189 next if f.get_text == nil
190 film.addFichier(f.get_text.value)
191 @filmsFichier[f.get_text.value] = film
192 }
193
194 e.elements.each('realisateurs/realisateur'){|f|
195 film.realisateurs << Personne::ajouter(f.get_text.value)
196 }
197
198 e.elements.each('acteurs/acteur'){|f|
199 film.acteurs << Personne::ajouter(f.get_text.value)
200 }
201
202 e.elements.each('lespays/pays'){|f|
203 film.pays << Pays::ajouter(f.get_text.value)
204 }
205
206 e.elements.each('genres/genre'){|f|
207 film.genres << Genre::ajouter(f.get_text.value)
208 }
209
210 debut = true
211 film.synopsis = ""
212 e.elements.each('synopsis/p'){|f|
213 film.synopsis += "\n" unless debut
214 film.synopsis += f.get_text.value if f.get_text != nil
215 debut = false
216 }
217 film.synopsis = nil if film.synopsis == ""
218
219 @films[film.titre] = film
220 }
221 end
222
223 # Retourne un nouvel id, utilisé alors de la création d'un nouveau film
224 def getNewId
225 id = @idDisponible
226 @idDisponible += 1
227 return id
228 end
229
230 # Ajoute un film à l'ensemble des films.
231 def ajouterFilm(film)
232 if film.plusieursReponses?
233 @filmsPlusieursReponses << film
234 return
235 end
236
237 if film.nbReponses == 0
238 @filmsAucuneReponse << film
239 return
240 end
241
242 if @films.has_key?(film.titre)
243 if !@filmsFichier.has_key?(film.fichiers[0])
244 puts "[i] Le film #{film.titre} possède une autre partie : #{film.fichiers[0]}"
245 @films[film.titre].addFichier(film.fichiers[0])
246 @filmsFichier[film.fichiers[0]] = @films[film.titre]
247 else
248 puts "[!] Film déjà dans la BD : #{film.titre} (#{film.fichiers[0]})"
249 end
250 else
251 puts "[i] Film ajouté : #{film.titre} (#{film.fichiers[0]})"
252 @films[film.titre] = film
253 @filmsFichier[film.fichiers[0]] = film
254 end
255 end
256
257 # Appelé par 'pomper'. Cette méthode itère récursivement sur l'arborescence d'un repertoire.
258 def pomperR(r)
259 Dir::foreach(r){|f|
260 next if f[0,1] == '.'
261 fichier = (r == '.' ? '' : r + "/") + f
262 if File::directory?(fichier)
263 pomperR(fichier)
264 else
265
266 #fichier = Iconv.iconv("UTF-8", "ISO-8859-1", fichier)[0] // sous windows il faut décommenter cette ligne
267
268 # véfication de l'extension
269 /^.*?\.([^.]{3,4})$/ =~ fichier
270 if !FILMS_EXTENSIONS.include?($1)
271 next
272 end
273
274 # on skip si le film est déjà dans la BD
275 if film = @filmsFichier[fichier]
276 puts "[!] Film déjà dans la BD : #{film.titre} (#{film.fichiers[0]})"
277 next
278 end
279
280 # pour limiter le nombre de connexion simultanée
281 if @nbConn >= NB_CONN_MAX
282 @threadsWait.next_wait
283 end
284
285 @nbConn += 1
286 @threadsWait.join_nowait(
287 Thread::start{
288 begin
289 film = Film::new(fichier, @modClasse::new)
290 @mutexId.synchronize{
291 film.id = getNewId # on lui donne un nouvel ID
292 }
293
294 film.loadData # chargement de ses données
295
296 @mutexAjout.synchronize{
297 ajouterFilm(film)
298 }
299 rescue Exception => e
300 puts e.message
301 puts e.backtrace
302 end
303 @nbConn -= 1
304 }
305 )
306 end
307 }
308 end
309 end