first commit

This commit is contained in:
2024-09-05 18:34:12 +02:00
commit d175ce4944
18 changed files with 514 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.env
.venv

66
data/diary.csv Normal file
View File

@@ -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
1 Watched Date Name Year Letterboxd URI Rating Rewatch Tags Watched Date
2 2023-12-15 Ghost in the Shell 1995 https://boxd.it/5k8vT3 3.5 2023-12-13
3 2024-01-03 Baby Driver 2017 https://boxd.it/5tDbaP 4.0 2024-01-02
4 2024-01-04 The Shawshank Redemption 1994 https://boxd.it/5ugTgP 4.5 2024-01-03
5 2024-01-05 Léon: The Professional 1994 https://boxd.it/5uRr6B 4.0 2024-01-04
6 2024-01-06 Brothers 2009 https://boxd.it/5vuRQN 3.5 2024-01-05
7 2024-01-07 Crazy, Stupid, Love. 2011 https://boxd.it/5wgeFn 4.0 2024-01-06
8 2024-01-08 There Will Be Blood 2007 https://boxd.it/5x4B37 4.5 2024-01-07
9 2024-01-09 (500) Days of Summer 2009 https://boxd.it/5xDHe1 4.0 2024-01-08
10 2024-01-20 Dallas Buyers Club 2013 https://boxd.it/5DrLPb 4.0 2024-01-19
11 2024-01-22 Boogie Nights 1997 https://boxd.it/5EEjCN 4.0 2024-01-21
12 2024-01-24 Saltburn 2023 https://boxd.it/5FxIYT 4.0 2024-01-23
13 2024-01-25 Under the Skin 2013 https://boxd.it/5G0SG9 2.5 2024-01-24
14 2024-01-26 Anatomy of a Fall 2023 https://boxd.it/5GuroR 4.0 2024-01-25
15 2024-01-31 Poor Things 2023 https://boxd.it/5J4AI7 4.5 2024-01-30
16 2024-02-01 In the Mood for Love 2000 https://boxd.it/5JweK7 4.5 2024-01-31
17 2024-02-07 Black Swan 2010 https://boxd.it/5MhygD 4.0 2024-02-06
18 2024-02-13 Rain Man 1988 https://boxd.it/5OXE5j 4.0 2024-02-12
19 2024-02-20 The 40 Year Old Virgin 2005 https://boxd.it/5Sgvxj 3.5 2024-02-19
20 2024-02-21 Captain Fantastic 2016 https://boxd.it/5SHgol 4.0 2024-02-20
21 2024-02-23 The Prestige 2006 https://boxd.it/5TwaTp 4.5 2024-02-22
22 2024-02-29 The Shining 1980 https://boxd.it/5WgsV9 4.0 2024-02-28
23 2024-03-02 Dune: Part Two 2024 https://boxd.it/5X4Ep5 5.0 2024-03-01
24 2024-03-12 Fantastic Mr. Fox 2009 https://boxd.it/622Xk5 4.5 2024-03-11
25 2024-03-13 Past Lives 2023 https://boxd.it/62uP1F 4.5 2024-03-12
26 2024-03-19 Little Women 2019 https://boxd.it/65jkNX 3.0 2024-03-18
27 2024-03-21 The Perks of Being a Wallflower 2012 https://boxd.it/668ca1 4.0 2024-03-20
28 2024-03-28 Amélie 2001 https://boxd.it/69lEoN 4.5 2024-03-27
29 2024-03-29 Lost in Translation 2003 https://boxd.it/69Kn1n 4.0 2024-03-28
30 2024-04-03 No Country for Old Men 2007 https://boxd.it/6clZZd 4.0 2024-04-02
31 2024-04-05 Fargo 1996 https://boxd.it/6dadIv 3.5 2024-04-04
32 2024-04-09 No Hard Feelings 2023 https://boxd.it/6f0cwL 2.5 2024-04-08
33 2024-04-17 OSS 117: Cairo, Nest of Spies 2006 https://boxd.it/6i7AOl 4.0 2024-04-16
34 2024-04-20 The Neon Demon 2016 https://boxd.it/6j9IF5 3.0 2024-04-19
35 2024-04-23 Taxi Driver 1976 https://boxd.it/6ktjeR 3.5 2024-04-22
36 2024-04-30 The Theory of Everything 2014 https://boxd.it/6nlzHL 4.5 2024-04-29
37 2024-05-09 Civil War 2024 https://boxd.it/6rbidl 4.5 2024-05-08
38 2024-05-10 Scott Pilgrim vs. the World 2010 https://boxd.it/6rxEAl 4.0 2024-05-09
39 2024-05-15 Asteroid City 2023 https://boxd.it/6txkqD 3.5 2024-05-14
40 2024-05-22 Battle of the Sexes 2017 https://boxd.it/6wiCsp 3.5 2024-05-22
41 2024-05-26 War Dogs 2016 https://boxd.it/6xS8YB 3.5 2024-05-25
42 2024-05-28 The Creator 2023 https://boxd.it/6yPVwT 4.5 2024-05-27
43 2024-05-30 Full Metal Jacket 1987 https://boxd.it/6zEwVV 4.0 2024-05-29
44 2024-06-04 Dead Poets Society 1989 https://boxd.it/6BNjNv 3.5 2024-06-03
45 2024-06-05 Perfect Days 2023 https://boxd.it/6C9zSb 4.0 2024-06-04
46 2024-06-07 Burn After Reading 2008 https://boxd.it/6CQVG3 3.0 2024-06-06
47 2024-06-12 Before Sunrise 1995 https://boxd.it/6EWJMD 4.0 2024-06-11
48 2024-06-20 OSS 117: Lost in Rio 2009 https://boxd.it/6I1d3j 3.5 2024-06-19
49 2024-06-23 Inside Out 2 2024 https://boxd.it/6J9mU7 4.0 2024-06-22
50 2024-06-26 May December 2023 https://boxd.it/6KqGP5 2.5 2024-06-25
51 2024-06-29 Ferrari 2023 https://boxd.it/6Lv8pV 3.0 2024-06-28
52 2024-06-30 Sicario 2015 https://boxd.it/6LUq2b 4.0 2024-06-29
53 2024-07-21 Gladiator 2000 https://boxd.it/6Vcoij 4.5 2024-07-20
54 2024-07-22 Perfect Blue 1997 https://boxd.it/6VIhi1 4.0 2024-07-21
55 2024-07-23 Paprika 2006 https://boxd.it/6WcVWb 4.5 2024-07-22
56 2024-07-24 Dream Scenario 2023 https://boxd.it/6WCG0t 4.0 2024-07-23
57 2024-07-29 Yes Man 2008 https://boxd.it/6ZbFWJ 3.0 2024-07-29
58 2024-07-30 Deadpool & Wolverine 2024 https://boxd.it/6Zo8Sb 3.5 2024-07-29
59 2024-07-31 Source Code 2011 https://boxd.it/6ZPH0H 3.5 2024-07-30
60 2024-08-04 Almost Famous 2000 https://boxd.it/71w9tb 4.0 2024-08-03
61 2024-08-05 The Master 2012 https://boxd.it/72e6pN 3.0 2024-08-05
62 2024-08-10 Millennium Actress 2001 https://boxd.it/747e7V 4.0 2024-08-10
63 2024-08-14 The King of Comedy 1982 https://boxd.it/75VeGl 4.5 2024-08-13
64 2024-08-15 American Fiction 2023 https://boxd.it/76lZ2J 4.0 2024-08-14
65 2024-08-18 The Diary of a Teenage Girl 2015 https://boxd.it/77HSDB 3.5 2024-08-17
66 2024-08-20 Punch-Drunk Love 2002 https://boxd.it/78Mk2P 4.0 2024-08-19

18
main.py Normal file
View File

@@ -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)

37
reorderPosters.py Normal file
View File

@@ -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é.")

0
requirements.txt Normal file
View File

Binary file not shown.

137
src/api/routes.py Normal file
View File

@@ -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/<movie_name>/<movie_year>', 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

Binary file not shown.

View File

@@ -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("<<ListboxSelect>>", 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)

View File

@@ -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("<Configure>", self.on_frame_configure)
self.master.bind("<Configure>", self.on_window_resize)
self.master.bind_all("<MouseWheel>", 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("<Button-1>", 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")

View File

@@ -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)

Binary file not shown.

Binary file not shown.

View File

@@ -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)

View File

@@ -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

View File

@@ -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', [])