{"id":34164160,"url":"https://github.com/as/hlscat","last_synced_at":"2026-06-09T07:31:03.139Z","repository":{"id":283386150,"uuid":"951602118","full_name":"as/hlscat","owner":"as","description":"hlscat stitches an hls manifest into one vod","archived":false,"fork":false,"pushed_at":"2025-04-13T01:07:26.000Z","size":62,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-18T05:22:47.856Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/as.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2025-03-20T00:12:45.000Z","updated_at":"2025-04-13T01:07:29.000Z","dependencies_parsed_at":"2025-03-27T10:25:41.096Z","dependency_job_id":null,"html_url":"https://github.com/as/hlscat","commit_stats":null,"previous_names":["as/hlscat"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/as/hlscat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fhlscat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fhlscat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fhlscat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fhlscat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/as","download_url":"https://codeload.github.com/as/hlscat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/as%2Fhlscat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34096950,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"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":"2025-12-15T09:43:15.114Z","updated_at":"2026-06-09T07:31:03.134Z","avatar_url":"https://github.com/as.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hlscat\n\n`hlscat` is a command line HLS playlist repackager experiment. The goal was to create a repackager that does not re-encode content or use any temporary files.\n\nIt handles `mpegts` segments and fragmented mp4s, as well as clearkey encryption (when the key is contained in the playlist). Given a master manifest on the command line, `hlscat` selects the best video and audio track and merges them together without reencoding, outputting the new stream to standard output without any temporary files.\n\n`hlscat` has two primary functions: Listing and Repackaging.\n\n## Feature Matrix\n\n|FEATURE\t|SUPPORT\t|COMMENT\t|\t\n| ------------- | ------------- |------------- |\n|mpegts \t|x|\tmpeg ts segments as input\t|\t\t\n|fmp4 \t|x|\tfragmented mp4 with init files\t\t\t|\n|ad removal\t|x|\tremove segments that have a EXT-X-CUE tag\t|\t\t\n|late bound audio\t|x|\tvideo and audio are in seperate files/containers\t|\t\t\n|stateless*\t|x|\tdoes not use any temporary files on disk or memory for repackaging\t|\t\t\n|drm (clearkey aes128 cbc)\t|x|\tsample aes128cbc encryption where the key is available via endpoint|\t\t\t\n|drm (sample)|\t\t|hardware drm, widevine, playready, fairplay\t\t\t|\n\n## Listing\n\nA master or media manifest can be listed with `hlscat`. \n\n```\nhlscat -l https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8\n/url_0/193039199_mp4_h264_aac_hd_7.m3u8\n/url_2/193039199_mp4_h264_aac_ld_7.m3u8\n/url_4/193039199_mp4_h264_aac_7.m3u8\n/url_6/193039199_mp4_h264_aac_hq_7.m3u8\n/url_8/193039199_mp4_h264_aac_fhd_7.m3u8\n\n; hlscat -l https://test-streams.mux.dev/x36xhzz/url_8/193039199_mp4_h264_aac_fhd_7.m3u8 | head\nbase path is https://test-streams.mux.dev/x36xhzz/url_8/\nmedia playlist duration=10m34.567s\nurl_590/193039199_mp4_h264_aac_fhd_7.ts\td=10.000000\tt=10.000000\nurl_591/193039199_mp4_h264_aac_fhd_7.ts\td=10.000000\tt=20.000000\nurl_592/193039199_mp4_h264_aac_fhd_7.ts\td=10.000000\tt=30.000000\nurl_593/193039199_mp4_h264_aac_fhd_7.ts\td=10.000000\tt=40.000000\nurl_594/193039199_mp4_h264_aac_fhd_7.ts\td=10.000000\tt=50.000000\nurl_595/193039199_mp4_h264_aac_fhd_7.ts\td=10.000000\tt=60.000000\nurl_596/193039199_mp4_h264_aac_fhd_7.ts\td=10.000000\tt=70.000000\nurl_597/193039199_mp4_h264_aac_fhd_7.ts\td=9.933000\tt=79.933000\nurl_598/193039199_mp4_h264_aac_fhd_7.ts\td=10.067000\tt=90.000000\nurl_599/193039199_mp4_h264_aac_fhd_7.ts\td=10.000000\tt=100.000000\n```\n\nThe `-abs` flag will produce absolute paths and `-r` will recurse into media manifests from a master manifests.\n\n```\nhlscat -abs -l https://test-streams.mux.dev/x36xhzz/url_8/193039199_mp4_h264_aac_fhd_7.m3u8 | head\n\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_590/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_591/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_592/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_593/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_594/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_595/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_596/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_597/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_598/193039199_mp4_h264_aac_fhd_7.ts\nhttps://test-streams.mux.dev/x36xhzz/url_8/url_599/193039199_mp4_h264_aac_fhd_7.ts\n```\n\n## Repackaging\n\n### Muxed TS segment stream (audio+video in one container)\n\n```\n; hlscat  https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8 \u003e av.mp4\n\nhls video: https://test-streams.mux.dev/x36xhzz/url_8/193039199_mp4_h264_aac_fhd_7.m3u8\nhls audio: https://test-streams.mux.dev/x36xhzz/url_8/193039199_mp4_h264_aac_fhd_7.m3u8\nhls single-file a/v\nmedia playlist duration=10m34.567s\n\n; minfo av.mp4\n{\"Path\":\"av.mp4\",\"StreamSize\":\"801278\",\"Duration\":\"634.567\",\"CodecID\":\"iso5\",\"CodecID_Compatible\":\"iso5/iso6/mp41\",\"Format\":\"MPEG-4\",\"Format_Profile\":\"Base Media\",\"AudioCount\":\"1\",\"FrameCount\":\"38074\",\"VideoCount\":\"1\",\"DataSize\":\"5125874\",\"FileSize\":\"487585690\",\"FooterSize\":\"482445342\",\"HeaderSize\":\"14474\",\"Encoded_Application\":\"Lavf59.34.101\",\"OverallBitRate\":\"6147003\",\"IsStreamable\":\"Yes\",\n\t\"Track\": [\n{\"@type\":\"Video\",\"ID\":\"1\",\"StreamSize\":\"474736434\",\"Duration\":\"634.567\",\"FrameCount\":\"38074\",\"FrameRate\":\"60\",\"FrameRate_Mode\":\"VFR\",\"CodecID\":\"avc1\",\"Format\":\"AVC\",\"Format_Profile\":\"High\",\"Format_Level\":\"4\",\"Format_Settings_RefFrames\":\"5\",\"Format_Settings_CABAC\":\"Yes\",\"BitRate\":\"6000000\",\"Width\":\"1920\",\"Height\":\"1080\",\"DisplayAspectRatio\":\"1.778\",\"PixelAspectRatio\":\"1\",\"BitDepth\":\"8\",\"ScanType\":\"Progressive\",\"ColorSpace\":\"YUV\",\"ChromaSubsampling\":\"4:2:0\"},\n\t\t{\"@type\":\"Audio\",\"ID\":\"2\",\"StreamOrder\":\"1\",\"StreamSize\":\"12047978\",\"Duration\":\"634.194\",\"FrameCount\":\"27312\",\"FrameRate\":\"7.178\",\"CodecID\":\"mp4a-40-27\",\"Format\":\"ER Parametric\",\"BitRate\":\"153725\",\"BitRate_Mode\":\"CBR\",\"SamplingCount\":\"4661326\",\"SamplingRate\":\"7350\",\"SamplesPerFrame\":\"1024\",\"AlternateGroup\":\"1\",\"Default\":\"Yes\"}]}\n```\n\n### Late bound audio stream (audio and video in seperate container)\n\n(same command as above, I don't have any public examples of such a playlist)\n\n### DRM (AES-128-CBC Clearkey Encryption)\n\n```\nhlscat https://test-streams.mux.dev/dai-discontinuity-deltatre/manifest.m3u8 \u003e av.mp4\n```\n\n### AD Removal\n\nThere are many ways to signal an AD break in a playlist so detecting one methodically can be difficult. For the purpose of this program, an AD is any segment that contains an EXT-CUE-OUT or EXT-CUE-OUT-CONT tag.\n\n```\nhlscat -noads $URL \u003e av.mp4\n```\n\n\n### Blackframe Insertion\n\nThis feature, instead of removing ADs, covers them with black frames and silent audio. Currently this feature does not work properly unless characteristic of the encoded stream are defined correctly on the command line using the z-variables. Using it or relying on it to produce stable output is not recommended.\n\n```\nhlscat -blackframe $URL \u003e av.mp4\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fas%2Fhlscat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fas%2Fhlscat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fas%2Fhlscat/lists"}