summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA Farzat <a@farzat.xyz>2025-10-09 08:59:52 +0300
committerA Farzat <a@farzat.xyz>2025-10-09 08:59:52 +0300
commit7e2a136db5921755a29fa5c9a1751028688c55cd (patch)
tree372d972fe21cb25f6c441a994aa4db669c89010b
parentd1b1c055bb9ed49530e9a96fd34b3ada5899bbb5 (diff)
downloadcsca5028-7e2a136db5921755a29fa5c9a1751028688c55cd.tar.gz
csca5028-7e2a136db5921755a29fa5c9a1751028688c55cd.zip
Add last_viewed handling
-rw-r--r--front-end/src/App.css31
-rw-r--r--front-end/src/App.jsx29
-rw-r--r--wsgi/__init__.py24
3 files changed, 82 insertions, 2 deletions
diff --git a/front-end/src/App.css b/front-end/src/App.css
index 714d6fb..20d839e 100644
--- a/front-end/src/App.css
+++ b/front-end/src/App.css
@@ -376,6 +376,35 @@ body {
transform: scale(1.05);
}
+.new-overlay {
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ background: linear-gradient(135deg, #ff6b6b, #ff4757);
+ color: white;
+ padding: 4px 8px;
+ border-radius: 4px;
+ font-size: 0.7rem;
+ font-weight: bold;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ box-shadow: 0 2px 8px rgba(255, 107, 107, 0.4);
+ z-index: 2;
+ animation: pulse 2s infinite;
+}
+
+@keyframes pulse {
+ 0% {
+ box-shadow: 0 2px 8px rgba(255, 107, 107, 0.4);
+ }
+ 50% {
+ box-shadow: 0 2px 16px rgba(255, 107, 107, 0.6);
+ }
+ 100% {
+ box-shadow: 0 2px 8px rgba(255, 107, 107, 0.4);
+ }
+}
+
.duration-overlay {
position: absolute;
bottom: 8px;
@@ -387,6 +416,7 @@ body {
font-size: 0.8rem;
font-weight: bold;
backdrop-filter: blur(4px);
+ z-index: 2;
}
.play-button {
@@ -399,6 +429,7 @@ body {
text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.7);
opacity: 0.9;
transition: all 0.3s ease;
+ z-index: 1;
}
.video-card:hover .play-button {
diff --git a/front-end/src/App.jsx b/front-end/src/App.jsx
index 104b561..da6057e 100644
--- a/front-end/src/App.jsx
+++ b/front-end/src/App.jsx
@@ -36,9 +36,10 @@ function App() {
id: id,
_id: subscription._id,
type: type,
- title: subscription.title || id, // Use title if available, fallback to ID
+ title: subscription.title,
last_fetch: subscription.last_fetch,
last_video_update: subscription.last_video_update,
+ last_viewed: subscription.last_viewed,
new_vids: subscription.new_vids,
time_between_fetches: subscription.time_between_fetches,
videos: subscription.videos
@@ -54,6 +55,23 @@ function App() {
}
};
+ const setViewed = async (subscriptionId, viewedTime) => {
+ try {
+ const formData = new FormData();
+ formData.append('_id', subscriptionId);
+ formData.append('viewed_time', viewedTime);
+
+ await axios.post(`${API_BASE_URL}/set-viewed/`, formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ });
+ } catch (err) {
+ console.error('Error setting viewed time:', err);
+ // Don't show error to user as this is a background operation
+ }
+ };
+
const fetchVideos = async (channelId) => {
try {
setLoading(true);
@@ -69,6 +87,9 @@ function App() {
if (response.data && Array.isArray(response.data)) {
setVideos(response.data);
+
+ const currentTime = new Date().toISOString();
+ setViewed(subscription._id, currentTime);
} else {
throw new Error('Invalid response format');
}
@@ -305,7 +326,7 @@ function App() {
id="time-between-fetches"
type="number"
value={timeBetweenFetches}
- onChange={(e) => setTimeBetweenFetches(parseInt(e.target.value) || 300)}
+ onChange={(e) => setTimeBetweenFetches(parseInt(e.target.value) || 0)}
min="60"
max="86400"
className="subscription-input"
@@ -387,6 +408,10 @@ function App() {
<div className="duration-overlay">
{formatDuration(video.duration)}
</div>
+ {channels.find(ch => ch.id === selectedChannelId)?.last_viewed &&
+ new Date(video.published) > new Date(channels.find(ch => ch.id === selectedChannelId).last_viewed) && (
+ <div className="new-overlay">NEW</div>
+ )}
<div className="play-button">▶</div>
</div>
diff --git a/wsgi/__init__.py b/wsgi/__init__.py
index dab7b7e..9a504b0 100644
--- a/wsgi/__init__.py
+++ b/wsgi/__init__.py
@@ -1,3 +1,4 @@
+from datetime import datetime, UTC
from typing import Any, Dict, List
from components.database import subscriptions
from components.subscriptions.main import Subscription
@@ -38,3 +39,26 @@ def add_sub() -> Dict[str, Any]:
)
sub.insert()
return sub_info_from_dict(sub.asdict())
+
+@app.post("/set-time-between-fetches/")
+def set_time_between_fetches() -> Dict[str, Any]:
+ return {}
+
+@app.post("/set-viewed/")
+def set_viewed() -> Dict[str, Any]:
+ viewed_time_str = request.form.get("viewed_time")
+ if viewed_time_str:
+ print(viewed_time_str)
+ viewed_time = datetime.fromisoformat(viewed_time_str)
+ else:
+ viewed_time = datetime.now(tz=UTC)
+ result = subscriptions.update_one(
+ {"_id": request.form["_id"]},
+ {"$set": {"last_viewed": viewed_time}}
+ )
+ if not result.modified_count:
+ raise Exception("Subscription %s not found" % request.form["_id"])
+ return {
+ "_id": request.form["_id"],
+ "last_viewed": viewed_time,
+ }