refactor of controllers for scrobbling
This commit is contained in:
parent
d4043ebc88
commit
3b7d937df5
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/models"
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/scrobble"
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/services/scrobble"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
|
|
@ -2,15 +2,27 @@ package controllers
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/models"
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/scrobble"
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/services/scrobble"
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/services/micropub"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
|
||||
func Scrobble(c *gin.Context){
|
||||
type ScrobbleController struct{
|
||||
db *gorm.DB
|
||||
scrobbler *scrobble.Scrobbler
|
||||
}
|
||||
|
||||
func NewScrobbleController(db *gorm.DB) *ScrobbleController{
|
||||
return &ScrobbleController{db, scrobble.NewScrobbler(db)}
|
||||
}
|
||||
|
||||
/*Do the actual post to the user's site*/
|
||||
func (s *ScrobbleController) DoScrobble(c *gin.Context){
|
||||
|
||||
err := c.Request.ParseForm()
|
||||
|
||||
|
@ -21,22 +33,56 @@ func Scrobble(c *gin.Context){
|
|||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||
"message": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
s.scrobbler.Scrobble(&c.Request.Form, currentUser)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*Display the scrobble form and allow user to search for and add media*/
|
||||
func (s *ScrobbleController) ScrobbleForm(c *gin.Context){
|
||||
|
||||
err := c.Request.ParseForm()
|
||||
|
||||
// this is an authed endpoint so 'user' must be set and if not panicking is fair
|
||||
currentUser := c.MustGet("user").(*models.BaseUser)
|
||||
|
||||
if err != nil{
|
||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||
"message": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: add validation of type
|
||||
scrobbleType := c.Request.Form.Get("type")
|
||||
|
||||
searchEngine := scrobble.NewSearchProvider(scrobbleType)
|
||||
if c.Request.Form.Get("item") != "" {
|
||||
|
||||
var searchResults []scrobble.ScrobbleMetaRecord = nil
|
||||
var item scrobble.ScrobbleMetaRecord = nil
|
||||
item, err := s.scrobbler.GetItemByID(&c.Request.Form)
|
||||
|
||||
query := c.Request.Form.Get("q")
|
||||
itemID := c.Request.Form.Get("item")
|
||||
|
||||
if itemID != "" {
|
||||
if err != nil {
|
||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||
"message": err,
|
||||
})
|
||||
return
|
||||
}else{
|
||||
c.HTML(http.StatusOK, "scrobble.tmpl", gin.H{
|
||||
"user": currentUser,
|
||||
"scrobbleType": scrobbleType,
|
||||
"scrobblePlaceholder": scrobble.ScrobblePlaceholders[scrobbleType],
|
||||
"scrobbleTypeName": scrobble.ScrobbleTypeNames[scrobbleType],
|
||||
"item": item,
|
||||
"now": time.Now().Format("2006-01-02T15:04"),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
item, err = searchEngine.SearchProvider.GetItem(itemID)
|
||||
}else if query := c.Request.Form.Get("q"); query != "" {
|
||||
|
||||
searchResults, err := s.scrobbler.Search(&c.Request.Form)
|
||||
|
||||
if err != nil{
|
||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||
|
@ -44,32 +90,29 @@ func Scrobble(c *gin.Context){
|
|||
})
|
||||
return
|
||||
}
|
||||
}else if query != "" {
|
||||
var err error = nil
|
||||
searchResults, err = searchEngine.SearchProvider.Search(query)
|
||||
|
||||
if err != nil{
|
||||
c.HTML(http.StatusBadRequest, "error.tmpl", gin.H{
|
||||
"message": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
c.HTML(http.StatusOK, "scrobble.tmpl", gin.H{
|
||||
"user": currentUser,
|
||||
"scrobbleType": scrobbleType,
|
||||
"scrobblePlaceholder": scrobble.ScrobblePlaceholders[scrobbleType],
|
||||
"scrobbleTypeName": scrobble.ScrobbleTypeNames[scrobbleType],
|
||||
"searchEngine": searchEngine.SearchProvider.GetName(),
|
||||
"searchEngine": s.scrobbler.GetSearchEngineNameForType(scrobbleType),
|
||||
"searchResults": searchResults,
|
||||
"item": item,
|
||||
"now": time.Now().Format("2006-01-02T15:04"),
|
||||
})
|
||||
}else{
|
||||
c.HTML(http.StatusOK, "scrobble.tmpl", gin.H{
|
||||
"user": currentUser,
|
||||
"scrobbleType": scrobbleType,
|
||||
"scrobblePlaceholder": scrobble.ScrobblePlaceholders[scrobbleType],
|
||||
"scrobbleTypeName": scrobble.ScrobbleTypeNames[scrobbleType],
|
||||
"now": time.Now().Format("2006-01-02T15:04"),
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func PreviewScrobble(c *gin.Context){
|
||||
func (s *ScrobbleController) PreviewScrobble(c *gin.Context){
|
||||
|
||||
err := c.Request.ParseForm()
|
||||
|
||||
|
@ -84,7 +127,7 @@ func PreviewScrobble(c *gin.Context){
|
|||
|
||||
scrobbleType := c.Request.Form.Get("type")
|
||||
|
||||
searchEngine := scrobble.NewSearchProvider(scrobbleType)
|
||||
searchEngine := scrobble.NewSearchProvider(scrobbleType, s.db)
|
||||
|
||||
itemID := c.Request.Form.Get("item")
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MediaItem struct{
|
||||
gorm.Model
|
||||
MediaID string `gorm:"not null uniqueIndex"`
|
||||
ThumbnailURL sql.NullString
|
||||
DisplayName sql.NullString
|
||||
CanonicalURL sql.NullString
|
||||
Data sql.NullString
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Post struct {
|
||||
gorm.Model
|
||||
URL string `gorm:"uniqueIndex"`
|
||||
PostType string `gorm:"index"`
|
||||
UserID int `gorm:"foreignKey"`
|
||||
User User
|
||||
MediaItemID int `gorm:"index"`
|
||||
MediaItem MediaItem
|
||||
ScrobbledAt sql.NullTime
|
||||
Content sql.NullString
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var alphanum = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
|
||||
|
||||
func randSeq(n int) string {
|
||||
b := make([]rune, n)
|
||||
for i := range b {
|
||||
b[i] = alphanum[rand.Intn(len(alphanum))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
type BaseUser struct {
|
||||
Me string
|
||||
Token string
|
||||
UserRecord *User
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Me string `gorm:"uniqueIndex"`
|
||||
APIKey string
|
||||
}
|
||||
|
||||
func (u *User) GenerateRandomKey() {
|
||||
u.APIKey = randSeq(16)
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package scrobble
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/StalkR/imdb"
|
||||
"github.com/gregjones/httpcache"
|
||||
"github.com/gregjones/httpcache/diskcache"
|
||||
)
|
||||
|
||||
type IMDBMetaRecord struct{
|
||||
title imdb.Title
|
||||
}
|
||||
|
||||
func (r *IMDBMetaRecord) GetID() string{
|
||||
return r.title.ID
|
||||
}
|
||||
|
||||
func (r *IMDBMetaRecord) GetDisplayName() string{
|
||||
return fmt.Sprintf("%v (%v)", r.title.Name, r.title.Year)
|
||||
}
|
||||
|
||||
|
||||
func (r *IMDBMetaRecord) GetCanonicalURL() string{
|
||||
return r.title.URL
|
||||
}
|
||||
|
||||
func (r *IMDBMetaRecord) GetThumbnailURL() string{
|
||||
return r.title.Poster.ContentURL
|
||||
}
|
||||
|
||||
|
||||
type IMDBScrobbleMetadataProvider struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func NewIMDBProvider() *IMDBScrobbleMetadataProvider {
|
||||
|
||||
cache := diskcache.New("cache")
|
||||
client := &http.Client{Transport: httpcache.NewTransport(cache)}
|
||||
return &IMDBScrobbleMetadataProvider{client:client}
|
||||
}
|
||||
|
||||
|
||||
func (i *IMDBScrobbleMetadataProvider) GetName() string { return "IMDB" }
|
||||
|
||||
|
||||
func (i *IMDBScrobbleMetadataProvider) GetItem(id string) (ScrobbleMetaRecord, error) {
|
||||
|
||||
title, err := imdb.NewTitle(i.client, id)
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &IMDBMetaRecord{title: *title}, nil
|
||||
|
||||
}
|
||||
|
||||
func (i *IMDBScrobbleMetadataProvider) Search(query string) ([]ScrobbleMetaRecord, error) {
|
||||
|
||||
titles, err := imdb.SearchTitle(i.client, query)
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records := make([]ScrobbleMetaRecord, len(titles))
|
||||
|
||||
for i, title := range titles {
|
||||
records[i] = &IMDBMetaRecord{title: title}
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
|
@ -36,8 +36,11 @@ func NewRouter(db *gorm.DB) *gin.Engine {
|
|||
authed := router.Use(middlewares.AuthMiddleware(true, iam))
|
||||
|
||||
// add scrobble endpoints
|
||||
authed.GET("/scrobble", controllers.Scrobble)
|
||||
authed.POST("/scrobble/preview", controllers.PreviewScrobble)
|
||||
scrobbleController := controllers.NewScrobbleController(db)
|
||||
|
||||
authed.GET("/scrobble", scrobbleController.ScrobbleForm)
|
||||
|
||||
authed.POST("/scrobble/preview", scrobbleController.PreviewScrobble)
|
||||
|
||||
// v1 := router.Group("v1")
|
||||
// {
|
||||
|
|
|
@ -30,6 +30,7 @@ func Init() {
|
|||
}
|
||||
|
||||
db.AutoMigrate(&models.User{})
|
||||
db.AutoMigrate(&models.Post{})
|
||||
|
||||
r := NewRouter(db)
|
||||
r.LoadHTMLGlob("templates/*.tmpl")
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package micropub
|
||||
|
||||
type MicroPubPostType struct{
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
type MicroPubSyndicateTarget struct{
|
||||
Name string
|
||||
Uid string
|
||||
}
|
||||
|
||||
type MicroPubConfig struct{
|
||||
MediaEndpoint string `json:"media-endpoint"`
|
||||
PostTypes []MicroPubPostType `json:"post-types"`
|
||||
SyndicateTargets []MicroPubSyndicateTarget `json:"syndicate-to"`
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package micropub
|
||||
|
||||
type HEntry struct{
|
||||
Type []string
|
||||
Properties map[string]interface{}
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
package micropub
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/models"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
const(USER_AGENT_STRING="IndieScrobble (indiescrobble.club)")
|
||||
|
||||
type MicropubDiscoveryService struct {
|
||||
}
|
||||
|
||||
func (m *MicropubDiscoveryService) doGet(url string ) (*http.Response, error) {
|
||||
client := http.Client{}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
|
||||
req.Header.Add("User-Agent", USER_AGENT_STRING)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client.Do(req)
|
||||
}
|
||||
|
||||
|
||||
func (m *MicropubDiscoveryService) doAuthGet(url string, bearerToken string ) (*http.Response, error) {
|
||||
client := http.Client{}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
|
||||
req.Header.Add("User-Agent", USER_AGENT_STRING)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %v",bearerToken))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client.Do(req)
|
||||
}
|
||||
|
||||
func (m *MicropubDiscoveryService) findMicropubEndpoint(me string) (string, error) {
|
||||
|
||||
res, err := m.doGet(me)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
|
||||
}
|
||||
|
||||
// Load the HTML document
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Find the review items
|
||||
s := doc.Find("link[rel=micropub]")
|
||||
|
||||
if s.Length() < 1 {
|
||||
return "", fmt.Errorf("no micropub endpoint found for %v", me)
|
||||
}
|
||||
|
||||
// parse the returned URL
|
||||
endpointUrl, err := url.Parse(s.AttrOr("href",""))
|
||||
|
||||
if err != nil{
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !endpointUrl.IsAbs(){
|
||||
|
||||
if(strings.HasPrefix(endpointUrl.Path, "/")) {
|
||||
|
||||
newUrl := *res.Request.URL
|
||||
newUrl.Path = endpointUrl.Path
|
||||
|
||||
return newUrl.String(), nil
|
||||
}else{
|
||||
return res.Request.URL.String() + endpointUrl.Path, nil
|
||||
}
|
||||
|
||||
}else{
|
||||
return endpointUrl.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MicropubDiscoveryService) getMicropubConfig(endpoint string, authToken string) (*MicroPubConfig, error) {
|
||||
|
||||
configUrl, err := url.Parse(endpoint)
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q := configUrl.Query()
|
||||
q.Set("q","config")
|
||||
configUrl.RawQuery = q.Encode()
|
||||
|
||||
res, err := m.doAuthGet(configUrl.String(), authToken)
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(res.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := MicroPubConfig{}
|
||||
|
||||
err = json.Unmarshal(body, &config)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
/* Discover endpoints for given me/domain identifier */
|
||||
func (m *MicropubDiscoveryService) Discover(me string, authToken string) (*MicroPubConfig, error) {
|
||||
|
||||
endpoint, err := m.findMicropubEndpoint(me)
|
||||
|
||||
if err != nil{
|
||||
log.Printf("Failed to get endpoint: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get endpoint config
|
||||
config, err := m.getMicropubConfig(endpoint, authToken)
|
||||
|
||||
|
||||
if err != nil{
|
||||
log.Printf("Failed to get configuration: %v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
|
||||
}
|
||||
|
||||
/* Send micropub to endpoint */
|
||||
func (m *MicropubDiscoveryService) SubmitScrobble(currentUser *models.BaseUser, post *models.Post) (error) {
|
||||
endpoint, err := m.findMicropubEndpoint(currentUser.Me)
|
||||
|
||||
if err != nil{
|
||||
return err
|
||||
}
|
||||
|
||||
postObj := make(map[string]interface{})
|
||||
postObj["type"] = []string{"h-entry"}
|
||||
postObj["visibility"] = []string{"public"}
|
||||
|
||||
properties := make(map[string]interface{})
|
||||
properties["media-type"] = []string{post.PostType}
|
||||
properties["media-item-id"] = []string{post.MediaItem.MediaID}
|
||||
properties["media-item-url"] = []string{post.MediaItem.CanonicalURL.String}
|
||||
|
||||
properties["indiescrobble-id"] = post.MediaItem.ID
|
||||
|
||||
if post.MediaItem.ThumbnailURL.Valid{
|
||||
properties["photo"] = []string{post.MediaItem.ThumbnailURL.String}
|
||||
}
|
||||
|
||||
if post.Content.Valid{
|
||||
postObj["content"] = post.Content.String
|
||||
}
|
||||
|
||||
postObj["properties"] = properties
|
||||
|
||||
bodyBytes, err := json.Marshal(postObj)
|
||||
|
||||
if err != nil{
|
||||
return err
|
||||
}
|
||||
|
||||
body := bytes.NewReader(bodyBytes)
|
||||
|
||||
req, err := http.NewRequest("POST", endpoint, body)
|
||||
|
||||
if err != nil{
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Add("User-Agent", USER_AGENT_STRING)
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %v", currentUser.Token))
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
|
||||
if err != nil{
|
||||
return err
|
||||
}
|
||||
|
||||
loc, err := resp.Location()
|
||||
|
||||
if err != nil{
|
||||
return err
|
||||
}
|
||||
|
||||
post.URL = loc.String()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
package scrobble
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/models"
|
||||
"github.com/StalkR/imdb"
|
||||
"github.com/gregjones/httpcache"
|
||||
"github.com/gregjones/httpcache/diskcache"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type IMDBMetaRecord struct{
|
||||
title imdb.Title
|
||||
}
|
||||
|
||||
func (r *IMDBMetaRecord) GetID() string{
|
||||
return r.title.ID
|
||||
}
|
||||
|
||||
func (r *IMDBMetaRecord) GetDisplayName() string{
|
||||
return fmt.Sprintf("%v (%v)", r.title.Name, r.title.Year)
|
||||
}
|
||||
|
||||
|
||||
func (r *IMDBMetaRecord) GetCanonicalURL() string{
|
||||
return r.title.URL
|
||||
}
|
||||
|
||||
func (r *IMDBMetaRecord) GetThumbnailURL() string{
|
||||
return r.title.Poster.ContentURL
|
||||
}
|
||||
|
||||
|
||||
type IMDBScrobbleMetadataProvider struct {
|
||||
client *http.Client
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewIMDBProvider(db *gorm.DB) *IMDBScrobbleMetadataProvider {
|
||||
|
||||
cache := diskcache.New("cache")
|
||||
client := &http.Client{Transport: httpcache.NewTransport(cache)}
|
||||
return &IMDBScrobbleMetadataProvider{client:client, db:db}
|
||||
}
|
||||
|
||||
|
||||
func (i *IMDBScrobbleMetadataProvider) GetName() string { return "IMDB" }
|
||||
|
||||
|
||||
func titleFromMediaItem(mediaItem *models.MediaItem) imdb.Title {
|
||||
title := imdb.Title{ID: mediaItem.MediaID, }
|
||||
return title
|
||||
}
|
||||
|
||||
func imdbRecordFromMediaItem(mediaItem *models.MediaItem) IMDBMetaRecord {
|
||||
title := imdb.Title{}
|
||||
json.Unmarshal([]byte(mediaItem.Data.String), &title)
|
||||
return IMDBMetaRecord{title:title}
|
||||
}
|
||||
|
||||
func imdbRecordToMediaItem(record *IMDBMetaRecord) (*models.MediaItem, error){
|
||||
|
||||
marshalledTitle, err := json.Marshal(record.title)
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
item := models.MediaItem{
|
||||
MediaID: record.title.ID,
|
||||
ThumbnailURL: sql.NullString{String: record.GetThumbnailURL(), Valid:true},
|
||||
CanonicalURL: sql.NullString{String: record.GetCanonicalURL(), Valid: true},
|
||||
DisplayName: sql.NullString{String: record.GetDisplayName(), Valid: true},
|
||||
Data: sql.NullString{String: string(marshalledTitle), Valid: true},
|
||||
}
|
||||
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
|
||||
func (i *IMDBScrobbleMetadataProvider) GetItem(id string) (ScrobbleMetaRecord, error) {
|
||||
|
||||
// see if item is in db first
|
||||
item := models.MediaItem{}
|
||||
|
||||
result := i.db.Where(&models.MediaItem{MediaID: id}).First(&item)
|
||||
|
||||
if result.Error == nil{
|
||||
record := imdbRecordFromMediaItem(&item)
|
||||
return &record, nil
|
||||
}
|
||||
|
||||
title, err := imdb.NewTitle(i.client, id)
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// cache the title in db and store
|
||||
record := IMDBMetaRecord{title: *title}
|
||||
mediaItem, err := imdbRecordToMediaItem(&record)
|
||||
|
||||
result = i.db.Create(mediaItem)
|
||||
|
||||
if result.Error != nil{
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &record, nil
|
||||
|
||||
}
|
||||
|
||||
func (i *IMDBScrobbleMetadataProvider) Search(query string) ([]ScrobbleMetaRecord, error) {
|
||||
|
||||
titles, err := imdb.SearchTitle(i.client, query)
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records := make([]ScrobbleMetaRecord, len(titles))
|
||||
|
||||
for i, title := range titles {
|
||||
records[i] = &IMDBMetaRecord{title: title}
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
|
@ -1,23 +1,20 @@
|
|||
package scrobble
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
|
||||
type MetaSearchProvider struct{
|
||||
ScrobbleType string
|
||||
SearchProvider ScrobbleMetaProvider
|
||||
}
|
||||
|
||||
func NewSearchProvider(scrobbleType string) *MetaSearchProvider{
|
||||
func NewSearchProvider(scrobbleType string, db *gorm.DB) *MetaSearchProvider{
|
||||
provider := &MetaSearchProvider{ScrobbleType: scrobbleType}
|
||||
|
||||
if scrobbleType == SCROBBLE_TYPE_MOVIE {
|
||||
provider.SearchProvider = NewIMDBProvider()
|
||||
provider.SearchProvider = NewIMDBProvider(db)
|
||||
}
|
||||
|
||||
return provider
|
||||
|
||||
}
|
||||
|
||||
|
||||
func (m *MetaSearchProvider) search(query string) {
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package scrobble
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/models"
|
||||
"git.jamesravey.me/ravenscroftj/indiescrobble/services/micropub"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Scrobbler struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewScrobbler(db *gorm.DB) *Scrobbler{
|
||||
return &Scrobbler{db:db}
|
||||
}
|
||||
|
||||
func (s *Scrobbler) ValidateType(form *url.Values) error {
|
||||
|
||||
scrobbleType := form.Get("type")
|
||||
if _, ok := ScrobbleTypeNames[scrobbleType]; !ok{
|
||||
return fmt.Errorf("Unknown/invalid scrobble type %v", scrobbleType)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Scrobbler) GetItemByID(form *url.Values) (ScrobbleMetaRecord, error) {
|
||||
|
||||
if err := s.ValidateType(form); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
searchEngine := NewSearchProvider(form.Get("type"), s.db)
|
||||
|
||||
item, err := searchEngine.SearchProvider.GetItem(form.Get("item"))
|
||||
|
||||
if err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return item, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *Scrobbler) Search(form *url.Values) ([]ScrobbleMetaRecord, error) {
|
||||
|
||||
if err := s.ValidateType(form); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
searchEngine := NewSearchProvider(form.Get("type"), s.db)
|
||||
|
||||
query := form.Get("q")
|
||||
|
||||
return searchEngine.SearchProvider.Search(query)
|
||||
|
||||
}
|
||||
|
||||
func (s *Scrobbler) GetSearchEngineNameForType(scrobbleType string) string {
|
||||
return NewSearchProvider(scrobbleType, s.db).SearchProvider.GetName()
|
||||
}
|
||||
|
||||
func (s *Scrobbler) Scrobble(form *url.Values, currentUser *models.BaseUser) (*models.Post, error) {
|
||||
|
||||
if err := s.ValidateType(form); err != nil{
|
||||
return nil, err
|
||||
}
|
||||
|
||||
item := models.MediaItem{}
|
||||
result := s.db.Where(&models.MediaItem{MediaID: form.Get("item")}).First(&item)
|
||||
|
||||
if result.Error != nil{
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
discovery := micropub.MicropubDiscoveryService{}
|
||||
|
||||
|
||||
|
||||
mediaItem := models.MediaItem{}
|
||||
|
||||
post := models.Post{MediaItem: mediaItem, User: *currentUser.UserRecord, PostType: form.Get("type") }
|
||||
|
||||
discovery.SubmitScrobble(currentUser, &post)
|
||||
|
||||
return &post, nil
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
<form method="POST" action="/scrobble/preview">
|
||||
|
||||
<p><a href="/">Add A Post</a> > <a href="/scrobble?type=movie">Add {{ .scrobbleTypeName }}</a> > {{.item.GetDisplayName}}</p>
|
||||
<p><a href="/">Add A Post</a> > <a href="/scrobble?type={{ .scrobbleType }}">Add {{ .scrobbleTypeName }}</a> > {{.item.GetDisplayName}}</p>
|
||||
|
||||
<h3>Preview Post: {{.item.GetDisplayName}}</h3>
|
||||
|
||||
|
@ -50,6 +50,8 @@
|
|||
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="type" value="{{ .scrobbleType }}"/>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
<div>
|
||||
|
||||
<label>When: </label> <input type="datetime-local" name='when'/><br/>
|
||||
<label>When: </label> <input type="datetime-local" name='when' value="{{.now}}"/><br/>
|
||||
|
||||
<label>Rating: (out of 5)</label> <input type="number" name='rating'/><br/>
|
||||
|
||||
|
|
Loading…
Reference in New Issue