From 98d4f308ba4da8c6203ba4e95a6ba4ae6ea7bbad Mon Sep 17 00:00:00 2001 From: Hugo Pierret Date: Wed, 24 Sep 2025 17:48:38 +0200 Subject: [PATCH] Add api dependencies --- main.py | 12 ++- src/api/__pycache__/routes.cpython-312.pyc | Bin 9725 -> 12702 bytes src/api/routes.py | 78 ++++++++++++++++-- .../__pycache__/tmdb_service.cpython-312.pyc | Bin 1931 -> 1960 bytes src/services/tmdb_service.py | 2 +- 5 files changed, 81 insertions(+), 11 deletions(-) diff --git a/main.py b/main.py index d233c9f..697afc8 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,17 @@ from config import config def create_app(): app = Flask(__name__) - CORS(app) + + origins = [ + "http://localhost:3000", + "http://127.0.0.1:3000" + ] + CORS(app, supports_credentials=True, origins=origins) + + app.config.update( + SESSION_COOKIE_SAMESITE="None", # necessary for cross-site cookies + SESSION_COOKIE_SECURE=False # True in prod (HTTPS) + ) env = os.environ.get('FLASK_ENV', 'default') app.config.from_object(config[env]) diff --git a/src/api/__pycache__/routes.cpython-312.pyc b/src/api/__pycache__/routes.cpython-312.pyc index 66b36120bc8492da09167ea56ac5193bdda7880d..6adee4811e1bd2d8b1de46fc1678772a425a0147 100644 GIT binary patch delta 4874 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 delta 2268 zcmaJ?YiJx*6rMYeoqg|Svztve*+*X8OuM?<`buK#1GCYh)oK;nwI!@O(`J)>#5JI>mxCi5-@DB6iyW!N)YN45 zyp#;s>hju@!9B&^lPYcu)CrGyGt0@b-mgQ6tD)Q%q1;=Mx2(6juY_(nu72eSec2v6 zy>VgmvNyhXzx=hFp+WeBu1B^3Sq7Nx*vYeLGEL%AvFcW_Li^cX*B?7DjqS=GE*Y~{ zxn8rCSz4}Hwq#mV3(y2wJtgztWT{@W3^0iLe%cROI*2AO57ny4q*?Zgd(g#mkTUIH zpSyD%Sc2_dE!?C#LD9Du@%SYV-)j_u<=7_w;1=4BGPXcdXc*-PvNmKL$k5kRfTfTq z#%_IsAMR#n{Y6I%q!M94Da?|A88X103f$buH{mT-D)mE#KDobKG3XxfGuStQ5fWpa z!HoEl$exj}U8jo{=I1o+SdspHA0#wjx!8}vxc0w(Eg;O32AOuVTw6DBu{+y(eZG}Q zT>nU0k_1>P-n|PTW??rtHH0yEy95iX;xvuTSVI;zgVQvYw}u)bdpQwd@lb-q*;r@; zNwTR>vTaQcsN{4g3441tluV{pLJYv+&dZi-Wvxqk*y?A$hsM1e4>E6Hj!m?0*whD3 zYv86ywme=0bgu$XBeq(qSLUiUb3$Wh+P63`7wu&~w8xKb1o4r`1e{6AIx=gdYIQ5M zd{SD7;t|kI$ne;01yh?GB6K%u@fdBH8bzI6hwB@W-Gpoy*$A>xV77xAwPMw<9aeo3 z@Jz?iG9d_i5NlW#-HUFpPmXhXJF4ymwgzvudv3N;FX}u<8SMC&x56AaCVc0QoSHc? zb6UINA2@c~q8b?{iI2Lr-7uaIE+t%fuXHIL%d1k85RjXylJ_p|>xh$f-c>i2mOorJ zE%QET&B+Zz{omc=OO&M7g>&t$4VK@Mk~mwz|8-KaqC9KunZpOVK6?&uEaiMU#uHa zH%mA%U2H*4vX_HSmQJr5wN!B7)L0#pL}(Bb?$3%v7j>yjR)(l9?-zFomWO>B?7=Yd zyl4g3`{^D>L(~<^&$cQ(>}vW!5@GN5#M#avKj~nTL)VZlc5)~R_xRPJBvDy&=rQ2i zwL`!!YS#d|f7V7F%VQ?HzKiT*+t(LJFFU*b?{!!%z>I^-iBoFYtk+tA<9+3A9%dhG z$lPi>sqyff0q0|TOsi9$U;!*m!)sI zACDKbIKx(mcCt4!9ida8J_C>W4hPhdJjAYM`qb~7zT^7Sp2a^iC&z-iwa(RfoSUKWZNqzE92<(?NP&TKx+|8kC1>-t>9*i2sp z9sXR+9W0rPvXR{F0S5{GCJAEKk_ggLh&`J#0$pSbb=f5mq@`h|Y`mxMvcLD-#JS;P UJy*!}zla*yL(t`hmdKO+11QGuR{#J2 diff --git a/src/api/routes.py b/src/api/routes.py index bfd90b6..89d042c 100644 --- a/src/api/routes.py +++ b/src/api/routes.py @@ -4,6 +4,7 @@ import os import zipfile import requests +import uuid from flask import Blueprint, jsonify, request, session, send_file from werkzeug.utils import secure_filename from src.services.tmdb_service import TMDBService @@ -25,9 +26,7 @@ user_csvs = {} 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 movie['poster_path'] return None @@ -106,6 +105,10 @@ def allowed_file(filename): def upload_csv(): user_id = session.get('user_id') + if not user_id: + user_id = str(uuid.uuid4()) + session['user_id'] = user_id + if 'file' not in request.files: return jsonify({'error': 'No file part'}), 400 file = request.files['file'] @@ -160,17 +163,74 @@ def delete_csv(): return jsonify({'error': 'An error occurred while deleting the CSV file'}), 500 +@api_bp.route('/fetch-diary', methods=['POST']) +def fetch_diary_from_username(): + """ + Récupère le diary via l'API publique (api.hugo-pierret.be), transforme en CSV et + le stocke dans user_csvs[user_id] pour que le front continue à utiliser le même flux que l'upload. + """ + try: + data = request.get_json() or {} + username = data.get('username') + if not username: + return jsonify({'error': 'username required'}), 400 + + user_id = session.get('user_id') + if not user_id: + user_id = str(uuid.uuid4()) + session['user_id'] = user_id + + api_url = f"https://api.hugo-pierret.be/letterboxd/diary?username={username}" + resp = requests.get(api_url) + if resp.status_code != 200: + return jsonify({'error': 'Failed to fetch diary from upstream API'}), 502 + + payload = resp.json() + entries = payload.get('entries', []) + if not entries: + return jsonify({'error': 'No entries returned by upstream API'}), 404 + + rows = [] + import re + for e in entries: + title = e.get('title') or "" + date = e.get('date') or "" + + m = re.match(r"^(?P.+?)\s*\((?P\d{4})\)\s*$", title) + if m: + name = m.group('name').strip() + year = m.group('year') + else: + year_match = re.search(r"(\d{4})", title) + year = year_match.group(1) if year_match else "" + name = re.sub(r"\(\d{4}\)", "", title).strip() + + rows.append({ + "Watched Date": date, + "Name": name, + "Year": year + }) + + import pandas as pd, io + rows.reverse() + df = pd.DataFrame(rows) + csv_str = df.to_csv(index=False) + + user_csvs[user_id] = csv_str + + return jsonify({'success': True, 'message': 'Diary fetched and stored', 'total': len(rows)}), 200 + + except Exception as e: + print("Error in fetch_diary_from_username:", str(e)) + return jsonify({'error': 'An internal error occurred'}), 500 + + @api_bp.route('/download-posters', methods=['POST']) def download_posters(): try: data = request.get_json() selected_posters = data.get('posters', []) - -<<<<<<< HEAD - print(data) - -======= ->>>>>>> 496f9e8f62940d1f3544da6e72b74be07e18bd25 + if not selected_posters: return jsonify({'error': 'No posters selected'}), 400 diff --git a/src/services/__pycache__/tmdb_service.cpython-312.pyc b/src/services/__pycache__/tmdb_service.cpython-312.pyc index 2e0cac17630092aed350d38c17dfee99977d90ee..f23a0b50326ea4704d290944057583946e66ff86 100644 GIT binary patch delta 113 zcmeC?U%}6NnwOW00SLVICT2Kq-`OJ-tGrEWleaY<@Xv2H+Sa&~Hw z?q+tTC}u59{mk6N^weVe%)I2B(v;MAFgHFYF)zIoC~ljYr;}EslUJIPGr5`d0ZZfs LzNpRVY$}WZ1j;4{ delta 55 zcmZ3%-_6f^nwOW00SH=cuB2IS%g%JR&y${y_ diff --git a/src/services/tmdb_service.py b/src/services/tmdb_service.py index 7593a31..f3b2d48 100644 --- a/src/services/tmdb_service.py +++ b/src/services/tmdb_service.py @@ -24,7 +24,7 @@ class TMDBService: return None def get_movie_posters(self, movie_id): - url = f"{self.base_url}/movie/{movie_id}/images?include_image_language=en" + url = f"{self.base_url}/movie/{movie_id}/images?include_image_language=en,fr,null" headers = { "accept": "application/json", "Authorization": f"Bearer {self.access_token}"