diff --git a/src/app/(app)/settings.tsx b/src/app/(app)/settings.tsx
index 1ce03fe..32def8a 100644
--- a/src/app/(app)/settings.tsx
+++ b/src/app/(app)/settings.tsx
@@ -4,7 +4,7 @@ import { Icon } from '@/components/icon';
import { SafeAreaView } from '@/components/safe-area-view';
import { UpdateDialog } from '@/components/update-dialog';
import { SITE_DOMAINS, SITE_DOMAIN_KEY, type SiteDomain } from '@/lib/hanime1/endpoints';
-import { type ThemeMode, useThemeConfig } from '@/lib/hooks';
+import { type ThemeMode, usePlaybackSettings, useThemeConfig } from '@/lib/hooks';
import { useSecuritySettings } from '@/lib/hooks/use-security-settings';
import { useUpdateCheck } from '@/lib/hooks/use-update-check';
import { LANGUAGE_LABELS, type Language } from '@/lib/i18n/resources';
@@ -16,7 +16,15 @@ import { Env } from '@env';
// bundles (binding missing at runtime), destructuring after import works.
import * as ApplicationNS from 'expo-application';
import { ActivityAction, startActivityAsync } from 'expo-intent-launcher';
-import { ActivityIndicator, Platform, Pressable, ScrollView, Text, View } from 'react-native';
+import {
+ ActivityIndicator,
+ Platform,
+ Pressable,
+ ScrollView,
+ Switch,
+ Text,
+ View,
+} from 'react-native';
import { showMessage } from 'react-native-flash-message';
import { useMMKVString } from 'react-native-mmkv';
@@ -106,6 +114,7 @@ export { ScreenErrorBoundary as ErrorBoundary };
export default function SettingsScreen() {
const t = useTranslate();
const [theme, setTheme] = useThemeConfig();
+ const { autoplay, setAutoplay } = usePlaybackSettings();
const [domain, setDomain] = useMMKVString(SITE_DOMAIN_KEY);
const [lang, setLang] = useSelectedLanguage();
const clearHistory = useHistoryStore((s) => s.clearHistory);
@@ -140,6 +149,15 @@ export default function SettingsScreen() {
))}
+ {t('settings.playback')}
+
+
+
+
{t('settings.domain')}
{SITE_DOMAINS.map((d) => (
diff --git a/src/app/watch/[id].tsx b/src/app/watch/[id].tsx
index 522bad1..bab504c 100644
--- a/src/app/watch/[id].tsx
+++ b/src/app/watch/[id].tsx
@@ -6,7 +6,7 @@ import { SafeAreaView } from '@/components/safe-area-view';
import { toOfflineDetail } from '@/lib/download/offline-detail';
import { buildUrl, endpoints } from '@/lib/hanime1/scraper';
import { haptic, hapticSuccess } from '@/lib/haptics';
-import { useVideoDownload } from '@/lib/hooks';
+import { usePlaybackSettings, useVideoDownload } from '@/lib/hooks';
import { useTranslate } from '@/lib/i18n/utils';
import {
useDownloadedStore,
@@ -79,6 +79,22 @@ export default function WatchScreen() {
});
const { isPlaying } = useEvent(player, 'playingChange', { isPlaying: player.playing });
+ const { autoplay } = usePlaybackSettings();
+
+ // Start playback automatically when the screen is entered or the source
+ // changes (new video via the stack, or a resolution switch) unless the
+ // user opted out in Settings. Coming back from a deeper screen (author /
+ // tag) stays paused — the focus cleanup below paused it on leave.
+ // biome-ignore lint/correctness/useExhaustiveDependencies: videoSource is an intentional trigger — replay when the source lands or switches.
+ useEffect(() => {
+ if (!autoplay) return;
+ try {
+ player.play();
+ } catch {
+ // Player not attached to a source yet — replays when videoSource lands.
+ }
+ }, [autoplay, player, videoSource]);
+
// Pause when this screen loses focus (opening the author page, a tag page or
// another video keeps this screen mounted in the stack — without this, its
// audio keeps playing under the new screen). On unmount expo-video may
diff --git a/src/lib/hooks/index.ts b/src/lib/hooks/index.ts
index 6216465..4f18c09 100644
--- a/src/lib/hooks/index.ts
+++ b/src/lib/hooks/index.ts
@@ -5,6 +5,7 @@ export {
PORTRAIT_MIN_TILE_WIDTH,
useColumns,
} from './use-columns';
+export { usePlaybackSettings } from './use-playback-settings';
export { useSearchHistory } from './use-search-history';
export { useUpdateCheck, type UpdateCheckState } from './use-update-check';
export { useVideoActions, type ActionableItem } from './use-video-actions';
diff --git a/src/lib/hooks/use-playback-settings.ts b/src/lib/hooks/use-playback-settings.ts
new file mode 100644
index 0000000..1e93af7
--- /dev/null
+++ b/src/lib/hooks/use-playback-settings.ts
@@ -0,0 +1,16 @@
+import { useMMKVBoolean } from 'react-native-mmkv';
+
+const AUTOPLAY_KEY = 'settings.autoplay';
+
+/**
+ * Toggle for "Autoplay on entering the watch screen" — on by default.
+ * `undefined` (never set) reads as enabled; an explicit `false` opts out.
+ */
+export const usePlaybackSettings = () => {
+ const [autoplay, setAutoplay] = useMMKVBoolean(AUTOPLAY_KEY);
+
+ return {
+ autoplay: autoplay ?? true,
+ setAutoplay,
+ };
+};
diff --git a/src/translations/en.json b/src/translations/en.json
index 169c459..1e590e1 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -68,6 +68,9 @@
"themeSystem": "System",
"themeLight": "Light",
"themeDark": "Dark",
+ "playback": "Playback",
+ "autoplay": "Autoplay",
+ "autoplayDesc": "Start playing automatically when you open a video",
"domain": "Source domain",
"domainDescription": "Switch mirror if the default is blocked or slow",
"openLinks": "Open site links",
diff --git a/src/translations/ja.json b/src/translations/ja.json
index 85a0d1b..e16d5f9 100644
--- a/src/translations/ja.json
+++ b/src/translations/ja.json
@@ -68,6 +68,9 @@
"themeSystem": "システム",
"themeLight": "ライト",
"themeDark": "ダーク",
+ "playback": "再生",
+ "autoplay": "自動再生",
+ "autoplayDesc": "再生画面を開いたときに自動で再生する",
"domain": "ソースドメイン",
"domainDescription": "デフォルトがブロックまたは遅い場合はミラーを切り替えてください",
"openLinks": "サイトリンクを開く",
diff --git a/src/translations/ko.json b/src/translations/ko.json
index d312dd5..18cec68 100644
--- a/src/translations/ko.json
+++ b/src/translations/ko.json
@@ -68,6 +68,9 @@
"themeSystem": "시스템",
"themeLight": "라이트",
"themeDark": "다크",
+ "playback": "재생",
+ "autoplay": "자동 재생",
+ "autoplayDesc": "재생 화면을 열면 자동으로 재생",
"domain": "소스 도메인",
"domainDescription": "기본 도메인이 차단되거나 느리면 미러를 전환하세요",
"openLinks": "사이트 링크 열기",
diff --git a/src/translations/zh-CN.json b/src/translations/zh-CN.json
index ff3dcec..f023982 100644
--- a/src/translations/zh-CN.json
+++ b/src/translations/zh-CN.json
@@ -68,6 +68,9 @@
"themeSystem": "跟随系统",
"themeLight": "浅色",
"themeDark": "深色",
+ "playback": "播放",
+ "autoplay": "自动播放",
+ "autoplayDesc": "进入播放页时自动开始播放",
"domain": "数据源域名",
"domainDescription": "若默认域名被封锁或缓慢,可切换镜像",
"openLinks": "打开站点链接",
diff --git a/src/translations/zh.json b/src/translations/zh.json
index 5c56cdd..6492145 100644
--- a/src/translations/zh.json
+++ b/src/translations/zh.json
@@ -68,6 +68,9 @@
"themeSystem": "跟隨系統",
"themeLight": "淺色",
"themeDark": "深色",
+ "playback": "播放",
+ "autoplay": "自動播放",
+ "autoplayDesc": "進入播放頁時自動開始播放",
"domain": "資料來源網域",
"domainDescription": "若預設網域被封鎖或緩慢,可切換鏡像",
"openLinks": "開啟站點連結",