{"id":13678473,"url":"https://github.com/natolambert/blogcaster","last_synced_at":"2025-04-09T17:25:58.379Z","repository":{"id":214542734,"uuid":"726993789","full_name":"natolambert/blogcaster","owner":"natolambert","description":"Python tools for easily translating your blog content to podcasts \u0026 YouTube","archived":false,"fork":false,"pushed_at":"2024-09-04T22:43:28.000Z","size":119750,"stargazers_count":205,"open_issues_count":7,"forks_count":16,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-02T10:34:26.508Z","etag":null,"topics":["blogging","podcasts","substack"],"latest_commit_sha":null,"homepage":"https://www.youtube.com/@interconnects","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/natolambert.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}},"created_at":"2023-12-04T00:32:01.000Z","updated_at":"2025-03-15T13:36:13.000Z","dependencies_parsed_at":"2025-01-03T21:21:33.871Z","dependency_job_id":null,"html_url":"https://github.com/natolambert/blogcaster","commit_stats":{"total_commits":87,"total_committers":3,"mean_commits":29.0,"dds":0.4252873563218391,"last_synced_commit":"6a806dd58c8770cdc6ba0e7874cafec507c12724"},"previous_names":["natolambert/interconnects-tools","natolambert/blogcaster"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/natolambert%2Fblogcaster","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/natolambert%2Fblogcaster/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/natolambert%2Fblogcaster/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/natolambert%2Fblogcaster/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/natolambert","download_url":"https://codeload.github.com/natolambert/blogcaster/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248076043,"owners_count":21043697,"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":["blogging","podcasts","substack"],"created_at":"2024-08-02T13:00:53.942Z","updated_at":"2025-04-09T17:25:58.355Z","avatar_url":"https://github.com/natolambert.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# BlogCast: Tools for Multimodal Blogging!\n\nWatch an example video [here](https://www.youtube.com/watch?v=0i0aBJGgtpk)!\n\nThis takes a markdown file that you would normally convert to HTML for Substack or upload elsewhere and convert it directly into `*.mp3` files for podcasts and `*.mp4` files for YouTube to tap into new audiences. It uses two state-of-the-art generative AI tools/systems:\n1. Elevenlabs.io's new multilingual models for audio generation.\n2. OpenAI's DALLE3 for fun images.\n\nIt costs a few dollars a post if you publish monthly. I'm on the $22/month Elevenlabs plan based on my Substack wordcount and each post costs $2 to 8 of DALLE API credits (0.08 per widescreen image).\n\nExpand your audience and have some fun! You can use my [Elevenlabs referral link](https://elevenlabs.io/?from=partnerbruce1418) if you're going to try this out!\n\n![Multimodal blog reality sketch](https://github.com/natolambert/interconnects-tools/assets/10695622/096b2f55-f14f-4484-b780-1cfb0bc898ee)\n\n**Notes on using generated audio**: \nIn my distribution, I make it clear that it is made via AI, you should do the same.\nSecond, from the [11labs documentation](https://help.elevenlabs.io/hc/en-us/articles/13313778519057-Are-there-any-restrictions-on-what-voices-I-can-upload-for-voice-cloning-), all audio is watermarked:\n\u003e All audio generated by our model will be watermarked, so that it can be instantly traced back to us;\n\nSecond, you cannot use my generated voice per 11labs terms and how it is created. \nMy API key in the git history is no longer valid, of course.\n\n## Installation\nRight now this is not on pypi, run as following:\n```\ngit clone https://github.com/natolambert/blogcaster\ncd interconnects-tools\n```\nInstall from `requirements.txt`\n```\nconda create -n media python=3.10\nconda activate media\npip install -r requirements.txt\n```\nNext, you need to make sure you have subsequent accounts \u0026 API keys.\n\n### API Keys \u0026 Models\nThis requires an Eleven Labs account with a model id from your voice lab. \nThis takes a few minutes to set up and is fine-tuned from 1-3minutes of recordings of yourself; very easy.\n\nTo set the OpenAI API key, add the following ([from the docs](https://platform.openai.com/docs/quickstart?context=python)) to your `bashrc`:\n```\nexport OPENAI_API_KEY='your-api-key-here'\n```\nSimilarly, for 11labs:\n```\nexport ELELABS_API_KEY='your-api-key-here'\n```\nAnd finally for HuggingFace (optional figure storage for podcast show notes):\n```\nexport HF_API_KEY='your-api-key-here'\n```\nI've begain using [this directory](https://huggingface.co/datasets/natolambert/interconnects-figures) for these images.\n\nAdditionally, the audio/visual tools I used require `ffmpeg`.\n\n**Blogs using these tools**:\n(open an issue to be featured!)\n\n| Blog            | Podcast Link                           | YouTube Link                                   |\n|-----------------|----------------------------------------|------------------------------------------------|\n| [interconnects](https://www.interconnects.ai/) | https://podcast.interconnects.ai/ | https://www.youtube.com/@interconnects |\n\n\n## Example usage\nThis is designed to work with the following data format (note, it is exactly as exported from Notion as markdown for an individual post):\n\n```\nscripts/\nsource/\n└─- post-title/\n|   |-- post-title-name.md\n|   └- post-title-name/\n|       | img0.png\n|       | ...\n|       └─ imgN.png\n| ...\n└─- post-title-two/\n| ...\n```\n\n### Config\nGenerate the config file (that contains the paragraphs etc)\n```\npython scripts/create-config.py source/test-post/ --date=\"December 24th 2023\"\n```\nThe image paths can be wrong if you change them on your local machine post export, double check!\n\n*Note: it is recommended to skim the config and combine things like lists, otherwise generation is split into many more parts and needlessly across images at times.*\n\nI recommend double checking file-paths for images in the config. They can be quite annoying, especially exporting from Notion.\n\n### Audio \nBase usage is as follows.\n```\npython scripts/tts.py --input=source/test-post/\n```\nAudio generation returns descriptions for youtube / podcast with chapters. E.g.:\n```\n----------------------------------\nPrinting podcast chapter versions (does not include `see figure` audio):\n----------------------------------\nInterconnects year in review: 2023\nThe core themes of ML and the blog this year. What changes in 2024.\nThis is AI generated audio with Python and 11Labs. Source code can be found here: https://github.com/natolambert/interconnects-tools\nOriginal post: https://www.interconnects.ai/p/TODO\n\n00:00 Interconnects year in review: 2023\n01:45 Brief 2024 predictions\n03:37 Top posts of the year\n05:07 Trends\n05:11 RLHF capabilities and understanding\n07:09 Open LLM ecosystem progress\n08:38 LLM techniques pieces\n10:12 Model releases\n11:15 Moats\n11:44 State of ML opinion pieces\n12:33 Understanding reward/preference models\n13:01 Wrap up\n```\n\n**Non-default usage**:\nMore paths will need to be passed to the `tts.py` script in the case that you're not using the same file structue and voice.\n```\npython scripts/tts.py --input=source/your-post/ --elelabs_voice='your_generative_id' \n```\nOptionally, add `--farewell_audio` and `--figure_audio` to add a farewell to every post or to tell the audience to look at the figure during the video.\n\nAudio add music + outro (not currently using this, need to think about music more):\n```\npython experimental/add-music.py --input=audio/20231129-synthetic.mp3\n```\n\n### Images\n```\npython scripts/ttv-generate.py --input=source/test-post/\n```\nOptionally, one can provide a HuggingFace dataset where the images will be uploaded, so the podcast show notes can have clickable links, e.g. (--do_not_gen is for not burning extra GPUs):\n```\npython scripts/ttv-generate.py --input=examples/test-post/ --do_not_gen --hf_fig_dir=natolambert/interconnects-figures\nPodcast figures:\nFigure 1: https://huggingface.co/datasets/natolambert/interconnects-figures/resolve/main/test-post/img_003.png\n```\n\n### Video\n```\npython scripts/ttv-merge.py --input=source/test-post/\n```\n\n----\n## Generated research video\nSick of workshopping 3-5minute videos no one watches? Use this tool! An example video is [here](https://youtu.be/mc-QmqMPhSA) and included in `examples/research-talk`.\nThis section requires `imagemagick`:\n```\nbrew install imagemagick\n```\nSource code is provided in `examples/research-talk`.\nUltimately, you must download a pdf of slides from google slides / powerpoint / etc and write a script.\n\nTo create the PNG files in the right format, do the following:\n```\nconvert -density 300 examples/research-talk/research-talk/talk.pdf -quality 100 examples/research-talk/images/img_%03d.png\n```\n\nTo use the `interconnects-tools`:\n```\npython scripts/create-config.py examples/research-talk/\npython scripts/tts.py --input=examples/research-talk/ --ignore_title\npython scripts/ttv-merge.py --input=examples/research-talk/ --ignore_title\n```\n\nTo speed up the final video to your desired length, do the math, then use the following command:\n```\nffmpeg -i input.mp4 -filter_complex \"[0:v]setpts=PTS/(SPEED_FACTOR)[v];[0:a]atempo=SPEED_FACTOR[a]\" -map \"[v]\" -map \"[a]\" output.mp4\n```\nHere's an example for 10%:\n```\nffmpeg -i examples/research-talk/video.mp4 -filter_complex \"[0:v]setpts=PTS/1.1[v];[0:a]atempo=1.1[a]\" -map \"[v]\" -map \"[a]\" output.mp4\n```\n\n### Other ffmpeg\nGenerate silence:\n```\nffmpeg -f lavfi -i anullsrc=r=44100:cl=stereo -t 0.5 silence.mp3\n```\n\n## Tips\n\n* Use shorter sequeneces, 11labs tends to have volume drift on longer segments.\n* Weird file paths can cause bugs still (such as () at the end of an image file).\n\n## TODO list\nKeeping note of features I want to add in a lazy manner:\n* Adding list of figures to podcast shownotes + link because inserting them is hard.\n* Verify that the try-except block in `tts.py` works as intended.\n* Acronym management (so the audio is not weird).\n* Additional voice for quote blocks.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnatolambert%2Fblogcaster","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnatolambert%2Fblogcaster","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnatolambert%2Fblogcaster/lists"}