{"id":20981577,"url":"https://github.com/gromnitsky/gmakepod","last_synced_at":"2025-12-25T18:04:16.575Z","repository":{"id":32328395,"uuid":"130643231","full_name":"gromnitsky/gmakepod","owner":"gromnitsky","description":"A tiny podcast client written in GNU Make with sprinkles of Ruby","archived":false,"fork":false,"pushed_at":"2024-02-12T08:46:47.000Z","size":383,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-02-12T09:53:39.637Z","etag":null,"topics":["gnu-make","make","podcast-client","podcatcher"],"latest_commit_sha":null,"homepage":null,"language":"Makefile","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/gromnitsky.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}},"created_at":"2018-04-23T05:12:29.000Z","updated_at":"2023-07-25T07:09:55.000Z","dependencies_parsed_at":"2023-11-24T05:23:42.313Z","dependency_job_id":"1f5c5627-ae7a-4063-850f-3055c021a48d","html_url":"https://github.com/gromnitsky/gmakepod","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gromnitsky%2Fgmakepod","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gromnitsky%2Fgmakepod/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gromnitsky%2Fgmakepod/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gromnitsky%2Fgmakepod/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gromnitsky","download_url":"https://codeload.github.com/gromnitsky/gmakepod/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243383497,"owners_count":20282151,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["gnu-make","make","podcast-client","podcatcher"],"created_at":"2024-11-19T05:39:17.658Z","updated_at":"2025-12-25T18:04:11.526Z","avatar_url":"https://github.com/gromnitsky.png","language":"Makefile","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gmakepod\n\nA tiny podcast client written in GNU Make with sprinkles of Ruby.\n\n## Why?\n\nWhy not? Besides [*because we can*][], this are the features you get when\nusing Make for something it wasn't intended for:\n\n[*because we can*]: https://sigwait.org/~alex/blog/2018/05/11/writing-a-podcast-client-in-gnu-make.html\n\n* download in parallel;\n* yt-dlp integration;\n* filter by subs name, enclosure type or url;\n* auto-convert ogg/m4a to mp3 or vice-versa;\n* sort in reverse, so you may fetch the 1st 2 episodes, instead of the\n  last 2, for example;\n* 'catch up' w/ feeds w/o downloading anything.\n\nDespite the number of src files, the client itself is rather small:\n\n~~~\n$ f='*rb *mk gmakepod'; join -j2 \u003c(wc -l $f) \u003c(du -bhc $f) | column -t -NFILE,LINES,SIZE\nFILE                  LINES  SIZE\nenclosures-print.rb   13     444\nini-parse.rb          6      174\nu.rb                  19     548\nenclosures-reject.mk  5      212\nfeed-parse.mk         24     893\ngenerate.mk           56     1.3K\nu.mk                  13     646\ngmakepod              57     1.3K\ntotal                 193    5.4K\n~~~\n\n## Install\n\nRequires GNU Make 4+, Ruby 2.7+, curl.\n\n~~~\n$ git clone --recurse-submodules https://github.com/gromnitsky/gmakepod.git\n$ cd gmakepod\n$ bundle\n~~~\n\nThen create a symlink to `gmakepod` somewhere in PATH.\n\n## Setup\n\nChose an 'umbrella' dir for your podcasts, e.g., `~/podcasts`; create\na file named `podcasts.ini` in that dir:\n\n~~~\n[BBC In Our Time]\nurl = http://downloads.bbc.co.uk/podcasts/radio4/iot/rss.xml\n\n[Dave Winer]\nurl = http://scripting.com/rss.xml\nconvert-to = .mp3\n~~~\n\nAn optional `convert-to` prop tells gmakepod that it'll need to\nconvert each enclosure (from that feed only) to `mp3` (the dot is\nimportant). Other valid props here are `e`, `reverse`, `filter.type` \u0026\n`filter.url`. Type `gmakepod help` to read what they mean.\n\nNow, cd to `~/podcasts` \u0026 type `gmakepod`. It should download the last\n2 enclosures tops from each feed.\n\n~~~\n$ tree --noreport media\nmedia/\n├── BBC_In_Our_Time\n│   ├── p02q5q4c.mp3\n│   └── p02q5phk.mp3\n└── Dave_Winer\n    └── denverPostAndBerkeleyside.mp3\n~~~\n\nIf you run `gmakepod` again it says '`make[1]: *** No targets.\nStop.`' because it refuses to process already processed enclosures.\n\nFor parallel downloads, pass `j=N` param.\n\n## yt-dlp\n\ntl;dr: to get audio from the Computer History Museum playlist:\n\n~~~\n[CHM Oral History]\nurl = https://apps.sigwait.org/youtube-dl-feeds/https://www.youtube.com/playlist?list=PLQsxaNhYv8daKdGi7s85ubzbWdTB36-_q\ncurl = yt-dlp -o $@ -x --audio-format mp3 --add-metadata --no-part\n~~~\n\nThis will fetch a specially augmented youtube feed \u0026 run yt-dlp\nfor each enclosure.\n\nYoutube provides several types of atom feeds, but they all lack\nenclosures in them. `apps.sigwait.org/youtube-dl-feeds` server injects\nenclosure links to youtube videos. (It doesn't log anything, have no\nstate, the source is available\n[here](https://github.com/gromnitsky/youtube-dl-feeds); you can run\nyour own server if you trust no one.)\n\nWe cannot put real enclosure links into the feed, for the only way to\nget a format of the audio/video, contained behind a youtube url, is to\nreplicate a yt-dlp job.\n\n`convert-to` prop is not applicable here.\n\n## How does it work?\n\ngmakepod target  | desc\n---------------- | -------------------------------------------------------------\n.feeds           | parse .ini to extract feeds names \u0026 urls\n.enclosures      | fetch \u0026 parse each feed to extract enclosures urls\n.files           | generate a proper output file name for each url\n.files.new       | check if we have already downloaded a url in the past, filter out\n.download.mk     | generate a makefile, where we list all the rules for all the enclosures\nrun              | run the makefile\n\nxxx-\u003emp3 conversions require ffmpeg (tested /w 4.1.4) \u0026 gawk.\n\n![kumamon](https://sigwait.org/~alex/mm/kumamon.jpg)\n\n## License\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgromnitsky%2Fgmakepod","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgromnitsky%2Fgmakepod","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgromnitsky%2Fgmakepod/lists"}