Skip to content

Commit

Permalink
feat(client): added autoplay feature, to automatically swipe to the n…
Browse files Browse the repository at this point in the history
…ext video when current ended
  • Loading branch information
Will Moss committed Aug 9, 2024
1 parent 0b0ad68 commit cedc52e
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 2 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ ENV AUTH_ENABLED "true"
ENV AUTH_SECRET "\$2a\$14\$qRW8no8UDmSwIWM6KHwdRe1j/LMrxoP4NSM756RVodqeUq5HzG6t."
ENV PUBLIC_URL "https://localhost"
ENV APP_TITLE "Erin - TikTok feed for your own clips"
ENV AUTOPLAY_ENABLED "false"
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Erin has all these features implemented :
- Display your own videos using TikTok's swipe feed
- Mask the videos you don't want to see in your feed\*
- Choose which feed you want to play\*\*
- Autoplay your feed without even swiping
- Simple lazy-loading mechanism for your videos
- Automatic clip naming based on file name
- Simple and optional security using a master password
Expand Down Expand Up @@ -101,6 +102,7 @@ To run Erin, you will need to set the following environment variables in a `.env
| `AUTH_ENABLED` | `string` | Whether Basic Authentication should be enabled. (This parameter is case sensitive) (Possible values : true, false) | true |
| `AUTH_SECRET` | `string` | The secure hash of the password used to protect your instance of Erin. | Hash of `secure-password` |
| `APP_TITLE` | `string` | The custom title that you would like to display in the browser's tab. (Tip: You can use `[VIDEO_TITLE]` here if you want Erin to dynamically display the title of the current video.) | Erin - TikTok feed for your own clips |
| `AUTOPLAY_ENABLED` | `boolean` | Whether autoplay should be enabled. (This parameter is case sensitive) (Possible values : true, false) | false |

> **Tip :** To generate a secure hash for your instance, use the following command :
Expand Down
1 change: 1 addition & 0 deletions docker/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
replace __PUBLIC_URL__ {$PUBLIC_URL}
replace __USE_SECRET__ {$AUTH_ENABLED}
replace __APP_TITLE__ "{$APP_TITLE}"
replace __AUTOPLAY_ENABLED__ {$AUTOPLAY_ENABLED}
}

# 404 Redirect
Expand Down
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
window.USE_SECRET = __USE_SECRET__;
if (window.location.protocol === "https:")
window.PUBLIC_URL = window.PUBLIC_URL.replace("http:", "https:");
window.AUTOPLAY_ENABLED = __AUTOPLAY_ENABLED__;
</script>
</head>
<body>
Expand Down
5 changes: 5 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ const App = () => {
_updatePageTitle(v);
setCurrentVideoIndex(i);
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const handleVideoFinish = () => {
if (!window.AUTOPLAY_ENABLED) return;
document.querySelector(".feed").scrollBy({ top: 1, left: 0, behavior: "smooth" });
};

// Member - Trick to trigger state updates on localStorage updates
const [blackListUpdater, setBlacklistUpdater] = useState(0);
Expand Down Expand Up @@ -331,6 +335,7 @@ const App = () => {
jumpBackForward={previousVideoIndex !== visibleVideos.length && currentVideoIndex > 1}
videos={visibleVideos}
onFocusVideo={handleVideoFocus}
onFinishVideo={handleVideoFinish}
/>
);
},
Expand Down
35 changes: 33 additions & 2 deletions src/components/VideoFeed/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { useEffect, useRef } from "react";
import VideoCard from "../VideoCard";
import "./index.css";

const VideoFeed = ({ videos, initialIndex, jumpToEnd, jumpBackForward, onFocusVideo }) => {
const VideoFeed = ({
videos,
initialIndex,
jumpToEnd,
jumpBackForward,
onFocusVideo,
onFinishVideo,
}) => {
// Member - Track the currently-visible video (0-indexed)
const currentVideoIndex = useRef(0);

Expand All @@ -21,8 +28,21 @@ const VideoFeed = ({ videos, initialIndex, jumpToEnd, jumpBackForward, onFocusVi
// Member - Number of videos to load at a time
const _bufferSize = 3;

// Member - Prevent double jump forward
const hasJumpedForward = useRef(false);

// Mechanism - On video end, call a listener to trigger autoscroll + autoplay if enabled
const throttle = useRef(false);
const handleVideoTimeUpdate = (e) => {
if (e.target.currentTime === 0 && throttle.current === true) {
throttle.current = false;
onFinishVideo();
setTimeout(() => {
throttle.current = true;
}, 500);
}
};

// Hook - On mount - Set the current scroll
useEffect(() => {
if (jumpToEnd) return feedRef.current.scrollBy({ top: 1, left: 0 });
Expand Down Expand Up @@ -58,9 +78,13 @@ const VideoFeed = ({ videos, initialIndex, jumpToEnd, jumpBackForward, onFocusVi
visibleIndex = currentIndex;
videoElement.play().catch((_) => {});
onFocusVideo(videos[currentIndex], currentIndex);
videoElement.addEventListener("timeupdate", handleVideoTimeUpdate, true);
}
// Case when a video is off-screen or being scrolled in / out of the screen
else videoElement.pause();
else {
videoElement.pause();
videoElement.removeEventListener("timeupdate", handleVideoTimeUpdate, true);
}
});

if (visibleIndex === false) return;
Expand Down Expand Up @@ -135,6 +159,13 @@ const VideoFeed = ({ videos, initialIndex, jumpToEnd, jumpBackForward, onFocusVi
};
}, [videos]); // eslint-disable-line react-hooks/exhaustive-deps

// Hook - On mount - Set our autoplay flag to ready
useEffect(() => {
setTimeout(() => {
throttle.current = true;
}, 500);
}, []);

initialIndex = _bufferSize === videos.length ? 0 : initialIndex;

return (
Expand Down

0 comments on commit cedc52e

Please sign in to comment.