diff options
| author | A Farzat <a@farzat.xyz> | 2025-10-09 08:59:52 +0300 |
|---|---|---|
| committer | A Farzat <a@farzat.xyz> | 2025-10-09 08:59:52 +0300 |
| commit | 7e2a136db5921755a29fa5c9a1751028688c55cd (patch) | |
| tree | 372d972fe21cb25f6c441a994aa4db669c89010b | |
| parent | d1b1c055bb9ed49530e9a96fd34b3ada5899bbb5 (diff) | |
| download | csca5028-7e2a136db5921755a29fa5c9a1751028688c55cd.tar.gz csca5028-7e2a136db5921755a29fa5c9a1751028688c55cd.zip | |
Add last_viewed handling
| -rw-r--r-- | front-end/src/App.css | 31 | ||||
| -rw-r--r-- | front-end/src/App.jsx | 29 | ||||
| -rw-r--r-- | wsgi/__init__.py | 24 |
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, + } |
