diff --git a/app/YtManagerApp/management/jobs/synchronize.py b/app/YtManagerApp/management/jobs/synchronize.py
index f6db0d8..5c6940a 100644
--- a/app/YtManagerApp/management/jobs/synchronize.py
+++ b/app/YtManagerApp/management/jobs/synchronize.py
@@ -10,6 +10,8 @@ from YtManagerApp.management.downloader import fetch_thumbnail, downloader_proce
from YtManagerApp.models import *
from YtManagerApp.utils import youtube
+from YtManagerApp.management import notification_manager
+
log = logging.getLogger('sync')
__lock = Lock()
@@ -104,6 +106,7 @@ def synchronize():
try:
log.info("Running scheduled synchronization... ")
+ notification_manager.notify_status_update(f'Synchronization started for all subscriptions.')
# Sync subscribed playlists/channels
log.info("Sync - checking videos")
@@ -119,6 +122,7 @@ def synchronize():
__fetch_thumbnails()
log.info("Synchronization finished.")
+ notification_manager.notify_status_update(f'Synchronization finished for all subscriptions.')
finally:
__lock.release()
@@ -128,6 +132,8 @@ def synchronize_subscription(subscription: Subscription):
__lock.acquire()
try:
log.info("Running synchronization for single subscription %d [%s]", subscription.id, subscription.name)
+ notification_manager.notify_status_update(f'Synchronization started for subscription {subscription.name}.')
+
yt_api = youtube.YoutubeAPI.build_public()
log.info("Sync - checking videos")
@@ -141,6 +147,7 @@ def synchronize_subscription(subscription: Subscription):
__fetch_thumbnails()
log.info("Synchronization finished for subscription %d [%s].", subscription.id, subscription.name)
+ notification_manager.notify_status_update(f'Synchronization finished for subscription {subscription.name}.')
finally:
__lock.release()
diff --git a/app/YtManagerApp/management/notification_manager.py b/app/YtManagerApp/management/notification_manager.py
new file mode 100644
index 0000000..c115d14
--- /dev/null
+++ b/app/YtManagerApp/management/notification_manager.py
@@ -0,0 +1,93 @@
+from django.contrib.auth.models import User
+from typing import Dict, Deque, Any, Optional
+from collections import deque
+from datetime import datetime, timedelta
+from YtManagerApp.utils.algorithms import bisect_left
+from threading import Lock
+
+# Clients will request updates at most every few seconds, so a retention period of 60 seconds should be more than
+# enough. I gave it 15 minutes so that if for some reason the connection fails (internet drops) and then comes back a
+# few minutes later, the client will still get the updates
+__RETENTION_PERIOD = 15 * 60
+__NOTIFICATIONS: Deque[Dict] = deque()
+__NEXT_ID = 0
+__LOCK = Lock()
+
+
+# Messages enum
+class Messages:
+ STATUS_UPDATE = 'st-up'
+ STATUS_OPERATION_PROGRESS = 'st-op-prog'
+ STATUS_OPERATION_END = 'st-op-end'
+
+
+def __add_notification(message, user: User=None, **kwargs):
+ global __NEXT_ID
+
+ __LOCK.acquire()
+
+ try:
+ # add notification
+ notification = {
+ 'time': datetime.now(),
+ 'msg': message,
+ 'id': __NEXT_ID,
+ 'uid': user and user.id,
+ }
+ notification.update(kwargs)
+ __NOTIFICATIONS.append(notification)
+ __NEXT_ID += 1
+
+ # trim old notifications
+ oldest = __NOTIFICATIONS[0]
+ while len(__NOTIFICATIONS) > 0 and oldest['time'] + timedelta(seconds=__RETENTION_PERIOD) < datetime.now():
+ __NOTIFICATIONS.popleft()
+ oldest = __NOTIFICATIONS[0]
+
+ finally:
+ __LOCK.release()
+
+
+def get_notifications(user: User, last_received_id: Optional[int]):
+
+ __LOCK.acquire()
+
+ try:
+ first_index = 0
+ if last_received_id is not None:
+ first_index = bisect_left(__NOTIFICATIONS,
+ {'id': last_received_id},
+ key=lambda item: item['id'])
+
+ for i in range(first_index, len(__NOTIFICATIONS)):
+ item = __NOTIFICATIONS[i]
+ if item['uid'] is None or item['uid'] == user.id:
+ yield item
+
+ finally:
+ __LOCK.release()
+
+
+def get_current_notification_id():
+ return __NEXT_ID
+
+
+def notify_status_update(status_message: str, user: User=None):
+ __add_notification(Messages.STATUS_UPDATE,
+ user=user,
+ status=status_message)
+
+
+def notify_status_operation_progress(op_id: Any, status_message: str, progress_percent: float, user: User=None):
+ __add_notification(Messages.STATUS_OPERATION_PROGRESS,
+ user=user,
+ operation=op_id,
+ status=status_message,
+ progress=progress_percent)
+
+
+def notify_status_operation_ended(op_id: Any, status_message: str, user: User=None):
+ __add_notification(Messages.STATUS_OPERATION_END,
+ user=user,
+ operation=op_id,
+ status=status_message)
diff --git a/app/YtManagerApp/templates/YtManagerApp/index.html b/app/YtManagerApp/templates/YtManagerApp/index.html
index cf158ee..0cf473d 100644
--- a/app/YtManagerApp/templates/YtManagerApp/index.html
+++ b/app/YtManagerApp/templates/YtManagerApp/index.html
@@ -9,6 +9,8 @@
{% block scripts %}
{% endblock %}
diff --git a/app/YtManagerApp/templates/YtManagerApp/js/index.js b/app/YtManagerApp/templates/YtManagerApp/js/index.js
index 6fe9cfd..88c9ab3 100644
--- a/app/YtManagerApp/templates/YtManagerApp/js/index.js
+++ b/app/YtManagerApp/templates/YtManagerApp/js/index.js
@@ -157,6 +157,30 @@ function videos_Submit(e)
e.preventDefault();
}
+///
+/// Notifications
+///
+const NOTIFICATION_INTERVAL = 1000;
+
+function get_and_process_notifications()
+{
+ $.get("{% url 'ajax_get_notifications' 12345 %}".replace("12345", LAST_NOTIFICATION_ID))
+ .done(function(data) {
+ for (let entry of data)
+ {
+ LAST_NOTIFICATION_ID = entry.id;
+ let dt = new Date(entry.time);
+
+ // Status update
+ if (entry.msg === 'st-up') {
+ let txt = `${dt.getHours()}:${dt.getMinutes()}${entry.status}`;
+ $('#status-message').html(txt);
+ }
+
+ }
+ });
+}
+
///
/// Initialization
///
@@ -186,4 +210,7 @@ $(document).ready(function ()
filters_form.find('select[name=show_watched]').on('change', videos_ReloadWithTimer);
filters_form.find('select[name=show_downloaded]').on('change', videos_ReloadWithTimer);
videos_Reload();
+
+ // Notification manager
+ setInterval(get_and_process_notifications, NOTIFICATION_INTERVAL);
});
diff --git a/app/YtManagerApp/templates/YtManagerApp/master_default.html b/app/YtManagerApp/templates/YtManagerApp/master_default.html
index 594902c..264876b 100644
--- a/app/YtManagerApp/templates/YtManagerApp/master_default.html
+++ b/app/YtManagerApp/templates/YtManagerApp/master_default.html
@@ -67,7 +67,7 @@