Nettoyage des fichiers de sauvegarde temporaires
This commit is contained in:
-21
@@ -1,21 +0,0 @@
|
|||||||
# On part d'une version légère de Python
|
|
||||||
FROM python:3.9-slim
|
|
||||||
|
|
||||||
# On définit le dossier de travail dans le conteneur
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# On installe les dépendances
|
|
||||||
COPY requirements.txt .
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
|
|
||||||
# On copie le code
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# On crée le dossier pour le cache
|
|
||||||
RUN mkdir -p /data
|
|
||||||
|
|
||||||
# On expose le port 5000
|
|
||||||
EXPOSE 5000
|
|
||||||
|
|
||||||
# Commande de démarrage
|
|
||||||
CMD ["python", "app.py"]
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
import os, json, logging, requests, re, time
|
|
||||||
from datetime import datetime
|
|
||||||
from flask import Flask, render_template, redirect, url_for
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
||||||
logger = logging.getLogger("CineSynthese")
|
|
||||||
|
|
||||||
CACHE_FILE = "/data/movies_cache.json"
|
|
||||||
HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36'}
|
|
||||||
|
|
||||||
def get_today_formats():
|
|
||||||
now = datetime.now()
|
|
||||||
jours_courts = ["Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"]
|
|
||||||
day_short = jours_courts[now.weekday()]
|
|
||||||
return {
|
|
||||||
"cosmo_id": f"seance-{now.strftime('%Y-%m-%d')}",
|
|
||||||
"cratere_date": f"{day_short}. {now.day:02d}/{now.month:02d}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- 1. SCRAPER ABC ---
|
|
||||||
def scrape_abc():
|
|
||||||
url = "https://abc-toulouse.fr/horaires.html"
|
|
||||||
movies = []
|
|
||||||
try:
|
|
||||||
r = requests.get(url, headers=HEADERS, timeout=15)
|
|
||||||
soup = BeautifulSoup(r.content, 'html.parser')
|
|
||||||
movie_blocks = soup.find_all('div', class_='table_infos')
|
|
||||||
for block in movie_blocks:
|
|
||||||
title_tag = block.find('h2')
|
|
||||||
if not title_tag: continue
|
|
||||||
title = title_tag.get_text().strip()
|
|
||||||
img_tag = block.find('img')
|
|
||||||
img_url = "https://abc-toulouse.fr" + img_tag.get('src') if img_tag else ""
|
|
||||||
next_div = block.find_next_sibling()
|
|
||||||
showtimes_text = next_div.get_text(" ") if next_div else block.get_text(" ")
|
|
||||||
times = re.findall(r'(\d{2}:\d{2})', showtimes_text)
|
|
||||||
if times:
|
|
||||||
movies.append({"title": title, "img": img_url, "times": sorted(list(set(times))), "cinema": "ABC", "color": "#00bcd4"})
|
|
||||||
except: logger.error("Fail ABC")
|
|
||||||
return movies
|
|
||||||
|
|
||||||
# --- 2. SCRAPER LE CRATÈRE ---
|
|
||||||
def scrape_cratere():
|
|
||||||
url_main = "https://www.cinemalecratere.fr/films"
|
|
||||||
target_date = get_today_formats()["cratere_date"]
|
|
||||||
movies_dict = {}
|
|
||||||
session = requests.Session()
|
|
||||||
try:
|
|
||||||
r = session.get(url_main, headers=HEADERS, timeout=15)
|
|
||||||
soup = BeautifulSoup(r.content, 'html.parser')
|
|
||||||
items = soup.find_all('div', attrs={'data-search': True})
|
|
||||||
for item in items:
|
|
||||||
if target_date in item['data-search']:
|
|
||||||
title_tag = item.find('span', class_='uk-text-bold')
|
|
||||||
title = title_tag.get_text(strip=True) if title_tag else "Inconnu"
|
|
||||||
time_match = re.search(r'(\d{2}h\d{2})', item['data-search'])
|
|
||||||
show_time = time_match.group(0).replace('h', ':') if time_match else "Séance"
|
|
||||||
if title not in movies_dict:
|
|
||||||
img_url = ""
|
|
||||||
link_tag = item.find('a', href=True)
|
|
||||||
if link_tag:
|
|
||||||
r_det = session.get("https://www.cinemalecratere.fr" + link_tag['href'], timeout=10)
|
|
||||||
img_tag = BeautifulSoup(r_det.content, 'html.parser').find('img', class_='el-image')
|
|
||||||
if img_tag:
|
|
||||||
img_url = img_tag.get('src')
|
|
||||||
if not img_url.startswith('http'): img_url = "https://www.cinemalecratere.fr" + img_url
|
|
||||||
movies_dict[title] = {"title": title, "img": img_url, "times": [show_time], "cinema": "Le Cratère", "color": "#e62117"}
|
|
||||||
else:
|
|
||||||
if show_time not in movies_dict[title]["times"]: movies_dict[title]["times"].append(show_time)
|
|
||||||
except: logger.error("Fail Cratère")
|
|
||||||
return list(movies_dict.values())
|
|
||||||
|
|
||||||
# --- 3. SCRAPER COSMOGRAPH (REPRISE EXACTE DE TON CODE FONCTIONNEL) ---
|
|
||||||
def scrape_cosmo():
|
|
||||||
BASE_URL = "https://www.american-cosmograph.fr"
|
|
||||||
url_horaires = f"{BASE_URL}/les-horaires.html"
|
|
||||||
target_id = get_today_formats()["cosmo_id"]
|
|
||||||
movies_today = []
|
|
||||||
session = requests.Session()
|
|
||||||
try:
|
|
||||||
r = session.get(url_horaires, headers=HEADERS, timeout=15)
|
|
||||||
soup = BeautifulSoup(r.content, 'html.parser')
|
|
||||||
day_container = soup.find('div', id=target_id)
|
|
||||||
if not day_container: return []
|
|
||||||
items = day_container.find_all('li', class_='heureReady')
|
|
||||||
for item in items:
|
|
||||||
title_tag = item.select_one('.filmTitle a')
|
|
||||||
if not title_tag: continue
|
|
||||||
title = title_tag.get_text(strip=True)
|
|
||||||
detail_path = title_tag.get('href')
|
|
||||||
time_tag = item.select_one('.horaire_debut')
|
|
||||||
time_raw = time_tag.get_text(strip=True) if time_tag else ""
|
|
||||||
show_time_match = re.search(r'\d{2}h\d{2}', time_raw)
|
|
||||||
show_time = show_time_match.group(0).replace('h', ':') if show_time_match else "Séance"
|
|
||||||
img_url = ""
|
|
||||||
if detail_path:
|
|
||||||
detail_url = f"{BASE_URL}{detail_path}"
|
|
||||||
try:
|
|
||||||
r_detail = session.get(detail_url, headers=HEADERS, timeout=10)
|
|
||||||
soup_detail = BeautifulSoup(r_detail.content, 'html.parser')
|
|
||||||
img_tag = soup_detail.find('img', class_='fc_field_image')
|
|
||||||
if img_tag:
|
|
||||||
img_url = img_tag.get('src')
|
|
||||||
if img_url and not img_url.startswith('http'):
|
|
||||||
img_url = f"{BASE_URL}{img_url}"
|
|
||||||
except: pass
|
|
||||||
movies_today.append({"title": title, "img": img_url, "times": [show_time], "cinema": "American Cosmograph", "color": "#ffc107"})
|
|
||||||
time.sleep(0.1)
|
|
||||||
except: pass
|
|
||||||
|
|
||||||
# Fusion des horaires pour le Cosmo
|
|
||||||
unique_movies = {}
|
|
||||||
for m in movies_today:
|
|
||||||
if m['title'] not in unique_movies:
|
|
||||||
unique_movies[m['title']] = m
|
|
||||||
else:
|
|
||||||
if m['times'][0] not in unique_movies[m['title']]['times']:
|
|
||||||
unique_movies[m['title']]['times'].append(m['times'][0])
|
|
||||||
unique_movies[m['title']]['times'].sort()
|
|
||||||
return list(unique_movies.values())
|
|
||||||
|
|
||||||
# --- LOGIQUE MISE À JOUR ---
|
|
||||||
def update_all_data():
|
|
||||||
logger.info("🕒 Mise à jour programmée...")
|
|
||||||
try:
|
|
||||||
data = scrape_abc() + scrape_cratere() + scrape_cosmo()
|
|
||||||
data = sorted(data, key=lambda x: x['times'][0] if x['times'] else "99:99")
|
|
||||||
payload = {"last_update": datetime.now().strftime("%d/%m/%Y à %H:%M"), "movies": data}
|
|
||||||
os.makedirs(os.path.dirname(CACHE_FILE), exist_ok=True)
|
|
||||||
with open(CACHE_FILE, 'w') as f: json.dump(payload, f)
|
|
||||||
return payload
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Erreur MAJ: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
scheduler = BackgroundScheduler()
|
|
||||||
scheduler.add_job(func=update_all_data, trigger="cron", hour=0, minute=5)
|
|
||||||
scheduler.start()
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
if not os.path.exists(CACHE_FILE):
|
|
||||||
cache = update_all_data()
|
|
||||||
else:
|
|
||||||
with open(CACHE_FILE, 'r') as f: cache = json.load(f)
|
|
||||||
if isinstance(cache, list): cache = update_all_data()
|
|
||||||
return render_template('index.html', films=cache["movies"], updated=cache["last_update"])
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
app.run(host='0.0.0.0', port=5000)
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
services:
|
|
||||||
cine-app:
|
|
||||||
build: .
|
|
||||||
container_name: cine_toulouse
|
|
||||||
ports:
|
|
||||||
- "5001:5000"
|
|
||||||
volumes:
|
|
||||||
# On lie le dossier ./data du NUC au dossier /data du conteneur
|
|
||||||
- ./data:/data
|
|
||||||
restart: unless-stopped
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
flask
|
|
||||||
requests
|
|
||||||
beautifulsoup4
|
|
||||||
thefuzz
|
|
||||||
gunicorn
|
|
||||||
Reference in New Issue
Block a user