commit 3062472c970ee13f11b80e448a3b024f0f44055a Author: redstorm Date: Tue Apr 25 00:12:13 2017 +0200 Premier commit diff --git a/config.py b/config.py new file mode 100644 index 0000000..864ff08 --- /dev/null +++ b/config.py @@ -0,0 +1,77 @@ +from configobj import ConfigObj +import shutil +import posixpath + + +class ConfigError(Exception): + pass + + +class Config: + """ + Gère le fichier de configuration + """ + def __init__(self): + + try: + self.obj = ConfigObj('config.conf', file_error=True) + except: + # pas de fichier: config par défaut + print('creating config file') + shutil.copyfile('default.conf', 'config.conf') + self.obj = ConfigObj('config.conf') + + self.check_config() + print('config loaded') + + @property + def domains(self): + return list(self.obj['domains'].keys()) + + @property + def app(self): + return self.obj['api']['app'] + + @property + def token(self): + return self.obj['api']['token'] + + @property + def server(self): + return self.obj['api']['server'] + + def check_config(self): + if not 'api' in self.obj: + raise ConfigError("no 'api' section in config file") + for k in ['app', 'token', 'extensions', 'server']: + if not k in self.obj['api']: + raise ConfigError("no '"+k+"' value in config file, subsection 'api'") + if not 'domains' in self.obj: + raise ConfigError("no 'domains' section in config file") + if len(self.obj['domains']) == 0: + raise ConfigError("no domains in config file, subsection domains") + for d in self.obj['domains']: + print('check dom:', d) + for k in ['movies', 'series']: + if not k in self.obj['domains'][d]: + raise ConfigError("no '"+k+"' value in config file, subsection 'domains/"+d+"'") + + def get_serie_dir(self, domain): + return self.obj['domains'][domain]['series'] + + def get_excluded_serie_dir(self, domain): + return self.obj['domains'][domain]['no_series'] + + def get_movie_dir(self, domain): + return self.obj['domains'][domain]['movies'] + + def get_excluded_movie_dir(self, domain): + return self.obj['domains'][domain]['no_movies'] + + def get_domain(self, domain): + return self.obj['domains'][domain]['domain'] + + def is_valid_file(self, name): + _, ext = posixpath.splitext(name) + return ext[1:] in self.obj['api']['extensions'] + diff --git a/default.conf b/default.conf new file mode 100644 index 0000000..7124354 --- /dev/null +++ b/default.conf @@ -0,0 +1,24 @@ + +[api] +# Valeur app à transmettre à l'API +app = '' +# Token à transmettre à l'API +token = '' +# Formats à reconnaitre +extensions = 'avi','mkv','mp4','m2ts','rmvb' +# Serveur où appeler l'API +server = 'http://piexel.rez' + +[domains] + +[[example]] + # Nom du domaine + domain='example.ftp' + + [[[directories]]] + # Dossiers contenant des films + movies = , + # Dossiers contenant des séries + series = , + + diff --git a/file.py b/file.py new file mode 100644 index 0000000..e268cd6 --- /dev/null +++ b/file.py @@ -0,0 +1,128 @@ +import posixpath + +class File: + """ + Décrit une référence de fichier dans le disque + """ + def __init__(self, path, name,api_id=None): + self.path = path + self.name = name + self.info_lang = [] + self.info_quality = [] + self.info_subtitles = [] + self.year = self._find_year() + self.title = self._get_title() + self.simple_name = self.title.replace(' ', '') + self.api_id = api_id + + def get_ext(self): + _, ext = posixpath.splitext(self.name) + return ext + + def without_ext(self): + _, ext = posixpath.splitext(self.name) + return ext + + def _get_title(self): + fname, ext = posixpath.splitext(self.name) + # 1) séparateurs + fname = fname.replace('.', ' ') + fname = fname.replace('_', ' ') + fname = fname.replace('-', ' ') + # 2.1) marqueurs de qualitée + for m in ['HDRiP', 'HDRip', 'HDTS', 'HD', 'DVDRIP', 'DvDRIP', '720p', '1080p', 'DVDSCR', 'BluRay', 'BrRip', 'BRRip']: + if m in fname: + self.info_quality.append(m) + fname = fname.replace(m, '') + # 2.2) marqueur de sous-titres + for m in ['SRT FR', 'STFR', 'STEN']: + if m in fname: + self.info_subtitles.append(m) + fname = fname.replace(m, '') + # 2.3) marqueurs de langues & sous-titres + for m in ['VOSTFR', 'VOST-FR', 'VOSTMulti']: + if m in fname: + self.info_lang.append(m) + self.info_subtitles.append(m) + fname = fname.replace(m, '') + # 2.4) marqueurs de langues + for m in ['VO', 'VF','FRENCH', 'FR', 'ENG', '[Eng]', 'VJAP']: + if m in fname: + self.info_lang.append(m) + fname = fname.replace(m, '') + # 2.5) marqueurs autres + for m in ['UNCENSORED', 'X264', 'x264', 'X265', '[www Cpasbien com]', '[www newpct1 com]', 'YIFY', 'JYK']: + fname = fname.replace(m, '') + # 3) minuscule + fname = fname.lower() + # 4) année + if self.year is not None: + fname = fname.replace('['+str(self.year)+']', '') + fname = fname.replace('[ '+str(self.year)+' ]', '') + fname = fname.replace('['+str(self.year)+' ]', '') + fname = fname.replace('[ '+str(self.year)+']', '') + # 5) espaces en bout et centraux + fname = fname.lstrip().rstrip() + while ' ' in fname: + fname = fname.replace(' ', ' ') + return fname + + def _find_year(self): + fname, ext = posixpath.splitext(self.name) + fname = fname.replace(' ', '') + for i in range(1800, 2100): + if '['+str(i)+']' in fname: + return i + return None + + def filename_same(self, other): + # Compare les noms de fichiers de self et de other + # En supprimant les espaces, la date, et les marques de qualitée + return self.simple_name == other.simple_name + + def additional_info(self): + """ + Info supplémentaires sur la langue, la qualitée et les sous-titres du fichier + """ + info = {} + if len(self.info_lang) > 0: + for m in ['VOSTFR', 'VOST-FR', 'VOSTMulti', 'VO']: + if m in self.info_lang: + info['lang'] = 'VO' + for m in ['ENG', '[Eng]']: + pass # notation? + for m in ['VJAP']: + pass # notation? + for m in ['VF','FRENCH', 'FR']: + if m in self.info_lang: + info['lang'] = 'FR' + if len(self.info_quality) > 0: + for m in ['HDRiP', 'HDTS', 'HD', '720p', 'BluRay']: + if m in self.info_quality: + info['quality'] = 'HD' + for m in ['DVDRIP', 'DVDSCR']: + pass # qualité? + for m in ['1080p']: + if m in self.info_quality: + info['quality'] = 'FULL HD' + if len(self.info_subtitles) > 0: + for m in ['SRT FR', 'STFR', 'VOSTFR', 'VOST-FR']: + if m in self.info_subtitles: + info['subtitles'] = 'FR' + for m in ['STEN']: + if m in self.info_subtitles: + info['subtitles'] = 'EN' + return info + + def __eq__(self, other): + return (self.path, self.name) == (other.path, other.name) + + def __hash__(self): + return hash((self.path, self.name)) + + def __str__(self): + return str(self.path+'/'+self.name+' year:['+str(self.year)+']') + + def __repr__(self): + return str(self) + diff --git a/main.py b/main.py new file mode 100644 index 0000000..4489705 --- /dev/null +++ b/main.py @@ -0,0 +1,290 @@ +''' + + + +Pierre Cadart +Script pour NAS sous Linux + +Utilise un accès en FTP + + +Organisation du programme (modules) finale: + + + |----------| |-------------| + | | | [piexel.py] | + | | | | + | accès | | accès | + | FTP | | API | + | | | | + |----------| |-------------| + \ / + |--------------| + | | + | arborescence | + | | + |--------------| + | + | <-- échange par liste d'actions ([actions.py]) sur des files ([file.py]) + | + |------------| |------------| |------------| + | | | | | | + | sauvegarde |----| code |-->| logs | + | | | de màj | | | + |------------| | | | | + |------------| |------------| + ^ / + | / + |------------|L |------------| + | [main.py] | | | + | main |<--| config | + | | | | + |------------| |------------| + | + | + |------------| + | | + | accès | + | manuel | + | | + |------------| + + + + +après avoir listé les fichiers avec les bonnes extensions localement, et les fichiers de la BDD, on effectue: +(1) transformation des dossiers en fichier + sous-titres + * parcours des 'file' de l'api qui sont des dossiers + - dans le cas d'un seul fichier video, on effectue automatiquement l'opération + - dans le cas de 0 fichiers videos, c'est soit: + - le dossier à changé de nom: + - on essaye de trouver son nouveau nom, en comparant les noms filtrés pour ne contenir que des lettres et l'année entre crochet + - le dossier a été déplacé: + - on peut chercher dans l'arborescence complète si le dossier existe + - dans le cas de plusieurs fichiers, on ne fait rien, mais on le met dans les logs, pour que cela soit fait manuellement + - lorsque l'on a un dossier local associé: + - on ajoute le fichier vidéo + - on ajoute les sous-titres à côté si la langue est disponible dans le nom +2) fichiers déplacés et/ou renommés + * parcours des fichiers dans l'API mais non dans le FTP + - cherche un renommage simple, par réduction des noms aux lettres seules et année + - sinon, recherche parmis tous les fichiers locaux non référencés les noms proches (même méthode) + - les fichiers restants (côté api) sont maintenant 'broken_link' +3) fichiers restants localement + * parcours des fichiers locaux restant (non dans un sous-dossier), + - pour chaque fichier, essaye de trouver le film/la série associé + - si déjà disponible, et broken_link, on complète simplement + - si non disponible, on ajoute le fichier + +à supprimer: + +exclure: reportages/documentaires + +''' + +import posixpath +import re +import config +import piexel +from ftplib import FTP +import time +import file + + +def ftpwalk(directory, ftp): + """ + Parcours en profondeur un dossier du serveur FTP + Effectue un parcours préfixe + """ + to_visit = [directory] + while len(to_visit) > 0: + current = to_visit.pop(0) + ftp.cwd(current) + Lfiles = [] + Ldirs = [] + for name, prop in ftp.mlsd(): + if name.startswith('.'): + continue + if prop['type'] == 'dir': + Ldirs.append(name) + to_visit.append(current+'/'+name) + elif prop['type'] == 'file': + Lfiles.append(name) + # ne construit pas la liste complète, + # mais retourne les résultats intermédiaires + yield (current, Ldirs, Lfiles) + +def visit_server(domain, conf, api): + # Connection au serveur + print('connect to:', conf.get_domain(domain)) + ftp = FTP(conf.get_domain(domain), user='rez', passwd='rez') + ftp.encoding = 'UTF-8' + # Initialisation des listes de mises à jour + L_missing = [] # fichiers non trouvés sur le serveur FTP + L_unreferenced = [] # fichiers non référencés dans l'API + L_moved = [] # fichiers déplacés sur le serveur FTP + # Lecture à distance des deux BDD + for directory in conf.get_movie_dir(domain): + # Visite l'arborescence de chaque dossier + Lloc = [] + for path, _, files in ftpwalk(directory, ftp): + # Vérifie si le parcours du dossier est autorisé + if not any(path.startswith(p) for p in conf.get_excluded_movie_dir(domain)): + # Ajoute les fichiers correspondants aux extensions + for f in files: + if conf.is_valid_file(f): + F = file.File(path, f) + Lloc.append(F) + # Récupère les fichiers de l'api + Lapi = [] + for info in api.get_files(path='ftp://'+conf.get_domain(domain)+directory, like=1): + Lapi.append(file.File(info['path'][len('ftp://'+conf.get_domain(domain)):], info['name'], api_id=info['filable_id'])) + ftp.close() + + # supprime les dossiers de l'api + Lapi = [f for f in Lapi if conf.is_valid_file(f.name)] + + # supprime les noms avec un '+' + Lloc = [f for f in Lloc if '+' not in f.name] + + # Compare avec la liste de l'api + Lmissing = [f for f in Lapi if f not in Lloc] # fichiers non présents localement + Lunref = [f for f in Lloc if f not in Lapi] # fichiers non référencés + + # Fichiers déplacés localement + Lrelink = [] # liste des références à changer + for file1 in Lmissing: + for file2 in Lunref: + if file1.filename_same(file2): + Lrelink.append((file1, file2)) + for fApi, fLoc in Lrelink: + Lmissing.remove(fApi) + Lunref.remove(fLoc) + print('moved:', Lrelink) + + # Linke les fichiers identiques au même film + Llink = [] + for file1 in Lunref: + for file2 in Lapi: + if file1.title == file2.title: + Llink.append((file1, file2.api_id)) + print('D add:', file1, file2, file2.api_id) + break + print('doubles:', sorted(Llink, key=lambda f:str(f))) + for f, _ in Llink: + Lunref.remove(f) + + # Linke les films par nom si possible + APIfilms = api.get_films() + API_alltitles = [] + for f in APIfilms: + if f['title']: + t = f['title'].replace(' ','').lower() + if t not in [e[0] for e in API_alltitles]: + API_alltitles.append((t, f['id'])) + if f['title_vo']: + t = f['title_vo'].replace(' ','').lower() + if t not in [e[0] for e in API_alltitles]: + API_alltitles.append((t, f['id'])) + API_alltitles = [f for f in API_alltitles if len(f[0])>10] + Llink2 = [] + for film in Lunref: + for title, fid in API_alltitles: + if title==film.simple_name: + Llink2.append((film, fid)) + break + #print(film, ' <-> ', [f for f in APIfilms if f['id']==fid][0]['title']) + print('easy ref:', sorted(Llink2, key=lambda f:str(f))) + for f, _ in Llink2: + Lunref.remove(f) + + print('missing:',Lmissing) + print('\n'*3) + print('unreferenced:','\n'.join(str(f) for f in Lunref)) + print('\n'*3) + print('unreferenced titles:', '\n'.join([f.title for f in Lunref])) + """ + # Put les renommages / déplacements + i = 0 + for filmApi, filmLoc in Lrelink: + i += 1 + print('['+str(i)+'/'+str(len(Lrelink))+']'+'relink:', filmApi.title) + try: + api.debug_print = True + api.put_file(id=filmApi.api_id, path='ftp://'+conf.get_domain(domain)+filmLoc.path, name=filmLoc.name) + api.debug_print = False + time.sleep(1) + except Exception as e: + print(e) + print('film '+filmApi.title+' not edited') + raise Exception('end') + + # Poste les ajouts de doubles + i = 0 + for film, filmID in Llink: + i += 1 + print('['+str(i)+'/'+str(len(Llink))+']'+'link:', film.title) + try: + api.debug_print = True + api.post_file(path='ftp://'+conf.get_domain(domain)+film.path, name=film.name, type='Film', type_id=filmID, **film.additional_info()) + api.debug_print = False + time.sleep(1) + except Exception as e: + print(e) + print('film '+film.title+' not added') + raise Exception('end') + + # Poste les ajouts de doubles plus complexes + i = 0 + for film, filmID in Llink2: + i += 1 + print('['+str(i)+'/'+str(len(Llink2))+']'+'link2:', film.title) + try: + api.debug_print = True + api.post_file(path='ftp://'+conf.get_domain(domain)+film.path, name=film.name, type='Film', type_id=filmID, **film.additional_info()) + api.debug_print = False + time.sleep(1) + except Exception as e: + print(e) + print('film '+film.title+' not added') + raise Exception('end') + + # Poste tout les films locaux + i = 0 + for film in Lunref: + i += 1 + print('['+str(i)+'/'+str(len(Lunref))+']'+'post:', film.title) + try: + api.debug_print = True + if film.year is not None: + resp = api.post_film(title=film.title, year=film.year) + else: + resp = api.post_film(title=film.title) + if "id" in resp: + api.post_file(path='ftp://'+conf.get_domain(domain)+film.path, name=film.name, type='Film', type_id=resp["id"], **film.additional_info()) + api.debug_print = False + time.sleep(1) + except Exception as e: + print(e) + print('film '+film.title+' not posted') + raise Exception('end') + """ + # Marque comme broken les films référencés non présents + ''' + i = 0 + for film in Lmissing: + i += 1 + print('['+str(i)+'/'+len(Lmissing)+']'+'broken:', film) + time.sleep(1) + ''' + print('visit finished') + +def main(): + conf = config.Config() + api = piexel.Piexel(conf.server, conf.app, conf.token) + for dom in conf.domains: + visit_server(dom, conf, api) + +if __name__ == '__main__': + main() + diff --git a/piexel.py b/piexel.py new file mode 100644 index 0000000..3c97eb0 --- /dev/null +++ b/piexel.py @@ -0,0 +1,297 @@ +import requests + +class PiexelErrors(Exception): + pass + +class InvalidToken(PiexelErrors): + pass + +class ParameterError(PiexelErrors): + pass + +class Piexel: + def __init__(self, domain, app='', token='', endpoint='/api/'): + self.app = app + self.token = token + self.domain = domain + self.endpoint = endpoint + self.debug_print = False + + def _get_response(self, controller, fields, request_type='get'): + """ + Build response + :param controller: controller à utiliser + :param fields: champ à passer + """ + fields['app'] = self.app + fields['token'] = self.token + url = self.domain + self.endpoint + controller + if request_type == 'get': + response = requests.get(url, fields) + elif request_type == 'post': + response = requests.post(url, fields) + elif request_type == 'put': + response = requests.put(url, fields) + elif request_type == 'delete': + response = requests.delete(url, data=fields) + if self.debug_print: + print('resp:',response.text) + response.encoding = 'utf-8' + code = response.status_code + data = response.json() + if code == 403: # FORBIDDEN + raise InvalidToken(data['message']) + elif code == 400: + raise ParameterError(data['message']) + return data + + def _get_request(self, allowed, mandatory=[], **params): + """ + Créé la requète + """ + fields = {} + for key, val in params.items(): + if key in allowed: + fields[key] = val + for m in mandatory: + if m not in fields.keys(): + raise ParameterError('The parameter ' + m + ' is required.') + return fields + + def get_films(self, **params): + """ + Récupère les films + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'title', 'title_vo', 'imdb_id', 'limit', 'first', 'first', 'files'], [], **params) + return self._get_response('films', fields) + + def get_series(self, **params): + """ + Récupère les séries + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'title', 'imdb_id', 'limit', 'first', 'first', 'episodes'], [], **params) + return self._get_response('series', fields) + + def get_episodes(self, **params): + """ + Récupère les épisodes + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'serie_id', 'title', 'imdb_id', 'limit', 'first', 'first', 'episodes'], [], **params) + return self._get_response('episodes', fields) + + def get_files(self, **params): + """ + Récupère les fichiers + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'path', 'name', 'limit', 'first', 'filable'], [], **params) + return self._get_response('files', fields) + + def get_actors(self, **params): + """ + Récupère les acteurs + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'name', 'imdb_id', 'tmdb_id', 'limit', 'first', 'films', 'series'], [], **params) + return self._get_response('files', fields) + + def get_files(self, **params): + """ + Récupère les fichiers + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'path', 'name', 'limit', 'first', 'filable', 'like'], [], **params) + return self._get_response('files', fields) + + def get_subtitles(self, **params): + """ + Récupère les sous-titres + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'path', 'name', 'limit', 'first', 'subtitlable'], [], **params) + return self._get_response('subtitles', fields) + + def get_broken(self, **params): + """ + Récupère les liens morts + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'type'], [], **params) + return self._get_response('broken', fields) + + def post_film(self, **params): + """ + Ajoute un film + :param params: paramètres à passer + """ + fields = self._get_request(['title', 'year'], ['title'], **params) + return self._get_response('films', fields, 'post') + + def post_file(self, **params): + """ + Ajoute un fichier + :param params: paramètres à passer + """ + fields = self._get_request(['path', 'name', 'type', 'type_id', 'quality', 'lang', 'subtitles'], ['path', 'name', 'type', 'type_id'], **params) + return self._get_response('files', fields, 'post') + + def post_subtitle(self, **params): + """ + Ajoute un sous-titre + :param params: paramètres à passer + """ + fields = self._get_request(['path', 'name', 'type', 'type_id', 'quality', 'lang'], ['path', 'name', 'type', 'type_id'], **params) + return self._get_response('subtitles', fields, 'post') + + def post_serie(self, **params): + """ + Ajoute une série + :param params: paramètres à passer + """ + fields = self._get_request(['title'], ['title'], **params) + return self._get_response('series', fields, 'post') + + def post_episode(self, **params): + """ + Ajoute un épisode d'une série + :param params: paramètres à passer + """ + fields = self._get_request(['season', 'episode', 'serie_id'], ['season', 'episode', 'serie_id'], **params) + return self._get_response('episodes', fields, 'post') + + def post_actor(self, **params): + """ + Ajoute un acteur + :param params: paramètres à passer + """ + fields = self._get_request(['name'], ['name'], **params) + return self._get_response('episodes', fields, 'post') + + def post_broken(self, **params): + """ + Ajoute un lien mort + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'type'], ['id', 'type'], **params) + return self._get_response('broken', fields, 'post') + + def put_film(self, **params): + """ + Edite un film + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'title', 'title_vo', 'tmdb_id', 'summary', 'cover_img', 'rating', 'runtime', 'release_date', 'autoFill'], ['id'], **params) + return self._get_response('films', fields, 'put') + + def put_file(self, **params): + """ + Edite un fichier + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'path', 'name', 'filable_type', 'filable_id', 'quality', 'lang', 'subtitles'], ['id'], **params) + return self._get_response('files', fields, 'put') + + def put_subtitle(self, **params): + """ + Edite un sous-titre + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'path', 'name', 'subtitlable_type', 'subtitlable_id', 'quality', 'lang'], ['id'], **params) + return self._get_response('subtitles', fields, 'put') + + def put_serie(self, **params): + """ + Edite une série + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'title', 'status', 'imdb_id', 'tmdb_id', 'summary', 'cover_img', 'rating', 'release_date', 'autoFill'], ['id'], **params) + return self._get_response('series', fields, 'put') + + def put_episode(self, **params): + """ + Edite un épisode d'une série + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'title', 'serid_id', 'tmdb_id', 'summary', 'cover_img', 'season', 'episode', 'rating', 'release_date'], ['id'], **params) + return self._get_response('episodes', fields, 'put') + + def put_actor(self, **params): + """ + Edite un acteur + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'name', 'imdb_id', 'tmdb_id', 'cover_img'], ['id'], **params) + return self._get_response('actors', fields, 'put') + + def put_broken(self, **params): + """ + Edite un lien mort + :param params: paramètres à passer + """ + fields = self._get_request(['id', 'treated'], ['id', 'treated'], **params) + return self._get_response('broken', fields, 'put') + + def delete_film(self, **params): + """ + Supprime un film + :param params: paramètres à passer + """ + fields = self._get_request(['id'], ['id'], **params) + return self._get_response('films', fields, 'delete') + + def delete_file(self, **params): + """ + Supprime un fichier + :param params: paramètres à passer + """ + fields = self._get_request(['id'], ['id'], **params) + return self._get_response('files', fields, 'delete') + + def delete_subtitle(self, **params): + """ + Supprime un sous-titre + :param params: paramètres à passer + """ + fields = self._get_request(['id'], ['id'], **params) + return self._get_response('subtitles', fields, 'delete') + + def delete_serie(self, **params): + """ + Supprime une série + :param params: paramètres à passer + """ + fields = self._get_request(['id'], ['id'], **params) + return self._get_response('series', fields, 'delete') + + def delete_episode(self, **params): + """ + Supprime un épisode d'une série + :param params: paramètres à passer + """ + fields = self._get_request(['id'], ['id'], **params) + return self._get_response('episodes', fields, 'delete') + + def delete_actor(self, **params): + """ + Supprime un acteur + :param params: paramètres à passer + """ + fields = self._get_request(['id'], ['id'], **params) + return self._get_response('actors', fields, 'delete') + + def delete_broken(self, **params): + """ + Supprime un lien mort + :param params: paramètres à passer + """ + fields = self._get_request(['id'], ['id'], **params) + return self._get_response('broken', fields, 'delete') + + +if __name__ == '__main__': + piexel = Piexel('http://piexel.rez', 'app', 'token') + film = piexel.get_films(title='test') + print(film)