Commit graph

8 commits

Author SHA1 Message Date
9550b207ab v0.1.0-T (vc=7): bug fixes + Opus audit pass #2 + home redesign
User-visible:
- BUG: 'clicking a second item went back to the first video' — VideoDetailViewModel
  guard was short-circuiting on Activity-scoped ViewModel reuse. Now tracks
  loadedUrl and only skips when the requested URL matches.
- BUG: PiP / window mode now auto-enters on Home gesture (Android 12+ via
  setAutoEnterEnabled). Manual PiP button reports failure cause via Toast.
- HOME REDESIGN: replaced 3-tab bottom nav with hamburger ModalNavigationDrawer.
  Default view = sub feed. Drawer items = Subscriptions / History / Search /
  Settings. Top-left hamburger like normal Android apps.

Audit pass #2 (Opus max-effort) — CRIT + HIGH fixes shipped:
- CRIT-1: PlaybackService now calls startForeground() inside onStartCommand
  with a media-playback notification + channel. Pre-fix could throw
  ForegroundServiceDidNotStartInTimeException on Android 12+ and crash-kill.
- CRIT-2: AndroidManifest service exported=false. Previously any installed
  app could craft an Intent and drive playback from attacker URLs.
- HIGH-1: 🎧 background handoff stops the activity player before starting
  the service so we don't dual-host two ExoPlayers + MediaSessions.
- HIGH-2: onStartCommand returns START_NOT_STICKY and tears down on null
  intent; no more crash-restart-crash loop after OS kills.
- HIGH-3: stop service on STATE_ENDED / STATE_IDLE via Player.Listener.
  onTaskRemoved checks playbackState properly so we don't hold WAKE_LOCK
  forever after a video ends in background.
- HIGH-4: Downloader validates scheme=https + googlevideo/youtube host
  before handing the URL to DownloadManager.
- HIGH-5: filename sanitization extended to ASCII control chars, DEL,
  Unicode bidi-override block, leading-dot, trailing whitespace.
- HIGH-6: SubscriptionFeedViewModel cancels prior in-flight refresh,
  caps parallelism at 8 via Semaphore, applies 15s per-channel timeout.
- HIGH-7: sub feed error banner now shows above cached items when refresh
  fails (previously hidden, looked indistinguishable from success).
- HIGH-8: PlayerViewModel falls back to lowest-available stream when no
  stream is under the max-resolution ceiling (was: silent black screen).
