Added YouTube summarizer app - #50
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds a new Android sample app (“tl;dw”) that fetches YouTube transcripts and generates on-device TL;DR summaries using the Leap SDK, including the initial app architecture (data/domain/ui), Gradle project setup, and a basic model download + summarize UI flow.
Changes:
- Introduces a new Android project with Gradle wrapper, version catalog, and module configuration.
- Implements transcript fetching (InnerTube + captions XML parsing) and on-device TL;DR generation via Leap SDK model runner/downloader.
- Adds initial Compose UI flows (model download screen + transcript screen) plus unit tests for core logic.
Reviewed changes
Copilot reviewed 66 out of 68 changed files in this pull request and generated 19 comments.
Show a summary per file
| File | Description |
|---|---|
| Android/YouTubeSummarizer/settings.gradle.kts | Gradle settings + dependency repositories for the new project |
| Android/YouTubeSummarizer/README.md | Project readme + demo link |
| Android/YouTubeSummarizer/gradlew.bat | Gradle wrapper script (Windows) |
| Android/YouTubeSummarizer/gradlew | Gradle wrapper script (POSIX) |
| Android/YouTubeSummarizer/gradle/wrapper/gradle-wrapper.properties | Gradle distribution configuration |
| Android/YouTubeSummarizer/gradle/libs.versions.toml | Version catalog for dependencies/plugins |
| Android/YouTubeSummarizer/gradle.properties | Gradle/Android build flags |
| Android/YouTubeSummarizer/build.gradle.kts | Top-level build configuration (plugin aliases) |
| Android/YouTubeSummarizer/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker | Enables Mockito inline mock maker for tests |
| Android/YouTubeSummarizer/app/src/test/java/com/tldw/app/ui/transcriptscreen/TranscriptViewModelTest.kt | Unit tests for transcript screen ViewModel state machine |
| Android/YouTubeSummarizer/app/src/test/java/com/tldw/app/domain/util/YouTubeUrlParserTest.kt | Unit tests for parsing YouTube URLs/video IDs |
| Android/YouTubeSummarizer/app/src/test/java/com/tldw/app/domain/usecase/FetchTranscriptUseCaseTest.kt | Unit tests for transcript fetching use case |
| Android/YouTubeSummarizer/app/src/test/java/com/tldw/app/data/remote/YouTubeApiServiceTest.kt | Tests for HTTP request formation and DTO parsing |
| Android/YouTubeSummarizer/app/src/main/res/values/strings.xml | App strings (and currently a misplaced color resource) |
| Android/YouTubeSummarizer/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml | Adaptive launcher icon definition |
| Android/YouTubeSummarizer/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml | Round adaptive launcher icon definition |
| Android/YouTubeSummarizer/app/src/main/res/drawable/ic_launcher_foreground.xml | Launcher icon foreground vector |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/transcriptscreen/TranscriptViewModelFactory.kt | Factory wiring for transcript ViewModel |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/transcriptscreen/TranscriptViewModel.kt | Transcript screen logic: fetch, generate, state handling |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/transcriptscreen/TranscriptState.kt | Transcript screen state + phase enum |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/transcriptscreen/TranscriptScreenRoute.kt | Route-level wiring for transcript screen dependencies |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/transcriptscreen/TranscriptEvent.kt | UI event definitions for transcript screen |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/theme/TldwTheme.kt | Material3 theme definitions |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/modelscreen/ModelDownloadViewModelFactory.kt | Factory wiring for model download ViewModel |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/modelscreen/ModelDownloadViewModel.kt | Model download flow logic + error mapping |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/modelscreen/ModelDownloadState.kt | Model download screen state |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/modelscreen/ModelDownloadScreenRoute.kt | Route-level wiring for model download screen |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/modelscreen/ModelDownloadScreen.kt | Compose UI for initial/download/failure/no-internet states |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/modelscreen/ModelDownloadEvent.kt | UI event definitions for model download screen |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/mocks/tweaks-panel.jsx | Design mock tooling (currently located under Android java sources) |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/mocks/tldw-screens.jsx | Design mock screens (currently located under Android java sources) |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/mocks/tldw-frame.jsx | Design mock frame (currently located under Android java sources) |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/mocks/tldw-app.jsx | Design mock app harness (currently located under Android java sources) |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/mocks/design-canvas.jsx | Design mock canvas (currently located under Android java sources) |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/mocks/android-frame.jsx | Design mock Android frame (currently located under Android java sources) |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/ui/component/ShapeProgressIndicator.kt | Reusable custom path-based progress indicator |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/MainViewModelFactory.kt | Factory wiring for main app ViewModel |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/MainViewModel.kt | Main app state: checks if model is downloaded |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/MainActivity.kt | App entry point + navigation between model download and transcript flow |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/util/YouTubeUrlParser.kt | YouTube URL/video ID parsing utility |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/usecase/UnloadModelUseCase.kt | Domain use case: unload model |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/usecase/LoadModelUseCase.kt | Domain use case: load model |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/usecase/GenerateTldrUseCase.kt | Domain use case: generate summary |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/usecase/FetchTranscriptUseCase.kt | Domain use case: parse URL and fetch transcript |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/usecase/DownloadModelUseCase.kt | Domain use case: download model with cancellation cleanup |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/usecase/CheckModelDownloadedUseCase.kt | Domain use case: check model availability |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/repository/TranscriptRepository.kt | Transcript repository contract |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/repository/ModelRepository.kt | Model repository contract |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/model/VideoInfo.kt | Domain model for video metadata + formatting helpers |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/model/TranscriptSnippet.kt | Domain model for transcript snippet |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/model/Transcript.kt | Domain model for transcript + flattening helper |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/model/GenerationFailure.kt | Domain model for generation failure info |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/model/DownloadProgress.kt | Domain model for download progress |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/domain/model/DownloadFailure.kt | Domain model for download failure types |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/data/repository/TranscriptRepositoryImpl.kt | InnerTube/captions implementation of transcript repository |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/data/repository/ModelRepositoryImpl.kt | Leap downloader/runner implementation of model repository |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/data/remote/YouTubeApiService.kt | OkHttp/Gson service for YouTube HTTP calls |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/data/remote/TranscriptXmlParser.kt | XML parsing for timedtext transcript responses |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/data/dto/TranscriptSnippetDto.kt | DTO for parsed XML snippets |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/data/dto/InnerTubeResponseDto.kt | DTOs for InnerTube player endpoint response |
| Android/YouTubeSummarizer/app/src/main/java/com/tldw/app/Consts.kt | App constants (model identifiers, prompt, sample videos) |
| Android/YouTubeSummarizer/app/src/main/AndroidManifest.xml | App manifest: permissions, launcher, share intent |
| Android/YouTubeSummarizer/app/proguard-rules.pro | ProGuard rules placeholder |
| Android/YouTubeSummarizer/app/build.gradle.kts | Android module build configuration + dependencies |
| Android/YouTubeSummarizer/.gitignore | Android project gitignore |
Comments suppressed due to low confidence (1)
Android/YouTubeSummarizer/app/src/main/AndroidManifest.xml:22
- The activity also references
@style/Theme.AppCompat.DayNight.NoActionBarhere; once the app theme is corrected, it’s usually better to reference the same app theme (or omit the activity theme) to avoid drifting configurations.
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
iamstuffed
left a comment
There was a problem hiding this comment.
codex: I verified that ./gradlew assembleDebug passes for the new demo, but ./gradlew test fails at compileDebugUnitTestKotlin because TranscriptViewModelTest is missing the new unloadModelUseCase constructor argument. Leaving these as review comments rather than requesting changes.
ca803a6 to
ea0683c
Compare
| maven { | ||
| name = "Central Portal Snapshots" | ||
| url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") | ||
| } | ||
| maven { url = uri("https://jitpack.io") } | ||
| } |
| distributionBase=GRADLE_USER_HOME | ||
| distributionPath=wrapper/dists | ||
| distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip | ||
| networkTimeout=10000 |
| [versions] | ||
| agp = "9.2.0" | ||
| kotlin = "2.3.21" | ||
| coreKtx = "1.18.0" | ||
| leapSdkAndroid = "0.10.2" | ||
| lifecycleRuntimeKtx = "2.10.0" | ||
| lifecycleRuntimeCompose = "2.10.0" | ||
| activityCompose = "1.13.0" | ||
| composeBom = "2026.04.01" | ||
| coroutines = "1.10.2" | ||
| viewmodelCompose = "2.10.0" | ||
| okhttp = "5.3.2" | ||
| gson = "2.14.0" |
| } finally { | ||
| // Use NonCancellable to ensure model unload completes even if job is cancelled | ||
| viewModelScope.launch(NonCancellable) { unloadModelUseCase() } | ||
| } |
| val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager | ||
| val capabilities = remember { cm.getNetworkCapabilities(cm.getActiveNetwork()) } | ||
| val wifiConnected = remember { | ||
| capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true | ||
| } | ||
| val mobileConnected = remember { | ||
| capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true | ||
| } |
| Text( | ||
| text = "Paste a YouTube link", | ||
| fontSize = 32.sp, | ||
| fontWeight = FontWeight.Medium, | ||
| lineHeight = 36.sp, | ||
| letterSpacing = (-0.8).sp, | ||
| color = MaterialTheme.colorScheme.onSurface, | ||
| ) | ||
|
|
||
| if (!state.urlError) { | ||
| Spacer(Modifier.height(10.dp)) | ||
| Text( | ||
| text = "We'll grab the transcript and summarize it on-device in a few seconds.", | ||
| style = MaterialTheme.typography.bodyMedium, | ||
| color = MaterialTheme.colorScheme.onSurfaceVariant, | ||
| lineHeight = 20.sp, | ||
| ) | ||
| } | ||
|
|
||
| Spacer(Modifier.height(24.dp)) | ||
|
|
||
| // URL input | ||
| OutlinedTextField( | ||
| value = state.url, | ||
| onValueChange = { onEvent(TranscriptEvent.UrlChanged(it)) }, | ||
| modifier = Modifier.fillMaxWidth(), | ||
| placeholder = { | ||
| Text( | ||
| text = "youtube.com/watch?v=…", | ||
| color = MaterialTheme.colorScheme.outline, | ||
| fontSize = 15.sp, |
| if (modelClass.isAssignableFrom(MainViewModel::class.java)) { | ||
| return MainViewModel(checkModelDownloadedUseCase) as T | ||
| } | ||
| throw IllegalArgumentException("Unknown ViewModel class") |
| // Standard watch URL: ?v=VIDEO_ID | ||
| if (trimmed.contains("v=")) { | ||
| val id = trimmed.substringAfter("v=").substringBefore("&").substringBefore("#") | ||
| if (VIDEO_ID_PATTERN.matches(id)) return id | ||
| } |
This pull request introduces the initial implementation of the YouTube summarizer Android app, including project setup, core app structure, network and data layer, and basic UI flow. The main themes are project configuration, app architecture, and YouTube data fetching.
tl;dw
TLDW (Too Long Didn't Watch) Is a summary generation app for YouTube videos.
Demo
Screen_recording_20260504_222008.mp4