Compare commits

...

13 Commits
v0.3 ... main

6 changed files with 153 additions and 363 deletions

View File

@ -1,15 +1,40 @@
# Release Notes # Release Notes
## 1.1.0 ## v0.4
### Add
- config.json.example.
- It's now possible to add a Item Description as ALT text for Images.
-- For this I use the Item Description from the META Tags.
- For me its not possible to read the special LR META Tags so its now possible to add a Post text while upload it.
- New Post text Design. (Date // Camera // Title(Text) default Tags)
### Changed
- Changed CHANGELOG.md to new Format
- Change README for better first Start
### Removed
- one useless / old file
## v0.3 - release
### Changed
- Change README for readability
## v0.2 - unreleased
### Added ### Added
- Create a Window Frontend to select Image, Add Conteten and Upload - Option to delete files after upload
## 1.0.0 ## v0.1 - unreleased
### Added ### Added
- Import all Images from Folder - Import all Images from Folder
- Create new Post for each Image - Default Tags at the end of a Content
- Add default Tags at the end of a Content - Uploade Type

View File

@ -1,55 +0,0 @@
import os
import json
import requests
class PixelfedAPI:
def __init__(self, url, token):
self.serverurl = url
self.accesstoken = token
def mediaUpload(self, image):
serverurl = self.serverurl()
apiurl = serverurl + "/api/v1/media"
accessToken = self.accesstoken()
headers = {
"Authorization": f"Bearer {accessToken}"
}
try:
with open(image, "rb") as imageFile:
f = {"file": imageFile}
response = requests.post(apiurl, headers=headers, files=f)
try:
data = response.json()
return(data)
except json.decoder.JSONDecodeError:
print("API response contains non-valid JSON data")
#print(response.text)
except requests.exceptions.RequestException as e:
print(f"An error occurred during the API call: {e}")
def createNewPost(self, ImageID, ImageDescription, ImageAltDescription = ''):
serverurl = self.serverurl()
apiurl = serverurl + "/api/v1/statuses"
print(apiurl)
accessToken = self.accesstoken()
headers = {
"Authorization": f"Bearer {accessToken}"
}
data = {
"status": ImageDescription,
"media_ids": [ImageID]
}
try:
response = requests.post(apiurl, headers=headers, json=data)
try:
responsedata = response.json()
return(responsedata)
except json.decoder.JSONDecodeError:
print("API response contains non-valid JSON data")
#print(response.text)
except requests.exceptions.RequestException as e:
print(f"An error occurred during the API call: {e}")

View File

@ -4,10 +4,21 @@ import requests
import glob import glob
import exifread import exifread
from PIL import Image, ExifTags from PIL import Image
from PIL.ExifTags import TAGS from PIL.ExifTags import TAGS
from PIL.PngImagePlugin import PngImageFile, PngInfo from PIL.PngImagePlugin import PngImageFile, PngInfo
def confirm():
confirmMsg = input("[y]es or [n]o ")
if confirmMsg == 'y' or confirmMsg == 'yes':
return True
elif confirmMsg == 'n' or confirmMsg == 'no':
return False
else:
print("\n Invalid Option. Please Enter a Valid Option.")
return confirm()
return False
def load_config(file_path): def load_config(file_path):
with open(file_path, "r") as config_file: with open(file_path, "r") as config_file:
config_data = json.load(config_file) config_data = json.load(config_file)
@ -27,30 +38,70 @@ def getImages(dir):
return result return result
def getItemDescription(filename): def getItemDescription(filename):
type = Image.open(filename) img = Image.open(filename)
exif_data = img._getexif()
imageDescription = ""
exif_tags = open(filename, 'rb') if exif_data:
tags = exifread.process_file(exif_tags) for tag, value in exif_data.items():
tag_name = TAGS.get(tag, tag)
if str(tag_name) == "ImageDescription":
imageDescription = str(value)
break
else:
imageDescription = "This image has no description. Please let me know so that I can update it."
else:
raise ValueError("No EXIF data found.")
return imageDescription
exif_array = [] def getPostText(filename):
postText = ""
tags = getTags("tags.txt")
date = ""
cam = ""
title = ""
if type.format != "PNG": img = Image.open(filename)
for i in tags: exif_data = img._getexif()
compile = i, str(tags[i])
exif_array.append(compile)
if type.format == "PNG": if exif_data:
image = PngImageFile(filename) for tag, value in exif_data.items():
metadata = PngInfo() tag_name = TAGS.get(tag, tag)
if tag_name == "DateTimeOriginal":
dateTime = value
year = dateTime[:4]
month = dateTime[5:7]
date = "🗓 " + year + "-" + month
if tag_name == "Make":
cam += "📸 " + value
if cam != "" and tag_name == "Model":
cam += " " + value
if tag_name == "Object Name":
print(value)
title = value
else:
raise ValueError("No EXIF data found.")
if date != "":
postText = date
if cam != "":
if postText != "":
postText += " || "
postText += cam
if title != "":
postText += "\n\r\n\r" + title
elif title == "":
print(f"No post Text found. Do you want to add a post text for file " + os.path.basename(filename) + "?")
if confirm():
ownPostText = input("Post text:")
ownPostText.encode('utf-8')
postText += "\n\r\n\r" + ownPostText
postText += "\n\r\n\r" + tags
for i in image.text: return postText
compile = i, str(image.text[i])
exif_array.append(compile)
Description = exif_array[0][1] def mediaUpload(access_token, url, file, itemDescription):
return Description
def mediaUpload(access_token, url, file):
api_url = url + "/api/v1/media" api_url = url + "/api/v1/media"
headers = { headers = {
@ -59,9 +110,16 @@ def mediaUpload(access_token, url, file):
try: try:
with open(file, "rb") as imageFile: with open(file, "rb") as imageFile:
f = {"file": imageFile} f = {
response = requests.post(api_url, headers=headers, files=f) "file": (file, imageFile)
response.raise_for_status() # Wirft eine HTTPError-Exception, wenn der Statuscode nicht erfolgreich ist }
data = {
"description": itemDescription.encode('utf-8')
}
response = requests.post(api_url, headers=headers, files=f, data=data)
response.raise_for_status()
try: try:
data = response.json() data = response.json()
@ -98,31 +156,46 @@ def createNewPost(access_token, url, ImageID, ImageDescription):
def sendImages(Token, url, imagedir, uploadType, deleteFile): def sendImages(Token, url, imagedir, uploadType, deleteFile):
files = getImages(imagedir) files = getImages(imagedir)
tags = getTags("tags.txt") if not files:
tagDescription = "" raise ValueError("No file found.")
postText = ""
newFileID = "" newFileID = ""
itemDescription = ""
postText = ""
for f in files: for f in files:
newFile = mediaUpload(Token, url, f) itemDescription = getItemDescription(f)
description = getItemDescription(f) newFile = mediaUpload(Token, url, f, itemDescription)
if uploadType == 0: if uploadType == 0:
tagDescription = description + ' ' + tags postText = getPostText(f)
newFileID = newFile.get("id") newFileID = newFile.get("id")
createNewPost(Token, url, newFileID, tagDescription) print(f"----------------------------------------")
print(f"Post Summary: ")
print(f"ImageID: " + newFileID)
print(f"Image Description: " + newFile.get("description"))
print(f"Post Text: " + postText)
print(f"----------------------------------------")
print(f"Do you want to Publish this?")
if confirm():
createNewPost(Token, url, newFileID, postText)
else:
deleteFile = False
print(f"File upload aborted.")
else: else:
if tagDescription == "": if postText == "":
tagDescription = description + ' ' + tags postText = getPostText(f)
if newFileID != "": if newFileID != "":
newFileID = newFileID + "," + newFile.get("id") newFileID = newFileID + "," + newFile.get("id")
else: else:
newFileID = newFile.get("id") newFileID = newFile.get("id")
if uploadType == 1: if uploadType == 1:
createNewPost(Token, url, newFileID, tagDescription) createNewPost(Token, url, newFileID, postText)
if deleteFile: if deleteFile:
for f in files: for f in files:
if os.path.exists(f): if os.path.exists(f):
os.remove(f) os.remove(f)
return True return True
if __name__ == "__main__": if __name__ == "__main__":
@ -134,5 +207,8 @@ if __name__ == "__main__":
imagedir = config.get("image_path") imagedir = config.get("image_path")
uploadType = config.get("upload_type") uploadType = config.get("upload_type")
deleteFile = config.get("delete_file") deleteFile = config.get("delete_file")
sendImages(accessToken, url, imagedir, uploadType, deleteFile) try:
sendImages(accessToken, url, imagedir, uploadType, deleteFile)
except ValueError as e:
print(f"Error while uploading the image: {e}")

View File

@ -3,6 +3,13 @@
a Python app to publish all images from a folder to Pixelfed. a Python app to publish all images from a folder to Pixelfed.
This script use Image Meta Tag Description fot the Post Description. This script use Image Meta Tag Description fot the Post Description.
## How to use
- create new confg file like the example one
- execute PixelfedImporter.py
if you want to add some default tags create a new tags.txt file with all Default Tags. You also can add a default Text in ths File.
## Config ## Config
To use this you need a config.json if this cannot be created automatically, here is the structure: To use this you need a config.json if this cannot be created automatically, here is the structure:

7
config.json.example Normal file
View File

@ -0,0 +1,7 @@
{
"server_url": "URL TO YOUR PIXELFED",
"access_token": "YOUR ACCESS TOKEN",
"image_path": "PATH TO YOUR IMAGES",
"upload_type": 0|1,
"delete_file": true|false
}

270
main.py
View File

@ -1,270 +0,0 @@
import os
import exifread
import json
import gettext
import tkinter as tk
from PixelfedAPI import *
from tkinter import filedialog, Menu, Toplevel
from PIL import Image, ImageTk, ExifTags
from PIL.ExifTags import TAGS
from PIL.PngImagePlugin import PngImageFile, PngInfo
class ImageUploaderApp:
def __init__(self, root):
serverURL = self.get_setting_serverURL
serverAccesskey = self.get_setting_accessKey
self.Pixelfed = PixelfedAPI(serverURL, serverAccesskey)
self.root = root
self.root.title("Pixelfed Uploader")
self.image_paths = []
self.current_index = 0
self.create_menu()
self.button_frame = tk.Frame(self.root)
self.prev_button = tk.Button(self.button_frame, text="Previous", command=self.show_previous_image)
self.prev_button.pack(side=tk.LEFT)
self.canvas = tk.Canvas(self.button_frame, width=400, height=400)
self.canvas.pack(side=tk.LEFT)
self.next_button = tk.Button(self.button_frame, text="Next", command=self.show_next_image)
self.next_button.pack(side=tk.RIGHT)
self.button_frame.pack()
self.description_label = tk.Label(self.root, text="Post Content:")
self.description_label.pack()
self.description_text = tk.Text(self.root, height=10, wrap=tk.WORD)
self.description_text.pack()
self.description_length_var = tk.StringVar()
self.description_length_label = tk.Label(self.root, textvariable=self.description_length_var)
self.description_length_label.pack()
self.description_text.bind("<KeyRelease>", self.update_description_length)
self.tag_text_label = tk.Label(self.root, text="Tags:")
self.tag_text_label.pack()
self.tag_text = tk.Text(self.root, height=5, wrap=tk.WORD)
self.tag_text.pack()
defaultTags = self.getTags()
self.tag_text.delete("1.0", tk.END)
self.tag_text.insert(tk.END, defaultTags)
#self.alt_text_label = tk.Label(self.root, text="Alt-Text:")
#self.alt_text_label.pack()
#self.alt_text = tk.Text(self.root, height=10, wrap=tk.WORD)
#self.alt_text.pack()
#self.alt_length_var = tk.StringVar()
#self.alt_length_label = tk.Label(self.root, textvariable=self.alt_length_var)
#self.alt_length_label.pack()
#self.alt_text.bind("<KeyRelease>", self.update_alt_length)
self.upload_button = tk.Button(self.root, text="Upload", command=self.upload_image)
self.upload_button.pack()
self.open_folder()
def open_folder(self):
self.load_images()
def create_menu(self):
self.menu_bar = Menu(self.root)
self.root.config(menu=self.menu_bar)
self.file_menu = Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label="File", menu=self.file_menu)
self.file_menu.add_command(label="Open Folder", command=self.load_images)
self.file_menu.add_command(label="Settings", command=self.open_settings)
def open_settings(self):
settings_window = Toplevel(self.root)
settings_window.title("Settings")
settings_window.geometry("500x250")
server_url_label = tk.Label(settings_window, text="Server-URL:")
server_url_label.pack()
server_url_entry = tk.Entry(settings_window, width=50)
server_url_entry.pack()
access_key_label = tk.Label(settings_window, text="API AccessKey:")
access_key_label.pack()
access_key_entry = tk.Entry(settings_window, width=50)
access_key_entry.pack()
image_folder_label = tk.Label(settings_window, text="Default Folder:")
image_folder_label.pack()
image_folder_entry = tk.Entry(settings_window, width=50)
image_folder_entry.pack()
enableLogVar = tk.BooleanVar()
enableLog_label = tk.Label(settings_window, text="Log:")
enableLog_label.pack()
enableLog = tk.Checkbutton(settings_window, variable=enableLogVar)
enableLog.pack()
save_button = tk.Button(settings_window, text="Speichern", command=lambda: self.save_settings(server_url_entry.get(), access_key_entry.get(), image_folder_entry.get(), enableLogVar.get()))
save_button.pack()
config = self.load_config()
if config:
server_url_entry.insert(0, config.get("server_url", ""))
access_key_entry.insert(0, config.get("access_token", ""))
image_folder_entry.insert(0, config.get("image_path", ""))
def load_config(self):
try:
with open("config.json", "r") as config_file:
settings = json.load(config_file)
return settings
except FileNotFoundError:
return {}
def get_config_File(self):
config = self.load_config()
return config
def get_setting_serverURL(self):
config = self.get_config_File()
url = config.get("server_url")
return url
def get_setting_accessKey(self):
config = self.get_config_File()
accessToken = config.get("access_token")
return accessToken
def get_setting_imageFolder(self):
config = self.get_config_File()
imagedir = config.get("image_path")
return imagedir
def get_setting_log(self):
config = self.get_config_File()
isLogEnable = config.get("log")
return isLogEnable
def save_settings(self, server_url, access_key, image_path, log=False):
settings = {
"server_url": server_url,
"access_token": access_key,
"image_path": image_path,
"log": log
}
with open("config.json", "w") as config_file:
json.dump(settings, config_file)
print("Settings have been saved.")
def update_description_length(self, event):
description = self.description_text.get("1.0", tk.END)
description_length = len(description)
self.description_length_var.set(f"({description_length})")
def update_alt_length(self, event):
alt = self.alt_text.get("1.0", tk.END)
alt_length = len(alt)
self.alt_length_var.set(f"({alt_length})")
def load_images(self):
imagedir = self.get_setting_imageFolder()
if not imagedir:
folder_path = filedialog.askdirectory()
else:
folder_path = imagedir
if folder_path:
self.image_paths = [os.path.join(folder_path, filename) for filename in os.listdir(folder_path) if filename.lower().endswith(('.jpg', '.jpeg'))]
self.current_index = 0
self.show_current_image()
def show_current_image(self):
if self.image_paths:
image_path = self.image_paths[self.current_index]
image = Image.open(image_path)
image.thumbnail((400, 400))
photo = ImageTk.PhotoImage(image)
self.canvas.create_image(0, 0, anchor=tk.NW, image=photo)
self.canvas.image = photo
description = self.getItemDescription(image_path)
self.description_text.delete("1.0", tk.END)
self.description_text.insert(tk.END, description)
def getItemDescription(self, filename):
type = Image.open(filename)
exif_tags = open(filename, 'rb')
tags = exifread.process_file(exif_tags)
exif_array = []
if type.format != "PNG":
for i in tags:
compile = i, str(tags[i])
exif_array.append(compile)
if type.format == "PNG":
image = PngImageFile(filename)
metadata = PngInfo()
for i in image.text:
compile = i, str(image.text[i])
exif_array.append(compile)
Description = exif_array[0][1]
return Description
def getTags(self):
try:
file = open('tags.txt','r')
fileContent = file.read()
file.close
return fileContent
except Exception as e:
print("Error reading File:", e)
return ""
def show_previous_image(self):
if self.image_paths:
self.current_index = (self.current_index - 1) % len(self.image_paths)
self.show_current_image()
def show_next_image(self):
if self.image_paths:
self.current_index = (self.current_index + 1) % len(self.image_paths)
self.show_current_image()
def upload_image(self):
description = self.description_text.get("1.0", tk.END)
description = description + self.tag_text.get("1.0", tk.END)
#alt_text = self.alt_text.get("1.0", tk.END)
selected_image_path = self.image_paths[self.current_index]
media = self.Pixelfed.mediaUpload(selected_image_path)
if media:
mediaID = media.get("id")
#self.Pixelfed.createNewPost(mediaID, description, alt_text)
self.Pixelfed.createNewPost(mediaID, description)
print("Post created:")
print("Content:", description)
#print("Alt-Text:", alt_text)
print("Image:", selected_image_path)
if __name__ == "__main__":
root = tk.Tk()
app = ImageUploaderApp(root)
root.mainloop()