Gaara a écrit@zmo:
C'est clair que vu mon niveau, je ne peux pas te contredire, mais tout ça c'est du chinois pour moi ! Je débute en python (et en programmation en général) depuis le début d'année, avec un simple "Hello World". Donc pour moi, du moment que ça marche, je suis ravi!
je comprends, et s'il y a un bon conseil que je peux te donner, vu que tu démarres en python, n'apprends que le python3 et abandonne le python2, ça t'évitera d'avoir à subir une période de transition (j'ai arrêté de faire du python2 depuis 6 mois ;-) ).
Ton script en ligne de commande à marché chez moi, et j'ai l'impression que le téléchargement va plus vite. Ceci dit, pour l'interface graphique, tout le monde ne tourne pas en Qt, surtout avec Ubuntu. Donc pourquoi installer 100Mo de dépendances, alors que ça peut marcher avec Gtk2/3 ? :/
Parce que j'ai fait près de 10 ans de développement avec Gtk en C et en python (il y a même des bouts de codes à moi dans pygtk), mais force est de constater que le gtk c'est, certes, un peu plus léger et un peu plus joli. Mais c'est surtout moins stable, moins portable et moins bien conçu – côté programmation – que le Qt. Sachant que comme Gtk n'est pas Gnome et Qt n'est pas KDE, les seules dépendances nécessaires sont celles de la bibliothèques qui ne font que quelques dizaines de méga, plus python-qt4 qui fait aussi une quinzaine de mégas.
Et pour pouvoir modifier ton script, il faut avoir de solides connaissances. J'ai regardé un peu, j'ai rien compris ! :lol:
c'est pourquoi j'ai commencé à séparer les modules:
je définie la classe PluzzMovie:
https://github.com/guyzmo/pypluzz/blob/master/pluzz/pluzz.py#L44
qui a pour méthodes:
PluzzMovie:
__init__(url)
retrieve_data()
save(target, callback, avconv_path, verbose)
keys()
items()
__getitem__(key)
l'idée de cette classe, c'est de faire en sorte qu'elle se comporte comme un dictionnaire de métadonnées pour le film. Donc les trois dernières méthodes permettent d'implémenter une fonction comme:
def print_metadata():
m = PluzzMovie('http://pluzz.francetv.fr/videos/doctor_who.html')
m.retrieve_data()
for k,v in m.items():
print("{}: {}".format(k,v))
qui affiche toutes les métadonnées du film.
C'est ce que je fais pour la fonctionnalité `get()` dans
https://github.com/guyzmo/pypluzz/blob/master/pluzz/pluzz.py#L173
Ensuite, la méthode la plus compliquée de la classe c'est la méthode `save()`:
https://github.com/guyzmo/pypluzz/blob/master/pluzz/pluzz.py#L66
Mais je l'ai rendue compliquée afin d'en simplifier l'usage. Donc voilà les étapes essentielles:
p = requests.get(list(filter(lambda x: x['format'] == 'm3u8-download', self.data['videos']))[0]['url'], headers=self.headers).text
video_url = list(filter(lambda l: "index_2" in l, p.split()))[0]
peut se décomposer comme suit:
# récupère l'url de la playlist m3u8 dans la liste des vidéos dans les métadonnées:
url = list(filter(lambda x: x['format'] == 'm3u8-download', self.data['videos']))[0]['url']
# récupère la playlist
p = requests.get(url).text
# pour chaque ligne de la page, ne garde que celle qui contient `index_2`, et récupère la première correspondance
video_url = list(filter(lambda l: "index_2" in l, p.split()))[0]
ensuite j'utilise subprocess pour appeler `avconv` et j'en récupère l'output pour le traiter:
p = subprocess.Popen([avconv_path, '-i', video_url] + self.avconv_args + [self.dest_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out = io.TextIOWrapper(p.stdout)
err = io.TextIOWrapper(p.stderr)
toutefois, le problème, c'est que avconv écrit sur stdout et sur stderr… Donc j'ai du utiliser une technique ancestrale: utiliser `select()` sur les descripteurs de fichiers pour savoir quand avconv a dit quelque chose:
while p.poll() == None:
ret = select.select([out.fileno(), err.fileno()], [], [])
ensuite pour chacun des deux (stdout et stderr), on appelle la fonction `output_parser()` définie ici:
https://github.com/guyzmo/pypluzz/blob/master/pluzz/pluzz.py#L69
for fd in ret[0]
if fd == out.fileno():
output_parser(out.readline())
if fd == err.fileno():
output_parser(err.readline())
dans cette fonction, j'utilise une technique de gens qui font du python: j'utilise un dictionnaire mis par défaut dans un argument comme variable statique. Ça permet de stocker des informations locales à la fonction sans avoir une variable globale, ou définir une classe pour une tâche aussi peu importante que de parser des lignes et afficher du texte. C'est la variable `env` qui joue ce rôle là.
Cette fonction utilise les trois regex précompilées:
https://github.com/guyzmo/pypluzz/blob/master/pluzz/pluzz.py#L59
qui permette de parser l'output d'avconv, une ligne à la fois. Pour permettre d'utiliser cette même fonction pour faire avancer
une progressbar graphique ou en ligne de commande, cette fonction appelle une callback externe à la fonction `save()` et à
la class `PluzzMovie`, avec les paramètres utiles pour traiter une progression.
Et cette fonction, elle est alors toute simple:
https://github.com/guyzmo/pypluzz/blob/master/pluzz/pluzz.py#L144 !
def show_progress(position, total, spent, start):
width = (get_term_size()[0]*0.6)
adv = position/total
eta = int((time.time() - start)*total/position)
print((_('Download and convert')+': [{: <'+str(int(width))+'s}] {:0.0%} ETA: {}s/{}s').format('#'*int(width*adv), adv, spent, eta), end='\r')
Ce qui rend l'usage de la fonction `save()` trivial:
m.save(args['--target'],
callback=show_progress,
avconv_path=args['--avconv'],
verbose=args['--verbose'])
Bien que j'ai du copier cette autre fonction
https://github.com/guyzmo/pypluzz/blob/master/pluzz/pluzz.py#L121 qui permet de connaître la taille
du terminal à chaque rafraîchissement permettant de garder la barre bien proportionnée si on redimensionne le terminal.
Donc au final, rien ne t'empèche d'utiliser mon code pour le lier à ton interface en Gtk ;-)
Tu as parlé de lxml, justement, j'ai étudié hier l'implémentation du moteur de recherche Pluzz dans dPluzz, pour ne pas avoir à chercher les adresses à la main. C'est pas encore fait, mais j'avance. J'ai découvert aussi Beautifulsoup.
J'avance à mon rythme !
lxml utilise (entre autre) BeautifulSoup comme moteur (il utilise aussi html5lib), et est plus puissant dans sa capacité de chercher et de construire/déconstruire un HTML. Je te recommande vivement de l'étudier plus en profondeur.