Created
November 29, 2023 17:52
-
-
Save andyshinn/e2f5812d50a5417fb077f4109d5dd4d5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from flask import Flask, request, send_file, jsonify | |
import hashlib | |
import requests | |
import json | |
from PIL import Image | |
from io import BytesIO | |
from geolite2 import geolite2 | |
app = Flask(__name__) | |
# Configuration | |
appUrl = 'https://plex-discord-webhook.herokuapp.com' | |
webhookKey = 'YOUR_DISCORD_WEBHOOK_KEY' | |
def format_title(metadata): | |
if metadata.get('grandparentTitle'): | |
return metadata['grandparentTitle'] | |
else: | |
ret = metadata['title'] | |
if metadata.get('year'): | |
ret += f" ({metadata['year']})" | |
return ret | |
def format_subtitle(metadata): | |
ret = '' | |
if metadata.get('grandparentTitle'): | |
if metadata['type'] == 'track': | |
ret = metadata['parentTitle'] | |
elif metadata.get('index') and metadata.get('parentIndex'): | |
ret = f"S{metadata['parentIndex']} E{metadata['index']}" | |
elif metadata.get('originallyAvailableAt'): | |
ret = metadata['originallyAvailableAt'] | |
if metadata.get('title'): | |
ret += f" - {metadata['title']}" | |
elif metadata['type'] == 'movie': | |
ret = metadata.get('tagline', '') | |
return ret | |
def format_summary(summary): | |
ret = '' | |
if summary and len(summary): | |
if len(summary) > 300: | |
ret += summary[:300] + '...' | |
else: | |
ret += summary | |
if ret: | |
ret = f"\r\n\r\n{ret}" | |
return ret | |
def notify_discord(image_url, payload, location, action): | |
location_text = '' | |
if location: | |
if location.get('city'): | |
location_text = f" near {location['city']}, {location.get('region_name', location['country_name'])}" | |
else: | |
location_text = f", {location.get('region_name', location['country_name'])}" | |
data = { | |
"content": "", | |
"username": "Plex", | |
"avatar_url": f"{appUrl}/plex-icon.png", | |
"embeds": [ | |
{ | |
"title": format_title(payload['Metadata']), | |
"description": f"{format_subtitle(payload['Metadata'])}{format_summary(payload['Metadata']['summary'])}", | |
"footer": { | |
"text": f"{action} by {payload['Account']['title']} on {payload['Player']['title']} from {payload['Server']['title']} {location_text}", | |
"icon_url": payload['Account']['thumb'] | |
}, | |
"thumbnail": { | |
"url": image_url, | |
"height": 200, | |
"width": 200 | |
} | |
} | |
] | |
} | |
requests.post(f"https://discordapp.com/api/webhooks/{webhookKey}", json=data) | |
@app.route('/', methods=['POST']) | |
def handle_payload(): | |
payload = json.loads(request.form['payload']) | |
is_video = payload['Metadata']['librarySectionType'] in ['movie', 'show'] | |
is_audio = payload['Metadata']['librarySectionType'] == 'artist' | |
if payload.get('user') and payload.get('Metadata') and (is_audio or is_video): | |
key = hashlib.sha1((payload['Server']['uuid'] + payload['Metadata']['guid']).encode()).hexdigest() | |
if payload['event'] in ['media.play', 'media.rate']: | |
# Save the image | |
if 'thumb' in request.files: | |
image_data = request.files['thumb'].read() | |
image = Image.open(BytesIO(image_data)) | |
image.thumbnail((75, 75)) | |
buffer = BytesIO() | |
image.save(buffer, format="JPEG") | |
buffer.seek(0) | |
redis_client.setex(key, 7 * 24 * 60 * 60, buffer.read()) | |
if (payload['event'] == 'media.scrobble' and is_video) or payload['event'] in ['media.rate', 'media.play']: | |
# Geolocate player | |
reader = geolite2.reader() | |
match = geolite2.lookup(payload['Player']['publicAddress']) | |
geolite2.close() | |
location = match.location if match else None | |
action = '' | |
if payload['event'] == 'media.scrobble' or payload['event'] == 'media.play': | |
action = 'played' | |
elif payload['event'] == 'media.rate': | |
if payload.get('rating', 0) > 0: | |
action = 'rated ' + '★' * (payload['rating'] // 2) | |
else: | |
action = 'unrated' | |
# Send the event to Discord | |
image_url = f"{appUrl}/images/{key}" if redis_client.get(key) else None | |
if not location or (location and location.get('city') and len(location['city']) > 1): | |
notify_discord(image_url, payload, location, action) | |
else: | |
print('Location city missing, trying OSM lat lng lookup') | |
response = requests.get(f"http://nominatim.openstreetmap.org/reverse?format=json&lat={location['latitude']}&lon={location['longitude']}&accept-language=en", headers={'User-Agent': '[email protected]'}) | |
if response.status_code == 200: | |
location = response.json().get('address') | |
location['region_name'] = location.get('state') | |
location['country_name'] = location.get('country') | |
if not image_url: | |
notify_discord(None, payload, location, action) | |
else: | |
notify_discord(image_url, payload, location, action) | |
return '', 200 | |
@app.route('/images/<key>') | |
def get_image(key): | |
image_data = redis_client.get(key) | |
if image_data: | |
return send_file(BytesIO(image_data), mimetype='image/jpeg') | |
else: | |
return '', 404 | |
if __name__ == '__main__': | |
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 11000))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment