commit d175ce494403de04b10e0c2e3f7016a13f8252d4 Author: Hugo Pierret Date: Thu Sep 5 18:34:12 2024 +0200 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85c55eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +.venv diff --git a/data/diary.csv b/data/diary.csv new file mode 100644 index 0000000..496fac2 --- /dev/null +++ b/data/diary.csv @@ -0,0 +1,66 @@ +Watched Date,Name,Year,Letterboxd URI,Rating,Rewatch,Tags,Watched Date +2023-12-15,Ghost in the Shell,1995,https://boxd.it/5k8vT3,3.5,,,2023-12-13 +2024-01-03,Baby Driver,2017,https://boxd.it/5tDbaP,4.0,,,2024-01-02 +2024-01-04,The Shawshank Redemption,1994,https://boxd.it/5ugTgP,4.5,,,2024-01-03 +2024-01-05,Léon: The Professional,1994,https://boxd.it/5uRr6B,4.0,,,2024-01-04 +2024-01-06,Brothers,2009,https://boxd.it/5vuRQN,3.5,,,2024-01-05 +2024-01-07,"Crazy, Stupid, Love.",2011,https://boxd.it/5wgeFn,4.0,,,2024-01-06 +2024-01-08,There Will Be Blood,2007,https://boxd.it/5x4B37,4.5,,,2024-01-07 +2024-01-09,(500) Days of Summer,2009,https://boxd.it/5xDHe1,4.0,,,2024-01-08 +2024-01-20,Dallas Buyers Club,2013,https://boxd.it/5DrLPb,4.0,,,2024-01-19 +2024-01-22,Boogie Nights,1997,https://boxd.it/5EEjCN,4.0,,,2024-01-21 +2024-01-24,Saltburn,2023,https://boxd.it/5FxIYT,4.0,,,2024-01-23 +2024-01-25,Under the Skin,2013,https://boxd.it/5G0SG9,2.5,,,2024-01-24 +2024-01-26,Anatomy of a Fall,2023,https://boxd.it/5GuroR,4.0,,,2024-01-25 +2024-01-31,Poor Things,2023,https://boxd.it/5J4AI7,4.5,,,2024-01-30 +2024-02-01,In the Mood for Love,2000,https://boxd.it/5JweK7,4.5,,,2024-01-31 +2024-02-07,Black Swan,2010,https://boxd.it/5MhygD,4.0,,,2024-02-06 +2024-02-13,Rain Man,1988,https://boxd.it/5OXE5j,4.0,,,2024-02-12 +2024-02-20,The 40 Year Old Virgin,2005,https://boxd.it/5Sgvxj,3.5,,,2024-02-19 +2024-02-21,Captain Fantastic,2016,https://boxd.it/5SHgol,4.0,,,2024-02-20 +2024-02-23,The Prestige,2006,https://boxd.it/5TwaTp,4.5,,,2024-02-22 +2024-02-29,The Shining,1980,https://boxd.it/5WgsV9,4.0,,,2024-02-28 +2024-03-02,Dune: Part Two,2024,https://boxd.it/5X4Ep5,5.0,,,2024-03-01 +2024-03-12,Fantastic Mr. Fox,2009,https://boxd.it/622Xk5,4.5,,,2024-03-11 +2024-03-13,Past Lives,2023,https://boxd.it/62uP1F,4.5,,,2024-03-12 +2024-03-19,Little Women,2019,https://boxd.it/65jkNX,3.0,,,2024-03-18 +2024-03-21,The Perks of Being a Wallflower,2012,https://boxd.it/668ca1,4.0,,,2024-03-20 +2024-03-28,Amélie,2001,https://boxd.it/69lEoN,4.5,,,2024-03-27 +2024-03-29,Lost in Translation,2003,https://boxd.it/69Kn1n,4.0,,,2024-03-28 +2024-04-03,No Country for Old Men,2007,https://boxd.it/6clZZd,4.0,,,2024-04-02 +2024-04-05,Fargo,1996,https://boxd.it/6dadIv,3.5,,,2024-04-04 +2024-04-09,No Hard Feelings,2023,https://boxd.it/6f0cwL,2.5,,,2024-04-08 +2024-04-17,"OSS 117: Cairo, Nest of Spies",2006,https://boxd.it/6i7AOl,4.0,,,2024-04-16 +2024-04-20,The Neon Demon,2016,https://boxd.it/6j9IF5,3.0,,,2024-04-19 +2024-04-23,Taxi Driver,1976,https://boxd.it/6ktjeR,3.5,,,2024-04-22 +2024-04-30,The Theory of Everything,2014,https://boxd.it/6nlzHL,4.5,,,2024-04-29 +2024-05-09,Civil War,2024,https://boxd.it/6rbidl,4.5,,,2024-05-08 +2024-05-10,Scott Pilgrim vs. the World,2010,https://boxd.it/6rxEAl,4.0,,,2024-05-09 +2024-05-15,Asteroid City,2023,https://boxd.it/6txkqD,3.5,,,2024-05-14 +2024-05-22,Battle of the Sexes,2017,https://boxd.it/6wiCsp,3.5,,,2024-05-22 +2024-05-26,War Dogs,2016,https://boxd.it/6xS8YB,3.5,,,2024-05-25 +2024-05-28,The Creator,2023,https://boxd.it/6yPVwT,4.5,,,2024-05-27 +2024-05-30,Full Metal Jacket,1987,https://boxd.it/6zEwVV,4.0,,,2024-05-29 +2024-06-04,Dead Poets Society,1989,https://boxd.it/6BNjNv,3.5,,,2024-06-03 +2024-06-05,Perfect Days,2023,https://boxd.it/6C9zSb,4.0,,,2024-06-04 +2024-06-07,Burn After Reading,2008,https://boxd.it/6CQVG3,3.0,,,2024-06-06 +2024-06-12,Before Sunrise,1995,https://boxd.it/6EWJMD,4.0,,,2024-06-11 +2024-06-20,OSS 117: Lost in Rio,2009,https://boxd.it/6I1d3j,3.5,,,2024-06-19 +2024-06-23,Inside Out 2,2024,https://boxd.it/6J9mU7,4.0,,,2024-06-22 +2024-06-26,May December,2023,https://boxd.it/6KqGP5,2.5,,,2024-06-25 +2024-06-29,Ferrari,2023,https://boxd.it/6Lv8pV,3.0,,,2024-06-28 +2024-06-30,Sicario,2015,https://boxd.it/6LUq2b,4.0,,,2024-06-29 +2024-07-21,Gladiator,2000,https://boxd.it/6Vcoij,4.5,,,2024-07-20 +2024-07-22,Perfect Blue,1997,https://boxd.it/6VIhi1,4.0,,,2024-07-21 +2024-07-23,Paprika,2006,https://boxd.it/6WcVWb,4.5,,,2024-07-22 +2024-07-24,Dream Scenario,2023,https://boxd.it/6WCG0t,4.0,,,2024-07-23 +2024-07-29,Yes Man,2008,https://boxd.it/6ZbFWJ,3.0,,,2024-07-29 +2024-07-30,Deadpool & Wolverine,2024,https://boxd.it/6Zo8Sb,3.5,,,2024-07-29 +2024-07-31,Source Code,2011,https://boxd.it/6ZPH0H,3.5,,,2024-07-30 +2024-08-04,Almost Famous,2000,https://boxd.it/71w9tb,4.0,,,2024-08-03 +2024-08-05,The Master,2012,https://boxd.it/72e6pN,3.0,,,2024-08-05 +2024-08-10,Millennium Actress,2001,https://boxd.it/747e7V,4.0,,,2024-08-10 +2024-08-14,The King of Comedy,1982,https://boxd.it/75VeGl,4.5,,,2024-08-13 +2024-08-15,American Fiction,2023,https://boxd.it/76lZ2J,4.0,,,2024-08-14 +2024-08-18,The Diary of a Teenage Girl,2015,https://boxd.it/77HSDB,3.5,,,2024-08-17 +2024-08-20,Punch-Drunk Love,2002,https://boxd.it/78Mk2P,4.0,,,2024-08-19 diff --git a/main.py b/main.py new file mode 100644 index 0000000..0b47046 --- /dev/null +++ b/main.py @@ -0,0 +1,18 @@ +from flask import Flask +from flask_cors import CORS +from src.api.routes import api_bp +from flask_sse import sse + +def create_app(): + app = Flask(__name__) + CORS(app) + + app.config["REDIS_URL"] = "redis://localhost:6379" + app.register_blueprint(sse, url_prefix='/stream') + app.register_blueprint(api_bp, url_prefix='/api') + + return app + +if __name__ == "__main__": + app = create_app() + app.run(debug=True) \ No newline at end of file diff --git a/reorderPosters.py b/reorderPosters.py new file mode 100644 index 0000000..add6d57 --- /dev/null +++ b/reorderPosters.py @@ -0,0 +1,37 @@ +import os +import pandas as pd +import re +from pathlib import Path + +def clean_filename(filename): + return re.sub(r'[\\/*?:"<>|]', '_', filename) + +csv_file = './data/diary.csv' +movies = pd.read_csv(csv_file) + +output_folder = 'posters' + +poster_files = [f for f in os.listdir(output_folder) if os.path.isfile(os.path.join(output_folder, f))] + +# Parcourir les films dans l'ordre du fichier CSV +for index, row in movies.iterrows(): + title = row['Name'] + year = row['Year'] + safe_title = clean_filename(f"{title}_{year}") + + # Trouver le fichier poster correspondant + for poster_file in poster_files: + if safe_title in poster_file: + # Déterminer le nouveau nom du fichier avec un préfixe basé sur l'index du film + new_name = f"{index+1:03d}_{poster_file}" + old_path = os.path.join(output_folder, poster_file) + new_path = os.path.join(output_folder, new_name) + + # Renommer le fichier + os.rename(old_path, new_path) + print(f"Renommé : {poster_file} -> {new_name}") + break + else: + print(f"Poster non trouvé pour : {title} ({year})") + +print("Tri terminé.") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/api/__pycache__/routes.cpython-312.pyc b/src/api/__pycache__/routes.cpython-312.pyc new file mode 100644 index 0000000..a7e92a8 Binary files /dev/null and b/src/api/__pycache__/routes.cpython-312.pyc differ diff --git a/src/api/routes.py b/src/api/routes.py new file mode 100644 index 0000000..795a0bf --- /dev/null +++ b/src/api/routes.py @@ -0,0 +1,137 @@ +import math +import os +from flask import Blueprint, jsonify, request +from werkzeug.utils import secure_filename +from src.services.tmdb_service import TMDBService +from src.services.image_service import ImageService +import pandas as pd +from functools import lru_cache + +api_bp = Blueprint('api', __name__) + +tmdb_service = TMDBService() +image_service = ImageService() + +UPLOAD_FOLDER = './data' +ALLOWED_EXTENSIONS = {'csv'} + +csv_file = os.path.join(UPLOAD_FOLDER, 'diary.csv') + +@lru_cache(maxsize=1000) +def get_poster_url(movie_name, movie_year): + movie = tmdb_service.search_movie(movie_name, movie_year) + if movie: + posters = tmdb_service.get_movie_posters(movie['id']) + if posters: + return posters[0] + return None + +@api_bp.route('/movies', methods=['GET']) +def get_movies(): + try: + if not os.path.exists(csv_file): + return jsonify({'movies': [], 'message': 'No movies found. Please upload a CSV file.'}), 200 + + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 10)) + + df = pd.read_csv(csv_file) + + df = df.iloc[::-1].reset_index(drop=True) + + total_movies = len(df) + total_pages = math.ceil(total_movies / limit) + + start_index = (page - 1) * limit + end_index = start_index + limit + + df_page = df.iloc[start_index:end_index] + + movies = df_page.to_dict('records') + + movies_with_posters = [ + { + 'id': start_index + i + 1, + 'Watched Date': movie['Watched Date'], + 'Name': movie['Name'], + 'Year': movie['Year'], + 'Poster': get_poster_url(movie['Name'], movie['Year']), + } + for i, movie in enumerate(movies) + ] + + return jsonify({ + 'movies': movies_with_posters, + 'total': total_movies, + 'page': page, + 'total_pages': total_pages + }) + except Exception as e: + print(f"Error in get_movies: {str(e)}") + return jsonify({'error': 'An error occurred while fetching movies'}), 500 + +@api_bp.route('/posters//', methods=['GET']) +def get_movie_posters(movie_name, movie_year): + + tmdb_service = TMDBService() + + movie = tmdb_service.search_movie(movie_name, movie_year) + + if movie: + posters = tmdb_service.get_movie_posters(movie['id']) + return jsonify({'posters': posters}) + else: + return jsonify({'error': 'Movie not found'}), 404 + +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +@api_bp.route('/upload-csv', methods=['POST']) +def upload_csv(): + if 'file' not in request.files: + return jsonify({'error': 'No file part'}), 400 + file = request.files['file'] + if file.filename == '': + return jsonify({'error': 'No selected file'}), 400 + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file_path = os.path.join(UPLOAD_FOLDER, filename) + file.save(file_path) + + try: + df = pd.read_csv(file_path) + df = df.rename(columns={ + 'Date': 'Watched Date', + 'Name': 'Name', + 'Year': 'Year' + }) + + df.to_csv(csv_file, index=False) + + return jsonify({'success': True, 'message': 'File uploaded and processed successfully'}), 200 + except Exception as e: + return jsonify({'error': f'Error processing file: {str(e)}'}), 500 + return jsonify({'error': 'File type not allowed'}), 400 + +@api_bp.route('/check-csv', methods=['GET']) +def check_csv(): + try: + if os.path.exists(csv_file): + return jsonify({'fileExists': True}), 200 + else: + return jsonify({'fileExists': False}), 200 + except Exception as e: + print(f"Error in check_csv: {str(e)}") + return jsonify({'error': 'An error occurred while checking the CSV file'}), 500 + +@api_bp.route('/delete-csv', methods=['DELETE']) +def delete_csv(): + try: + if os.path.exists(csv_file): + os.remove(csv_file) + return jsonify({'success': True, 'message': 'CSV file deleted successfully'}), 200 + else: + return jsonify({'error': 'CSV file not found'}), 404 + except Exception as e: + print(f"Error in delete_csv: {str(e)}") + return jsonify({'error': 'An error occurred while deleting the CSV file'}), 500 \ No newline at end of file diff --git a/src/gui/__pycache__/poster_selector.cpython-312.pyc b/src/gui/__pycache__/poster_selector.cpython-312.pyc new file mode 100644 index 0000000..b9ef7ea Binary files /dev/null and b/src/gui/__pycache__/poster_selector.cpython-312.pyc differ diff --git a/src/gui/components/__pycache__/movie_listbox.cpython-312.pyc b/src/gui/components/__pycache__/movie_listbox.cpython-312.pyc new file mode 100644 index 0000000..f00b114 Binary files /dev/null and b/src/gui/components/__pycache__/movie_listbox.cpython-312.pyc differ diff --git a/src/gui/components/__pycache__/poster_frame.cpython-312.pyc b/src/gui/components/__pycache__/poster_frame.cpython-312.pyc new file mode 100644 index 0000000..5e02bff Binary files /dev/null and b/src/gui/components/__pycache__/poster_frame.cpython-312.pyc differ diff --git a/src/gui/components/movie_listbox.py b/src/gui/components/movie_listbox.py new file mode 100644 index 0000000..644fdbb --- /dev/null +++ b/src/gui/components/movie_listbox.py @@ -0,0 +1,28 @@ +from tkinter import Listbox, SINGLE +from services.file_utils import clean_filename +import os + +class MovieListbox(Listbox): + def __init__(self, master, movies, on_select_callback): + super().__init__(master, selectmode=SINGLE, bg='#23272a', fg='white', + selectbackground='#7289da', width=50) + self.movies = movies + self.on_select_callback = on_select_callback + self.populate_listbox() + self.bind("<>", self.on_movie_select) + + def populate_listbox(self): + for index, row in self.movies.iterrows(): + movie_title = f"{row['Name']} ({row['Year']})" + safe_title = clean_filename(f"{row['Name']}_{row['Year']}") + poster_path = os.path.join('posters', f"{safe_title}.jpg") + + self.insert(index, movie_title) + if os.path.exists(poster_path): + self.itemconfig(index, {'fg': 'green'}) + + def on_movie_select(self, event): + selected_index = self.curselection() + if selected_index: + selected_movie = self.movies.iloc[selected_index[0]] + self.on_select_callback(selected_movie) \ No newline at end of file diff --git a/src/gui/components/poster_frame.py b/src/gui/components/poster_frame.py new file mode 100644 index 0000000..97d29bc --- /dev/null +++ b/src/gui/components/poster_frame.py @@ -0,0 +1,144 @@ +from tkinter import Frame, Label, Canvas, NW, StringVar, messagebox +from tkinter.ttk import Progressbar +from PIL import ImageTk +from threading import Thread +from concurrent.futures import ThreadPoolExecutor +import os + +from src.services.image_service import ImageService +from services.file_utils import clean_filename + +class PosterFrame(Frame): + def __init__(self, master): + super().__init__(master, bg='#2c2f33') + self.canvas = Canvas(self, bg='#2c2f33', highlightthickness=0) + self.canvas.pack(side="right", fill="both", expand=True) + + self.poster_frame = Frame(self.canvas, bg='#2c2f33') + self.canvas.create_window((0, 0), window=self.poster_frame, anchor=NW) + + self.poster_frame.bind("", self.on_frame_configure) + self.master.bind("", self.on_window_resize) + self.master.bind_all("", self.on_mouse_wheel) + + self.loading_label = None + self.progress_bar = None + self.resizing = False + self.current_movie = None + + self.initial_message = Label(self.poster_frame, text="Aucun poster à afficher. Veuillez sélectionner un film.", bg='#2c2f33', fg='white') + self.initial_message.pack(pady=20) + + def show_posters(self, posters, movie, image_service): + self.clear_poster_frame() + self.current_movie = movie + + self.loading_label = Label(self.poster_frame, text="Chargement des posters...", + bg='#2c2f33', fg='white') + self.loading_label.grid(row=0, column=0, pady=20) + + self.progress_bar = Progressbar(self.poster_frame, orient="horizontal", + mode="determinate", length=400) + self.progress_bar.grid(row=1, column=0, pady=10) + + Thread(target=self.download_and_display_posters, + args=(posters, image_service)).start() + + def download_and_display_posters(self, posters, image_service): + max_columns = max(1, self.canvas.winfo_width() // 200) + images = [] + + with ThreadPoolExecutor(max_workers=10) as executor: + future_to_url = {executor.submit(image_service.download_image, + f"https://image.tmdb.org/t/p/w200{poster['file_path']}"): + poster['file_path'] for poster in posters} + + total = len(future_to_url) + for i, future in enumerate(future_to_url): + image = future.result() + if image: + images.append(image) + self.update_progress_bar(i + 1, total) + + self.display_posters(images, max_columns) + + def display_posters(self, images, max_columns): + self.clear_poster_frame() + for idx, image in enumerate(images): + image.thumbnail((200, 300)) + photo = ImageTk.PhotoImage(image) + label = Label(self.poster_frame, image=photo, bg='#2c2f33') + label.image = photo + row = idx // max_columns + col = idx % max_columns + label.grid(row=row, column=col, padx=5, pady=5) + label.bind("", lambda e, idx=idx: self.download_selected_poster(idx)) + + def download_selected_poster(self, idx): + poster = self.posters[idx] + poster_path = poster['file_path'] + poster_url = f"https://image.tmdb.org/t/p/original{poster_path}" + + title = self.current_movie['Name'] + year = self.current_movie['Year'] + safe_title = clean_filename(f"{title}_{year}") + + output_folder = 'posters' + os.makedirs(output_folder, exist_ok=True) + save_path = os.path.join(output_folder, f"{safe_title}.jpg") + + if os.path.exists(save_path): + response = messagebox.askyesno("Poster déjà existant", + f"Le poster pour {title} ({year}) existe déjà. Voulez-vous l'écraser?") + if not response: + return + + # Téléchargement du poster en haute qualité + image = self.image_service.download_image(poster_url, save_path) + + if image: + messagebox.showinfo("Téléchargement réussi", f"Poster téléchargé et enregistré : {save_path}") + # Mettre à jour la liste des films pour refléter le nouveau téléchargement + self.master.movie_listbox.populate_listbox() + else: + messagebox.showerror("Erreur", "Échec du téléchargement du poster.") + + def update_progress_bar(self, value, total): + if self.progress_bar: + progress = (value / total) * 100 + self.master.after(0, lambda: self.safe_update_progress(progress)) + + def safe_update_progress(self, progress): + if self.progress_bar and self.progress_bar.winfo_exists(): + self.progress_bar['value'] = progress + self.update_idletasks() + + def clear_poster_frame(self): + for widget in self.poster_frame.winfo_children(): + widget.destroy() + + def on_frame_configure(self, event): + self.canvas.configure(scrollregion=self.canvas.bbox("all")) + + def on_window_resize(self, event): + if not self.resizing: + self.resizing = True + self.after(100, self.resize_done) + + def resize_done(self): + self.resizing = False + self.update_poster_layout() + + def update_poster_layout(self): + for widget in self.poster_frame.winfo_children(): + widget.grid_forget() + + max_columns = max(1, self.poster_frame.winfo_width() // 200) + + for idx, widget in enumerate(self.poster_frame.winfo_children()): + row = idx // max_columns + col = idx % max_columns + widget.grid(row=row, column=col, padx=5, pady=5) + + def on_mouse_wheel(self, event): + self.canvas.yview_scroll(-1 * (event.delta // 120), "units") \ No newline at end of file diff --git a/src/gui/poster_selector.py b/src/gui/poster_selector.py new file mode 100644 index 0000000..efe9d03 --- /dev/null +++ b/src/gui/poster_selector.py @@ -0,0 +1,28 @@ +from tkinter import Tk, Frame +from .components.movie_listbox import MovieListbox +from .components.poster_frame import PosterFrame +from src.services.tmdb_service import TMDBService +from src.services.image_service import ImageService + +class PosterSelector(Tk): + def __init__(self, movies): + super().__init__() + self.movies = movies + self.tmdb_service = TMDBService() + self.image_service = ImageService() + + self.title("Sélectionnez un Poster") + self.geometry("1200x600") + self.configure(bg='#2c2f33') + + self.movie_listbox = MovieListbox(self, movies, self.on_movie_select) + self.poster_frame = PosterFrame(self) + + self.movie_listbox.pack(side="left", fill="y") + self.poster_frame.pack(side="right", fill="both", expand=True) + + def on_movie_select(self, selected_movie): + movie = self.tmdb_service.search_movie(selected_movie['Name'], selected_movie['Year']) + if movie: + posters = self.tmdb_service.get_movie_posters(movie['id']) + self.poster_frame.show_posters(posters, selected_movie, self.image_service) \ No newline at end of file diff --git a/src/services/__pycache__/image_service.cpython-312.pyc b/src/services/__pycache__/image_service.cpython-312.pyc new file mode 100644 index 0000000..8135912 Binary files /dev/null and b/src/services/__pycache__/image_service.cpython-312.pyc differ diff --git a/src/services/__pycache__/tmdb_service.cpython-312.pyc b/src/services/__pycache__/tmdb_service.cpython-312.pyc new file mode 100644 index 0000000..2e0cac1 Binary files /dev/null and b/src/services/__pycache__/tmdb_service.cpython-312.pyc differ diff --git a/src/services/file_utils.py b/src/services/file_utils.py new file mode 100644 index 0000000..bb81a0e --- /dev/null +++ b/src/services/file_utils.py @@ -0,0 +1,8 @@ +import re +import os + +def clean_filename(filename): + return re.sub(r'[\\/*?:"<>|]', '_', filename) + +def ensure_directory(directory): + os.makedirs(directory, exist_ok=True) \ No newline at end of file diff --git a/src/services/image_service.py b/src/services/image_service.py new file mode 100644 index 0000000..d40992e --- /dev/null +++ b/src/services/image_service.py @@ -0,0 +1,13 @@ +import requests +from PIL import Image +import io + +class ImageService: + def download_image(self, image_url, save_path=None): + response = requests.get(image_url, stream=True) + if response.status_code == 200: + image = Image.open(io.BytesIO(response.content)) + if save_path: + image.save(save_path) + return image + return None \ No newline at end of file diff --git a/src/services/tmdb_service.py b/src/services/tmdb_service.py new file mode 100644 index 0000000..7593a31 --- /dev/null +++ b/src/services/tmdb_service.py @@ -0,0 +1,33 @@ +import os +import requests +from dotenv import load_dotenv + +load_dotenv() + +class TMDBService: + def __init__(self): + self.api_key = os.getenv('TMDB_API_KEY') + self.access_token = os.getenv('TMDB_ACCESS_TOKEN') + self.base_url = 'https://api.themoviedb.org/3' + + def search_movie(self, title, year): + url = f"{self.base_url}/search/movie" + params = { + 'api_key': self.api_key, + 'query': title, + 'year': year + } + response = requests.get(url, params=params) + response_data = response.json() + if response_data['results']: + return response_data['results'][0] + return None + + def get_movie_posters(self, movie_id): + url = f"{self.base_url}/movie/{movie_id}/images?include_image_language=en" + headers = { + "accept": "application/json", + "Authorization": f"Bearer {self.access_token}" + } + response = requests.get(url, headers=headers) + return response.json().get('posters', []) \ No newline at end of file