Skip to content

feat: add the YouTube layer's contract - #149

Merged
j0ker70 merged 9 commits into
mainfrom
feat/youtube-contract
Oct 21, 2026
Merged

j0ker70 merged 9 commits into
mainfrom
feat/youtube-contract

Conversation

@j0ker70

@j0ker70 j0ker70 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Closes #34.

What changed and why

This pull request defines the YouTube layer's contract: Dewfall's interfaces, the models they return and the error type, with no NewPipeExtractor yet. #35 adds NewPipeExtractor and implements search, video details, a channel's latest uploads and stream links. #148 implements subtitles. Both stay open.

  • :core:youtube: five interfaces, one for each thing the app needs from YouTube: SearchRemoteDataSource, VideoDetailsRemoteDataSource, ChannelUploadsRemoteDataSource, StreamLinksRemoteDataSource and SubtitlesRemoteDataSource. Every function is a main-safe suspend function that returns YouTubeResult and throws only CancellationException, and each KDoc says so. A blank search returns empty lists without a request. A channel's latest uploads hold its long-form videos and live streams, newest first, never Shorts, with upcoming ones marked VideoKind.UPCOMING.
  • :core:model: VideoId and ChannelId value classes that check the format strictly, with parseOrNull for outside text such as the Takeout import. Their error messages and toString leave out the ID, which ADR 0007 counts as user data. YouTubeResult and the YouTubeError enum, and the models for search results, video details, channel uploads, stream links and subtitles. Times are kotlin.time's Instant, and lengths its Duration.
  • Tests: VideoIdTest and ChannelIdTest. The other types are data classes, enums and interfaces with no logic.
  • ADR 0008: compares a sealed result type, Kotlin's Result and Dewfall's own exceptions, and records the error categories, the ID value classes and the time types, with the reasons.
  • architecture.md: a new "The YouTube layer" section with the interfaces, the models, Map NewPipeExtractor types to Dewfall models #35's plan to read a channel's Videos and Live tabs and merge them, the mapping from NewPipeExtractor's exceptions to the categories, and what NewPipeExtractor's dev branch changes. The YouTube parts of "Data flow" and "Error handling" are filled in.
  • AGENTS.md: a new architecture rule: the layer returns YouTubeResult, throws only CancellationException, lets no NewPipeExtractor exception out, and uses the ID classes.
  • roadmap.md: Read subtitle tracks and lines from YouTube #148's task added to Phase 3, after the mapping task.

New dependencies

None.

Checklist

  • OpenSpec change archived, or not needed. Not needed, because nothing users see changes.
  • docs/architecture.md updated, or the structure of the app didn't change. Updated.
  • ADR added for any decision that would be hard to reverse, or there was none. ADR 0008 is added.

Reviewer

Final verdict: Ready.

Ready after two rounds. Round 1 found one Must fix. The KDoc of VideoDetails and VideoDetailsRemoteDataSource said YouTube gives no details for a video that hasn't started, which no source supports. It now says the layer can't read them, so the call fails with UNAVAILABLE. Round 1 also found six Should fix items, all fixed in two follow-up commits:

  • The KDoc and docs say the work moves to an injected dispatcher.
  • "Both tabs give the length" is limited to videos that can be watched from start to end.
  • VideoKind.LIVE covers a premiere that is playing now.
  • The AGENTS.md rule covers video and channel IDs only, and AudioTrack's KDoc marks its ID as user data.
  • The error table's last column is marked as a plan for the later OpenSpec changes.
  • ADR 0008 says what ADR 0006 actually notes about Kotlin Multiplatform.

The reviewer checked the cited facts against their sources: the NewPipeExtractor v0.26.5 line links, the dev branch at 405f549, Room 2.6.0's value class support, Kotlin 2.3 making Instant stable, kotlinx-datetime's Alpha status and the data layer guide's quotes.

The reviewer's questions for the maintainer are both answered:

  • Sort order of a channel's uploads: answered. The contract defines it in ChannelUploads' and ChannelUploadsRemoteDataSource's KDoc and in architecture.md, and Map NewPipeExtractor types to Dewfall models #35 implements and tests it.
  • ADR 0008's date: answered. It keeps the sentence form that ADR 0006 and ADR 0007 use.

Round 3, on the sort order and one Stream links sentence: Ready, with one Should fix (upcoming videos come first only when they have a scheduled start), now fixed.

🤖 Generated with Claude Code

- Add VideoId and ChannelId as value classes that check the format,
  because the Takeout import and other outside text can hold anything.
  Their messages and toString leave out the ID, which is user data
  under ADR 0007
- Add YouTubeResult and the YouTubeError enum, so the layer returns
  failures instead of throwing, and no library message can reach the
  crash handler
- Add the models for search, video details, a channel's latest
  uploads, stream links and subtitles, with kotlin.time types for
  times and lengths, and PublishedTime to mark estimated times
- Put them in :core:model, which :core:data exposes, so screens and
  reducers can see them without seeing :core:youtube
- VideoIdTest and ChannelIdTest cover the accepted format, wrong
  lengths, wrong prefixes, characters outside ASCII, handles and link
  paths, so a bad Takeout row can't become an ID
- Both check that the error message and toString leave out the ID,
  as ADR 0007 asks
- Add one interface per function, so each caller depends only on what
  it uses, and a test can pass a lambda for the four fun interfaces
- Each interface's KDoc states the contract: main-safe, every YouTube
  problem returned as a Failure, and CancellationException as the
  only exception
- ChannelUploadsRemoteDataSource says its list holds long-form videos
  and live streams, never Shorts, with upcoming ones marked
- SearchRemoteDataSource says a blank query returns empty lists
  without a request
- Compare a sealed result type, Kotlin's Result and Dewfall's own
  exceptions, and record why the sealed type won with MVI and ADR 0007
- Record the error categories, the strict ID value classes and the
  kotlin.time types, because Phases 4 to 8 build on them
- Add a YouTube layer section to architecture.md with the interfaces,
  the models, the plan for #35 to read the Videos and Live tabs, the
  exception mapping and the dev branch risks
- Fill in the YouTube parts of Data flow and Error handling, so the
  path from a failure to a screen is written down
- Add an AGENTS.md rule that the layer returns YouTubeResult, throws
  only CancellationException and uses the ID classes
- Add the subtitles task (#148) to Phase 3 in the roadmap, because
  #35 no longer implements subtitles
- VideoDetails and VideoDetailsRemoteDataSource said YouTube gives no
  details for a video that hasn't started. No source says that. The
  cause is that the layer can't read them, so the KDoc now says so
- The interfaces say the work moves to an injected dispatcher, to
  match AGENTS.md's rule on dispatchers
- VideoKind.LIVE now covers a premiere that is playing now, so #35
  doesn't have to guess its kind
- AudioTrack's KDoc says its ID is user data, as ADR 0007 asks
- Narrow the AGENTS.md rule to video and channel IDs, because
  AudioTrack's ID is a YouTube ID typed as a string
- Mark the error table's last column as a plan, because the OpenSpec
  changes for the screens and background jobs decide it
- Limit "both tabs give the length" to videos that can be watched from
  start to end, and say that LIVE includes a premiere playing now
- Say what ADR 0006 actually notes about Kotlin Multiplatform, and
  that the contract avoids depending on the dev branch's changes
@j0ker70
j0ker70 merged commit 59e6aed into main Oct 21, 2026
1 check passed
@j0ker70
j0ker70 deleted the feat/youtube-contract branch October 21, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wrap NewPipeExtractor behind Dewfall interfaces

1 participant