From dccee845f0e77cb9aa260ad919d637b27213faae Mon Sep 17 00:00:00 2001 From: fbngrmr Date: Thu, 9 Feb 2023 21:40:58 +0100 Subject: [PATCH 1/3] Improve description of download command --- cmd/download.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/download.go b/cmd/download.go index a13e07f..e9154e1 100644 --- a/cmd/download.go +++ b/cmd/download.go @@ -33,8 +33,8 @@ func init() { var downloadCmd = &cobra.Command{ Use: "download [URL] [targetDirectory]", - Short: "Download all episodes of a given program", - Long: "Download all episodes of a given program to the given target directory. Limited to 100 episodes.", + Short: "Download all episodes of a program/collection or an individual episode in the ARD Audiothek.", + Long: "Download all episodes of a program/collection or an individual episode in the ARD Audiothek. Limited to 100 episodes.", Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { run(args[0], args[1]) From 8415bffc6698c1e073777c44e2c18c8fc49c5aa5 Mon Sep 17 00:00:00 2001 From: fbngrmr Date: Thu, 9 Feb 2023 22:05:34 +0100 Subject: [PATCH 2/3] Include program name and episode title in file name --- cmd/download.go | 77 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/cmd/download.go b/cmd/download.go index e9154e1..6093027 100644 --- a/cmd/download.go +++ b/cmd/download.go @@ -41,9 +41,21 @@ var downloadCmd = &cobra.Command{ }, } -func downloadFile(url string, targetDirectory string) (err error) { - fileName := path.Base(url) - filePath := filepath.Join(targetDirectory, fileName) +type File struct { + url string + fileName string +} + +var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9 äÄöÖüÜ\-\_]+`) + +func toFileName(episodeTitle string, programTitle string) string { + value := programTitle + "_-_" + episodeTitle + return strings.Replace(nonAlphanumericRegex.ReplaceAllString(strings.Replace(value, "/", "_", -1), ""), " ", "_", -1) +} + +func downloadFile(file File, targetDirectory string) (err error) { + fileExtension := filepath.Ext(path.Base(file.url)) + filePath := filepath.Join(targetDirectory, file.fileName+fileExtension) out, createFileErr := os.Create(filePath) if createFileErr != nil { @@ -51,7 +63,7 @@ func downloadFile(url string, targetDirectory string) (err error) { } defer out.Close() - httpResponse, httpErr := http.Get(url) + httpResponse, httpErr := http.Get(file.url) if httpErr != nil { return httpErr } @@ -69,18 +81,18 @@ func downloadFile(url string, targetDirectory string) (err error) { return nil } -func extractDownloadUrls(response *ItemsResponse) []string { - var urls []string +func extractDownloadUrls(response *ItemsResponse) []File { + var files []File for _, nodes := range response.Result.Items.Nodes { for _, audios := range nodes.Audios { if audios.DownloadUrl != "" { - urls = append(urls, audios.DownloadUrl) + files = append(files, File{url: audios.DownloadUrl, fileName: toFileName(audios.Title, nodes.ProgramSet.Title)}) } } } - return urls + return files } func extractQueryId(url string) (string, QueryType) { @@ -117,7 +129,11 @@ type ItemsResponse struct { Result struct { Items struct { Nodes []struct { + ProgramSet struct { + Title string + } Audios []struct { + Title string DownloadUrl string } } @@ -140,7 +156,7 @@ func sendGraphQlQuery(query string, variables map[string]interface{}, response i return nil } -func getProgramUrls(queryId string) ([]string, error) { +func getProgramUrls(queryId string) ([]File, error) { query := `query ProgramSetEpisodesQuery($id: ID!, $offset: Int!, $count: Int!) { result: programSet(id: $id) { items( @@ -150,7 +166,11 @@ func getProgramUrls(queryId string) ([]string, error) { filter: { isPublished: { equalTo: true } } ) { nodes { + programSet { + title + } audios { + title downloadUrl } } @@ -175,16 +195,18 @@ func getProgramUrls(queryId string) ([]string, error) { return urls, nil } -func getCollectionUrls(queryId string) ([]string, error) { +func getCollectionUrls(queryId string) ([]File, error) { query := `query EpisodesQuery($id: ID!, $offset: Int!, $limit: Int!) { result: editorialCollection(id: $id, offset: $offset, limit: $limit) { items { nodes { id + programSet { + title + } audios { - url + title downloadUrl - allowDownload } } } @@ -203,23 +225,31 @@ func getCollectionUrls(queryId string) ([]string, error) { return nil, graphQlError } - urls := extractDownloadUrls(&response) + files := extractDownloadUrls(&response) - return urls, nil + return files, nil } type ItemResponse struct { Result struct { + ProgramSet struct { + Title string + } Audios []struct { + Title string DownloadUrl *string } } } -func getEpisodeUrls(queryId string) ([]string, error) { +func getEpisodeUrls(queryId string) ([]File, error) { query := `query EpisodeQuery($id: ID!) { result: item(id: $id) { + programSet { + title + } audios { + title downloadUrl } } @@ -235,19 +265,20 @@ func getEpisodeUrls(queryId string) ([]string, error) { return nil, graphQlError } - var urls []string + var files []File for _, audios := range response.Result.Audios { if audios.DownloadUrl != nil { if *audios.DownloadUrl != "" { - urls = append(urls, *audios.DownloadUrl) + file := File{url: *audios.DownloadUrl, fileName: toFileName(audios.Title, response.Result.ProgramSet.Title)} + files = append(files, file) } } } - return urls, nil + return files, nil } -func getDownloadUrls(url string) ([]string, error) { +func getDownloadUrls(url string) ([]File, error) { queryId, queryType := extractQueryId(url) switch queryType { @@ -266,17 +297,17 @@ func getDownloadUrls(url string) ([]string, error) { } func run(url string, targetDirectory string) { - urls, err := getDownloadUrls(url) + files, err := getDownloadUrls(url) if err != nil { panic(err) } - for _, url := range urls { - downloadErr := downloadFile(url, targetDirectory) + for _, file := range files { + downloadErr := downloadFile(file, targetDirectory) if downloadErr != nil { - fmt.Printf("Downloading file %s failed with error: %v\n", url, downloadErr) + fmt.Printf("Downloading file %s failed with error: %v\n", file.url, downloadErr) } } } From bdc2c54f972a03600cd7c8ef5e0e6b0f4c6af06a Mon Sep 17 00:00:00 2001 From: fbngrmr Date: Sun, 31 Mar 2024 19:39:20 +0200 Subject: [PATCH 3/3] Code-Style --- cmd/download.go | 76 +++++++++++++++++++++++-------------------------- cmd/root.go | 4 +++ main.go | 2 ++ 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/cmd/download.go b/cmd/download.go index 6093027..90ffc06 100644 --- a/cmd/download.go +++ b/cmd/download.go @@ -16,7 +16,7 @@ import ( "github.com/spf13/cobra" ) -var GRAPHQL_ENDPOINT = "https://api.ardaudiothek.de/graphql" +var graphqlEndPoint = "https://api.ardaudiothek.de/graphql" type QueryType int64 @@ -27,20 +27,6 @@ const ( Unknown ) -func init() { - rootCmd.AddCommand(downloadCmd) -} - -var downloadCmd = &cobra.Command{ - Use: "download [URL] [targetDirectory]", - Short: "Download all episodes of a program/collection or an individual episode in the ARD Audiothek.", - Long: "Download all episodes of a program/collection or an individual episode in the ARD Audiothek. Limited to 100 episodes.", - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - run(args[0], args[1]) - }, -} - type File struct { url string fileName string @@ -141,21 +127,6 @@ type ItemsResponse struct { } } -func sendGraphQlQuery(query string, variables map[string]interface{}, response interface{}) error { - client := graphql.NewClient(GRAPHQL_ENDPOINT, nil) - - rawGraphqlResponse, graphQlErr := client.ExecRaw(context.Background(), query, variables) - if graphQlErr != nil { - return graphQlErr - } - - if jsonError := json.Unmarshal(rawGraphqlResponse, &response); jsonError != nil { - return jsonError - } - - return nil -} - func getProgramUrls(queryId string) ([]File, error) { query := `query ProgramSetEpisodesQuery($id: ID!, $offset: Int!, $count: Int!) { result: programSet(id: $id) { @@ -230,19 +201,19 @@ func getCollectionUrls(queryId string) ([]File, error) { return files, nil } -type ItemResponse struct { - Result struct { - ProgramSet struct { - Title string - } - Audios []struct { - Title string - DownloadUrl *string +func getEpisodeUrls(queryId string) ([]File, error) { + type ItemResponse struct { + Result struct { + ProgramSet struct { + Title string + } + Audios []struct { + Title string + DownloadUrl *string + } } } -} -func getEpisodeUrls(queryId string) ([]File, error) { query := `query EpisodeQuery($id: ID!) { result: item(id: $id) { programSet { @@ -278,6 +249,21 @@ func getEpisodeUrls(queryId string) ([]File, error) { return files, nil } +func sendGraphQlQuery(query string, variables map[string]interface{}, response interface{}) error { + client := graphql.NewClient(graphqlEndPoint, nil) + + rawGraphqlResponse, graphQlErr := client.ExecRaw(context.Background(), query, variables) + if graphQlErr != nil { + return graphQlErr + } + + if jsonError := json.Unmarshal(rawGraphqlResponse, &response); jsonError != nil { + return jsonError + } + + return nil +} + func getDownloadUrls(url string) ([]File, error) { queryId, queryType := extractQueryId(url) @@ -296,6 +282,16 @@ func getDownloadUrls(url string) ([]File, error) { } } +var DownloadCmd = &cobra.Command{ + Use: "download [URL] [targetDirectory]", + Short: "Download all episodes of a program/collection or an individual episode in the ARD Audiothek.", + Long: "Download all episodes of a program/collection or an individual episode in the ARD Audiothek. Limited to 100 episodes.", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + run(args[0], args[1]) + }, +} + func run(url string, targetDirectory string) { files, err := getDownloadUrls(url) diff --git a/cmd/root.go b/cmd/root.go index 51051c3..a0d4802 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,6 +4,10 @@ import ( "github.com/spf13/cobra" ) +func SetupCli() { + rootCmd.AddCommand(DownloadCmd) +} + var rootCmd = &cobra.Command{ Use: "audiotheker", Short: "`audiotheker allows downloading all episodes of a program in the ARD Audiothek.", diff --git a/main.go b/main.go index e1a80a4..cd8fc18 100644 --- a/main.go +++ b/main.go @@ -5,5 +5,7 @@ import ( ) func main() { + cmd.SetupCli() + cmd.Execute() }