diff --git a/src/App.tsx b/src/App.tsx
index 9031569..f4335b9 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -49,6 +49,7 @@ export default function App() {
const [search, setSearch] = useState("");
const [downloadedOnly, setDownloadedOnly] = useState(() => remembered("downloadedOnly"));
const [hideShorts, setHideShorts] = useState(() => remembered("hideShorts"));
+ const [autoplayNext, setAutoplayNext] = useState(() => remembered("autoplayNext"));
const [sidebarHidden, setSidebarHidden] = useState(() => remembered("sidebarHidden"));
const [sidebarPeek, setSidebarPeek] = useState(false);
const [view, setView] = useState(() => {
@@ -161,12 +162,13 @@ export default function App() {
localStorage.setItem("flighttube.browser", browser);
localStorage.setItem("flighttube.downloadedOnly", downloadedOnly ? "1" : "0");
localStorage.setItem("flighttube.hideShorts", hideShorts ? "1" : "0");
+ localStorage.setItem("flighttube.autoplayNext", autoplayNext ? "1" : "0");
localStorage.setItem("flighttube.sidebarHidden", sidebarHidden ? "1" : "0");
} catch {
/* storage blocked */
}
}, [view, quality, bulkLimit, streamQuality, subLang, subStyle, browser, downloadedOnly,
- hideShorts, sidebarHidden]);
+ hideShorts, autoplayNext, sidebarHidden]);
// The language a download embeds. Like the player's fetch, this does not
// depend on the on/off preference: that says what is shown, and a file
@@ -624,6 +626,7 @@ export default function App() {
onSubLang={setSubLang}
subStyle={subStyle}
onSubStyle={setSubStyle}
+ autoplayNext={autoplayNext}
onOpenChannel={() => {
setChannelId(playing.item.channel_id);
setSearch("");
@@ -683,6 +686,8 @@ export default function App() {
onSubLang={setSubLang}
hideShorts={hideShorts}
onHideShorts={setHideShorts}
+ autoplayNext={autoplayNext}
+ onAutoplayNext={setAutoplayNext}
browser={browser}
onBrowser={setBrowser}
onError={setFailure}
diff --git a/src/components/Player.tsx b/src/components/Player.tsx
index 9905359..a22e4a5 100644
--- a/src/components/Player.tsx
+++ b/src/components/Player.tsx
@@ -27,6 +27,8 @@ interface Props {
onSubStyle: (s: SubStyle) => void;
/** Leaves the player for this video's channel. */
onOpenChannel: () => void;
+ /** Roll straight on to the next video when this one ends. */
+ autoplayNext: boolean;
/** Persists a subtitle choice made from the transport bar. */
onSubLang: (l: string) => void;
/** Position in the current feed, for the "3 of 180" readout. */
@@ -122,7 +124,7 @@ function placeCues(vtt: string, line: number | null): string {
export default function Player({
item, path, onClose, onDelete, onPrev, onNext, onDownload, downloading,
- maxHeight, subLang, onSubLang, subStyle, onSubStyle, onOpenChannel,
+ maxHeight, subLang, onSubLang, subStyle, onSubStyle, onOpenChannel, autoplayNext,
index, total, titleBarInset,
}: Props) {
const streaming = path === null;
@@ -457,6 +459,12 @@ export default function Player({
onTimeUpdate={onTimeUpdate}
onLoadedMetadata={onLoadedMetadata}
onPause={persist}
+ onEnded={() => {
+ persist();
+ // Only where there is somewhere to go: the last video in the
+ // list simply stops.
+ if (autoplayNext) onNext?.();
+ }}
onLoadStart={() => setBuffering(true)}
onWaiting={() => setBuffering(true)}
onStalled={() => setBuffering(true)}
diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx
index f95b9ce..62ff155 100644
--- a/src/components/Settings.tsx
+++ b/src/components/Settings.tsx
@@ -30,6 +30,8 @@ interface Props {
onSubLang: (l: string) => void;
hideShorts: boolean;
onHideShorts: (v: boolean) => void;
+ autoplayNext: boolean;
+ onAutoplayNext: (v: boolean) => void;
browser: string;
onBrowser: (b: string) => void;
onError: (message: string) => void;
@@ -58,6 +60,7 @@ export default function Settings({
onClose, onImported, appearance, onAppearance, quality, onQuality,
bulkLimit, onBulkLimit,
streamQuality, onStreamQuality, subLang, onSubLang, hideShorts, onHideShorts,
+ autoplayNext, onAutoplayNext,
browser, onBrowser, onError,
}: Props) {
const [prereqs, setPrereqs] = useState(null);
@@ -301,6 +304,24 @@ export default function Settings({
+
+