99 "net/url"
1010 "os"
1111 "path/filepath"
12+ "strings"
1213 "syscall"
1314 "time"
1415)
@@ -17,14 +18,27 @@ import (
1718// CONSTANTS
1819
1920const (
20- srcUrl = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main" // The location of the models
21- srcExt = ".bin" // Filename extension
22- bufSize = 1024 * 64 // Size of the buffer used for downloading the model
21+ srcUrl = "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ " // The location of the models
22+ srcExt = ".bin" // Filename extension
23+ bufSize = 1024 * 64 // Size of the buffer used for downloading the model
2324)
2425
2526var (
2627 // The models which will be downloaded, if no model is specified as an argument
27- modelNames = []string {"ggml-tiny.en" , "ggml-tiny" , "ggml-base.en" , "ggml-base" , "ggml-small.en" , "ggml-small" , "ggml-medium.en" , "ggml-medium" , "ggml-large-v1" , "ggml-large-v2" , "ggml-large-v3" , "large-v3-turbo" }
28+ modelNames = []string {
29+ "tiny" , "tiny-q5_1" , "tiny-q8_0" ,
30+ "tiny.en" , "tiny.en-q5_1" , "tiny.en-q8_0" ,
31+ "base" , "base-q5_1" , "base-q8_0" ,
32+ "base.en" , "base.en-q5_1" , "base.en-q8_0" ,
33+ "small" , "small-q5_1" , "small-q8_0" ,
34+ "small.en" , "small.en-q5_1" , "small.en-q8_0" ,
35+ "medium" , "medium-q5_0" , "medium-q8_0" ,
36+ "medium.en" , "medium.en-q5_0" , "medium.en-q8_0" ,
37+ "large-v1" ,
38+ "large-v2" , "large-v2-q5_0" , "large-v2-q8_0" ,
39+ "large-v3" , "large-v3-q5_0" ,
40+ "large-v3-turbo" , "large-v3-turbo-q5_0" , "large-v3-turbo-q8_0" ,
41+ }
2842)
2943
3044var (
4458func main () {
4559 flag .Usage = func () {
4660 name := filepath .Base (flag .CommandLine .Name ())
47- fmt .Fprintf (flag .CommandLine .Output (), "Usage: %s [options] <model>\n \n " , name )
61+ fmt .Fprintf (flag .CommandLine .Output (), `
62+ Usage: %s [options] [<model>...]
63+
64+ Options:
65+ -out string Specify the output folder where models will be saved.
66+ Default: Current working directory.
67+ -timeout duration Set the maximum duration for downloading a model.
68+ Example: 10m, 1h (default: 30m0s).
69+ -quiet Suppress all output except errors.
70+
71+ Examples:
72+ 1. Download a specific model:
73+ %s -out ./models tiny-q8_0
74+
75+ 2. Download all models:
76+ %s -out ./models
77+
78+ ` , name , name , name )
79+
4880 flag .PrintDefaults ()
4981 }
5082 flag .Parse ()
@@ -114,23 +146,87 @@ func GetOut() (string, error) {
114146// GetModels returns the list of models to download
115147func GetModels () []string {
116148 if flag .NArg () == 0 {
117- return modelNames
118- } else {
119- return flag .Args ()
149+ fmt .Println ("No model specified." )
150+ fmt .Println ("Preparing to download all models..." )
151+
152+ // Calculate total download size
153+ fmt .Println ("Calculating total download size..." )
154+ totalSize , err := CalculateTotalDownloadSize (modelNames )
155+ if err != nil {
156+ fmt .Println ("Error calculating download sizes:" , err )
157+ os .Exit (1 )
158+ }
159+
160+ fmt .Println ("View available models: https://huggingface.co/ggerganov/whisper.cpp/tree/main" )
161+ fmt .Printf ("Total download size: %.2f GB\n " , float64 (totalSize )/ (1024 * 1024 * 1024 ))
162+ fmt .Println ("Would you like to download all models? (y/N)" )
163+
164+ // Prompt for user input
165+ var response string
166+ fmt .Scanln (& response )
167+ if response != "y" && response != "Y" {
168+ fmt .Println ("Aborting. Specify a model to download." )
169+ os .Exit (0 )
170+ }
171+
172+ return modelNames // Return all models if confirmed
120173 }
174+ return flag .Args () // Return specific models if arguments are provided
175+ }
176+
177+ func CalculateTotalDownloadSize (models []string ) (int64 , error ) {
178+ var totalSize int64
179+ client := http.Client {}
180+
181+ for _ , model := range models {
182+ modelURL , err := URLForModel (model )
183+ if err != nil {
184+ return 0 , err
185+ }
186+
187+ // Issue a HEAD request to get the file size
188+ req , err := http .NewRequest ("HEAD" , modelURL , nil )
189+ if err != nil {
190+ return 0 , err
191+ }
192+
193+ resp , err := client .Do (req )
194+ if err != nil {
195+ return 0 , err
196+ }
197+ resp .Body .Close ()
198+
199+ if resp .StatusCode != http .StatusOK {
200+ fmt .Printf ("Warning: Unable to fetch size for %s (HTTP %d)\n " , model , resp .StatusCode )
201+ continue
202+ }
203+
204+ size := resp .ContentLength
205+ totalSize += size
206+ }
207+ return totalSize , nil
121208}
122209
123210// URLForModel returns the URL for the given model on huggingface.co
124211func URLForModel (model string ) (string , error ) {
212+ // Ensure "ggml-" prefix is added only once
213+ if ! strings .HasPrefix (model , "ggml-" ) {
214+ model = "ggml-" + model
215+ }
216+
217+ // Ensure ".bin" extension is added only once
125218 if filepath .Ext (model ) != srcExt {
126219 model += srcExt
127220 }
221+
222+ // Parse the base URL
128223 url , err := url .Parse (srcUrl )
129224 if err != nil {
130225 return "" , err
131- } else {
132- url .Path = filepath .Join (url .Path , model )
133226 }
227+
228+ // Ensure no trailing slash in the base URL
229+ url .Path = fmt .Sprintf ("%s/%s" , strings .TrimSuffix (url .Path , "/" ), model )
134230 return url .String (), nil
135231}
136232
0 commit comments