{"id":15278679,"url":"https://github.com/jersou/studio-pack-generator","last_synced_at":"2025-04-06T04:09:02.421Z","repository":{"id":44538145,"uuid":"413553539","full_name":"jersou/studio-pack-generator","owner":"jersou","description":"Convert a folder or a RSS URL to Studio pack zip for Lunii device","archived":false,"fork":false,"pushed_at":"2025-01-04T09:04:00.000Z","size":35043,"stargazers_count":96,"open_issues_count":0,"forks_count":12,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-30T03:03:26.555Z","etag":null,"topics":["deno","lunii","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/jersou.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2021-10-04T19:15:24.000Z","updated_at":"2025-03-20T07:36:56.000Z","dependencies_parsed_at":"2024-04-11T00:28:27.100Z","dependency_job_id":"dfc67d37-a681-40c1-9723-277ad43fadea","html_url":"https://github.com/jersou/studio-pack-generator","commit_stats":{"total_commits":288,"total_committers":6,"mean_commits":48.0,"dds":"0.14930555555555558","last_synced_commit":"d84e8daf232dd38130d0c20cc782e26f745676c1"},"previous_names":[],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jersou%2Fstudio-pack-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jersou%2Fstudio-pack-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jersou%2Fstudio-pack-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jersou%2Fstudio-pack-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jersou","download_url":"https://codeload.github.com/jersou/studio-pack-generator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247430868,"owners_count":20937874,"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":["deno","lunii","typescript"],"created_at":"2024-09-30T12:01:05.520Z","updated_at":"2025-04-06T04:09:02.338Z","avatar_url":"https://github.com/jersou.png","language":"TypeScript","funding_links":[],"categories":["Tools"],"sub_categories":["XML"],"readme":"# Studio-Pack-Generator (SPG)\n\nThis project convert a folder or a RSS URL to\n[Studio](https://github.com/marian-m12l/studio) pack zip for Lunii device, see\nfile structure below.\n\nSupported OS: Windows / Linux / macOS\n\n**[⭐ Une grosse communauté est présente sur Discord pour créer et partager des pack Lunii ! ⭐](https://discord.com/invite/jg9MjHBWQC)**\n\n**[⭐ A big french community is present on Discord to create and share Lunii packs ⭐](https://discord.com/invite/jg9MjHBWQC)**\n\n**🔊💬🎵 Other devices/Apps use the Studio pack format :**\n\n- [Telmi-story-teller](https://github.com/DantSu/Telmi-story-teller) for the\n  Miyoo Mini retro gaming console\n- [Conty](https://github.com/Akylas/conty) -\n  [android app on the PlayStore](https://play.google.com/store/apps/details?id=com.akylas.conty)\n- [Boîte à histoires - android app apk](https://github.com/Cyri1/bah)\n- [Nimilou](https://github.com/octera/Nimilou) -\n  [android app on the PlayStore](https://play.google.com/store/apps/details?id=info.octera.droidstorybox)\n- [Grigri - the open storyteller](https://github.com/olup/grigri)\n- [open-story-teller - Open source hardware/software](https://github.com/arabine/open-story-teller)\n\n## Quick start\n\n```shell\nstudio-pack-generator \"my story folder OR a RSS URL\"\n```\n\nwill generate \"my story folder-xxxxxxxxxx.zip\" that can be imported in\n[Studio](https://github.com/marian-m12l/studio)\n\nExamples:\n\n- `studio-pack-generator http://radiofrance-podcast.net/podcast09/rss_19721.xml`\n- `studio-pack-generator \"Musics\"`\n- `studio-pack-generator \"Encore une histoire\"`\n\n## Optional dependencies\n\n**[Windows release](https://github.com/jersou/studio-pack-generator/releases) of\nstudio-pack-generator embeds these tools in zip file, and use Windows TTS\ninstead of picoTTS (unless you have WSL and picoTTS installed).**\n\n- **ffmpeg** : used to extract images from story mp3 files, increase volume of\n  files, convert to the right format.\n  \u003cbr\u003e→ Use `--skip-audio-convert` and `--skip-extract-image-from-mp3` to avoid\n  this usage.\n- **imagemagick** : used to generate menu image files.\n  \u003cbr\u003e→ Use `--skip-image-item-gen` to avoid this usage.\n- **picoTTS** : used to generate menu audio files.\n  \u003cbr\u003e→ Use `--skip-audio-item-gen` to avoid this usage.\n\nInstall optional dependencies :\n`sudo apt update \u0026\u0026 sudo apt install -y ffmpeg libttspico-utils imagemagick`\n\nUse \"-miva\" option to skip all generations that use these tools.\n\n## Install studio-pack-generator\n\n### Install binary from [release page](https://github.com/jersou/studio-pack-generator/releases) and run it :\n\n```\nstudio-pack-generator-x86_64-linux            \"my story folder or a rss url\"\nor  studio-pack-generator-x86_64-windows.exe  \"my story folder or a rss url\"\nor  studio-pack-generator-aarch64-apple       \"my story folder or a rss url\"\nor  studio-pack-generator-x86_64-apple        \"my story folder or a rss url\"\n```\n\n#### Or clone the repo and run with [Deno](https://deno.land/) :\n\nThis project is written in Typescript for [deno](https://deno.land/) runtime.\nInstall deno : https://deno.land/\n\n```\ngit clone https://github.com/jersou/studio-pack-generator\ncd studio-pack-generator\ndeno -A studio_pack_generator.ts \"my story folder or a rss url\"\n```\n\n#### Or run from web directly (will be cached for the next launches) :\n\n```\ndeno -A jsr:@jersou/studio-pack-generator \"my story folder or a rss url\"\n```\n\n## Story folder structure\n\nSimplest example, only 1 menu level, without audio/image of menus/items :\n\n```shell\n📂 Story folder\n└── 📂 Choose a story         ← 📂 first menu\n    ├── 🎵 the story 1.mp3      ← 📗 audio story\n    ├── 🎵 the story 2.mp3      ← 📗 audio story\n    └── 🎵 the story 3.mp3      ← 📗 audio story\n```\n\nSimple example, 2 levels of menus, without audio/image of menus/items :\n\n```shell\n📂 Story folder\n└── 📂 Choose a character          ← 📂 first menu\n    ├── 📂 Alice                     ← 📂 first choice of the first menu\n    │   └── 📂 Choose a place          ← 📂 second menu\n    │       ├── 🎵 the city.mp3          ← 📗 audio story\n    │       └── 🎵 the jungle.mp3        ← 📗 audio story\n    └── 📂 Bob                       ← 📂 second choice of the first menu\n        └── 📂 Choose a place          ← 📂 second menu\n            ├── 🎵 the desert.mp3        ← 📗 audio story\n            └── 🎵 the jungle.mp3        ← 📗 audio story\n```\n\nstudio-pack-generator will generate menu files, they could be manually\noverwritten, and the next studio-pack-generator run will not regenerate these\nfiles :\n\n```shell\n📂 Story folder\n├── 🎵 0-item.mp3                     ← ⏩ story audio title, generated if missing\n├── 🔳 0-item.png                     ← ⏩ story image title, generated if missing\n├── 🔳 0-night-mode.mp3               ← ⏩ story audio night mode transition, generated if missing and if the mode is enable\n└── 📂 Choose a character             ← 📂 first menu\n    ├── 🎵 0-item.mp3                   ← ⏩ audio menu, generated if missing\n    ├── 📂 Alice                        ← 📂 first choice of the first menu\n    │   ├── 🎵 0-item.mp3                 ← ⏩ audio choice, generated if missing\n    │   ├── 🔳 0-item.png                 ← ⏩ image choice, generated if missing\n    │   └── 📂 Choose a place             ← 📂 second menu\n    │       ├── 🎵 0-item.mp3               ← ⏩ audio menu, generated if missing\n    │       ├── 🔳 0-item.png               ← ⏩ audio menu, generated if missing\n    │       ├── 🎵 the city.item.mp3        ← ⏩ audio story title, generated if missing\n    │       ├── 🔳 the city.item.png        ← ⏩ image story title, generated if missing\n    │       ├── 🎵 the city.mp3             ← 📗 audio story\n    │       ├── 🎵 the jungle.item.mp3      ← ⏩ audio story title, generated if missing\n    │       ├── 🔳 the jungle.item.png      ← ⏩ image story title, generated if missing\n    │       └── 🎵 the jungle.mp3           ← 📗 audio story\n    └── 📂 Bob                          ← 📂 second choice of the first menu\n        ├── 🎵 0-item.mp3                 ← ⏩ audio choice, generated if missing\n        ├── 🔳 0-item.png                 ← ⏩ image choice, generated if missing\n        └── 📂 Choose a place                ← 📂 second menu\n            ├── 🔳 0-item.mp3               ← ⏩ audio menu, generated if missing\n            ├── 🔳 0-item.png               ← ⏩ audio menu, generated if missing\n            ├── 🎵 the desert.item.mp3      ← ⏩ audio story title, generated if missing\n            ├── 🔳 the desert.item.png      ← ⏩ image story title, generated if missing\n            ├── 🎵 the desert.mp3           ← 📗 audio story\n            ├── 🎵 the jungle.item.mp3      ← ⏩ audio story title, generated if missing\n            ├── 🔳 the jungle.item.png      ← ⏩ image story title, generated if missing\n            └── 🎵 the jungle.mp3           ← 📗 audio story\n```\n\nThere is no limit to the nesting of menus, for example :\n\n```shell\n📂 Story folder\n└── 📂 Choose a character                 ← 📂 first menu\n    ├── 📂 Alice                            ← 📂 first choice of the first menu\n    │   └── 📂 Choose a place                ← 📂 second menu\n    │       └── 📂 Building                    ← 📂 second choice of the first menu\n    │       │   └── 📂 Choose the floor          ← 📂 third menu\n    │       │       ├── 🎵 the floor 1.mp3         ← 📗 audio story\n    │       │       └── 🎵 the floor 2.mp3         ← 📗 audio story\n    │       ├── 🎵 the city.mp3                ← 📗 audio story : mix menus/stories is possible\n    │       └── 🎵 the jungle.mp3              ← 📗 audio story : mix menus/stories is possible\n    ├── 🎵 Bob.mp3                         ← 📗 audio story : mix menus/stories is possible\n    ...\n```\n\n### Zip Pack aggregation\n\n_Since v0.1.11._\n\nstudio-pack-generator can embed zip studio packs in the tree structure :\n\n```shell\n📂 Story folder\n└── 📂 Choose a character   ← 📂 first menu\n    ├── 📦 Alice.zip           ← 📦 pack as menu entry\n    ├── 🎵 Bob.mp3             ← 📗 audio story\n    ...\n```\n\nThe \"super pack\" will look like :\n\n```shell\n📂 Story folder\n└── 📂 Choose a character                 ← 📂 first menu\n    ├── 📂 Alice                            ← 📂 The Alice.zip pack\n    │   └── 📂 Choose a place                ← 📂 second menu\n    │       └── 📂 Building                    ← 📂 second choice of the first menu\n    │       │   └── 📂 Choose the floor          ← 📂 third menu\n    │       │       ├── 🎵 the floor 1.mp3         ← 📗 audio story\n    │       │       └── 🎵 the floor 2.mp3         ← 📗 audio story\n    │       ├── 🎵 the city.mp3                ← 📗 audio story\n    │       └── 🎵 the jungle.mp3              ← 📗 audio story\n    ├── 🎵 Bob.mp3                         ← 📗 audio story\n    ...\n```\n\n## Tips\n\n- The first digit of file/folder name are ignored, it's useful to sort\n  stories/menus.\n- To keep numbers in generated items : \"- 3 petits cochons.mp3\" or \"12 - 3\n  petits cochons.mp3\".\n- Image formats : png, jpg, bmp.\n- Audio formats : mp3, ogg, opus, wav.\n\n## GUI\n\nTo run the GUI, use `--gui` : `studio-pack-generator --gui story-path-here`\n\nThe GUI does not work in RSS mode. This mode serve a web app on\nhttp://localhost:5555/\n\n![gui.png](gui.png)\n\n## CLI usage\n\n```\nUsage: studio-pack-generator [options] [--] \u003cstory path | RSS URL\u003e   convert a folder or RSS url to Studio pack\n\nOptions:\n -h, --help                         Show this help                                                  [default: false]\n     --config                       The json config file                                                    [string]\n -d, --add-delay                    add 1 second at the beginning and the end of audio files        [default: false]\n -n, --auto-next-story-transition   go to next story of group at end of stories                     [default: false]\n -b, --select-next-story-at-end     select the next story in the menu at end                        [default: false]\n -l, --lang                         the lang used to generate menu and items. Auto detected by default [default: \"\"]\n -t, --night-mode                   enable night mode : add transitions to an uniq endpoint         [default: false]\n -o, --output-folder                zip output folder                                                       [string]\n -c, --seek-story                   cut the beginning of stories: 'HH:mm:ss' format or 'N' sec              [string]\n -v, --skip-audio-convert           skip convert audio (and skip increase volume)                   [default: false]\n -j, --skip-image-convert           skip image convert                                              [default: false]\n -a, --skip-audio-item-gen          skip audio item generation                                      [default: false]\n -m, --skip-extract-image-from-mp-3 skip extract item image from story mp3                          [default: false]\n -i, --skip-image-item-gen          skip image item generation                                      [default: false]\n     --image-item-gen-font          font used for image item generation                           [default: \"Arial\"]\n     --thumbnail-from-first-item    gen thumbnail from first item instead of first chapter          [default: false]\n -s, --skip-not-rss                 skip all except download RSS files                              [default: false]\n     --rss-split-length             RSS will be split in parts of N length                             [default: 10]\n     --rss-split-seasons            RSS create different packs per season                           [default: false]\n     --rss-episode-numbers          add RSS episode number to stages                                [default: false]\n     --rss-min-duration             RSS min episode duration                                            [default: 0]\n     --rss-use-subtitle-as-title    Use rss items subtitle as title                                 [default: false]\n     --rss-use-image-as-thumbnail   Use rss image (first item with image) as thumbnail              [default: false]\n     --use-thumbnail-as-root-image  Use thumbnail as 'root' image instead of generated one          [default: false]\n -r, --skip-rss-image-dl            skip RSS image download of items                                [default: false]\n -w, --skip-wsl                     disable WSL usage                                               [default: false]\n -z, --skip-zip-generation          only process item generation, don't create zip                  [default: false]\n -e, --use-open-ai-tts              generate missing audio item with Open AI TTS                    [default: false]\n -k, --open-ai-api-key              the OpenAI API key                                                      [string]\n -g, --open-ai-model                OpenAi model : tts-1, tts-1-hd                                [default: \"tts-1\"]\n -p, --open-ai-voice                OpenAi voice : alloy, echo, fable, onyx, nova, shimmer         [default: \"onyx\"]\n     --use-coqui-tts                use coqui TTS                                                   [default: false]\n     --coqui-tts-use-cuda           enable CUDA in coqui TTS                                        [default: false]\n     --coqui-tts-model              coqui TTS model       [default: \"tts_models/multilingual/multi-dataset/xtts_v2\"]\n     --coqui-tts-language-idx       coqui TTS language_idx                                           [default: \"fr\"]\n     --coqui-tts-speaker-idx        coqui TTS speaker_idx                                  [default: \"Abrahan Mack\"]\n -x, --extract                      extract a zip pack (reverse mode)                               [default: false]\n     --extract-disable-night-mode   disable night mode in extract mode                              [default: false]\n -u, --gui                          open GUI (on localhost:5555)                                    [default: false]\n     --port                         port of GUI server                                               [default: 5555]\n     --skip-read-tts-cache          disable the TTS cache usage                                     [default: false]\n     --skip-write-tts-cache         disable the TTS cache write                                     [default: false]\n     --tts-cache-path               path to the TTS cache    [default: \"\u003chome dir\u003e/.Studio-Pack-Generator/TTS-cache\"]\n     --custom-script                custom script to be used for custom image... handling                   [string]\n     --metadata                     Metadata of the pack                                                    [object]\n     --i-18-n                       Custom i18n                                                             [object]\n```\n\nSeparate options by spaces, ex :\n\n- short version : `studio_pack_generator -v -j -a \"the story\"` or\n  `studio_pack_generator -vja \"the story\"`\n- long version :\n  `studio_pack_generator --skip-audio-convert --skip-image-convert --skip-audio-item-gen \"the story\"`\n\n## Features\n\n- Generate studio pack from file tree.\n- Generate menu image/audio file if missing.\n- Extract image from mp3 file as story image in menu.\n- Increase audio volume of stories if needed.\n- Download podcast from a RSS url and generate the story tree, cut by parts of\n  10 stories.\n- Convert mp3 files to right format (mp3, 44100 Hz, mono).\n- Convert image files to right format (320x240).\n- Generate story thumbnail.\n- Option to chaining the stories.\n- Option enable the night mode.\n- Option to add 1 sec of silence at the beginning and end of sound files.\n- Option to skip the beginning of stories.\n- Zip Pack aggregation\n- OpenAI \u0026 Coqui TTS\n- a GUI\n\n### Overwrite metadata\n\nIf the file `metadata.json` exists in the story folder, it will be used to\noverwrite the `story.json` metadata.\n\nAll key/value are optional, ex:\n\n```json\n{\n  \"title\": \"title - overwrite\",\n  \"description\": \"description - overwrite\",\n  \"format\": \"v1\",\n  \"version\": 1,\n  \"nightMode\": false\n}\n```\n\n## TTS cache\n\nA folder `\u003cstudio-pack-generator install dir\u003e/.spg-TTS-cache/` is used to keep\nthe generated audio files.\n\n## OpenAI TTS\n\nTo use OpenAI TTS, use `--use-open-ai-tts` option, and you must set the API key:\n\n- set OPENAI_API_KEY in the environnement variables\n- or use --open-ai-api-key parameter\n- or enter the key when the program prompt\n\n## Coqui TTS\n\nCoqui is \"a deep learning toolkit for Text-to-Speech\":\nhttps://github.com/idiap/coqui-ai-TTS\n\n[To install coqui](https://github.com/idiap/coqui-ai-TTS?tab=readme-ov-file#installation)\n: `pip install coqui-tts`\n\n## Reverse process : extract pack from zip\n\nExtract a file stucture from zip pack :\n\n```shell\n-x, --extract                      extract a zip pack (reverse mode)                        [boolean] [default: false]\n```\n\nExample :\n\n```shell\nstudio-pack-generator -x 2-full.zip\n```\n\nor\n\n```shell\nstudio-pack-generator -x -o output/dir  2-full.zip\n```\n\nNote: it doesn't work well with \"menu\" nodes and with pack without \"question\"\nstage.\n\n## TTS cache\n\nTo speed up / save CPU\n\n## Custom script to fetch RSS image\n\nUsage : `--custom-script=\u003cpath\u003e`\n\n```\nexport interface CustomModule {\n   fetchRssItemImage?: (item: RssItem, opt: StudioPackGenerator) =\u003e Promise\u003cstring\u003e;\n   fetchRssItemTitle?: (item: RssItem, opt: StudioPackGenerator) =\u003e Promise\u003cstring\u003e;\n}\n```\n\n## json config file\n\nThe parameters can be imported from a json file with :\n\n```\n--config-file=\u003cjson file path\u003e\n```\n\nFile format (all the properties are optionals) :\n\n```json\n{\n  \"addDelay\": false,\n  \"autoNextStoryTransition\": false,\n  \"selectNextStoryAtEnd\": false,\n  \"nightMode\": false,\n  \"skipAudioConvert\": false,\n  \"skipImageConvert\": false,\n  \"skipAudioItemGen\": false,\n  \"skipExtractImageFromMp3\": false,\n  \"skipImageItemGen\": false,\n  \"skipNotRss\": false,\n  \"skipRssImageDl\": false,\n  \"skipWsl\": false,\n  \"skipZipGeneration\": false,\n  \"useOpenAiTts\": false,\n  \"lang\": \"fr-FR\",\n  \"outputFolder\": \"/tmp/\",\n  \"seekStory\": \"1\",\n  \"openAiApiKey\": \"\",\n  \"openAiModel\": \"tts-1\",\n  \"openAiVoice\": \"onyx\",\n  \"gui\": false,\n  \"imageItemGenFont\": \"Arial\",\n  \"thumbnailFromFirstItem\": false,\n  \"rssSplitLength\": 10,\n  \"rssSplitSeasons\": false,\n  \"rssMinDuration\": 0,\n  \"rssUseImageAsThumbnail\": false,\n  \"useThumbnailAsRootImage\": false,\n  \"rssEpisodeNumbers\": false,\n  \"useCoquiTts\": false,\n  \"coquiTtsModel\": \"tts_models/multilingual/multi-dataset/xtts_v2\",\n  \"coquiUseCuda\": false,\n  \"coquiTtsLanguageIdx\": \"fr\",\n  \"coquiTtsSpeakerIdx\": \"Abrahan Mack\",\n  \"port\": 5555,\n  \"skipWriteTtsCache\": false,\n  \"skipReadTtsCache\": false,\n  \"ttsCachePath\": \"/tmp/spg-tts-cache\",\n  \"i18n\": {\n    \"special\": \"Special\",\n    \"season\": \"Season %d\",\n    \"storyQuestion\": \"Choose your story\",\n    \"partQuestion\": \"Choose your part\",\n    \"NightModeTransition\": \"Want to listen to a new story?\"\n  }\n}\n```\n\nAt the end of the generation, a file `0-config.json` will be written in the\nstory folder. It can be use for the next run :\n`studio_pack_generator --config-file=\"\u003cstory path\u003e/0-config.json\" \"\u003cstory path\u003e\"`\n\n## Development\n\nSome dev command are listed in the deno.json file :\n\n- fmt: format the code\n- gen-bin: generate the binaries\n- gen-cov: generate the test coverage\n- check: deno check studio_pack_generator.ts\n- lint: lint the code\n- pre-commit: fmt \u0026\u0026 lint \u0026\u0026 test \u0026\u0026 check\n- start: run studio_pack_generator.ts\n- test: launch tests\n- test-watch: launch tests on file change\n\nUsage : `deno task \u003ccommand\u003e`, ex : `deno task fmt`\n\nNote: some dependencies are vendored in `vendor/` folder, it's to publish on JSR\n(which does not allow http imports).\n\n## Possible improvements\n\n- https://github.com/jersou/studio-pack-generator/issues/19 : end node\n- clean file download option unless -z\n- use https://github.com/rhasspy/piper for TTS\n- use git LFS\n- ...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjersou%2Fstudio-pack-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjersou%2Fstudio-pack-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjersou%2Fstudio-pack-generator/lists"}