{"id":51913138,"url":"https://github.com/stiles/lapd-shootings","last_synced_at":"2026-07-27T07:30:40.809Z","repository":{"id":372064742,"uuid":"1305057786","full_name":"stiles/lapd-shootings","owner":"stiles","description":"Detailing LAPD's officer-involved shootings by parsing its critical incident briefings from YouTube. ","archived":false,"fork":false,"pushed_at":"2026-07-18T16:46:31.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-18T18:23:43.838Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","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/stiles.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":"2026-07-18T16:28:27.000Z","updated_at":"2026-07-18T16:46:35.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stiles/lapd-shootings","commit_stats":null,"previous_names":["stiles/lapd-shootings"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/stiles/lapd-shootings","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Flapd-shootings","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Flapd-shootings/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Flapd-shootings/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Flapd-shootings/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stiles","download_url":"https://codeload.github.com/stiles/lapd-shootings/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stiles%2Flapd-shootings/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35942774,"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":[],"created_at":"2026-07-27T07:30:40.034Z","updated_at":"2026-07-27T07:30:40.790Z","avatar_url":"https://github.com/stiles.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LAPD shooting videos\n\nThis project fetches Los Angeles Police Department YouTube uploads, identifies\nofficer-involved shooting (OIS) briefing videos and analyzes their caption\ntranscripts.\n\n## Setup\n\nRequires Python 3.11 or later.\n\n```sh\npython -m venv .venv\nsource .venv/bin/activate\npython -m pip install --editable \".[dev]\"\n```\n\nSet a YouTube Data API key:\n\n```sh\nexport YOUTUBE_KEY=\"your-key\"\n```\n\nTranscript fetching uses the ScrapeOps proxy port by default because YouTube\ncan block repeated caption requests. Set its key before fetching:\n\n```sh\nexport SCRAPE_PROXY_KEY=\"your-key\"\n```\n\nDatacenter proxies cost 1 API credit per request. Add `--residential` to route\nrequests through residential IP pools at 10 credits per request if datacenter\nrequests get blocked. You can also fetch directly without a proxy with\n`--no-proxy`. Direct requests may be blocked or rate-limited.\n\nMany LAPD critical incident videos are age-restricted, and YouTube only serves\ntheir captions to logged-in sessions. To fetch those transcripts, sign in to\nYouTube in a browser and pass that browser's name so yt-dlp can reuse its\nsession cookies:\n\n```sh\nlapd-shootings fetch-transcripts --cookies-from-browser chrome\n```\n\nThese requests go directly to YouTube from your IP, not through the proxy.\nAutomated fetching with account cookies can violate YouTube's terms of\nservice and may put the account at risk, so consider a secondary account. On\nmacOS, reading Chrome cookies triggers a keychain permission prompt.\n\n## Usage\n\nRun every stage:\n\n```sh\nlapd-shootings run\n```\n\nRun a stage on its own:\n\n```sh\nlapd-shootings fetch-videos\nlapd-shootings fetch-transcripts\nlapd-shootings parse\nlapd-shootings visualize\n```\n\nUse `--dry-run` before a command to list its outputs without reading\ncredentials or making network requests:\n\n```sh\nlapd-shootings --dry-run run\n```\n\nUse `--limit 25` with `fetch-videos` or `run` to test a smaller catalog. The\ntranscript command saves its cache after each batch, preserves fetched and\nunavailable records and retries blocked records on each new command\ninvocation.\n\n## Outputs\n\n- `data/raw/lapd_videos.json`: all fetched public uploads\n- `data/interim/lapd_ois_videos.json`: uploads whose titles contain `OIS`\n- `data/interim/lapd_ois_transcripts.json`: transcript cache and retrieval\n  status\n- `data/processed/lapd_ois_cases.json`: video-level data including transcript\n  text\n- `data/processed/lapd_ois_cases.csv`: video-level data without transcript text\n- `data/processed/lapd_ois_cases_by_year.csv`: annual OIS and ghost-gun counts\n- `visuals/lapd_ois_videos_by_year.png`: annual stacked bar chart\n\n## Notes on the data\n\nThe LAPD channel began posting critical incident briefing videos in 2018, so\nthese records do not represent the department's complete OIS history. One\nvideo may cover multiple incidents and some incidents may have no posted\nvideo. YouTube captions may be auto-generated and contain transcription\nerrors.\n\nThe current `ghost gun` finding matches `ghost gun` and `ghost-gun` in\ntranscript text. It signals a narration match, not a verified incident\nattribute.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstiles%2Flapd-shootings","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstiles%2Flapd-shootings","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstiles%2Flapd-shootings/lists"}