commit
2d8f34ace2
@ -0,0 +1,18 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
*.db
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
assets/*
|
||||
keys/*
|
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PodGrab</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>{{ .title }}</h1>
|
||||
|
||||
<ol>
|
||||
{{range .podcasts}}
|
||||
<li>{{.Title}}
|
||||
</li> {{end}}
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,95 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/akhilrex/podgrab/service"
|
||||
|
||||
"github.com/akhilrex/podgrab/db"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SearchQuery struct {
|
||||
Q string `binding:"required" form:"q"`
|
||||
Type string `form:"type"`
|
||||
}
|
||||
|
||||
type SearchByIdQuery struct {
|
||||
Id string `binding:"required" uri:"id" json:"id" form:"id"`
|
||||
}
|
||||
|
||||
type AddPodcastData struct {
|
||||
url string `binding:"required" form:"url" json:"url"`
|
||||
}
|
||||
|
||||
func GetAllPodcasts(c *gin.Context) {
|
||||
var podcasts []db.Podcast
|
||||
db.GetAllPodcasts(&podcasts)
|
||||
c.JSON(200, podcasts)
|
||||
}
|
||||
|
||||
func GetPodcastById(c *gin.Context) {
|
||||
var searchByIdQuery SearchByIdQuery
|
||||
|
||||
if c.ShouldBindUri(&searchByIdQuery) == nil {
|
||||
|
||||
var podcast db.Podcast
|
||||
|
||||
err := db.GetPodcastById(searchByIdQuery.Id, &podcast)
|
||||
fmt.Println(err)
|
||||
c.JSON(200, podcast)
|
||||
} else {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
|
||||
}
|
||||
}
|
||||
|
||||
func GetPodcastItemsByPodcastId(c *gin.Context) {
|
||||
var searchByIdQuery SearchByIdQuery
|
||||
|
||||
if c.ShouldBindUri(&searchByIdQuery) == nil {
|
||||
|
||||
var podcastItems []db.PodcastItem
|
||||
|
||||
err := db.GetAllPodcastItemsByPodcastId(searchByIdQuery.Id, &podcastItems)
|
||||
fmt.Println(err)
|
||||
c.JSON(200, podcastItems)
|
||||
} else {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
|
||||
}
|
||||
}
|
||||
|
||||
func GetAllPodcastItems(c *gin.Context) {
|
||||
var podcasts []db.PodcastItem
|
||||
db.GetAllPodcastItems(&podcasts)
|
||||
c.JSON(200, podcasts)
|
||||
}
|
||||
|
||||
func GetPodcastItemById(c *gin.Context) {
|
||||
var searchByIdQuery SearchByIdQuery
|
||||
|
||||
if c.ShouldBindUri(&searchByIdQuery) == nil {
|
||||
|
||||
var podcast db.PodcastItem
|
||||
|
||||
err := db.GetPodcastItemById(searchByIdQuery.Id, &podcast)
|
||||
fmt.Println(err)
|
||||
c.JSON(200, podcast)
|
||||
} else {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
|
||||
}
|
||||
}
|
||||
|
||||
func AddPodcast(c *gin.Context) {
|
||||
var addPodcastData AddPodcastData
|
||||
err := c.ShouldBindJSON(&addPodcastData)
|
||||
if err == nil {
|
||||
|
||||
service.AddPodcast(addPodcastData.url)
|
||||
// fmt.Println(time.Unix(addPodcastData.StartDate, 0))
|
||||
c.JSON(200, addPodcastData)
|
||||
} else {
|
||||
fmt.Println(err.Error())
|
||||
c.JSON(http.StatusBadRequest, err)
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
//Base is
|
||||
type Base struct {
|
||||
ID string `sql:"type:uuid;primary_key"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt *time.Time `gorm:"index"`
|
||||
}
|
||||
|
||||
//BeforeCreate
|
||||
func (base *Base) BeforeCreate(tx *gorm.DB) error {
|
||||
tx.Statement.SetColumn("ID", uuid.NewV4().String())
|
||||
return nil
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
//DB is
|
||||
var DB *gorm.DB
|
||||
|
||||
//Init is used to Initialize Database
|
||||
func Init() (*gorm.DB, error) {
|
||||
// github.com/mattn/go-sqlite3
|
||||
db, err := gorm.Open(sqlite.Open("podgrab.db"), &gorm.Config{})
|
||||
if err != nil {
|
||||
fmt.Println("db err: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
localDB, _ := db.DB()
|
||||
localDB.SetMaxIdleConns(10)
|
||||
//db.LogMode(true)
|
||||
DB = db
|
||||
return DB, nil
|
||||
}
|
||||
|
||||
//Migrate Database
|
||||
func Migrate() {
|
||||
DB.AutoMigrate(&Podcast{}, &PodcastItem{})
|
||||
|
||||
}
|
||||
|
||||
// Using this function to get a connection, you can create your connection pool here.
|
||||
func GetDB() *gorm.DB {
|
||||
return DB
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm/clause"
|
||||
)
|
||||
|
||||
func GetPodcastByURL(url string, podcast *Podcast) error {
|
||||
result := DB.Preload(clause.Associations).Where(&Podcast{URL: url}).First(&podcast)
|
||||
return result.Error
|
||||
}
|
||||
func GetAllPodcasts(podcasts *[]Podcast) error {
|
||||
|
||||
result := DB.Preload("PodcastItems").Find(&podcasts)
|
||||
return result.Error
|
||||
}
|
||||
func GetAllPodcastItems(podcasts *[]PodcastItem) error {
|
||||
|
||||
result := DB.Preload("Podcast").Find(&podcasts)
|
||||
return result.Error
|
||||
}
|
||||
func GetPodcastById(id string, podcast *Podcast) error {
|
||||
|
||||
result := DB.Preload(clause.Associations).First(&podcast, "id=?", id)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
func GetPodcastItemById(id string, podcastItem *PodcastItem) error {
|
||||
|
||||
result := DB.Preload(clause.Associations).First(&podcastItem, "id=?", id)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
func GetAllPodcastItemsByPodcastId(podcastId string, podcasts *[]PodcastItem) error {
|
||||
|
||||
result := DB.Preload(clause.Associations).Where(&PodcastItem{PodcastID: podcastId}).Find(&podcasts)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
func GetAllPodcastItemsToBeDownloaded() (*[]PodcastItem, error) {
|
||||
var podcastItems []PodcastItem
|
||||
result := DB.Preload(clause.Associations).Where(&PodcastItem{DownloadDate: time.Time{}}).Find(&podcastItems)
|
||||
return &podcastItems, result.Error
|
||||
}
|
||||
|
||||
func GetPodcastItemByPodcastIdAndGUID(podcastId string, guid string, podcastItem *PodcastItem) error {
|
||||
|
||||
result := DB.Preload(clause.Associations).Where(&PodcastItem{PodcastID: podcastId, GUID: guid}).First(&podcastItem)
|
||||
return result.Error
|
||||
}
|
||||
func GetPodcastByTitleAndAuthor(title string, author string, podcast *Podcast) error {
|
||||
|
||||
result := DB.Preload(clause.Associations).Where(&Podcast{Title: title, Author: author}).First(&podcast)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
func CreatePodcast(podcast *Podcast) error {
|
||||
tx := DB.Create(&podcast)
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
func CreatePodcastItem(podcastItem *PodcastItem) error {
|
||||
tx := DB.Create(&podcastItem)
|
||||
return tx.Error
|
||||
}
|
||||
func UpdatePodcastItem(podcastItem *PodcastItem) error {
|
||||
tx := DB.Save(&podcastItem)
|
||||
return tx.Error
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
//Podcast is
|
||||
type Podcast struct {
|
||||
Base
|
||||
Title string
|
||||
|
||||
Summary string `gorm:"type:text"`
|
||||
|
||||
Author string
|
||||
|
||||
Image string
|
||||
|
||||
URL string
|
||||
|
||||
PodcastItems []PodcastItem
|
||||
}
|
||||
|
||||
//PodcastItem is
|
||||
type PodcastItem struct {
|
||||
Base
|
||||
PodcastID string
|
||||
Podcast Podcast
|
||||
Title string
|
||||
Summary string `gorm:"type:text"`
|
||||
|
||||
EpisodeType string
|
||||
|
||||
Duration int
|
||||
|
||||
PubDate time.Time
|
||||
|
||||
FileURL string
|
||||
|
||||
GUID string
|
||||
|
||||
DownloadDate time.Time
|
||||
DownloadPath string
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
module github.com/akhilrex/podgrab
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/gin-gonic/gin v1.6.3
|
||||
github.com/gobeam/stringy v0.0.0-20200717095810-8a3637503f62
|
||||
github.com/jasonlvhit/gocron v0.0.1
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
||||
gorm.io/driver/sqlite v1.1.3
|
||||
gorm.io/gorm v1.20.2
|
||||
)
|
@ -0,0 +1,102 @@
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgrijalva/jwt-go v1.0.2 h1:KPldsxuKGsS2FPWsNeg9ZO18aCrGKujPoWXn2yo+KQM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||
github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gobeam/stringy v0.0.0-20200717095810-8a3637503f62 h1:5yOhT/D6x5qqfcRnR98bvpGlduR4Shkj8Jat+SalYpQ=
|
||||
github.com/gobeam/stringy v0.0.0-20200717095810-8a3637503f62/go.mod h1:W3620X9dJHf2FSZF5fRnWekHcHQjwmCz8ZQ2d1qloqE=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jasonlvhit/gocron v0.0.1 h1:qTt5qF3b3srDjeOIR4Le1LfeyvoYzJlYpqvG7tJX5YU=
|
||||
github.com/jasonlvhit/gocron v0.0.1/go.mod h1:k9a3TV8VcU73XZxfVHCHWMWF9SOqgoku0/QlY2yvlA4=
|
||||
github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
|
||||
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
|
||||
github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
|
||||
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gorm.io/driver/sqlite v1.1.3 h1:BYfdVuZB5He/u9dt4qDpZqiqDJ6KhPqs5QUqsr/Eeuc=
|
||||
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=
|
||||
gorm.io/gorm v1.20.1/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||
gorm.io/gorm v1.20.2 h1:bZzSEnq7NDGsrd+n3evOOedDrY5oLM5QPlCjZJUK2ro=
|
||||
gorm.io/gorm v1.20.2/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
@ -0,0 +1,80 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/akhilrex/podgrab/controllers"
|
||||
"github.com/akhilrex/podgrab/db"
|
||||
"github.com/akhilrex/podgrab/service"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jasonlvhit/gocron"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// os.Remove("./podgrab.db")
|
||||
|
||||
var err error
|
||||
db.DB, err = db.Init()
|
||||
if err != nil {
|
||||
fmt.Println("statuse: ", err)
|
||||
}
|
||||
db.Migrate()
|
||||
|
||||
r := gin.Default()
|
||||
r.Static("/assets", "./assets")
|
||||
r.LoadHTMLGlob("client/*")
|
||||
|
||||
r.GET("/podcasts", controllers.AddPodcast)
|
||||
r.POST("/podcasts", controllers.GetAllPodcasts)
|
||||
r.GET("/podcasts/:id", controllers.GetPodcastById)
|
||||
r.GET("/podcasts/:id/items", controllers.GetPodcastItemsByPodcastId)
|
||||
|
||||
r.GET("/podcastitems", controllers.GetAllPodcastItems)
|
||||
r.GET("/podcastitems/:id", controllers.GetPodcastItemById)
|
||||
|
||||
r.GET("/ping", func(c *gin.Context) {
|
||||
|
||||
data, err := service.AddPodcast(c.Query("url"))
|
||||
go service.AddPodcastItems(&data)
|
||||
//data, err := db.GetAllPodcastItemsToBeDownloaded()
|
||||
if err == nil {
|
||||
c.JSON(200, data)
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
})
|
||||
r.GET("/pong", func(c *gin.Context) {
|
||||
|
||||
data, err := db.GetAllPodcastItemsToBeDownloaded()
|
||||
|
||||
for _, item := range *data {
|
||||
url, _ := service.Download(item.FileURL, item.Title, item.Podcast.Title)
|
||||
service.SetPodcastItemAsDownloaded(item.ID, url)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
c.JSON(200, data)
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
r.GET("/", func(c *gin.Context) {
|
||||
//var podcasts []db.Podcast
|
||||
podcasts := service.GetAllPodcasts()
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{"title": "Main website", "podcasts": podcasts})
|
||||
})
|
||||
|
||||
go intiCron()
|
||||
|
||||
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
|
||||
|
||||
}
|
||||
|
||||
func intiCron() {
|
||||
gocron.Every(20).Minutes().Do(service.DownloadMissingEpisodes)
|
||||
gocron.Every(20).Minutes().Do(service.RefreshEpisodes)
|
||||
<-gocron.Start()
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
stringy "github.com/gobeam/stringy"
|
||||
)
|
||||
|
||||
func Download(link string, episodeTitle string, podcastName string) (string, error) {
|
||||
client := httpClient()
|
||||
resp, err := client.Get(link)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fileName := getFileName(link, episodeTitle, ".mp3")
|
||||
folder := createIfFoldeDoesntExist(podcastName)
|
||||
finalPath := path.Join(folder, fileName)
|
||||
file, err := os.Create(finalPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
_, erra := io.Copy(file, resp.Body)
|
||||
//fmt.Println(size)
|
||||
defer file.Close()
|
||||
if erra != nil {
|
||||
return "", erra
|
||||
}
|
||||
return finalPath, nil
|
||||
|
||||
}
|
||||
func httpClient() *http.Client {
|
||||
client := http.Client{
|
||||
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||
r.URL.Opaque = r.URL.Path
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return &client
|
||||
}
|
||||
|
||||
func createIfFoldeDoesntExist(folder string) string {
|
||||
str := stringy.New(folder)
|
||||
folder = str.RemoveSpecialCharacter()
|
||||
folderPath := path.Join("./assets/", folder)
|
||||
if _, err := os.Stat(folderPath); os.IsNotExist(err) {
|
||||
os.MkdirAll(folderPath, 0755)
|
||||
}
|
||||
return folderPath
|
||||
}
|
||||
|
||||
func getFileName(link string, title string, defaultExtension string) string {
|
||||
fileUrl, err := url.Parse(link)
|
||||
checkError(err)
|
||||
|
||||
parsed := fileUrl.Path
|
||||
ext := filepath.Ext(parsed)
|
||||
|
||||
if len(ext) == 0 {
|
||||
ext = defaultExtension
|
||||
}
|
||||
str := stringy.New(title)
|
||||
str = stringy.New(str.RemoveSpecialCharacter())
|
||||
return str.KebabCase().Get() + ext
|
||||
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package service
|
||||
|
||||
import "encoding/xml"
|
||||
|
||||
//PodcastData is
|
||||
type PodcastData struct {
|
||||
XMLName xml.Name `xml:"rss"`
|
||||
Text string `xml:",chardata"`
|
||||
Itunes string `xml:"itunes,attr"`
|
||||
Atom string `xml:"atom,attr"`
|
||||
Media string `xml:"media,attr"`
|
||||
Psc string `xml:"psc,attr"`
|
||||
Omny string `xml:"omny,attr"`
|
||||
Content string `xml:"content,attr"`
|
||||
Googleplay string `xml:"googleplay,attr"`
|
||||
Acast string `xml:"acast,attr"`
|
||||
Version string `xml:"version,attr"`
|
||||
Channel struct {
|
||||
Text string `xml:",chardata"`
|
||||
Language string `xml:"language"`
|
||||
Link []struct {
|
||||
Text string `xml:",chardata"`
|
||||
Rel string `xml:"rel,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Href string `xml:"href,attr"`
|
||||
} `xml:"link"`
|
||||
Title string `xml:"title"`
|
||||
Description string `xml:"description"`
|
||||
Type string `xml:"type"`
|
||||
Summary string `xml:"summary"`
|
||||
Owner struct {
|
||||
Text string `xml:",chardata"`
|
||||
Name string `xml:"name"`
|
||||
Email string `xml:"email"`
|
||||
} `xml:"owner"`
|
||||
Author string `xml:"author"`
|
||||
Copyright string `xml:"copyright"`
|
||||
Explicit string `xml:"explicit"`
|
||||
Category struct {
|
||||
Text string `xml:",chardata"`
|
||||
AttrText string `xml:"text,attr"`
|
||||
Category struct {
|
||||
Text string `xml:",chardata"`
|
||||
AttrText string `xml:"text,attr"`
|
||||
} `xml:"category"`
|
||||
} `xml:"category"`
|
||||
Image struct {
|
||||
Text string `xml:",chardata"`
|
||||
Href string `xml:"href,attr"`
|
||||
URL string `xml:"url"`
|
||||
Title string `xml:"title"`
|
||||
Link string `xml:"link"`
|
||||
} `xml:"image"`
|
||||
Item []struct {
|
||||
Text string `xml:",chardata"`
|
||||
Title string `xml:"title"`
|
||||
Description string `xml:"description"`
|
||||
Encoded string `xml:"encoded"`
|
||||
Summary string `xml:"summary"`
|
||||
EpisodeType string `xml:"episodeType"`
|
||||
Author string `xml:"author"`
|
||||
Image struct {
|
||||
Text string `xml:",chardata"`
|
||||
Href string `xml:"href,attr"`
|
||||
} `xml:"image"`
|
||||
Content []struct {
|
||||
Text string `xml:",chardata"`
|
||||
URL string `xml:"url,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Player struct {
|
||||
Text string `xml:",chardata"`
|
||||
URL string `xml:"url,attr"`
|
||||
} `xml:"player"`
|
||||
} `xml:"content"`
|
||||
Guid struct {
|
||||
Text string `xml:",chardata"`
|
||||
IsPermaLink string `xml:"isPermaLink,attr"`
|
||||
} `xml:"guid"`
|
||||
ClipId string `xml:"clipId"`
|
||||
PubDate string `xml:"pubDate"`
|
||||
Duration string `xml:"duration"`
|
||||
Enclosure struct {
|
||||
Text string `xml:",chardata"`
|
||||
URL string `xml:"url,attr"`
|
||||
Length string `xml:"length,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
} `xml:"enclosure"`
|
||||
Link string `xml:"link"`
|
||||
StitcherId string `xml:"stitcherId"`
|
||||
Episode string `xml:"episode"`
|
||||
} `xml:"item"`
|
||||
} `xml:"channel"`
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/akhilrex/podgrab/db"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
//Fetch is
|
||||
func FetchURL(url string) (PodcastData, error) {
|
||||
body := makeQuery(url)
|
||||
var response PodcastData
|
||||
err := xml.Unmarshal(body, &response)
|
||||
return response, err
|
||||
}
|
||||
func GetAllPodcasts() *[]db.Podcast {
|
||||
var podcasts []db.Podcast
|
||||
db.GetAllPodcasts(&podcasts)
|
||||
return &podcasts
|
||||
}
|
||||
func AddPodcast(url string) (db.Podcast, error) {
|
||||
|
||||
data, err := FetchURL(url)
|
||||
if err != nil {
|
||||
fmt.Println("Error")
|
||||
log.Fatal(err)
|
||||
return db.Podcast{}, err
|
||||
}
|
||||
var podcast db.Podcast
|
||||
err = db.GetPodcastByTitleAndAuthor(data.Channel.Title, data.Channel.Author, &podcast)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
podcast := db.Podcast{
|
||||
Title: data.Channel.Title,
|
||||
Summary: data.Channel.Summary,
|
||||
Author: data.Channel.Author,
|
||||
Image: data.Channel.Image.URL,
|
||||
URL: url,
|
||||
}
|
||||
err = db.CreatePodcast(&podcast)
|
||||
return podcast, err
|
||||
}
|
||||
return podcast, err
|
||||
|
||||
}
|
||||
|
||||
func AddPodcastItems(podcast *db.Podcast) error {
|
||||
fmt.Println("Creating: " + podcast.ID)
|
||||
data, err := FetchURL(podcast.URL)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
obj := data.Channel.Item[i]
|
||||
var podcastItem db.PodcastItem
|
||||
err := db.GetPodcastItemByPodcastIdAndGUID(podcast.ID, obj.Guid.Text, &podcastItem)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
duration, _ := strconv.Atoi(obj.Duration)
|
||||
pubDate, _ := time.Parse(time.RFC1123Z, obj.PubDate)
|
||||
podcastItem = db.PodcastItem{
|
||||
PodcastID: podcast.ID,
|
||||
Title: obj.Title,
|
||||
Summary: obj.Summary,
|
||||
EpisodeType: obj.EpisodeType,
|
||||
Duration: duration,
|
||||
PubDate: pubDate,
|
||||
FileURL: obj.Enclosure.URL,
|
||||
GUID: obj.Guid.Text,
|
||||
}
|
||||
db.CreatePodcastItem(&podcastItem)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func SetPodcastItemAsDownloaded(id string, location string) {
|
||||
var podcastItem db.PodcastItem
|
||||
db.GetPodcastItemById(id, &podcastItem)
|
||||
|
||||
podcastItem.DownloadDate = time.Now()
|
||||
podcastItem.DownloadPath = location
|
||||
|
||||
db.UpdatePodcastItem(&podcastItem)
|
||||
}
|
||||
|
||||
func DownloadMissingEpisodes() error {
|
||||
data, err := db.GetAllPodcastItemsToBeDownloaded()
|
||||
|
||||
fmt.Println("Processing episodes: %d", len(*data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range *data {
|
||||
url, _ := Download(item.FileURL, item.Title, item.Podcast.Title)
|
||||
SetPodcastItemAsDownloaded(item.ID, url)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RefreshEpisodes() error {
|
||||
var data []db.Podcast
|
||||
err := db.GetAllPodcasts(&data)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range data {
|
||||
AddPodcastItems(&item)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeQuery(url string) []byte {
|
||||
//link := "https://www.goodreads.com/search/index.xml?q=Good%27s+Omens&key=" + "jCmNlIXjz29GoB8wYsrd0w"
|
||||
//link := "https://www.goodreads.com/search/index.xml?key=jCmNlIXjz29GoB8wYsrd0w&q=Ender%27s+Game"
|
||||
//fmt.Println(url)
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
fmt.Println("Response status:", resp.Status)
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
|
||||
return body
|
||||
|
||||
}
|
Loading…
Reference in new issue