- HIGH-9: network_security_config explicit cleartextTrafficPermitted='false'
  on the RYD domain-config block (doesn't inherit from base-config).
- MED-1: PlaybackService.onDestroy nulls field before releasing session to
  close a race with onGetSession during teardown.
- MED-6: Downloader catches enqueue exceptions, returns -1L, caller toasts
  'download refused (bad URL)' instead of crashing.

Deferred (audit said 'can wait'): MED-2..5, MED-7..11, HIGH-10 UX consistency.
2026-05-24 07:49:35 -07:00
Kayos
081f238355 Straw phases P/Q/R/S — bottom nav, sub feed, downloads, background audio
Phase P — bottom navigation:
- StrawHome restructured as a Scaffold with Material3 NavigationBar.
- Three tabs: Home (search + last 10 watches), Library (full watch
  history with count), Subs (channel chips + aggregated feed).

Phase Q — subscription feed:
- New SubscriptionFeedViewModel fans out per-channel ChannelInfo +
  ChannelTabs.VIDEOS fetches in parallel via async/awaitAll.
- Each channel contributes top 5; merged across all subs, capped at
  200, sorted by view count as a soft-recency proxy (extractor doesn't
  reliably surface upload timestamps).
- 10-minute cache TTL avoids hammering YT on tab re-entry.
- Subs tab renders the feed below the avatar row with a Refresh button.

Phase R — download:
- Download button on VideoDetail (next to Play / Share). Pops a tiny
  dialog: Audio (best audioStream) or Video (best videoStream/
  videoOnly fallback).
- Uses Android's DownloadManager — saves into app-private external
  files dir (Android/data/com.sulkta.straw.debug/files/Movies/<kind>/).
  Notification + progress for free. No WRITE_EXTERNAL_STORAGE needed.
- Filenames sanitized (no /:*?\"<>| chars), capped at 120 chars.

Phase S — background audio:
- New "Background" overlay button (🎧) on the player. Tap to pause the
  activity player and start PlaybackService with the audio URL.
- PlaybackService is a Media3 MediaSessionService with its own ExoPlayer
  configured with our custom DataSource.Factory (User-Agent set, cross-
  protocol redirects). Foreground service + media notification.
- Audio survives activity death — swipe the app out of recents, audio
  keeps playing. Stop via notification or open-the-app-and-tap-stop.
- onTaskRemoved keeps the service alive iff something is playing.

Versions shipped: P+Q as vc=4, R as vc=5, S as vc=6. Each landed in the
F-Droid repo for the day-by-day refresh path.

Day-N+ ideas: real MediaController unification (single Player for both
foreground + background paths), MergingMediaSource on the service side
for high-res YT videos, real upload-timestamp sort for feed once the
extractor exposes it consistently, queue/playlist.
2026-05-24 04:30:06 -07:00
Kayos
fa97b698fe Straw phase O: related videos + max-resolution picker (v0.1.0-O / vc=3)
VideoDetail screen:
- New "Related" section at the bottom — pulls
  StreamInfo.relatedItems, filters to StreamInfoItem, renders as
  inline thumbnail rows. Tap → push another VideoDetail. Up to 20
  items shown. Each row uses bestThumbnail() for hi-res.

Settings screen + PlayerViewModel:
- New "Playback" section with a Max-Resolution picker:
  Auto / 1080p / 720p / 480p / 360p / 144p. Persisted to
  SharedPreferences (KEY_MAX_RES) via SettingsStore.maxResolution
  StateFlow.
- PlayerViewModel.resolve filters videoStreams + videoOnlyStreams by
  the ceiling before picking the max-bitrate one. Auto (Int.MAX_VALUE)
  is unchanged behavior. Choosing 720p caps the renderer so 1080p/4K
  streams are skipped — saves bandwidth on mobile + helps low-end
  decoders.

Phase P next ideas: bottom navigation tabs (Home / Subs feed /
Library), Download (audio + video), the MediaSessionService refactor
for true background audio after activity death.
2026-05-24 03:44:54 -07:00
Kayos
253c5e268b Straw phase N: share + playback speed + audio-only toggle (v0.1.0-N / vc=2)
Player overlay (top-right) now hosts four buttons in a row:
- Speed: 1× by default; tap opens a dialog of 0.25× / 0.5× / 0.75× / 1× /
  1.25× / 1.5× / 1.75× / 2×. Applies via exoPlayer.playbackParameters.
- Audio-only toggle (📻/📺): toggles Player.TRACK_TYPE_VIDEO via
  TrackSelectionParameters. Saves bandwidth + battery for screen-off
  listening. Toast confirms state.
- Share (↗): Intent.ACTION_SEND with text/plain containing the YouTube
  URL + the video title as EXTRA_SUBJECT. Hands off to Android share
  sheet.
- PiP (⊟): same as M-1 but now part of the row instead of its own
  floating square.

VideoDetail screen: Play button now lives in a Row with an OutlinedButton
"Share" that fires the same ACTION_SEND chooser. Same UX surface for
users who land on detail without going to player.

Version: STRAW_VERSION_CODE 1 → 2, STRAW_VERSION_NAME "0.1.0-day1" →
"0.1.0-N" so the F-Droid client sees this as an upgrade.

Phase O next (per Cobb's "where are all the features"): quality picker,
related videos on detail, download (audio + video).
2026-05-23 21:20:15 -07:00
Kayos
ff4dc6f121 Sulkta day-1: straw — KMP/Compose YouTube client fork
Initial Sulkta fork of NewPipe with a new :strawApp module that ships a
clean Compose-based Android APK at applicationId com.sulkta.straw.

What's here:
- :strawApp — thin Android application shell, MaterialTheme + StrawHome
  Composable. Lives alongside legacy :app so we don't break upstream.
- buildSrc — STRAW_APPLICATION_ID/VERSION constants alongside the existing
  NEWPIPE_APPLICATION_ID_OLD/NEW.
- docs/sulkta — RECON.md (NewPipe codebase breakdown) + DECISIONS.md
  (stack + scope decisions).

NewPipe's :shared KMP scaffold is at 892 LOC and renders nothing; this
fork picks up there and races ahead. Day-1 ships a hello APK; day-2 wires
NewPipeExtractor + Media3 player + SponsorBlock + Return YouTube Dislike.

GPL-3.0-or-later per upstream.
2026-05-23 17:37:55 -07:00
tobigr
d3d6b0283f Merge branch 'master' into dev 2026-05-23 20:18:19 +02:00
Aayush Gupta
d1bc8c23cf Better share version information between modules
Move important version properties to buildSrc directory to access between modules
as needed.

Also add a simple task to generate a simple BuildConfig class to access version name.
This is better than adding dependency on a third-party library/plugin.

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2026-05-20 18:27:08 +08:00
Aayush Gupta
b06b7c35ca Relocate toml lint task to buildSrc and extend against default task
Fixes build errors after Gradle 9.x upgrade

Ref: https://docs.gradle.org/current/userguide/implementing_custom_tasks.html

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
2025-11-21 20:08:26 +08:00