{"id":51921089,"url":"https://github.com/damiansire/youtube-community-parser","last_synced_at":"2026-07-27T18:30:59.699Z","repository":{"id":164988840,"uuid":"410727922","full_name":"damiansire/youtube-community-parser","owner":"damiansire","description":"Desktop application to parse, analyze, and understand your YouTube subscriber community data.","archived":false,"fork":false,"pushed_at":"2026-07-10T00:21:54.000Z","size":413,"stargazers_count":45,"open_issues_count":0,"forks_count":16,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-07-10T02:08:35.123Z","etag":null,"topics":["community-management","data","data-analysis","desktop-app","parser","rust","youtube"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/damiansire.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-09-27T03:20:59.000Z","updated_at":"2026-07-10T00:21:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"f7bde8bc-327d-450f-983a-3ba50e9018e5","html_url":"https://github.com/damiansire/youtube-community-parser","commit_stats":null,"previous_names":["damiansire/youtube-community-parser"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/damiansire/youtube-community-parser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damiansire%2Fyoutube-community-parser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damiansire%2Fyoutube-community-parser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damiansire%2Fyoutube-community-parser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damiansire%2Fyoutube-community-parser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/damiansire","download_url":"https://codeload.github.com/damiansire/youtube-community-parser/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/damiansire%2Fyoutube-community-parser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35961441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-27T02:00:06.776Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["community-management","data","data-analysis","desktop-app","parser","rust","youtube"],"created_at":"2026-07-27T18:30:59.010Z","updated_at":"2026-07-27T18:30:59.687Z","avatar_url":"https://github.com/damiansire.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# YouTube Community Parser\n\n[![CI](https://github.com/damiansire/youtube-community-parser/actions/workflows/ci.yml/badge.svg)](https://github.com/damiansire/youtube-community-parser/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\nA desktop app to **get to know your YouTube community**: from the comments of a\nchannel or a video, it shows who comments the **most**, who comments the\n**least**, and the overall shape of your community.\n\nThe goal is to help communities become stronger and get to know each other\nbetter. Use it ethically and for the common good.\n\n## Architecture\n\n```\nchannel / video\n   │\n   ▼\n[ src-tauri/src/youtube.rs ]  native client (reqwest, async): hits the\n   │                       YouTube Data API v3 (commentThreads/playlistItems) → core types\n   ▼\n[ sdp-core ]    pure Rust domain: models + rankings (testable, UI-free)\n   │\n   ▼\n[ src-tauri ]   Tauri v2 desktop app: #[tauri::command] commands\n   │\n   ▼\n[ app ]         webview UI (HTML/CSS/JS): spectrum + commenter roster\n```\n\nIngestion is **native in Rust** (there is no Node sidecar): an async `reqwest`\nclient that hits the [YouTube Data API v3](https://developers.google.com/youtube/v3)\ndirectly. This makes the app **distributable** (it doesn't require `node` or the\nsource tree on the end user's machine).\n\nInternal crates use the historical `sdp-` prefix (from \"Subscriber Data Parser\",\nthe project's original name).\n\n| Folder           | What it is                                                            |\n|------------------|----------------------------------------------------------------------|\n| `crates/core`    | Domain: `Commenter`, `Comment` and the rankings. No network, no UI.  |\n| `crates/storage` | Local SQLite persistence of the history. Each analysis is saved (idempotent upsert, covered by `crates/storage/tests/`) and `analyze_history` re-analyzes it without spending quota again. |\n| `crates/llm`     | `sdp-llm`: multi-provider AI refinement layer (async `dyn`-compatible trait) behind the paid cost-confirmation gate. Adapters tested against a mocked HTTP server (`wiremock`, see `crates/llm/tests/`), no real network. |\n| `src-tauri`      | Tauri v2 backend + native YouTube client (`src/youtube.rs`).         |\n| `app`            | Webview frontend.                                                    |\n\n## API key threat model\n\nThe YouTube Data API v3 key is entered in the UI and used to authenticate the\ncalls. Decisions and scope:\n\n- **What it protects.** The key travels over IPC to the native process and, as\n  soon as it crosses that boundary, it is wrapped in\n  [`secrecy::SecretString`](https://docs.rs/secrecy): it is not logged, not\n  printed via `Debug`, and is **zeroized** on drop, so it doesn't stay in the\n  clear in process memory longer than necessary. The value is exposed only for\n  the exact instant needed to encode it into the request URL.\n- **What it does NOT protect (and why that's acceptable here).** This is a\n  **single-user desktop app**: the key is **not persisted** (not saved to disk\n  or to a keychain), it is requested every session. It doesn't defend against\n  another process of the same user inspecting the process memory (an attacker\n  with that level of access already controls the session). A full OS keychain\n  (Stronghold/keyring) would be **disproportionate** for the use case and adds\n  dependency surface; it stays as an optional improvement if persisting the key\n  across sessions is decided later.\n- **Basic hygiene.** The key goes over the HTTPS request URL (TLS encrypts it in\n  transit) and never through argv or a subprocess environment (there is no\n  subprocess anymore). It doesn't reintroduce the old Node-sidecar risk, where\n  the key crossed into another process's `env`.\n\n## Cost-confirmation gate threat model\n\nPaid AI operations go through an **estimate → confirm → execute** flow bound by a\nsingle-use token (`src-tauri/src/confirm.rs`). `estimate_*` returns the exact\ncost plus a token tied to a fingerprint (operation + amount + hash of the corpus\nto process); `run_*` consumes the token and re-checks the fingerprint server-side\nat execution time.\n\n- **What it protects.** **Accidental** spend (you can't execute without going\n  through `estimate_*` first), **TOCTOU** (the amount or corpus can't change\n  between estimating and executing without invalidating the token), and **replay**\n  (the token is one-time-use). This targets the real, frequent risk: a\n  double-click, an accidental retry, or a UI bug spending money on a mismatched\n  amount.\n- **What it does NOT protect (and why that's acceptable here).** It does **not**\n  defend against a **compromised webview**. The token proves that estimate and\n  execution agree — not that a human saw the modal and accepted. A front with\n  arbitrary code execution (e.g. `withGlobalTauri: true` + XSS) can call\n  `estimate_*` itself to mint a legitimate token and chain it into `run_*`,\n  spending without ever showing a modal. Truly defending that case requires the\n  **backend** to drive confirmation (a native dialog via `tauri-plugin-dialog`\n  inside `run_*`), not the webview. It is deferred on purpose: for a single-user\n  desktop app, a front running arbitrary code already controls the session (the\n  same boundary as the API-key threat model above). It stays as an optional\n  hardening if the app ever loads untrusted web content.\n\n## Requirements\n\n- **Rust + Cargo** with the **MSVC** toolchain (`stable-x86_64-pc-windows-msvc`).\n  The `windows-gnu` toolchain does **not** work for Tauri on Windows.\n- **Visual Studio C++ Build Tools** (the \"Desktop development with C++\"\n  workload): it provides the `link.exe` linker and the Windows SDK that\n  Tauri/WebView2 need. Without it, the desktop app won't link.\n- **WebView2 Runtime** (ships by default on Windows 11).\n- **YouTube Data API v3 key**\n  ([how to get one](https://developers.google.com/youtube/v3/getting-started)).\n\n\u003e On Windows, once the Build Tools are installed, pin the MSVC toolchain for\n\u003e this repo: `rustup override set stable-x86_64-pc-windows-msvc`.\n\n## Development\n\n```bash\n# Domain + AI-layer tests (pure Rust, works with any toolchain)\ncargo test -p sdp-core -p sdp-llm\n\n# Native YouTube client + persistence tests (requires MSVC to link)\ncargo test -p sdp-desktop -p sdp-storage\n\n# Launch the desktop app (requires MSVC + Build Tools + @tauri-apps/cli)\ncargo tauri dev\n```\n\nThe app opens with a **\"View with sample data\"** button to explore the interface\nwithout an API key. For real data, paste the channel or video ID and your API key.\n\n## Status\n\nIn-progress conversion of the original parser (Node, 2021) into a tracking system\nin Rust. Done: tested domain, **native ingestion in Rust** (reqwest, no Node\nsidecar — the app is distributable), **local persistence (SQLite) wired** to the\ncommands (each analysis is saved and `analyze_history` re-analyzes without\nspending quota), **configurable quota caps** with partial results (F4), a **\"view\naccumulated history\" button** that re-analyzes the local store without spending\nquota, app scaffold and UI. Pending: packaging/installing the desktop app\n(requires the Build Tools above).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdamiansire%2Fyoutube-community-parser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdamiansire%2Fyoutube-community-parser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdamiansire%2Fyoutube-community-parser/lists"}