added readme

This commit is contained in:
Paul Wilde 2021-05-25 19:24:15 +01:00
parent 33bea9b20e
commit ce1325606d
8 changed files with 163 additions and 26 deletions

4
.gitignore vendored
View file

@ -1,3 +1 @@
podcasts.toml podCatch
db/*
podcatch-bin

22
README.md Normal file
View file

@ -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

8
defaults/podcasts.toml Normal file
View file

@ -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"

2
defaults/settings.toml Normal file
View file

@ -0,0 +1,2 @@
Directory = "~/podcasts/"
Limit = 10

2
go.mod
View file

@ -1,4 +1,4 @@
module podcatch module podCatch
go 1.16 go 1.16

View file

@ -1,6 +1,6 @@
package main package main
import ( import (
"podcatch/podcatch" "podCatch/podcatch"
) )
func main(){ func main(){
podcatch.Start() podcatch.Start()

View file

@ -1,9 +1,10 @@
package podcatch package podcatch
import ( import (
"fmt" "fmt"
. "podcatch/structs" . "podCatch/structs"
"github.com/pelletier/go-toml" "github.com/pelletier/go-toml"
"encoding/xml" "encoding/xml"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"regexp" "regexp"
@ -15,13 +16,40 @@ var Version string = "0.1"
var Settings Settings var Settings Settings
var Podcasts map[string]Podcast = make(map[string]Podcast) var Podcasts map[string]Podcast = make(map[string]Podcast)
var donefile string var donefile string
var podcatchdir string
var homedir string
var dbdir string
func Start(){ func Start(){
fmt.Printf("Starting PodCatch Version : %s...\r\n", Version ) fmt.Printf("Starting PodCatch Version : %s...\r\n", Version )
getHomeDirs()
getSettings() getSettings()
getPodcasts() getPodcasts()
} }
func getHomeDirs(){
h, err := os.UserHomeDir()
if err != nil {
log.Fatal( err )
}
homedir = h
podcatchdir = h + "/.podcatch/"
}
func getSettings(){ 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 { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -29,6 +57,10 @@ func getSettings(){
if e != nil { if e != nil {
log.Fatal(err) log.Fatal(err)
} }
Settings.Directory = strings.Replace(Settings.Directory,"~",homedir,1)
dbdir = Settings.Directory + ".db/"
os.Mkdir(dbdir,0755)
} }
func getPodcasts(){ func getPodcasts(){
if len(Podcasts) == 0 { if len(Podcasts) == 0 {
@ -42,7 +74,22 @@ func getPodcasts(){
} }
} }
func getPodcastFiles() { 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 { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -101,8 +148,10 @@ func downloadCasts(podcast Podcast) {
} }
} }
func podcastDownloaded(item Item) bool { func podcastDownloaded(item Item) bool {
db := dbdir + "complete"
if checkCreate(db) {
if len(donefile) < 1 { if len(donefile) < 1 {
content, err := ioutil.ReadFile(".db/complete") content, err := ioutil.ReadFile(db)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -114,6 +163,7 @@ func podcastDownloaded(item Item) bool {
if strings.Contains(donefile,item.Media.URL){ if strings.Contains(donefile,item.Media.URL){
return true return true
} }
}
return false return false
} }
func downloadMp3(url string, file string) bool { func downloadMp3(url string, file string) bool {
@ -150,8 +200,9 @@ func createNFO(item Item, file string) {
} }
} }
func markAsReceived(item Item) { func markAsReceived(item Item) {
os.Mkdir(".db", 0777) db := dbdir + "complete"
file, err := os.OpenFile(".db/complete", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) checkCreate(db)
file, err := os.OpenFile(db, os.O_APPEND|os.O_WRONLY, 0755)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
@ -162,8 +213,9 @@ func markAsReceived(item Item) {
} }
} }
func markAsErrored(item Item) { func markAsErrored(item Item) {
os.Mkdir(".db", 0777) db := dbdir + "error"
file, err := os.OpenFile(".db/error", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755) checkCreate(db)
file, err := os.OpenFile(db, os.O_APPEND|os.O_WRONLY, 0755)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
@ -173,3 +225,60 @@ func markAsErrored(item Item) {
log.Fatal(err) 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
}

View file

@ -1,2 +0,0 @@
Directory = "/home/psw/podcasts/"
Limit = 50