mirror of
https://github.com/Horhik/Instagram2Fedi.git
synced 2025-01-15 05:16:40 +00:00
commit
85a3191426
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
*~
|
||||
already_posted.txt
|
||||
src/__pycache__/
|
||||
.venv
|
||||
.env.sh
|
||||
|
|
0
Dockerfile
Normal file → Executable file
0
Dockerfile
Normal file → Executable file
72
Docs.md
Normal file
72
Docs.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
# Instagram2Fedi Docs 📜
|
||||
|
||||
## How to use
|
||||
You can use Instagram2Fedi via docker or just like a python script
|
||||
|
||||
### With Docker 🐋
|
||||
|
||||
Specify your variables in `./env.sh` and then run `./run.sh`
|
||||
|
||||
You can modify `docker run` arguments in `./run.sh`
|
||||
|
||||
### Just a python script 🐍
|
||||
|
||||
Run `pip3 install -r requirements.txt` and then run `./insta2fedi`.
|
||||
|
||||
Specify your arguments. You should use `--use-docker 0`.
|
||||
|
||||
For example:
|
||||
``` bash
|
||||
./insta2fedi --use-docker false --instagram-user <instagram username> --instance <instance domain> --token <OAuth token> --check-interval 10 --post-interval 10 --use-mastodon 4
|
||||
# will check for new post each 10 seconds
|
||||
```
|
||||
|
||||
## Command line arguments 🖥
|
||||
|
||||
`--use-mastodon` - set not positive number (`0`, `-1`...) if your instance don't have max image count limit.
|
||||
|
||||
For example, default maximum photo count in mastodon is `4`
|
||||
|
||||
---
|
||||
|
||||
`--instance` - Your instance url
|
||||
|
||||
---
|
||||
|
||||
`--instagram-user` - Your instagram user name.
|
||||
|
||||
---
|
||||
|
||||
`--token` - Your OAuth token
|
||||
|
||||
---
|
||||
|
||||
`--check-interval` - Interval in seconds how often to check for new posts
|
||||
|
||||
---
|
||||
|
||||
`--post-interval` - Interval in seconds between posting new fetched posts.
|
||||
|
||||
If theres more than one new post, sets with which time interval should it post them
|
||||
|
||||
---
|
||||
|
||||
`--fetch-count` - How many new posts to select
|
||||
|
||||
---
|
||||
|
||||
`--use-docker` - If you're running it via docker container, set to `1` or `True`
|
||||
|
||||
|
||||
## Default values ⚙
|
||||
Default values are:
|
||||
``` bash
|
||||
--instance None
|
||||
--instagram-user None
|
||||
--token None
|
||||
--check-interval 3600
|
||||
--post-interval 3600
|
||||
--fetch-count 10
|
||||
--use-mastodon 4
|
||||
--use-docker True
|
||||
```
|
18
README.md
Normal file → Executable file
18
README.md
Normal file → Executable file
|
@ -1,22 +1,24 @@
|
|||
# Instagram2Fedi <span><img width="50px" src="https://upload.wikimedia.org/wikipedia/commons/9/93/Fediverse_logo_proposal.svg"></span>
|
||||
|
||||
Simple python 🐍 script for crossposting from instagram to Mastodon/Pixelfed
|
||||
Simple tool for crossposting posts from instagram to Mastodon/Pixelfed.
|
||||
|
||||
## Installing
|
||||
## Using without docker
|
||||
See [Docs.md](./Docs.md)
|
||||
|
||||
Just clone repo, build a docker container and run it
|
||||
## Using with Docker
|
||||
|
||||
Just clone repo, specify variables and run it.
|
||||
You can write all needed variables in `./env.sh` and then do `source ./run.sh`
|
||||
|
||||
``` bash
|
||||
git clone https://github.com/horhik/instagram2fedi
|
||||
cd instagram2fedi
|
||||
docker build -t $YOUR_CONTAINER_NAME .
|
||||
docker container run -it -d -v $(pwd):/app $YOUR_CONTAINER_NAME $I2M_INSTAGRAM_USER $I2M_INSTANCE $I2M_TOKEN
|
||||
nano ./env.sh
|
||||
source ./run.sh
|
||||
```
|
||||
|
||||
You can write all needed variables in `./env.sh` and then do `source ./run.sh`
|
||||
|
||||
|
||||
![image](https://user-images.githubusercontent.com/46262811/131577640-a3103ff2-af37-422d-96f1-60f1acdef939.png)
|
||||
![screenshot](./img.png)
|
||||
|
||||
|
||||
|
||||
|
|
7
env.sh
7
env.sh
|
@ -1,8 +1,13 @@
|
|||
#!/bin/sh
|
||||
#!b/in/sh
|
||||
|
||||
# Required
|
||||
export YOUR_CONTAINER_NAME=
|
||||
export I2M_INSTAGRAM_USER=
|
||||
export I2M_INSTANCE=
|
||||
export I2M_TOKEN=
|
||||
|
||||
export I2M_CHECK_INTERVAL=3600 #1 hour
|
||||
export I2M_POST_INTERVAL=3600 #1 hour
|
||||
export I2M_USE_MASTODON=4 #max carousel length is 4, if there's no limit set to -1
|
||||
export I2M_FETCH_COUNT=5 # how many instagram posts to fetch per check_interval
|
||||
|
||||
|
|
2
insta2fedi
Executable file
2
insta2fedi
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
python3 src/main.py $@
|
3
requirements.txt
Executable file
3
requirements.txt
Executable file
|
@ -0,0 +1,3 @@
|
|||
Mastodon.py==1.5.1
|
||||
colorama==0.4.4
|
||||
instaloader==4.8.1
|
2
run.sh
2
run.sh
|
@ -1,3 +1,3 @@
|
|||
#!/bin/sh
|
||||
source ./env.sh
|
||||
docker build -t $YOUR_CONTAINER_NAME .; docker container run -it -v $(pwd):/app $YOUR_CONTAINER_NAME --instagram-user $I2M_INSTAGRAM_USER --instance $I2M_INSTANCE --token $I2M_TOKEN
|
||||
docker build -t $YOUR_CONTAINER_NAME .; docker container run -it -v $(pwd):/app $YOUR_CONTAINER_NAME --use-docker 1 --instagram-user $I2M_INSTAGRAM_USER --instance $I2M_INSTANCE --token $I2M_TOKEN --check-interval $I2M_CHECK_INTERVAL --post-interval $I2M_POST_INTERVAL --fetch-count $I2M_FETCH_COUNT --use-mastodon $I2M_USE_MASTODON
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from colorama import Fore, Back, Style
|
||||
def process_arguments(args, defaults):
|
||||
count = 1
|
||||
|
@ -11,28 +13,24 @@ def process_arguments(args, defaults):
|
|||
defaults["token"] = args[count + 1]
|
||||
|
||||
elif (args[count] == "--check-interval"):
|
||||
defaults["check-interval"] = args[count + 1]
|
||||
defaults["check-interval"] = int(args[count + 1])
|
||||
|
||||
elif (args[count] == "--post-interval"):
|
||||
defaults["post-interval"] = args[count + 1]
|
||||
defaults["post-interval"] = int(args[count + 1])
|
||||
|
||||
elif (args[count] == "--fetch-count"):
|
||||
defaults["fetch-count"] = args[count + 1]
|
||||
defaults["fetch-count"] = int(args[count + 1])
|
||||
|
||||
elif (args[count] == "--using-mastodon"):
|
||||
elif (args[count] == "--use-mastodon"):
|
||||
defaults["carousel-limit"] = int(args[count + 1])
|
||||
elif (args[count] == "--use-docker"):
|
||||
defaults["use-docker"] = args[count + 1]
|
||||
|
||||
else:
|
||||
print(Fore.RED + '❗ -> Wrong Argument Name!...')
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
|
||||
count +=2
|
||||
return defaults
|
||||
|
||||
#fuck this shit im out''
|
||||
#teenagers scare the living shit out of me
|
||||
#deeeespaaaacito quero esperanto de despacito
|
||||
#хорошо всё будет хорошо
|
||||
#и камнем вниииз
|
||||
#u kinda smell *smif* like a BAKA
|
||||
#Yeren Yegaaaaaaa!!!!!!!!!
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from colorama import Fore, Back, Style
|
||||
import datetime
|
||||
|
||||
def split_array(arr, size):
|
||||
count = len(arr) // size + 1
|
||||
|
@ -14,9 +16,10 @@ def try_to_get_carousel(array, post):
|
|||
return urls
|
||||
print(Fore.GREEN + "🎠 > Found carousel!")
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
except Exception as e:
|
||||
print(Fore.RED + "🎠💥 > No carousel :( \n", e)
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
return array
|
||||
|
||||
|
||||
|
|
25
src/main.py
25
src/main.py
|
@ -1,6 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
import json
|
||||
from mastodon import Mastodon
|
||||
from colorama import Fore, Back, Style
|
||||
|
@ -11,9 +13,6 @@ from arguments import process_arguments
|
|||
from network import get_new_posts
|
||||
|
||||
|
||||
id_filename = "/app/already_posted.txt"
|
||||
with open(id_filename, "a") as f:
|
||||
f.write("\n")
|
||||
|
||||
print(sys.argv)
|
||||
print("ARGUMENTS")
|
||||
|
@ -24,13 +23,24 @@ default_settings = {
|
|||
"check-interval": 3600,
|
||||
"post-interval": 3600,
|
||||
"fetch-count" : 10,
|
||||
"carousel-limit": 4
|
||||
"carousel-limit": 4,
|
||||
"use-docker": True
|
||||
}
|
||||
|
||||
settings = process_arguments(sys.argv, default_settings)
|
||||
|
||||
print(settings)
|
||||
|
||||
agree = [1, True, "true", "True", "yes", "Yes"]
|
||||
if (agree.count(settings["use-docker"])):
|
||||
id_filename = "/app/already_posted.txt"
|
||||
else:
|
||||
id_filename = "./already_posted.txt"
|
||||
|
||||
|
||||
with open(id_filename, "a") as f:
|
||||
f.write("\n")
|
||||
|
||||
fetched_user = settings["instagram-user"]
|
||||
mastodon_instance = settings["instance"]
|
||||
mastodon_token = settings["token"]
|
||||
|
@ -42,19 +52,16 @@ post_interval = settings["post-interval"]#1m
|
|||
using_mastodon = settings["carousel-limit"] > 0;
|
||||
mastodon_carousel_size = settings["carousel-limit"]
|
||||
|
||||
print(Fore.GREEN + '🚀 > Connecting to Instagram...')
|
||||
print(Style.RESET_ALL)
|
||||
|
||||
L = Instaloader()
|
||||
profile = Profile.from_username(L.context, fetched_user)
|
||||
|
||||
print(Fore.GREEN + '🚀 > Connecting to Mastodon/Pixelfed...')
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
mastodon = Mastodon(
|
||||
access_token = mastodon_token,
|
||||
api_base_url = mastodon_instance
|
||||
# api_base_url = 'https://pixelfed.tokyo/'
|
||||
)
|
||||
while True:
|
||||
get_new_posts(mastodon, profile, mastodon_carousel_size, post_limit, id_filename, using_mastodon, mastodon_carousel_size, post_interval, fetched_user)
|
||||
get_new_posts(mastodon, mastodon_carousel_size, post_limit, id_filename, using_mastodon, mastodon_carousel_size, post_interval, fetched_user)
|
||||
time.sleep(time_interval_sec)
|
||||
|
|
|
@ -1,44 +1,63 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from colorama import Fore, Back, Style
|
||||
import requests
|
||||
import time
|
||||
import datetime
|
||||
from already_posted import already_posted, mark_as_posted
|
||||
from converters import split_array, try_to_get_carousel
|
||||
import hashlib
|
||||
from instaloader import Profile, Instaloader, LatestStamps
|
||||
|
||||
def get_instagram_user(user):
|
||||
L = Instaloader()
|
||||
|
||||
print(Fore.GREEN + '🚀 > Connecting to Instagram...')
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
|
||||
return Profile.from_username(L.context, user)
|
||||
|
||||
def get_image(url):
|
||||
try:
|
||||
print(Fore.YELLOW + "🚀 > Downloading Image...", url)
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
|
||||
response = requests.get(url)
|
||||
response.raw.decode_content = True
|
||||
|
||||
|
||||
print(Fore.GREEN + "✨ > Downloaded!")
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
|
||||
return response.content
|
||||
except Exception as e:
|
||||
|
||||
print(Fore.RED + "💥 > Failed to download image. \n", e)
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
|
||||
|
||||
def upload_image_to_mastodon(url, mastodon):
|
||||
try:
|
||||
print(Fore.YELLOW + "🐘 > Uploading Image...")
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
media = mastodon.media_post(media_file = get_image(url), mime_type = "image/jpeg") # sending image to mastodon
|
||||
print(Fore.GREEN + "✨ > Uploaded!")
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
return media["id"]
|
||||
except Exception as e:
|
||||
print(Fore.RED + "💥 > failed to upload image to mastodon. \n", e)
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
|
||||
def toot(urls, title, mastodon, fetched_user ):
|
||||
try:
|
||||
print(Fore.YELLOW + "🐘 > Creating Toot...", title)
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
ids = []
|
||||
for url in urls:
|
||||
ids.append(upload_image_to_mastodon(url, mastodon))
|
||||
|
@ -51,28 +70,38 @@ def toot(urls, title, mastodon, fetched_user ):
|
|||
except Exception as e:
|
||||
print(Fore.RED + "😿 > Failed to create toot \n", e)
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
|
||||
def get_new_posts(mastodon, profile, mastodon_carousel_size, post_limit, already_posted_path, using_mastodon, carousel_size, post_interval, fetched_user):
|
||||
def get_new_posts(mastodon, mastodon_carousel_size, post_limit, already_posted_path, using_mastodon, carousel_size, post_interval, fetched_user):
|
||||
# fetching user profile to get new posts
|
||||
profile = get_instagram_user(fetched_user)
|
||||
# get list of all posts
|
||||
posts = profile.get_posts()
|
||||
stupidcounter = 0
|
||||
for post in posts:
|
||||
stupidcounter += 1
|
||||
url_arr = try_to_get_carousel([post.url], post)
|
||||
if stupidcounter <= post_limit:
|
||||
# checking only `post_limit` last posts
|
||||
if stupidcounter < post_limit:
|
||||
stupidcounter += 1
|
||||
if already_posted(str(post.mediaid), already_posted_path):
|
||||
print(Fore.YELLOW + "🐘 > Already Posted ", post.url)
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
continue
|
||||
print("Posting... ", post.url)
|
||||
print(datetime.datetime.now())
|
||||
if using_mastodon:
|
||||
urls_arr = split_array(url_arr, carousel_size)
|
||||
for urls in urls_arr:
|
||||
toot(urls, post.caption, mastodon, fetched_user)
|
||||
else:
|
||||
toot(url_arr, post.caption, mastodon, fetched_user)
|
||||
mark_as_posted(str(post.mediaid), already_posted_path)
|
||||
mark_as_posted(str(post.mediaid), already_posted_path)
|
||||
time.sleep(post_interval)
|
||||
else:
|
||||
return
|
||||
break
|
||||
print(Fore.GREEN + "✨ > Fetched All")
|
||||
print(Style.RESET_ALL)
|
||||
print(datetime.datetime.now())
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue