Skip to content

Contributing

OnlyXianzo edited this page Jun 10, 2026 · 1 revision

Contributing

Thanks for your interest in contributing to TrueStream. This guide explains the process, code style, commit conventions, and architecture rules you'll need to follow.

Getting started

  1. Fork the repository on GitHub.
  2. Create a feature branch from main.
  3. Make your changes following the code style and guidelines below.
  4. Verify that your changes pass all checks.
  5. Submit a pull request targeting main.

Code style

Flutter and Dart

  • Follow Effective Dart guidelines and the settings in analysis_options.yaml.
  • Use Riverpod for shared state. Never use setState() for state shared across widgets. Use ConsumerWidget or ConsumerStatefulWidget with providers.
  • Body text uses Instrument Sans via GoogleFonts.instrumentSans(). Mono text for speeds, URLs, and format codes uses Theme.of(context).textTheme.mono, which provides Iosevka Charon Mono through the TrueStreamTextStyles extension.
  • All colors must reference DESIGN.md tokens through TrueStreamColors. Never hardcode hex values.
  • Use const constructors where possible. Avoid mutable state in widgets.
  • Aim for 48x48 minimum touch targets for interactive elements.
  • Add semantic labels using the Semantics widget for screen reader support.
  • Run flutter analyze before committing. It must pass with zero errors.

Python

  • Follow PEP 8 formatting standards.
  • Use the YoutubeDL class API from yt-dlp. Never call subprocess.run() with the yt-dlp binary.
  • Run all blocking operations in threading.Thread with a cancel event.
  • Use structured JSON for Flutter communication. Don't use print() statements for machine-readable output.
  • Keep functions focused on a single responsibility. Each module in engine/truestream_engine/ owns one concern.
  • Use set_paths() to inject all binary paths. Never hardcode paths.
  • Run pytest engine/tests/ -v before committing. All tests must pass.

Commit conventions

Use the conventional commit format:

type: scope: description in present tense

Types are init for scaffold, feat for a new feature or screen, fix, chore for config or deps, docs, style for formatting with no logic change, refactor, test, and perf.

Scope is optional but recommended. For example, you might use a task ID like P4-003.

One logical change per commit. Never bundle unrelated changes in a single commit. Each commit diff should be reviewable in under two minutes.

Branch strategy

  • main is stable and always deployable.
  • Feature branches branch from main and should be named by task, for example feat/sponsorblock-settings or fix/cancel-race.
  • Squash merge into main when the pull request is approved.

Testing

Always run these before committing:

# Flutter static analysis
flutter analyze

# Flutter widget and unit tests
flutter test

# Python engine tests
pytest engine/tests/ -v

The CI pipeline in .github/workflows/verify.yml enforces all three on every push and pull request.

Testing conventions

  • Flutter tests: Write widget tests for screens using WidgetTester. Mock providers with ProviderScope overrides. Place tests in test/.
  • Python tests: Write pytest functions for each module in engine/tests/test_<module>.py. Use plain assert statements. One test class per module.

Pull request process

  1. Make sure your branch is up to date with main.
  2. Run the full test suite: flutter analyze, flutter test, and pytest engine/tests/ -v.
  3. Fill out the pull request template. Include motivation, description, and verification steps.
  4. Mark which platforms you tested on: Android, Windows, or Linux.
  5. A maintainer reviews your changes. Address feedback before merging.
  6. Squash commits if the reviewer requests it.

Architecture rules

  • yt-dlp: YoutubeDL class API only. Never use subprocess.run() with the yt-dlp binary.
  • Downloads: Always run in threading.Thread with a cancel event. Never block the main thread.
  • State: Use Riverpod with ConsumerWidget, Notifier, or AsyncNotifier. Never use setState() for shared state.
  • Fonts: Body text uses GoogleFonts.instrumentSans(). Mono text uses Theme.of(context).textTheme.mono for Iosevka Charon Mono. Never use system fonts.
  • Colors: Always use DESIGN.md tokens through TrueStreamColors. Never use hex literals.
  • Paths: Never hardcode paths. All binary paths are injected through set_paths().
  • Format options: Never use both merge_output_format and remux_video at the same time in yt-dlp options.
  • Layout structure: Screens go in lib/features/<feature>/screens/. Providers go in lib/providers/. Theme and engine bridge code go in lib/core/.

How to add a new screen

  1. Create the screen file at lib/features/<feature>/screens/<name>_screen.dart. Extend ConsumerWidget or ConsumerStatefulWidget if you need local state like text controllers. Use the super.key pattern.
  2. Add a provider (if needed) at lib/providers/<name>_provider.dart. Extend StateNotifier<State> or AsyncNotifier<State> for shared state. Expose the provider as a global final.
  3. Wire navigation using Navigator.of(context).push(MaterialPageRoute(...)) for simple navigation. For bottom-nav screens, add the route in lib/features/shell/screens/app_shell.dart.
  4. Update the app entry point only if the screen is a top-level route like onboarding. Add it in lib/main.dart.
  5. Add semantic labels for accessibility using the Semantics widget.
  6. Write a widget test in test/ covering the screen's basic rendering and interactions.

How to add a new Python module

  1. Create the module at engine/truestream_engine/<module>.py. Import from sibling modules where needed, for example from truestream_engine.paths import .... Keep one responsibility per module.
  2. Add tests at engine/tests/test_<module>.py. Write pytest functions with plain assert statements. Cover normal cases, edge cases, and error paths.
  3. Wire into the IPC dispatch in engine/truestream_engine/__main__.py. Add an elif method == "<namespace>/<action>" branch that calls your function. Return a JSON-serializable dict and include "success": True/False for error reporting.
  4. Update the public API in engine/truestream_engine/__init__.py if the function should be accessible from the package level.
  5. Run tests with pytest engine/tests/ -v and verify that everything passes.

Clone this wiki locally