From ce1325606d5f9a016e09278713298d5269ecba04 Mon Sep 17 00:00:00 2001 From: Paul Wilde Date: Tue, 25 May 2021 19:24:15 +0100 Subject: [PATCH] added readme --- .gitignore | 4 +- README.md | 22 ++++++ defaults/podcasts.toml | 8 +++ defaults/settings.toml | 2 + go.mod | 2 +- main.go | 2 +- podcatch/podcatch.go | 147 +++++++++++++++++++++++++++++++++++------ settings.toml | 2 - 8 files changed, 163 insertions(+), 26 deletions(-) create mode 100644 README.md create mode 100644 defaults/podcasts.toml create mode 100644 defaults/settings.toml delete mode 100644 settings.toml diff --git a/.gitignore b/.gitignore index b00c912..ab68c9c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ -podcasts.toml -db/* -podcatch-bin +podCatch diff --git a/README.md b/README.md new file mode 100644 index 0000000..082164a --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# PodCatch - a simple Podcast downloader + +PodCatch is a _very_ simple Podcast downloader written in GoLand built to help me download podcasts +and store them in my media directory so [Jellyfin](https://jellyfin.org/) can +index them and thus can be listened to using Jellyfin's various apps. +Due to how PodCatch is configurable, I'm sure it would have other uses too. + +### Why PodCatch over other podcast downloaders? +I tried a few other podcast downloaders and largely they were fine, in fact +PodCatch definitely borrows some nice ideas from other podcast downloaders. My +personal issue with the others is that the filename they download as is not always +"friendly"; that is to say, if there's no ID3 data containing the podcast title, +trying to organise a list of podcasts with a _GUID_.mp3 filename in Jellyfin is not easy. +To tackle this issue, PodCatch looks up the title of the Podcast from its RSS feed, +strips it of invalid characters, and uses that name as the file name. This allows for a +much easier task when organising podcasts. + +### Install +Very simple install: +```git clone this repo +cd podcatch +go build -o podcatch-bin diff --git a/defaults/podcasts.toml b/defaults/podcasts.toml new file mode 100644 index 0000000..11f3cda --- /dev/null +++ b/defaults/podcasts.toml @@ -0,0 +1,8 @@ + +[HelloInternet] + Name = "Hello Internet" + URL = "http://www.hellointernet.fm/podcast?format=rss" + +[NSTAAF] + Name = "No Such Thing as a Fish" + URL = "https://audioboom.com/channels/2399216.rss" diff --git a/defaults/settings.toml b/defaults/settings.toml new file mode 100644 index 0000000..1be7b30 --- /dev/null +++ b/defaults/settings.toml @@ -0,0 +1,2 @@ +Directory = "~/podcasts/" +Limit = 10 diff --git a/go.mod b/go.mod index e9af507..63ec5b8 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module podcatch +module podCatch go 1.16 diff --git a/main.go b/main.go index 3ba2fc5..f5bbafe 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,6 @@ package main import ( - "podcatch/podcatch" + "podCatch/podcatch" ) func main(){ podcatch.Start() diff --git a/podcatch/podcatch.go b/podcatch/podcatch.go index 5aecabc..26c0097 100644 --- a/podcatch/podcatch.go +++ b/podcatch/podcatch.go @@ -1,9 +1,10 @@ - package podcatch +package podcatch import ( "fmt" - . "podcatch/structs" + . "podCatch/structs" "github.com/pelletier/go-toml" "encoding/xml" + "io" "io/ioutil" "net/http" "regexp" @@ -15,13 +16,40 @@ var Version string = "0.1" var Settings Settings var Podcasts map[string]Podcast = make(map[string]Podcast) var donefile string +var podcatchdir string +var homedir string +var dbdir string func Start(){ fmt.Printf("Starting PodCatch Version : %s...\r\n", Version ) + getHomeDirs() getSettings() getPodcasts() } +func getHomeDirs(){ + h, err := os.UserHomeDir() + if err != nil { + log.Fatal( err ) + } + homedir = h + podcatchdir = h + "/.podcatch/" +} func getSettings(){ - content, err := ioutil.ReadFile("settings.toml") + settings := podcatchdir + "settings.toml" + if !checkFileExists(settings){ + pwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + fmt.Println("Copying default settings.toml to user dir.") + ok, err := copyFile(pwd + "/defaults/settings.toml",settings) + if err != nil { + log.Fatal(err) + } + if ok > 0 { + fmt.Println("Copied.") + } + } + content, err := ioutil.ReadFile(settings) if err != nil { log.Fatal(err) } @@ -29,6 +57,10 @@ func getSettings(){ if e != nil { log.Fatal(err) } + Settings.Directory = strings.Replace(Settings.Directory,"~",homedir,1) + dbdir = Settings.Directory + ".db/" + os.Mkdir(dbdir,0755) + } func getPodcasts(){ if len(Podcasts) == 0 { @@ -42,7 +74,22 @@ func getPodcasts(){ } } func getPodcastFiles() { - content, err := ioutil.ReadFile("podcasts.toml") + pcs := podcatchdir + "podcasts.toml" + if !checkFileExists(pcs){ + pwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + fmt.Println("Copying default podcasts.toml to user dir.") + ok, err := copyFile(pwd + "/defaults/podcasts.toml",pcs) + if err != nil { + log.Fatal(err) + } + if ok > 0 { + fmt.Println("Copied.") + } + } + content, err := ioutil.ReadFile(podcatchdir + "podcasts.toml") if err != nil { log.Fatal(err) } @@ -101,18 +148,21 @@ func downloadCasts(podcast Podcast) { } } func podcastDownloaded(item Item) bool { - if len(donefile) < 1 { - content, err := ioutil.ReadFile(".db/complete") - if err != nil { - log.Fatal(err) + db := dbdir + "complete" + if checkCreate(db) { + if len(donefile) < 1 { + content, err := ioutil.ReadFile(db) + if err != nil { + log.Fatal(err) + } + donefile = string(content) + } + if strings.Contains(donefile,item.Title){ + return true + } + if strings.Contains(donefile,item.Media.URL){ + return true } - donefile = string(content) - } - if strings.Contains(donefile,item.Title){ - return true - } - if strings.Contains(donefile,item.Media.URL){ - return true } return false } @@ -150,8 +200,9 @@ func createNFO(item Item, file string) { } } func markAsReceived(item Item) { - os.Mkdir(".db", 0777) - file, err := os.OpenFile(".db/complete", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) + db := dbdir + "complete" + checkCreate(db) + file, err := os.OpenFile(db, os.O_APPEND|os.O_WRONLY, 0755) if err != nil { log.Println(err) } @@ -162,8 +213,9 @@ func markAsReceived(item Item) { } } func markAsErrored(item Item) { - os.Mkdir(".db", 0777) - file, err := os.OpenFile(".db/error", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) + db := dbdir + "error" + checkCreate(db) + file, err := os.OpenFile(db, os.O_APPEND|os.O_WRONLY, 0755) if err != nil { log.Println(err) } @@ -173,3 +225,60 @@ func markAsErrored(item Item) { log.Fatal(err) } } +func checkFileExists(file string) bool { + if _, err := os.Stat(file); err == nil { + // fmt.Println("Exists") + // exists + return true + } else if os.IsNotExist(err) { + // fmt.Println("Not Exists") + // not exists + return false + } else { + // fmt.Println("Maybe Exists, Maybe Not") + return false + // Schrodinger: file may or may not exist. See err for details. + // Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence + } + return false +} +func checkCreate(file string) bool { + if checkFileExists(file) { + return true + } else { + if createFile(file) { + return true + } + } + return false +} +func createFile(file string) bool { + f, err := os.Create(file) + if err != nil { + log.Fatal(err) + return false + } + defer f.Close() + return true +} +func copyFile(src, dst string) (int64, error) { + sourceFileStat, err := os.Stat(src) + if err != nil { + return 0, err + } + if !sourceFileStat.Mode().IsRegular() { + return 0, fmt.Errorf("%s is not a regular file", src) + } + source, err := os.Open(src) + if err != nil { + return 0, err + } + defer source.Close() + destination, err := os.Create(dst) + if err != nil { + return 0, err + } + defer destination.Close() + nBytes, err := io.Copy(destination, source) + return nBytes, err +} diff --git a/settings.toml b/settings.toml deleted file mode 100644 index b3ee729..0000000 --- a/settings.toml +++ /dev/null @@ -1,2 +0,0 @@ -Directory = "/home/psw/podcasts/" -Limit = 50