@krx3d/tizentube2 1.15.810 → 1.15.820

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bash
2
+ # Build the release body for a TizenTube Standalone release.
3
+ #
4
+ # Usage: build-release-notes.sh <release_sha> <release_version> <app_id> <app_name> <out_file>
5
+ #
6
+ # The changelog is derived from the merge/squash commits between the previous
7
+ # published release and this build, grouped by conventional-commit prefix.
8
+ # Version-bump commits made by CI carry no PR number and drop out on their own.
9
+ set -euo pipefail
10
+
11
+ RELEASE_SHA="$1"
12
+ RELEASE_VERSION="$2"
13
+ APP_ID="$3"
14
+ APP_NAME="$4"
15
+ OUT="$5"
16
+
17
+ REPO="${GITHUB_REPOSITORY:-}"
18
+
19
+ # Previous release = the newest published release that isn't the one being cut.
20
+ # Falls back to the newest version tag, then to "no previous release".
21
+ PREV_TAG=""
22
+ if [ -n "$REPO" ] && command -v gh >/dev/null 2>&1; then
23
+ PREV_TAG=$(gh api "repos/${REPO}/releases" --jq \
24
+ "[.[] | select(.draft == false) | .tag_name] | map(select(. != \"${RELEASE_VERSION}\")) | .[0] // empty" \
25
+ 2>/dev/null || true)
26
+ fi
27
+ # Reject anything that isn't an ancestor of this build: version-string order is
28
+ # not history order (a v2.x standalone tag can sort above the current v1.15.x
29
+ # line and would drag in every commit since the fork point).
30
+ if [ -n "$PREV_TAG" ] && ! git merge-base --is-ancestor "${PREV_TAG}^{commit}" "$RELEASE_SHA" 2>/dev/null; then
31
+ echo "Ignoring ${PREV_TAG}: not an ancestor of ${RELEASE_SHA}" >&2
32
+ PREV_TAG=""
33
+ fi
34
+ if [ -z "$PREV_TAG" ]; then
35
+ PREV_TAG=$(git describe --tags --abbrev=0 "${RELEASE_SHA}^" 2>/dev/null || true)
36
+ [ "$PREV_TAG" = "$RELEASE_VERSION" ] && PREV_TAG=""
37
+ fi
38
+
39
+ if [ -n "$PREV_TAG" ] && git rev-parse -q --verify "${PREV_TAG}^{commit}" >/dev/null; then
40
+ RANGE="${PREV_TAG}..${RELEASE_SHA}"
41
+ else
42
+ # First release, or the previous tag isn't in this clone — describe the last
43
+ # stretch of history rather than the entire project.
44
+ RANGE="${RELEASE_SHA}~50..${RELEASE_SHA}"
45
+ git rev-parse -q --verify "${RELEASE_SHA}~50" >/dev/null || RANGE="$RELEASE_SHA"
46
+ fi
47
+
48
+ # --- collect (pr_number, title) pairs -----------------------------------------
49
+ # GitHub merge commits: "Merge pull request #N from owner/branch" + body=title
50
+ # GitHub squash commits: "title (#N)"
51
+ declare -a FEATURES=() FIXES=() PORTS=() OTHER=()
52
+
53
+ while IFS=$'\x1f' read -r -d $'\x1e' subject body; do
54
+ subject="${subject#$'\n'}"
55
+ pr=""
56
+ title=""
57
+ if [[ "$subject" =~ ^Merge\ pull\ request\ \#([0-9]+) ]]; then
58
+ pr="${BASH_REMATCH[1]}"
59
+ title=$(printf '%s' "$body" | sed '/^[[:space:]]*$/d' | head -n1)
60
+ elif [[ "$subject" =~ ^(.*)\ \(\#([0-9]+)\)$ ]]; then
61
+ title="${BASH_REMATCH[1]}"
62
+ pr="${BASH_REMATCH[2]}"
63
+ fi
64
+ [ -z "$pr" ] && continue
65
+ [ -z "$title" ] && title="$subject"
66
+
67
+ entry="- ${title} (#${pr})"
68
+ shopt -s nocasematch
69
+ if [[ "$title" == Port\ upstream* ]]; then
70
+ PORTS+=("$entry")
71
+ elif [[ "$title" =~ ^feat(\(.*\))?: ]]; then
72
+ FEATURES+=("$entry")
73
+ elif [[ "$title" =~ ^fix(\(.*\))?: ]]; then
74
+ FIXES+=("$entry")
75
+ else
76
+ OTHER+=("$entry")
77
+ fi
78
+ shopt -u nocasematch
79
+ done < <(git log --first-parent --pretty=format:'%s%x1f%b%x1e' "$RANGE")
80
+
81
+ # --- write the body -----------------------------------------------------------
82
+ {
83
+ echo "TizenTube Standalone build from commit \`${RELEASE_SHA}\`, version-synced with the userscript."
84
+ echo "TBI_METADATA: {\"appId\":\"${APP_ID}\",\"appName\":\"${APP_NAME}\"}"
85
+ echo
86
+
87
+ section() {
88
+ local heading="$1"; shift
89
+ [ "$#" -eq 0 ] && return 0
90
+ echo "### ${heading}"
91
+ printf '%s\n' "$@"
92
+ echo
93
+ }
94
+
95
+ if [ ${#FEATURES[@]} -eq 0 ] && [ ${#FIXES[@]} -eq 0 ] && [ ${#PORTS[@]} -eq 0 ] && [ ${#OTHER[@]} -eq 0 ]; then
96
+ echo "No pull requests were merged since \`${PREV_TAG:-the previous build}\`."
97
+ else
98
+ echo "## What's Changed"
99
+ echo
100
+ section "Features" ${FEATURES[@]+"${FEATURES[@]}"}
101
+ section "Fixes" ${FIXES[@]+"${FIXES[@]}"}
102
+ section "Ported from upstream" ${PORTS[@]+"${PORTS[@]}"}
103
+ section "Other" ${OTHER[@]+"${OTHER[@]}"}
104
+ fi
105
+
106
+ if [ -n "$PREV_TAG" ] && [ -n "$REPO" ]; then
107
+ echo "**Full changelog:** https://github.com/${REPO}/compare/${PREV_TAG}...${RELEASE_VERSION}"
108
+ fi
109
+ } > "$OUT"
110
+
111
+ echo "Wrote release notes to $OUT:"
112
+ echo "---"
113
+ cat "$OUT"
@@ -286,13 +286,20 @@ jobs:
286
286
  name: TizenTube-Standalone-${{ env.RELEASE_SHA }}.wgt
287
287
  path: standalone/release/TizenTube.wgt
288
288
 
289
+ # Changelog is built from the PRs merged since the previous published
290
+ # release; the build/TBI_METADATA header stays first so the TizenBrew
291
+ # Installer still finds it.
292
+ - name: Build release notes
293
+ env:
294
+ GH_TOKEN: ${{ github.token }}
295
+ run: |
296
+ bash .github/scripts/build-release-notes.sh "${{ env.RELEASE_SHA }}" "${{ env.RELEASE_VERSION }}" "${{ env.APP_ID }}" "${{ env.APP_NAME }}" "$RUNNER_TEMP/release-notes.md"
297
+
289
298
  - name: Release TizenTube Standalone
290
299
  uses: softprops/action-gh-release@v1
291
300
  with:
292
301
  tag_name: ${{ env.RELEASE_VERSION }}
293
302
  target_commitish: ${{ env.RELEASE_SHA }}
294
- body: |
295
- TizenTube Standalone build from commit `${{ env.RELEASE_SHA }}`, version-synced with the userscript.
296
- TBI_METADATA: {"appId":"${{ env.APP_ID }}","appName":"${{ env.APP_NAME }}"}
303
+ body_path: ${{ runner.temp }}/release-notes.md
297
304
  files: |
298
305
  standalone/release/*
@@ -0,0 +1,138 @@
1
+ # Watch reisxd/TizenTube for new commits and open a PR that carries them.
2
+ #
3
+ # This fork has rewritten large parts of mods/ (adblock.js, hideWatched.js,
4
+ # settings.js in particular), so most upstream commits touching those files
5
+ # WILL conflict. The workflow does not pretend otherwise: it always produces a
6
+ # branch that merges cleanly into main, and when a file conflicted it keeps the
7
+ # fork's version and says so in the PR body, so the upstream change can be
8
+ # hand-ported afterwards.
9
+ name: Upstream sync
10
+
11
+ on:
12
+ schedule:
13
+ # 04:20 UTC daily — outside the usual release window.
14
+ - cron: '20 4 * * *'
15
+ workflow_dispatch:
16
+
17
+ concurrency:
18
+ group: upstream-sync
19
+ cancel-in-progress: false
20
+
21
+ env:
22
+ UPSTREAM_REPO: https://github.com/reisxd/TizenTube.git
23
+ UPSTREAM_BRANCH: main
24
+ SYNC_BRANCH: sync/upstream
25
+
26
+ jobs:
27
+ sync:
28
+ runs-on: ubuntu-latest
29
+ permissions:
30
+ contents: write
31
+ pull-requests: write
32
+ steps:
33
+ - name: Clone repo with full history
34
+ uses: actions/checkout@v5
35
+ with:
36
+ fetch-depth: 0
37
+
38
+ - name: Configure git identity
39
+ run: |
40
+ git config user.name 'github-actions[bot]'
41
+ git config user.email 'github-actions[bot]@users.noreply.github.com'
42
+
43
+ - name: Fetch upstream
44
+ run: |
45
+ set -euo pipefail
46
+ git remote add upstream "$UPSTREAM_REPO" 2>/dev/null || git remote set-url upstream "$UPSTREAM_REPO"
47
+ git fetch upstream "$UPSTREAM_BRANCH" --no-tags
48
+
49
+ - name: Check for new upstream commits
50
+ id: check
51
+ run: |
52
+ set -euo pipefail
53
+ COUNT=$(git rev-list --count "origin/main..upstream/${UPSTREAM_BRANCH}")
54
+ echo "count=$COUNT" >> "$GITHUB_OUTPUT"
55
+ echo "Upstream commits not in main: $COUNT"
56
+
57
+ - name: Merge upstream, keeping the fork's side of any conflict
58
+ id: merge
59
+ if: steps.check.outputs.count != '0'
60
+ run: |
61
+ set -euo pipefail
62
+ git checkout -B "$SYNC_BRANCH" origin/main
63
+
64
+ CONFLICTS=""
65
+ if ! git merge --no-edit "upstream/${UPSTREAM_BRANCH}"; then
66
+ # Keep this fork's version of every conflicted file. The point of
67
+ # the PR is to surface the change for review, not to guess at a
68
+ # resolution — committing conflict markers would only produce a
69
+ # branch that cannot build.
70
+ CONFLICTS=$(git diff --name-only --diff-filter=U)
71
+ echo "Conflicted files kept at fork version:"
72
+ echo "$CONFLICTS"
73
+ while IFS= read -r f; do
74
+ [ -z "$f" ] && continue
75
+ git checkout --ours -- "$f" 2>/dev/null || git rm -q -- "$f"
76
+ git add -- "$f" 2>/dev/null || true
77
+ done <<< "$CONFLICTS"
78
+ git commit --no-edit
79
+ fi
80
+
81
+ {
82
+ echo 'conflicts<<TT_EOF'
83
+ echo "$CONFLICTS"
84
+ echo 'TT_EOF'
85
+ } >> "$GITHUB_OUTPUT"
86
+
87
+ if git diff --quiet origin/main -- .; then
88
+ echo "changed=false" >> "$GITHUB_OUTPUT"
89
+ else
90
+ echo "changed=true" >> "$GITHUB_OUTPUT"
91
+ git push --force-with-lease origin "$SYNC_BRANCH"
92
+ fi
93
+
94
+ - name: Open or update the sync PR
95
+ if: steps.check.outputs.count != '0' && steps.merge.outputs.changed == 'true'
96
+ env:
97
+ GH_TOKEN: ${{ github.token }}
98
+ CONFLICTS: ${{ steps.merge.outputs.conflicts }}
99
+ NEW_COMMITS: ${{ steps.check.outputs.count }}
100
+ run: |
101
+ set -euo pipefail
102
+
103
+ {
104
+ echo "Automated sync with [reisxd/TizenTube](https://github.com/reisxd/TizenTube) — **${NEW_COMMITS}** new upstream commit(s)."
105
+ echo
106
+
107
+ if [ -n "${CONFLICTS//[[:space:]]/}" ]; then
108
+ echo "## :warning: Needs hand-porting"
109
+ echo
110
+ echo "These files conflicted and were **kept at this fork's version**, so the upstream changes to them are *not* in this PR. Merging as-is is safe but incomplete — port them by hand before or after merging:"
111
+ echo
112
+ while IFS= read -r f; do
113
+ [ -z "$f" ] && continue
114
+ echo "- \`$f\`"
115
+ done <<< "$CONFLICTS"
116
+ echo
117
+ else
118
+ echo "Merged cleanly — no conflicts."
119
+ echo
120
+ fi
121
+
122
+ echo "## Upstream commits"
123
+ echo
124
+ git log --reverse --no-merges --pretty=format:'- [`%h`](https://github.com/reisxd/TizenTube/commit/%H) %s' "origin/main..upstream/${UPSTREAM_BRANCH}"
125
+ echo
126
+ echo
127
+ echo "_Opened automatically by \`.github/workflows/upstream-sync.yml\`._"
128
+ } > /tmp/pr-body.md
129
+
130
+ EXISTING=$(gh pr list --head "$SYNC_BRANCH" --state open --json number --jq '.[0].number // empty')
131
+ TITLE="Sync with upstream (${NEW_COMMITS} new commit(s))"
132
+
133
+ if [ -n "$EXISTING" ]; then
134
+ gh pr edit "$EXISTING" --title "$TITLE" --body-file /tmp/pr-body.md
135
+ echo "Updated PR #$EXISTING"
136
+ else
137
+ gh pr create --base main --head "$SYNC_BRANCH" --title "$TITLE" --body-file /tmp/pr-body.md
138
+ fi
package/AGENTS.md CHANGED
@@ -788,6 +788,72 @@ install-blocked mode), and the user's hardware is an unconfirmed fit (has
788
788
  a Samsung soundbar, not confirmed Q-Symphony-compatible). Ask before
789
789
  implementing if this comes back up.
790
790
 
791
+ ## Hard-won lessons (read before touching `mods/features/adblock.js`)
792
+
793
+ These cost real debugging time and are not obvious from the code. They are
794
+ recorded here rather than in any one contributor's notes so a fork inherits
795
+ them.
796
+
797
+ ### There are two response-handling paths, and they are not shared code
798
+
799
+ YouTube delivers responses in two shapes, and `adblock.js` has a separate
800
+ handler for each:
801
+
802
+ - **array-root** → `processResponsePayload(payload, detectedPage)`
803
+ - **object-root** → the branch inside the patched `JSON.parse`
804
+
805
+ A filter added to one path and not the other works on some surfaces and
806
+ silently does nothing on others. This has bitten repeatedly: the array-root
807
+ grid handler was missing `filterShortsFromItems` and
808
+ `filterMembersOnlyFromItems`; `noteContinuationBatch` had to be hooked at
809
+ *both* continuation sites; `hideRelatedVideos` is called from both watch-next
810
+ sites.
811
+
812
+ When adding anything per-item or per-shelf, put the logic in its own module and
813
+ call it from both sites — `mods/features/sidebarChannelButton.js` is the shape
814
+ to copy — rather than duplicating the body. Grep for the sibling call site
815
+ before assuming one hook is enough.
816
+
817
+ ### `JSON.parse` is wrapped by several modules, and order matters
818
+
819
+ `playlistContinue.js`, `playlistBatchCollect.js`, `adblock.js` and
820
+ `customGuideAction.js` each wrap `JSON.parse`, in that order (set by the import
821
+ order in `userScript.js`). Every wrapper calls through to whatever it captured
822
+ as the original, so they compose — but an early `return` added to any of them
823
+ silently swallows every wrapper installed before it.
824
+
825
+ `captionStylePersistence.js` independently wraps `resolveCommand` alongside
826
+ `resolveCommand.js`'s own patch. Same rule applies.
827
+
828
+ ### A playlist continuation response must contain at least one item
829
+
830
+ YouTube's refill loop stops dead if a continuation comes back empty — loading
831
+ never resumes, even on scroll. `hideVideo` therefore deliberately keeps one
832
+ "helper" tile per continuation batch (`__ttKeepOneForContinuation`).
833
+
834
+ This was learned the hard way: PR #678 capped helpers at one per visit and
835
+ returned `[]` for the rest, and playlist loading stalled after two batches on
836
+ device. It was reverted in #679. If you change the helper logic, test a long,
837
+ mostly-watched playlist and confirm it still loads past batch 2.
838
+
839
+ ### Tizen 5.0 has no usable Polymer element APIs
840
+
841
+ `yt-virtual-list` positions its rows by transform from its own data model, and
842
+ the Polymer APIs you would reach for to force a re-render are `undefined` on
843
+ Tizen 5.0. Every DOM-level attempt to collapse the blank slot left behind by a
844
+ removed helper tile failed for this reason — rows are recycled and carry their
845
+ previous inline styles, and helper tiles carry no video-id attribute to target
846
+ (the id exists only inside a thumbnail's `background-image` URL).
847
+
848
+ Blank helper slots are an accepted limitation. Don't re-litigate it at the DOM
849
+ layer without new evidence.
850
+
851
+ ### Verifying that a change actually reached the bundle
852
+
853
+ `dist/userScript.js` is minified and Babel-transpiled: identifier names are
854
+ mangled, string literals survive. Grep for a distinctive **string literal**,
855
+ never a function name, and use `grep -a` — the bundle is detected as binary.
856
+
791
857
  ## Conventions this repo has established (follow these)
792
858
 
793
859
  - New app identities (Tizen `package`/app id) must be unique, not reused
@@ -818,3 +884,15 @@ implementing if this comes back up.
818
884
  mistake). If you need a per-dependency try/catch wrapper for
819
885
  diagnostics, wrap each literal `require('x')` call individually rather
820
886
  than passing the module name through a shared helper function.
887
+ - Never run the build by hand and commit the generated output (`dist/*`,
888
+ `standalone/service/dist/*`) alongside a source change. CI rebuilds and
889
+ commits it on version bump; doing it manually produces merge conflicts on
890
+ generated files for everyone branching off `main`. Build locally to check a
891
+ change compiles, then `git checkout -- dist/` before committing.
892
+ - Work on a branch and open a PR; don't push to `main`. Don't push further
893
+ commits onto a branch whose PR is already merged — the commits end up
894
+ orphaned. Check merge state before pushing to an existing branch.
895
+ - When porting an upstream commit, read it rather than applying it: upstream
896
+ code has shipped with operator-precedence bugs, dead branches behind early
897
+ returns, and renames that would reset this fork's stored settings. Fix them
898
+ in the port and say so in the PR.
package/README.md CHANGED
@@ -54,12 +54,98 @@ If a run failed because of a bug in the workflow file itself and that's since be
54
54
 
55
55
  # Features
56
56
 
57
- - Ad Blocker
58
- - [SponsorBlock](https://sponsor.ajay.app/) Support
59
- - Picture-in-Picture Mode
60
- - [DeArrow](https://dearrow.ajay.app/) Support
61
- - Customizable Themes (Custom Coloring)
62
- - More to come, if you [request](https://github.com/reisxd/TizenTube/issues/new) it!
57
+ Everything below is toggleable from the in-app settings menu (**green button** on
58
+ the remote, or `G` / `F2` / `2` when testing in Chrome). This fork carries a
59
+ number of features that upstream TizenTube does not — those are marked
60
+ **(fork)**.
61
+
62
+ Each row is one settings-menu entry; where an entry has its own sub-menu, its
63
+ options are described in the row rather than listed separately.
64
+
65
+ ## Blocking and filtering
66
+
67
+ | Feature | What it does |
68
+ | --- | --- |
69
+ | Ad Block | Removes video ads, ad slots, and the masthead/banner ad on Home |
70
+ | [SponsorBlock](https://sponsor.ajay.app/) | Skips sponsor, intro, outro, self-promo, interaction, filler, preview and non-music segments. Each category can be set to auto-skip, skip manually, or ignore, with optional toasts. Highlights (jump to the video's main point) can be enabled separately |
71
+ | Hide End Screen Cards | Removes the suggested-video cards overlaid at the end of a video |
72
+ | "Includes paid promotion" overlay | Toggle the paid-promotion banner |
73
+ | Hide Members-Only Videos **(fork)** | Filters videos you can't watch without a channel membership out of shelves and grids |
74
+ | Hide Channel Shelves **(fork)** | Removes whole channel-recommendation shelves from feeds |
75
+ | Hide Feedback Surveys **(fork)** | Removes YouTube's in-feed "help us improve" survey cards |
76
+ | Hide Special Playlists **(fork)** | Hides Liked Videos and/or Watch Later from the Library and Playlists pages |
77
+ | Enable Shorts | Off by default — Shorts shelves and Shorts tiles are stripped from every surface |
78
+
79
+ ## Watched-video handling
80
+
81
+ | Feature | What it does |
82
+ | --- | --- |
83
+ | Hide Watched Videos | Hides videos you've already watched. Configurable watched-percentage threshold, and per-page control over where it applies (Home, Search, Subscriptions, Channel pages, Library, individual playlists, History, Music, Gaming, More, Watch) |
84
+ | Playlist Batch Load **(fork)** | Loads a playlist's later batches up front instead of only as you scroll, so hide-watched can act on the whole playlist rather than the first ~30 items. The number of batches fetched is capped by a configurable limit |
85
+
86
+ ## Video player
87
+
88
+ | Feature | What it does |
89
+ | --- | --- |
90
+ | Preferred Video Quality | Locks playback to a chosen quality (or the next best available) instead of letting YouTube pick |
91
+ | Preferred Video Codec | Prefer VP9, AV1 or AVC1 when the video offers a choice |
92
+ | Auto Frame Rate | Matches the TV's output frame rate to the video, with a configurable pause duration while it switches (Tizen only) |
93
+ | Spoof Viewport Resolution **(fork)** | Reports a different screen resolution to YouTube. Useful when the TV reports a lower resolution to the browser than it can actually decode. Requires an app restart |
94
+ | Speed Settings Increments | Sets the step size used by the playback-speed control |
95
+ | Picture-in-Picture / Mini Player | Both available from the player options popup; the two buttons can be swapped |
96
+ | Screen Off | Blanks the screen while audio keeps playing — for using the TV as a music player |
97
+ | Hide Related Videos in Player | Removes the related/suggested-videos rail that slides in over the player, so a nudge on the D-pad during playback doesn't cover the video with recommendations |
98
+ | Number Keys Jump to Percentage | Pressing `1`–`9` jumps to that percentage of the video, `0` jumps to the start |
99
+ | Reload Player After TV Wakes **(fork)** | Rebuilds the player after standby, fixing the frozen first frame |
100
+ | Player UI patching | Optional Previous/Next, Super Thanks, Ask, and Speed Controls buttons |
101
+
102
+ ## Thumbnails and titles
103
+
104
+ | Feature | What it does |
105
+ | --- | --- |
106
+ | [DeArrow](https://dearrow.ajay.app/) | Community-sourced, non-clickbait titles, with optional DeArrow thumbnails |
107
+ | High Quality Thumbnails | Upgrades tile thumbnails to `hqdefault`/`sddefault` |
108
+ | Video Previews | Hover/focus previews on tiles |
109
+ | Disable Enlarged Thumbnails | Stops the focused tile from scaling up |
110
+ | Enable Shrinked Thumbnails | Shrinks unfocused tiles instead |
111
+
112
+ ## Interface
113
+
114
+ | Feature | What it does |
115
+ | --- | --- |
116
+ | Customizable Themes | Custom focus-container and route colouring (**red button**) |
117
+ | Clock | On-screen clock, 12/24-hour, optional seconds, and an option to hide it while a video is playing |
118
+ | Screen Dimming | Dims the screen after a configurable idle timeout, at a configurable opacity |
119
+ | Disable Sidebar Contents | Choose which sidebar entries to hide |
120
+ | Disable Channels on Sidebar | Removes subscribed-channel entries from the sidebar |
121
+ | Launch To on Startup | Choose which page the app opens to |
122
+ | Reload Home on Startup | Forces a fresh Home feed on launch |
123
+ | Library Tabs Buttons to Hide **(fork)** | Hides individual Library tabs (Music, Movies & Shows, Podcasts, My Videos, History, Watch Later, Playlists) |
124
+ | Sort Subscriptions Alphabetically | Alphabetical instead of YouTube's own ordering |
125
+ | Long Press Actions | Long-press a tile for Play, Watch Later, Save to Playlist and Add to Queue |
126
+ | Who's Watching Menu | Control whether the profile picker appears, including on app exit, and whether it stays permanently enabled |
127
+ | "Are you still watching?" prompt | Toggle YouTube's idle-playback interruption |
128
+ | Show Guest Sign In Reminder | Toggle the prompt asking a signed-out viewer to sign in |
129
+ | Show TT Welcome Message | Toggle the TizenTube toast shown on launch |
130
+ | Fix UI | Layout corrections for TVs that render the YouTube TV UI incorrectly |
131
+
132
+ ## Subtitles
133
+
134
+ | Feature | What it does |
135
+ | --- | --- |
136
+ | Show Local Subtitle | Surfaces subtitles in your own language |
137
+ | Show Hidden Subtitles | Exposes tracks YouTube hides from the picker |
138
+ | Remember Caption Style **(fork)** | Persists caption font/size/colour settings across sessions and restarts |
139
+
140
+ ## Maintenance and diagnostics
141
+
142
+ | Feature | What it does |
143
+ | --- | --- |
144
+ | Updater | Checks for TizenTube updates, optionally on startup |
145
+ | Debug Console **(fork)** | On-screen log console (**yellow button**), with configurable corner position and height |
146
+ | Remote Log Server **(fork)** | Streams logs to a PC receiver for on-device debugging, with a built-in connection test |
147
+
148
+ Missing something? [Request it](https://github.com/reisxd/TizenTube/issues/new).
63
149
 
64
150
  # Tampermonkey local debugging helpers (Windows + Chrome)
65
151