-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathapp.py
More file actions
194 lines (153 loc) · 7.43 KB
/
Copy pathapp.py
File metadata and controls
194 lines (153 loc) · 7.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
from flask import Flask, request, jsonify
import os
import time
import json
import requests
import urllib
JELLYFIN_API_URL = os.getenv("JELLYFIN_API_URL")
JELLYFIN_API_TOKEN = os.getenv("JELLYFIN_API_TOKEN")
EPISODE_START_INTERVAL = float(os.getenv("EPISODE_START_INTERVAL"))
EPISODE_COUNT = float(os.getenv("EPISODE_COUNT"))
JELLYFIN_MESSAGE = os.getenv("JELLYFIN_MESSAGE")
JELLYFIN_STOP_ACTION = os.getenv("JELLYFIN_STOP_ACTION")
EPISODE_SKIMMING_DURATION = float(os.getenv("EPISODE_SKIMMING_DURATION", 1)) # default 1 min
if JELLYFIN_MESSAGE is None:
JELLYFIN_MESSAGE = "Are you still watching?"
if JELLYFIN_STOP_ACTION is None:
JELLYFIN_STOP_ACTION = "STOP"
app = Flask(__name__)
# In-memory tracking
playback_tracker = {}
@app.route("/webhook", methods=["POST"])
def webhook():
try:
# Check the Content-Type header
content_type = request.headers.get('Content-Type')
if content_type == 'application/json':
# Parse JSON payload
event = request.json
else:
# Fall back to raw data and try to parse it
event = json.loads(request.data.decode('utf-8'))
# Debugging: Log the parsed payload
#print("Parsed Payload:", event)
# Extract key information
notification_type = event.get("NotificationType")
user_id = event.get("UserId")
device_id = event.get("DeviceId")
item_type = event.get("ItemType")
item_id = event.get("ItemId") # Current item ID
# Store session object with nullable fields
session = {
"NotificationUsername": event.get("NotificationUsername", None),
"Id": event.get("Id", None), #T The ID of the session to stop
"DeviceName": event.get("DeviceName", None),
"RemoteEndPoint": event.get("RemoteEndPoint", None)
}
# Check if this is a "PlaybackStart" event for an episode
if notification_type == "PlaybackStart": # and item_type == "Episode":
key = f"{user_id}-{device_id}"
now = time.time()
if key not in playback_tracker:
playback_tracker[key] = {
"count": 0,
"last_play_time": 0,
"last_item_id": None
}
tracker = playback_tracker[key]
time_since_last_play = now - tracker["last_play_time"]
# Ignore if episode changed too quickly
if (item_type == "Episode" and
tracker["last_item_id"] != item_id and
time_since_last_play < (EPISODE_SKIMMING_DURATION * 60)):
print(f"⏩ Skimming detected!")
time.sleep(1)
print(f"⏩ Count skipped, {session.get('NotificationUsername', 'Unknown')} still has played only {tracker['count']} episodes in a row.")
tracker["last_play_time"] = now
tracker["last_item_id"] = item_id
return jsonify({"message": "Episode skipped quickly; sleep-timer skipped."}), 200
print(f"ℹ️ PlaybackStart from user: {session.get('NotificationUsername', 'Unknown')} | 🌐 Device: {session.get('RemoteEndPoint', 'Unknown')}")
# Reset playback counter if Movie starts
if item_type == "Movie":
tracker["count"] = 0
print(f"Movie started, reset count for user: {session.get('NotificationUsername', 'Unknown')}")
# Reset the count if playback events are far apart (e.g., > 1 hour)
if time_since_last_play > (60 * EPISODE_START_INTERVAL):
tracker["count"] = 0
# Increment the playback count
if item_type == "Episode":
tracker["count"] += 1
tracker["last_play_time"] = now
tracker["last_item_id"] = item_id
print(f"ℹ️ {session.get('NotificationUsername', 'Unknown')} has played {tracker['count']} episodes in a row.")
# If more than 4 episodes have been played, stop playback
if tracker["count"] > (EPISODE_COUNT - 1):
if stop_playback(session):
tracker["count"] = 0
return jsonify({"message": "Playback stopped due to autoplay limit"}), 200
else:
return jsonify({"message": "Failed to stop playback"}), 500
except Exception as e:
print("Error processing webhook:", e)
return jsonify({"message": "Error processing request"}), 500
return jsonify({"message": "Event processed"}), 200
def stop_playback(session):
"""
Stop playback for a given session ID using the Jellyfin API.
"""
session_id = session['Id']
display_message(session_id, JELLYFIN_MESSAGE, 'Sleep Timer', 7000)
time.sleep(5)
try:
# Construct the URL to stop playback
stop_url = f"{JELLYFIN_API_URL}/Sessions/{session_id}/Playing/Stop?ApiKey={JELLYFIN_API_TOKEN}"
# Construct the URL to pause playback
pause_url = f"{JELLYFIN_API_URL}/Sessions/{session_id}/Playing/Pause/?ApiKey={JELLYFIN_API_TOKEN}"
# Send the request to stop playback
if JELLYFIN_STOP_ACTION == "STOP":
req = urllib.request.urlopen(urllib.request.Request(stop_url, method="POST"))
else:
req = urllib.request.urlopen(urllib.request.Request(pause_url, method="POST"))
print(f"👤 {session.get('NotificationUsername', 'Unknown')} has played {int(EPISODE_COUNT)} episodes in a row.\n❗ ⏹️ {JELLYFIN_STOP_ACTION} Playback ❗\n🌐 Device Address: {session.get('RemoteEndPoint', 'Unknown')}")
# Wait for 2 seconds before sending the next command
time.sleep(2)
# Construct the URL to send the 'GoHome' command
go_home_url = f"{JELLYFIN_API_URL}/Sessions/{session_id}/Command/GoHome?ApiKey={JELLYFIN_API_TOKEN}"
# Send the request to navigate back to the home screen
if JELLYFIN_STOP_ACTION == "STOP":
req = urllib.request.urlopen(urllib.request.Request(go_home_url, method="POST"))
return True
except Exception as e:
print(f"Error stopping playback for session {session_id}: {e}")
return False
def display_message(session_id, message, header="Notice", timeout_ms=5000):
"""
Send a display message to the Jellyfin client.
Args:
session_id (str): The session ID of the client.
message (str): The message to display.
header (str): The header for the message.
timeout_ms (int): Time in milliseconds to display the message.
"""
try:
# Construct the URL for the DisplayMessage command
display_message_url = f"{JELLYFIN_API_URL}/Sessions/{session_id}/Command"
headers = {
"Authorization": f"MediaBrowser Token={JELLYFIN_API_TOKEN}",
"Content-Type": "application/json"
}
payload = {
"Name": "DisplayMessage",
"Arguments": {
"Header": header,
"Text": message,
"TimeoutMs": timeout_ms
}
}
# Send the display message
req = urllib.request.Request(display_message_url, data=json.dumps(payload).encode('utf-8'), headers=headers, method="POST")
urllib.request.urlopen(req)
except Exception as e:
print(f"Error sending display message to session {session_id}: {e}")
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5553)