commit 370974a57d2a74b8123ea30399f743b5e376af7f Author: jonnybravo Date: Tue Aug 26 14:35:48 2025 +0200 init diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..f4e97a3 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,34 @@ +name: Deploy to ras-dan-01 + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install Ansible + run: | + python3 -m pip install --user ansible + ansible-galaxy collection install community.docker + + - name: Setup Ansible Vault + run: | + mkdir -p ~/.ansible + echo "${{ secrets.VAULT_PASSWORD }}" > ~/.ansible/.vault-pass + chmod 600 ~/.ansible/.vault-pass + + - name: Setup SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.ANSIBLE_PRIVATE_KEY }}" > ~/.ssh/ansible + chmod 600 ~/.ssh/ansible + + - name: Run Ansible Playbook + run: ansible-playbook deploy_remote.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ddd157c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM python:3.9-slim-bookworm + +WORKDIR /app + +# Install build dependencies for mariadb-connector-c +RUN apt-get update && apt-get install -y \ + default-libmysqlclient-dev \ + gcc \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +EXPOSE 5082 + +CMD ["python3", "main.py"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f13758e --- /dev/null +++ b/README.md @@ -0,0 +1,118 @@ +# Movie-DB + +Eine webbasierte Anwendung zur Verwaltung einer persönlichen Filmsammlung. + +## Funktionsumfang (Features) + +* **Benutzer-System:** Vollständiges Registrierungs- und Login-System, sodass jeder Benutzer seine eigene, private Filmsammlung verwalten kann. +* **Persönliche Filmlisten:** Jeder Benutzer sieht und durchsucht nur die Filme, die er selbst hinzugefügt hat. +* **Filme hinzufügen:** Filme können manuell oder durch ein automatisiertes Scraping von IMDb hinzugefügt werden. +* **Umfassende Suche:** Eine globale Suchleiste ermöglicht die gleichzeitige Suche nach Titel, Genre und Regisseur. +* **Paginierung:** Die Filmliste ist in Seiten aufgeteilt, um auch bei großen Sammlungen übersichtlich zu bleiben. +* **Sichere Verbindung:** Die Anwendung wird über HTTPS mit einem selbst-signierten SSL-Zertifikat bereitgestellt. +* **Containerisierung:** Die gesamte Anwendung ist mit Docker und Docker Compose containerisiert, was die Bereitstellung vereinfacht. +* **Automatisierte Bereitstellung:** Ein Ansible-Playbook automatisiert das Deployment der Anwendung. + +## Voraussetzungen + +Stellen Sie sicher, dass die folgende Software auf Ihrem System installiert ist: + +* Python 3 +* pip +* Docker +* Docker Compose +* Ansible + +## Inbetriebnahme + +Es gibt drei Möglichkeiten, die Anwendung zu starten. + +### 1. Direkter Start mit Python + +Dies ist die klassische Methode für die lokale Entwicklung. + +1. **Abhängigkeiten installieren:** + ```bash + pip install -r requirements.txt + ``` +2. **Anwendung starten:** + ```bash + python3 main.py + ``` +3. **Zugriff:** Öffnen Sie Ihren Browser und gehen Sie zu `https://localhost:5082`. Sie müssen eine Browser-Warnung aufgrund des selbst-signierten Zertifikats akzeptieren. + +### 2. Start mit Docker + +Diese Methode kapselt die Anwendung in einem Container. + +1. **Docker-Image erstellen:** + ```bash + docker build -t movie-db-app . + ``` +2. **Docker-Container starten:** + ```bash + docker run -p 5082:5082 movie-db-app + ``` +3. **Zugriff:** Öffnen Sie Ihren Browser und gehen Sie zu `https://localhost:5082`. + +### 3. Start mit Ansible und Docker Compose (Empfohlen) + +Dies ist die automatisierte Methode, die einen sauberen Deployment-Prozess sicherstellt. + +1. **Ansible Docker-Sammlung installieren (falls noch nicht geschehen):** + ```bash + ansible-galaxy collection install community.docker + ``` +2. **Ansible-Playbook ausführen:** + ```bash + ansible-playbook start.yml + ``` + (Möglicherweise müssen Sie Ihr `sudo`-Passwort eingeben.) + +3. **Zugriff:** Öffnen Sie Ihren Browser und gehen Sie zu `https://localhost:5082`. + +## Projektstruktur + +Die wichtigsten Dateien und Ordner im Überblick: + +``` +. +├── Dockerfile +├── docker-compose.yml +├── main.py +├── moviedb_func.py +├── movie_db.db +├── README.md +├── requirements.txt +├── start.yml +├── cert.pem +├── key.pem +└── templates/ + ├── base.html + ├── index.html + ├── login.html + └── register.html +``` + +* `main.py`: Die Hauptdatei der Flask-Anwendung. Sie enthält die Routen und die Anwendungslogik. +* `moviedb_func.py`: Enthält die gesamte Geschäftslogik, Datenbankfunktionen und die `User`-Klasse. +* `templates/`: Der Ordner für alle HTML-Templates. +* `movie_db.db`: Die SQLite-Datenbankdatei. +* `Dockerfile`: Die Bauanleitung für das Docker-Image der Anwendung. +* `docker-compose.yml`: Definiert die Anwendung als Docker-Dienst für eine einfache Verwaltung. +* `start.yml`: Das Ansible-Playbook für die automatisierte Bereitstellung. +* `requirements.txt`: Liste der Python-Abhängigkeiten. +* `cert.pem`, `key.pem`: Das selbst-signierte SSL-Zertifikat und der private Schlüssel. + +## Benutzung + +1. **Registrieren:** Erstellen Sie auf der Registrierungsseite einen neuen Benutzeraccount. +2. **Einloggen:** Melden Sie sich mit Ihren neuen Zugangsdaten an. +3. **Filme hinzufügen:** Nutzen Sie den "Film hinzufügen"-Button, um Filme entweder durch die IMDb-Suche oder manuell zu Ihrer Sammlung hinzuzufügen. +4. **Suchen:** Verwenden Sie die Suchleiste, um Ihre Sammlung nach Titeln, Genres oder Regisseuren zu durchsuchen. + +## Mögliche zukünftige Erweiterungen + +* **Bearbeiten & Löschen:** Funktionen zum Bearbeiten und Löschen von bereits hinzugefügten Filmen. +* **Detailseite:** Eine eigene Seite für jeden Film mit mehr Details (Schauspieler, Handlung, etc.). +* **Sortieroptionen:** Die Möglichkeit, die Filmliste nach verschiedenen Kriterien zu sortieren. \ No newline at end of file diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..5e6cc7e --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,12 @@ +[defaults] +inventory = ./inv/ +log_path = ~/.ansible/log_ansible.log +private_key_file = ~/.ssh/ansible +retry_files_enabled = true +retry_files_save_path = ~/.ansible/retry-files +force_handlers = true +vault_identity_list = ~/.ansible/.vault-pass +#Gather CONFIG +gathering = smart +fact_caching = yaml +fact_caching_connection = ~/.ansible/fact_caching diff --git a/cert.pem b/cert.pem new file mode 100644 index 0000000..afa98ca --- /dev/null +++ b/cert.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIFCTCCAvGgAwIBAgIUDplU7xn71S8Rb5v7dXQ9cZmD1S4wDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDgyNjExNTIwMVoXDTI2MDgy +NjExNTIwMVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA0J1PNqsrg+BL/zy7nTaRwfP96gGGvnQtHFHBifX7qF6e +bXKvxU77tGE9D0i5t36N2ktMl6PGI2CmhFz5mkcVrXnfWGLoYV2jLlYQOdKvwFJo +7R4QuGa8nLHLdGUkhSSgRxkol9rHUcok5ZpfA1iVFbrpgqel3Lmh0h3qXx7YiPTo +Y8ZhFQDoF4TXJYGrSC2BlAtbOSwT/L/n983VIA3dugzQ+3H78OWmfGcCB8Q8d5hN +FP+igWj/29U3vCmvlgXpzaM+X1TtL9uxqm/GWw/+jIj4Wn+2mxhMFS2B/OeWEW5y +oTYvZywK/eap9ieyKpzi+lND3eTvD+icpj1hFBt5qa+R9P1bj369q6IMSxNrf5YJ +cDesywy5MIIAPN+21nNYi13ZSwSnfEI/13klBk0rdhBulDAPGvH1DZEAc4ePagMQ +uhJtW6wl50yNJADxszAa1mbIIk/v7r+vPt32XTylqlaI3koU2XjjmyXcsAFLdgcS +ymCtxNWe37U58qb1J3QKDbaskq6kJiJBWZlASa3ANp19UdWvAnuznhhfXws0cV6b +qFoeKKkyWOsPln+Ifuw1VONJOMJmoT4L+kHuKnMDqdusuGdIlJXfeW00vMLIRx8O +8j8dUHfFqWRnE98VO9WZlsbagYgW96n9zyeZvfW3VgncrqMVZYC2YadbSH+hG40C +AwEAAaNTMFEwHQYDVR0OBBYEFIp7yQD9oKNeYS2hFMBzgIQWoZzGMB8GA1UdIwQY +MBaAFIp7yQD9oKNeYS2hFMBzgIQWoZzGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBAHQpvGXNoeHjmVxuMmZSuiccOdfJXiWBTM7g7Eqq0nzTuzK/ +IvmBkTxK8BG1j9X+9vBDJ3KZAIMVviepqxUNMGMQNa9DcVnOxaB01UdRqM9hh+jg +EkBgN325BgrX5Vh/HS7MX2VboM+WmBcfWtt3afKv2Tv2mzZ6bPPDdbH/SJouxfXD +Ea+n3IXniskmY7kUVTy7uRQUAJXyLulcrXAJgOwws80JEXrrjZwcpt9PxzzxXu4c +TUZQ1A5FgHgXecYM1WJA9nQr7NH9TewssKaoYOEltwOxbqc0bxAOtFX02dSTpRY/ +0LwLC1Nqm34c96CX1t/WLfWf/RY2j5FoqFKxOY6CnpCW4DXjMZ3OEPmikT6XJ85G +q9LB4AqT/G1cyQzoT78SwDk6Ln3ahzP0MiaQeZ/KBJbqnfxOtcDWAmRTtIPYpMYH +eW8K+Y0Z3UvR+FqawuR5pvz5T/XYC/tutK4+KLCecorruT/Ye5qEALb+XcugU5iE +3FeZ/iAUxn8UKAGtfogQHYMOZ5lllAgRCKYhEYp28Rlkrk9LBXPzMimUfPZ2RSwx +XSkTYZEFEMw+4YQdOGqesW/hGpG6Ojmb/bmqG5TAzTdFUXsc6YgrJIP7lCIkEBnb +jF+S8Yb9Gq1s3ohiyI71kdVoOgWUGbqkkQ9xEZQzRzB1zcOLKelpdI7BEkjE +-----END CERTIFICATE----- diff --git a/deploy_remote.yml b/deploy_remote.yml new file mode 100644 index 0000000..1df455d --- /dev/null +++ b/deploy_remote.yml @@ -0,0 +1,48 @@ +--- +- name: Deploy and start Movie-DB Application + hosts: all + become: yes + + vars: + project_name: movie-db + install_dir: "/opt/{{ project_name }}" + + tasks: + - name: Create installation directory + ansible.builtin.file: + path: "{{ install_dir }}" + state: directory + mode: '0755' + + - name: Copy project files + ansible.builtin.copy: + src: "{{ item }}" + dest: "{{ install_dir }}/" + owner: root + group: root + mode: '0644' + with_items: + - cert.pem + - docker-compose.yml + - Dockerfile + - genre_list + - imdb_suche.py + - key.pem + - main.py + - moviedb_func.py + - regie_name.csv + - requirements.txt + + - name: Copy templates directory + ansible.builtin.copy: + src: templates/ + dest: "{{ install_dir }}/templates/" + owner: root + group: root + + - name: Start application with Docker Compose + community.docker.docker_compose: + project_src: "{{ install_dir }}" + state: present + restarted: yes + build: yes diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6214c68 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +services: + web: + build: . + container_name: movie-db-web + ports: + - "5082:5082" + volumes: + - .:/app + restart: always + # command: python3 main.py # Use this if you want to run in foreground for debugging \ No newline at end of file diff --git a/genre_list b/genre_list new file mode 100644 index 0000000..c8711eb --- /dev/null +++ b/genre_list @@ -0,0 +1,36 @@ +action +adventure +animation +biography +comedy +crime +cult movie +disney +documentary +drama +erotic +family +fantasy +film-noir +gangster +gay and lesbian +history +horror +military +music +musical +mystery +nature +neo-noir +period +pixar +road movie +romance +sci-fi +short +spy +super hero +thriller +visually stunning +war +western diff --git a/imdb_suche.py b/imdb_suche.py new file mode 100644 index 0000000..840731f --- /dev/null +++ b/imdb_suche.py @@ -0,0 +1,59 @@ +# Benötigt die Installation von 'requests' und 'beautifulsoup4' +# pip install requests beautifulsoup4 + +import requests +from bs4 import BeautifulSoup +import sys +import re + +def suche_film_url(film_titel): + """ + Sucht auf IMDb nach einem Film und gibt die deutsche URL der Filmseite zurück. + + Args: + film_titel: Der Titel des Films, nach dem gesucht werden soll. + + Returns: + Die deutsche URL der Filmseite oder None, wenn nichts gefunden wurde. + """ + such_url = f"https://www.imdb.com/find?q={film_titel.replace(' ', '+')}" + # Header, um die deutsche Seite zu bevorzugen + headers = { + 'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7', + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36' + } + + try: + response = requests.get(such_url, headers=headers) + response.raise_for_status() # Löst eine Ausnahme für HTTP-Fehler aus + + soup = BeautifulSoup(response.text, 'html.parser') + + # Finden des ersten Links im Titel-Ergebnisbereich + ergebnis_bereich = soup.find('section', {'data-testid': 'find-results-section-title'}) + if ergebnis_bereich: + link_tag = ergebnis_bereich.find('a', href=re.compile(r'/title/tt\d+')) + if link_tag and 'href' in link_tag.attrs: + pfad = link_tag['href'] + # Extrahieren der Titel-ID (z.B. tt0133093) + match = re.search(r'(tt\d+)', pfad) + if match: + titel_id = match.group(1) + return f"https://www.imdb.com/de/title/{titel_id}" + + except requests.exceptions.RequestException as e: + print(f"Ein Fehler bei der Anfrage ist aufgetreten: {e}") + return None + + return None + +if __name__ == "__main__": + if len(sys.argv) > 1: + film_name = " ".join(sys.argv[1:]) + url = suche_film_url(film_name) + if url: + print(f"URL für '{film_name}' gefunden: {url}") + else: + print(f"Konnte keinen Film mit dem Titel '{film_name}' finden.") + else: + print("Benutzung: python imdb_suche.py ") diff --git a/inv/hosts b/inv/hosts new file mode 100644 index 0000000..30e6c1b --- /dev/null +++ b/inv/hosts @@ -0,0 +1,8 @@ +FlaskServer: + hosts: + ras-dan-01: + vars: + ansible_python_interpreter: "/usr/bin/python3" + ansible_ssh_common_args: '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' + ansible_user: jonnybravo + ansible_become: trueras-dan-01 diff --git a/key.pem b/key.pem new file mode 100644 index 0000000..37e0097 --- /dev/null +++ b/key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDQnU82qyuD4Ev/ +PLudNpHB8/3qAYa+dC0cUcGJ9fuoXp5tcq/FTvu0YT0PSLm3fo3aS0yXo8YjYKaE +XPmaRxWted9YYuhhXaMuVhA50q/AUmjtHhC4Zrycsct0ZSSFJKBHGSiX2sdRyiTl +ml8DWJUVuumCp6XcuaHSHepfHtiI9OhjxmEVAOgXhNclgatILYGUC1s5LBP8v+f3 +zdUgDd26DND7cfvw5aZ8ZwIHxDx3mE0U/6KBaP/b1Te8Ka+WBenNoz5fVO0v27Gq +b8ZbD/6MiPhaf7abGEwVLYH855YRbnKhNi9nLAr95qn2J7IqnOL6U0Pd5O8P6Jym +PWEUG3mpr5H0/VuPfr2rogxLE2t/lglwN6zLDLkwggA837bWc1iLXdlLBKd8Qj/X +eSUGTSt2EG6UMA8a8fUNkQBzh49qAxC6Em1brCXnTI0kAPGzMBrWZsgiT+/uv68+ +3fZdPKWqVojeShTZeOObJdywAUt2BxLKYK3E1Z7ftTnypvUndAoNtqySrqQmIkFZ +mUBJrcA2nX1R1a8Ce7OeGF9fCzRxXpuoWh4oqTJY6w+Wf4h+7DVU40k4wmahPgv6 +Qe4qcwOp26y4Z0iUld95bTS8wshHHw7yPx1Qd8WpZGcT3xU71ZmWxtqBiBb3qf3P +J5m99bdWCdyuoxVlgLZhp1tIf6EbjQIDAQABAoICAD8OHWBX0ppU1IO8drTpRptK +RYhSl8i/UtdJeudZW4yjtN0+GhTYb8owzgTtpXzqGrRNqEP5c61DzHEbrTJNpCbA +PJr5omWzqNwblWH05byK7D4npUQngYEXWHjBDY3U6UE0QInNzgtRv19LDsbzInTj +/yrHw6F41omtEydXKnoTFJ772mI/2Ocq9uDWVdBGOPFnkVYxlYUC+Hl1VKMQreFL +eGtYI5/Huk/Fzt5MGT7I4RukvRjChPuMcgFE1FbTSL0oR3UHNX4OuJ/ckyb5KaaO +zAqGqcotSOnpzG2GDl0Mdkd2GlPkAxqFev/1NawdqZmiS1Qpm3zCgX7Z9JrgRyBP +nMKMdH/+Yi6sQ16BLuq1/wBSoN4hh5AIjbS2SRV8P0RR9TfuKP0G3UZeXEJVu7VZ +LjMqfNc5Frco8iJdm4dv9bAjMgDgKBwRzECTSPcUByS7H72v7I81X9HDaNlMk9Py +B0sy5mduihQE4ZeSUVloiq/Mm8K3/6lPuC94+8hfZ7ZhmMTsXkeHtyo+tNNEHtFS +wnF96zzUE5l7quCmqPzz5l5ll9wYAjMXr48Fv9C5E5QshoYhmzBrF6+Kn315bSa6 +PHf3v2nJ//lJHyJR6o/MWLAdW61nbvnlLbwBUt0blv+PUnoOFKQhDcHteU7Ht5zi +vyzJKEGK0nL+hScit94dAoIBAQD+ttlWLHVo32jX83cVLNQFo5TVQJlbf05yZUqm +rEVu+kH59JAUPYxypiQ+CWc3OMFqDHiSrP9VdL1HSfZURS3K8hw25ThWn5jXEoWF +SkyCWBcPp8EyCwW14DUCp45VKyZlNpM/halT+eG04lElEilUHDMbinz01R2Gwkrk +zgirxRBpAU1OMnvCTTjwmLUXCKvHBDek4AZBY0Gw9eWgir+If6Gyn88Nr2N1ZtFt ++kkHrLgyo+nanOu1N/02RC3Je3lSxI7VsLP0Lt/zs5Qag8r/a7lEDUI69eQbh/Rh +29NaqUyPOQn8DjfPzB8a0F7qXbcB94CYWfoKZ/RkdJ9dj4mTAoIBAQDRquN/ftwE +CBfgwE5BJNZuofj733LBrnvZH51UOMvnoTxPDdCyf684un8qylmYu4WIV5VvWZUH +G3gOw+zmmpEOHerh8/G7X9FPST8txTlAxlFBVN0iXMyU8SDypw2XF+CKcq0goz2s +/XA0b9NdtF9zziYsZSj+hR5E1Nf16GX2SeczSlaxPRTZ1pxqRbmC+1iE+yJuXcQ2 +sJgr2zI9jUf0zGamYPqpKvZDKLclZSJAIYTLE/nUMR87QqgLU90x3bBeRR4uLnD3 +yemtFuJDRcSZa+4j/m5fmLZvA2wpvZvvd70DC3xfh6RKZYyzj26rHgK3qnY9owzS +q44qLTbNH3pfAoIBAQCHmjy2XdDb1iRw41LAWS8/GUlBBrUeOvY+fKJzfT8xx9Wc +BpE11VaSKtUcmdDzWynNFXqTS7pbelzSORQE5Rdt9dsDdqC5rRmnVlrzDQ0ST2dn +lD2MMMYHf6d5FE74FJLKS3W02am95Ug+DkZ43+RUvPaQcPf5OAQSHBnXGcEQAUet +P14yhTnRxV7M599wtdMNjrc49XMfGIJ+fUqUX4l8V8Zz3BRVCsx+UhCo5hg/F7qw +awcytVvfDEGnxEBEBZIfeDk6lmoXU2UyzqXIP+WweV8WVaCu1v1rksON3YP1vi/e +B9XC9KbN37OniMsfJmkpiORTN4OgSpuXZ2b4XlQ1AoIBAQCsVFMMdc6MmcJWfnlT +Xd3JrOOi0fPWcX1l5nA/yzb9OS8Cow5HyF7St6bSGuMtf+OvPp2amfy9jLxPFoZe +u+hBvCFUelbTdVvqWP/OtO3r6ZTbp87h8XhNaytzhg7Lg1qRMNRPoRyOVPimgMsv +l5Nk8am6j6L0H6HTXUXr3on/tpqLM6yxnMzIh3akeo6Rs9j75BZWtEcT1G4ejdlr +cfYUpPQyCD++T4t9g7eUyt0t7N7is5aB66YJ5S2Nse56kAXoVdMKji1X3ONt2wM5 +29xNZcKkXvmFJVW9RQjf/fJvDvGR0Rz2v9wvMexFobyKdO4y26631o4xkcQVsnf6 +fMbtAoIBAE6rOneLYxM8X6hJ7NoyVIwUH52WKuqWn4SFHU42C8SGeUTxFtr6ze+L +z0ZYTowYufMOUMjtS4nwa2kWkohPJccDfJHG1NJf6WA80Yt/qQItsjv/TGItxDE0 +4j47r+h1NGj01xzJHe0QG38NWnnpw0hHFSG5q73qWFyYSWs/unz+HMSGsg+KtpPT +K7RJMx2ROUXlMku8TmEWO0EfwhVmTp2g/1XoH7DvtAIv5GLiUL+pYp9qs7LtSVI/ +3WYsk3Op/x5VRG/V2hByPf25IrpLS3eoDB3uPeeIHb0rXx0QdMr2rElGg4vgcLUw +krvHLxyb9R+o1UzlvYkUiF+lOMVpLB8= +-----END PRIVATE KEY----- diff --git a/main.py b/main.py new file mode 100644 index 0000000..c9a520b --- /dev/null +++ b/main.py @@ -0,0 +1,127 @@ +import flask +from flask import render_template, redirect, url_for, flash +from flask_login import LoginManager, login_user, logout_user, login_required, current_user + +import moviedb_func + +app = flask.Flask(__name__) +app.secret_key = "Start1234!" + +login_manager = LoginManager() +login_manager.init_app(app) +login_manager.login_view = "login" + +@login_manager.user_loader +def load_user(user_id): + return moviedb_func.get_user_by_id(user_id) + +@app.route("/register", methods=["GET", "POST"]) +def register(): + if flask.request.method == "POST": + username = flask.request.form.get("username") + password = flask.request.form.get("password") + if moviedb_func.get_user_by_username(username): + flash("Benutzername existiert bereits") + return redirect(url_for("register")) + moviedb_func.add_user(username, password) + return redirect(url_for("login")) + return render_template("register.html") + +@app.route("/login", methods=["GET", "POST"]) +def login(): + if flask.request.method == "POST": + username = flask.request.form.get("username") + password = flask.request.form.get("password") + user = moviedb_func.get_user_by_username(username) + if user and user.check_password(password): + login_user(user) + return redirect(url_for("index")) + flash("Ungültiger Benutzername oder Passwort") + return render_template("login.html") + +@app.route("/logout") +@login_required +def logout(): + logout_user() + return redirect(url_for("login")) + + +@app.route("/", methods=["GET", "POST"]) +@login_required +def index(): + page = flask.request.args.get('page', 1, type=int) + per_page = 20 + search_query = None + if flask.request.method == "POST": + search_query = flask.request.form.get("search") + else: + search_query = flask.request.args.get("search") + + data_all, total_pages = moviedb_func.show_movie_list(user_id=current_user.id, search_query=search_query, page=page, per_page=per_page) + return render_template( + "index.html", + sitename="Meine Movieliste !!!", + data_all=data_all, + total_pages=total_pages, + current_page=page, + search_query=search_query + ) + + +@app.get("/add_movie") +def add_movie(): + return render_template( + "add_movie.html", + sitename="Meine Movieliste !!!", + url="output", + select_movie="add_movie", + select_genre="add_genre", + select_medium="add_medium", + data=moviedb_func.all_select(), + data_medium=moviedb_func.all_select(what_select="medium") + ) + + +@app.post("/output") +@login_required +def csv_output(): + select_add_movie = flask.request.form["add_movie"] + select_medium = flask.request.form["add_medium"] + select_medium_id = moviedb_func.search_id( + search_name=select_medium, select_from="medium", select_where="medium") + + if moviedb_func.scrape_and_add_movie(movie_name=select_add_movie, + medium_id=select_medium_id, user_id=current_user.id): + return redirect(url_for('index')) + else: + return redirect(url_for('add_movie_manually', movie_name=select_add_movie, medium_id=select_medium_id)) + +@app.get("/add_movie_manually") +@login_required +def add_movie_manually(): + movie_name = flask.request.args.get('movie_name') + medium_id = flask.request.args.get('medium_id') + return render_template( + "add_movie_manually.html", + sitename="Manuelle Eingabe", + movie_name=movie_name, + medium_id=medium_id + ) + +@app.post("/save_manual") +@login_required +def save_manual(): + movie_name = flask.request.form["movie_name"] + medium_id = flask.request.form["medium_id"] + director_name = flask.request.form["director_name"] + release_year = flask.request.form["release_year"] + genre_name = flask.request.form["genre_name"] + + moviedb_func.add_manual_movie(movie_name=movie_name, medium_id=int(medium_id), director_name=director_name, release_year=int(release_year), genre_name=genre_name, user_id=current_user.id) + + return redirect("/") + + +if __name__ == "__main__": + moviedb_func.create_movie_database() + app.run(port=5082, host="0.0.0.0", debug=True, ssl_context=('cert.pem', 'key.pem')) diff --git a/moviedb_func.py b/moviedb_func.py new file mode 100644 index 0000000..d8b76cd --- /dev/null +++ b/moviedb_func.py @@ -0,0 +1,427 @@ +import DBcm +from mariadb import ProgrammingError +import pandas as pd +import sqlite3 +import urllib.request +import json +import re +import ssl +import imdb_suche +from werkzeug.security import generate_password_hash, check_password_hash +from flask_login import UserMixin + + +def get_movie_details(imdb_url): + """ + Ruft eine IMDb-Filmseite auf und extrahiert den Regisseur und die Hauptdarsteller. + + Args: + imdb_url (str): Die vollständige URL zur IMDb-Filmseite. + + Returns: + dict: Ein Wörterbuch mit "director" und "actors" oder None bei einem Fehler. + """ + if not re.match(r"https://www.imdb.com/de/title/tt\d+/?", imdb_url): + print(f"Fehler: Ungültige IMDb-URL: {imdb_url}. Die URL sollte so aussehen: https://www.imdb.com/de/title/tt...") + return None + + try: + # SSL-Kontext erstellen, um Zertifikatsüberprüfungsfehler zu umgehen + context = ssl._create_unverified_context() + + # User-Agent setzen, um wie ein Browser auszusehen und Blockaden zu vermeiden + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'} + + req = urllib.request.Request(imdb_url, headers=headers) + + with urllib.request.urlopen(req, context=context) as response: + html_content = response.read().decode('utf-8') + + # Suchen Sie nach dem strukturierten Datenblock (JSON-LD), der Filminformationen enthält + match = re.search( + r'', html_content, re.DOTALL) + + if not match: + print("Fehler: Konnte den JSON-LD-Datenblock auf der Seite nicht finden.") + return None + + json_data = json.loads(match.group(1)) + + # Extrahieren des Regisseurs + director = [d.get('name') for d in json_data.get("director", [])] if isinstance( + json_data.get("director"), list) else [json_data.get("director", {}).get("name")] + + # Extrahieren der Schauspieler + actors = [a.get('name') for a in json_data.get("actor", [])] + + # Extrahieren des Erscheinungsjahres + release_year = json_data.get("datePublished", "").split("-")[0] + + # Extrahieren des Genres + genre = json_data.get("genre", []) + if isinstance(genre, list): + genre = ", ".join(genre) + + + return { + "director": [d for d in director if d], + "actors": [a for a in actors if a], + "release_year": release_year, + "genre": genre + } + + except urllib.error.HTTPError as e: + print(f"HTTP-Fehler beim Abrufen der URL: {e.code} {e.reason}") + return None + except Exception as e: + print(f"Ein Fehler ist aufgetreten: {e}") + return None + + +def create_movie_database(db_name="movie_db.db"): + + create_user_table = """ + CREATE TABLE IF NOT EXISTS user ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + username VARCHAR(64) NOT NULL UNIQUE, + password_hash VARCHAR(128) NOT NULL + ); + """ + + create_my_list = """ + create table if not exists movie_list ( + id integer not null primary key autoincrement, + titel varchar(64) not null, + genre_id integer not null, + regie_id integer, + medium_id integer not null, + release_year integer, + user_id integer not null, + FOREIGN KEY (user_id) REFERENCES user (id) + ) + """ + + create_genre = """ + create table if not exists genre ( + id integer not null primary key autoincrement, + name varchar(64) not null + ) + """ + + create_regie = """ + create table if not exists regie ( + id integer not null primary key autoincrement, + surname varchar(64) not null, + lastname varchar(64) not null + ) + """ + + create_medium = """ + create table if not exists medium ( + id integer not null primary key autoincrement, + medium varchar(64) not null + ) + """ + + ADD_GENRE_VALUE = """ + INSERT INTO genre(name) + SELECT ? + WHERE NOT EXISTS (SELECT 1 FROM genre WHERE name = ?); + """ + + ADD_MEDIUM_VALUE = """ + INSERT INTO medium(medium) + SELECT ? + WHERE NOT EXISTS (SELECT 1 FROM medium WHERE medium = ?); + """ + + ADD_REGIE_VALUE = """ + INSERT INTO regie (surname, lastname ) + SELECT ?, ? + WHERE NOT EXISTS + (SELECT surname, lastname + FROM regie + WHERE surname = ? AND lastname = ?); + """ + + with DBcm.UseDatabase(db_name) as db: + db.execute(create_user_table) + db.execute(create_my_list) + db.execute(create_genre) + db.execute(create_regie) + db.execute(create_medium) + + with open("genre_list", "r") as fs: + for genre_value in fs.readlines(): + with DBcm.UseDatabase(db_name) as db: + db.execute(ADD_GENRE_VALUE, + (genre_value.strip(), genre_value.strip())) + + usecols = ["Name", "Vorname"] + air = pd.read_csv("regie_name.csv", usecols=usecols) + for count, (reg_name, reg_vorname) in enumerate(zip(air["Name"], air["Vorname"])): + # print(count, reg_vorname, reg_name) + with DBcm.UseDatabase(db_name) as db: + db.execute(ADD_REGIE_VALUE, (reg_vorname, + reg_name, reg_vorname, reg_name)) + + LISTE_MEDIUM = ["BlueRay", "DVD", "Datei", + "BlueRay Steelbook", "DVD Steelbook"] + with DBcm.UseDatabase(db_name) as db: + for MEDIUM in LISTE_MEDIUM: + db.execute(ADD_MEDIUM_VALUE, (MEDIUM, MEDIUM)) + + +def all_select(db_name="movie_db.db", what_select="genre"): + ALL_SELECT = "SELECT * from " + what_select + if what_select == "genre" or what_select == "medium": + with DBcm.UseDatabase(db_name) as db: + db.execute(ALL_SELECT) + all_value = [i[1] for i in db.fetchall()] + return all_value + elif what_select == 'regie': + all_value = [] + with DBcm.UseDatabase(db_name) as db: + db.execute(ALL_SELECT) + for i in db.fetchall(): + all_value.append(i[1] + " " + i[2]) + return all_value + else: + return "Wrong Value !!!" + + +def get_or_create_regie(director_name: str, db_name: str = "movie_db.db"): + ADD_REGIE_VALUE = """ + INSERT INTO regie (surname, lastname ) + SELECT ?, ? + WHERE NOT EXISTS + (SELECT surname, lastname + FROM regie + WHERE surname = ? AND lastname = ?); + """ + director_split = director_name.split(" ") + surname = director_split[0] + lastname = " ".join(director_split[1:]) + with DBcm.UseDatabase(db_name) as db: + db.execute(ADD_REGIE_VALUE, (surname, lastname, surname, lastname)) + db.execute("SELECT id FROM regie WHERE surname = ? AND lastname = ?", (surname, lastname)) + regie_id = db.fetchone()[0] + return regie_id + + +def get_or_create_genre(genre_name: str, db_name: str = "movie_db.db"): + ADD_GENRE_VALUE = """ + INSERT INTO genre(name) + SELECT ? + WHERE NOT EXISTS (SELECT 1 FROM genre WHERE name = ?); + """ + with DBcm.UseDatabase(db_name) as db: + db.execute(ADD_GENRE_VALUE, (genre_name, genre_name)) + db.execute("SELECT id FROM genre WHERE name = ?", (genre_name,)) + genre_id = db.fetchone()[0] + return genre_id + + +def search_id(search_name: str, select_from: str = "genre", select_where: str = "name", db_name: str = "movie_db.db"): + if select_from == "regie": + split_search = search_name.split(" ") + GENRE_QUERY = f"""select id from {select_from} + where surname = ? and lastname = ?""" + with DBcm.UseDatabase(db_name) as db: + db.execute(GENRE_QUERY, (split_search[0], split_search[1],)) + regie_id = db.fetchone()[0] + return int(regie_id) + else: + try: + GENRE_QUERY = f"""select id from {select_from} + where {select_where} = ?""" + with DBcm.UseDatabase(db_name) as db: + db.execute(GENRE_QUERY, (search_name,)) + genre_id = db.fetchone()[0] + return int(genre_id) + except: + return int(0) + + +def scrape_and_add_movie(movie_name: str, medium_id: int, user_id: int, db_name: str = "movie_db.db"): + url = imdb_suche.suche_film_url(movie_name) + if not url: + return False + + details = get_movie_details(url + "/") + if not details: + return False + + regie_id = None + if details.get("director"): + regie_id = get_or_create_regie(director_name=details["director"][0]) + + release_year = details.get("release_year") + genre_name = details.get("genre") + genre_id = None + if genre_name: + genre_id = get_or_create_genre(genre_name=genre_name) + + SQL_PARAM = f""" + INSERT INTO movie_list (titel, genre_id, regie_id, medium_id, release_year, user_id) + VALUES (?, ?, ?, ?, ?, ?); + """ + try: + with DBcm.UseDatabase(db_name) as db: + db.execute(SQL_PARAM, (movie_name.lower(), genre_id, + regie_id, medium_id, release_year, user_id,)) + except ProgrammingError as e: + print(f"Error adding movie: {e}") + return False + return True + + +def add_manual_movie(movie_name: str, medium_id: int, director_name: str, release_year: int, genre_name: str, user_id: int, db_name: str = "movie_db.db"): + regie_id = get_or_create_regie(director_name=director_name) + genre_id = get_or_create_genre(genre_name=genre_name) + + SQL_PARAM = f""" + INSERT INTO movie_list (titel, genre_id, regie_id, medium_id, release_year, user_id) + VALUES (?, ?, ?, ?, ?, ?); + """ + try: + with DBcm.UseDatabase(db_name) as db: + db.execute(SQL_PARAM, (movie_name.lower(), genre_id, + regie_id, medium_id, release_year, user_id,)) + except ProgrammingError as e: + print(f"Error adding movie: {e}") + return False + return True + + +def show_movie_list(db_name="movie_db.db", user_id=None, search_query=None, page=1, per_page=20): + """Zeigt eine paginierte Liste von Filmen an, die nach einem Suchbegriff gefiltert werden können.""" + + # Basis-SQL-Abfrage + SQL_BASE = """FROM movie_list + INNER JOIN genre ON movie_list.genre_id=genre.id + INNER JOIN regie ON movie_list.regie_id=regie.id + INNER JOIN medium ON movie_list.medium_id=medium.id""" + + # Filter-Bedingungen + params = [] + where_clauses = [] + if user_id: + where_clauses.append("movie_list.user_id = ?") + params.append(user_id) + + if search_query: + search_param = f"%{search_query}%" + where_clauses.append("(titel LIKE ? OR genre.name LIKE ? OR (regie.surname || ' ' || regie.lastname) LIKE ?)") + params.extend([search_param, search_param, search_param]) + + SQL_WHERE = "" + if where_clauses: + SQL_WHERE = " WHERE " + " AND ".join(where_clauses) + + # Verbindung zur Datenbank + db = sqlite3.connect(db_name) + + # Gesamtzahl der Filme für die Paginierung ermitteln + COUNT_SQL = f"SELECT COUNT(movie_list.id) {SQL_BASE}{SQL_WHERE}" + total_movies = db.execute(COUNT_SQL, params).fetchone()[0] + total_pages = (total_movies + per_page - 1) // per_page + + # Filme für die aktuelle Seite abrufen + offset = (page - 1) * per_page + SELECT_SQL = f"""SELECT + movie_list.id, + titel, + genre.name AS genre, + regie.surname as regie_surname, + regie.lastname as regie_lastname, + medium.medium, + movie_list.release_year + {SQL_BASE}{SQL_WHERE} + LIMIT ? OFFSET ?""" + + paginated_params = params + [per_page, offset] + SELCET_VALUE = pd.read_sql(SELECT_SQL, db, params=paginated_params) + + # Ergebnis in eine Liste von Dictionaries umwandeln + return_list_dict = [] + for id, titel, genre, regie_surname, regie_lastname, medium, release_year in zip(SELCET_VALUE["id"], SELCET_VALUE["titel"], SELCET_VALUE["genre"], SELCET_VALUE["regie_surname"], SELCET_VALUE["regie_lastname"], SELCET_VALUE["medium"], SELCET_VALUE["release_year"]): + return_list_dict.append( + {"id": id, "titel": titel, "genre": genre, "regie": regie_surname + " " + regie_lastname, "medium": medium, "release_year": release_year}) + + return return_list_dict, total_pages + + +def get_movie_by_title(movie_title: str, db_name: str = "movie_db.db"): + SQL_PARAM = f"""SELECT + movie_list.id, + titel, + genre.name AS genre, + regie.surname as regie_surname, + regie.lastname as regie_lastname, + medium.medium, + movie_list.release_year + FROM movie_list + INNER JOIN genre ON movie_list.genre_id=genre.id + INNER JOIN regie ON movie_list.regie_id=regie.id + INNER JOIN medium ON movie_list.medium_id=medium.id + WHERE movie_list.titel = ? + ; + """ + with DBcm.UseDatabase(db_name) as db: + db.execute(SQL_PARAM, (movie_title.lower(),)) + movie = db.fetchone() + if movie: + return {"id": movie[0], "titel": movie[1], "genre": movie[2], "regie": movie[3] + " " + movie[4], "medium": movie[5], "release_year": movie[6]} + return None + +class User(UserMixin): + def __init__(self, id, username, password_hash): + self.id = id + self.username = username + self.password_hash = password_hash + + def set_password(self, password): + self.password_hash = generate_password_hash(password) + + def check_password(self, password): + return check_password_hash(self.password_hash, password) + + +def add_user(username, password, db_name="movie_db.db"): + password_hash = generate_password_hash(password) + SQL = "INSERT INTO user (username, password_hash) VALUES (?, ?)" + with DBcm.UseDatabase(db_name) as db: + db.execute(SQL, (username, password_hash)) + +def get_user_by_username(username, db_name="movie_db.db"): + SQL = "SELECT * FROM user WHERE username = ?" + with DBcm.UseDatabase(db_name) as db: + db.execute(SQL, (username,)) + user_data = db.fetchone() + if user_data: + return User(id=user_data[0], username=user_data[1], password_hash=user_data[2]) + return None + + +def get_user_by_id(user_id, db_name="movie_db.db"): + SQL = "SELECT * FROM user WHERE id = ?" + with DBcm.UseDatabase(db_name) as db: + db.execute(SQL, (user_id,)) + user_data = db.fetchone() + if user_data: + return User(id=user_data[0], username=user_data[1], password_hash=user_data[2]) + return None + + + +if __name__ == "__main__": + create_movie_database() + # print(all_select()) + # id_genre = search_id( + # search_name="DVD", select_from="medium", select_where="medium") + scrape_and_add_movie(movie_name="Die unglaubliche Reise in einem verrückten Flugzeug", + medium_id=1) + for test in show_movie_list(): + print(test.get("id"), test.get("medium"), test.get("release_year")) diff --git a/regie_name.csv b/regie_name.csv new file mode 100644 index 0000000..085201d --- /dev/null +++ b/regie_name.csv @@ -0,0 +1,329 @@ +Name,Vorname +Rodriguez,Robert +Smith,John +Johnson,James +Williams,William +Brown,Michael +Davis,David +Miller,Richard +Wilson,Joseph +Moore,Charles +Taylor,Thomas +Anderson,Daniel +Thomas,Paul +Jackson,Mark +White,Donna +Harris,Michelle +Martin,Laura +Thompson,Sara +Garcia,Ana +Rodriguez,Carlos +Martinez,Maria +Hernandez,Jose +Lopez,Luis +Gonzalez,Rosa +Perez,Pedro +Sanchez,Miguel +Ramirez,Juan +Flores,Ana +Cruz,Isabella +Rivera,Victor +Lee,Kevin +Walker,Brian +Hall,Emily +Allen,Ryan +Young,Aaron +King,Jeffrey +Wright,Joshua +Scott,Brandon +Turner,Frank +Carter,Gregory +Phillips,Samuel +Evans,Chris +Collins,Anthony +Stewart,Eric +Snyder,Frank +Baker,Thomas +Nelson,Jeremy +Roberts,Steven +Campbell,Edward +Miller,Ryan +Davis,Jacob +Garcia,David +Rodriguez,Sophia +Martinez,Emma +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie +Hall,Matthew +Allen,Emma +Young,Ryan +King,Ava +Wright,Ethan +Scott,Mia +Turner,William +Carter,James +Phillips,Olivia +Evans,Lucas +Collins,Sophie +Stewart,Noah +Snyder,Ava +Baker,Ethan +Nelson,Mia +Roberts,Noah +Campbell,Emma +Miller,William +Davis,James +Garcia,Olivia +Rodriguez,Lucas +Martinez,Sophie +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie +Hall,Matthew +Allen,Emma +Young,Ryan +King,Ava +Wright,Ethan +Scott,Mia +Turner,William +Carter,James +Phillips,Olivia +Evans,Lucas +Collins,Sophie +Stewart,Noah +Snyder,Ava +Baker,Ethan +Nelson,Mia +Roberts,Noah +Campbell,Emma +Miller,William +Davis,James +Garcia,Olivia +Rodriguez,Lucas +Martinez,Sophie +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie +Hall,Matthew +Allen,Emma +Young,Ryan +King,Ava +Wright,Ethan +Scott,Mia +Turner,William +Carter,James +Phillips,Olivia +Evans,Lucas +Collins,Sophie +Stewart,Noah +Snyder,Ava +Baker,Ethan +Nelson,Mia +Roberts,Noah +Campbell,Emma +Miller,William +Davis,James +Garcia,Olivia +Rodriguez,Lucas +Martinez,Sophie +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie +Hall,Matthew +Allen,Emma +Young,Ryan +King,Ava +Wright,Ethan +Scott,Mia +Turner,William +Carter,James +Phillips,Olivia +Evans,Lucas +Collins,Sophie +Stewart,Noah +Snyder,Ava +Baker,Ethan +Nelson,Mia +Roberts,Noah +Campbell,Emma +Miller,William +Davis,James +Garcia,Olivia +Rodriguez,Lucas +Martinez,Sophie +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie +Hall,Matthew +Allen,Emma +Young,Ryan +King,Ava +Wright,Ethan +Scott,Mia +Turner,William +Carter,James +Phillips,Olivia +Evans,Lucas +Collins,Sophie +Stewart,Noah +Snyder,Ava +Baker,Ethan +Nelson,Mia +Roberts,Noah +Campbell,Emma +Miller,William +Davis,James +Garcia,Olivia +Rodriguez,Lucas +Martinez,Sophie +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie +Hall,Matthew +Allen,Emma +Young,Ryan +King,Ava +Wright,Ethan +Scott,Mia +Turner,William +Carter,James +Phillips,Olivia +Evans,Lucas +Collins,Sophie +Stewart,Noah +Snyder,Ava +Baker,Ethan +Nelson,Mia +Roberts,Noah +Campbell,Emma +Miller,William +Davis,James +Garcia,Olivia +Rodriguez,Lucas +Martinez,Sophie +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie +Hall,Matthew +Allen,Emma +Young,Ryan +King,Ava +Wright,Ethan +Scott,Mia +Turner,William +Carter,James +Phillips,Olivia +Evans,Lucas +Collins,Sophie +Stewart,Noah +Snyder,Ava +Baker,Ethan +Nelson,Mia +Roberts,Noah +Campbell,Emma +Miller,William +Davis,James +Garcia,Olivia +Rodriguez,Lucas +Martinez,Sophie +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie +Hall,Matthew +Allen,Emma +Young,Ryan +King,Ava +Wright,Ethan +Scott,Mia +Turner,William +Carter,James +Phillips,Olivia +Evans,Lucas +Collins,Sophie +Stewart,Noah +Snyder,Ava +Baker,Ethan +Nelson,Mia +Roberts,Noah +Campbell,Emma +Miller,William +Davis,James +Garcia,Olivia +Rodriguez,Lucas +Martinez,Sophie +Hernandez,Noah +Lopez,Ava +Gonzalez,Ethan +Perez,Mia +Sanchez,William +Ramirez,James +Flores,Olivia +Cruz,Lucas +Rivera,Isabella +Lee,David +Walker,Sophie diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1f47d1d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +flask +dbcm +pandas +requests +beautifulsoup4 +Flask-Login \ No newline at end of file diff --git a/start.yml b/start.yml new file mode 100644 index 0000000..524ffb1 --- /dev/null +++ b/start.yml @@ -0,0 +1,28 @@ +--- +- name: Deploy and start Movie-DB Application + hosts: localhost + connection: local + become: yes + + vars: + project_name: movie-db + install_dir: "/opt/{{ project_name }}" + project_src: "{{ playbook_dir }}" + + tasks: + - name: Create installation directory + ansible.builtin.file: + path: "{{ install_dir }}" + state: directory + mode: '0755' + + - name: Copy docker-compose file + ansible.builtin.copy: + src: "{{ project_src }}/docker-compose.yml" + dest: "{{ install_dir }}/docker-compose.yml" + + - name: Start application with Docker Compose + community.docker.docker_compose: + project_src: "{{ install_dir }}" + state: present + restarted: yes diff --git a/templates/add_movie.html b/templates/add_movie.html new file mode 100644 index 0000000..a0c67f2 --- /dev/null +++ b/templates/add_movie.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% block body %} +
+
+ +
+
+ +
+ +
+{% endblock %} \ No newline at end of file diff --git a/templates/add_movie_manually.html b/templates/add_movie_manually.html new file mode 100644 index 0000000..1885aaf --- /dev/null +++ b/templates/add_movie_manually.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} +{% block body %} +

Film nicht gefunden. Bitte manuell eingeben:

+
+ + + +
+ + +
+
+ + +
+
+ + +
+ +
+{% endblock %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..00b43f8 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,36 @@ + + + + + + {{ sitename }} + + + + +
+ {% block body %}{% endblock %} +
+ + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..3306f9d --- /dev/null +++ b/templates/index.html @@ -0,0 +1,60 @@ +{% extends "base.html" %} +{% block body %} + Film hinzufügen + +
+
+
+ + +
+
+ +
+ + + + + + + + + + + + + + {% for list_all in data_all %} + + + + + + + + + {% endfor %} + +
IdNameGenreRegieMediumErscheinungsjahr
{{list_all.get("id")}}{{list_all.get("titel")}}{{list_all.get("genre")}}{{list_all.get("regie")}}{{list_all.get("medium")}}{{list_all.get("release_year")}}
+ + + +{% endblock %} \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..04fbb85 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% block body %} +
+

Login

+ {% with messages = get_flashed_messages() %} + {% if messages %} +
+ {{ messages[0] }} +
+ {% endif %} + {% endwith %} +
+
+ + +
+
+ + +
+ +
+

Noch keinen Account? Hier registrieren.

+
+{% endblock %} \ No newline at end of file diff --git a/templates/register.html b/templates/register.html new file mode 100644 index 0000000..da7c72e --- /dev/null +++ b/templates/register.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% block body %} +
+

Registrieren

+
+
+ + +
+
+ + +
+ +
+

Schon einen Account? Hier einloggen.

+
+{% endblock %} \ No newline at end of file