diff --git a/server/router.go b/server/router.go index 9c92ec9..4b85a13 100644 --- a/server/router.go +++ b/server/router.go @@ -42,6 +42,8 @@ func NewRouter(db *gorm.DB) *gin.Engine { authed.POST("/scrobble/preview", scrobbleController.PreviewScrobble) + authed.POST("/scrobble/do", scrobbleController.DoScrobble) + // v1 := router.Group("v1") // { // userGroup := v1.Group("user") diff --git a/server/server.go b/server/server.go index aafed0c..27b2159 100644 --- a/server/server.go +++ b/server/server.go @@ -29,8 +29,7 @@ func Init() { log.Fatalf("%v\n", err) } - db.AutoMigrate(&models.User{}) - db.AutoMigrate(&models.Post{}) + db.AutoMigrate(&models.User{}, &models.Post{}, &models.MediaItem{}) r := NewRouter(db) r.LoadHTMLGlob("templates/*.tmpl") diff --git a/services/micropub/service.go b/services/micropub/service.go index 0238511..16e86d8 100644 --- a/services/micropub/service.go +++ b/services/micropub/service.go @@ -159,64 +159,27 @@ func (m *MicropubDiscoveryService) Discover(me string, authToken string) (*Micro } /* Send micropub to endpoint */ -func (m *MicropubDiscoveryService) SubmitScrobble(currentUser *models.BaseUser, post *models.Post) (error) { +func (m *MicropubDiscoveryService) SubmitMicropub(currentUser *models.BaseUser, payload []byte) (*http.Response, error) { + endpoint, err := m.findMicropubEndpoint(currentUser.Me) if err != nil{ - return err + return nil, 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) + body := bytes.NewReader(payload) req, err := http.NewRequest("POST", endpoint, body) if err != nil{ - return err + return nil, err } req.Header.Add("User-Agent", USER_AGENT_STRING) req.Header.Add("Authorization", fmt.Sprintf("Bearer %v", currentUser.Token)) + req.Header.Add("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + return http.DefaultClient.Do(req) - if err != nil{ - return err - } - - loc, err := resp.Location() - - if err != nil{ - return err - } - - post.URL = loc.String() - - return nil } \ No newline at end of file diff --git a/services/scrobble/scrobbler.go b/services/scrobble/scrobbler.go index 72fe762..a4d2e78 100644 --- a/services/scrobble/scrobbler.go +++ b/services/scrobble/scrobbler.go @@ -1,9 +1,13 @@ package scrobble import ( + "database/sql" + "encoding/json" "fmt" "net/url" + "time" + "git.jamesravey.me/ravenscroftj/indiescrobble/config" "git.jamesravey.me/ravenscroftj/indiescrobble/models" "git.jamesravey.me/ravenscroftj/indiescrobble/services/micropub" "gorm.io/gorm" @@ -21,7 +25,7 @@ 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 fmt.Errorf("unknown/invalid scrobble type %v", scrobbleType) } return nil @@ -63,6 +67,47 @@ func (s *Scrobbler) GetSearchEngineNameForType(scrobbleType string) string { return NewSearchProvider(scrobbleType, s.db).SearchProvider.GetName() } +func (s *Scrobbler) buildMicroPubPayload(post *models.Post) ([]byte, error) { + postObj := make(map[string]interface{}) + postObj["type"] = []string{"h-entry"} + postObj["visibility"] = []string{"public"} + + properties := make(map[string]interface{}) + + if post.MediaItem.ThumbnailURL.Valid{ + properties["photo"] = []string{post.MediaItem.ThumbnailURL.String} + } + + if post.Rating.Valid{ + properties["rating"] = []string{post.Rating.String} + } + + properties["summary"] = fmt.Sprintf("%v %v and gave it %v/5", ScrobbleTypeVerbs[post.PostType], post.MediaItem.DisplayName.String, post.Rating.String) + + + citationProps := make(map[string]interface{}) + citationProps["name"] = []string{post.MediaItem.DisplayName.String} + citationProps["uid"] = []string{post.MediaItem.MediaID} + citationProps["url"] = []string{post.MediaItem.CanonicalURL.String} + citationProps["indiescrobble-id"] = post.MediaItem.ID + + citation := make(map[string]interface{}) + citation["type"] = []string{"h-cite"} + citation["properties"] = citationProps + + // use the appropriate citation property e.g. read-of or watch-of + properties[ScrobbleCitationProperties[post.PostType]] = citation + + if post.Content.Valid{ + properties["content"] = []string{post.Content.String} + } + + postObj["properties"] = properties + + + return json.Marshal(postObj) +} + func (s *Scrobbler) Scrobble(form *url.Values, currentUser *models.BaseUser) (*models.Post, error) { if err := s.ValidateType(form); err != nil{ @@ -78,13 +123,54 @@ func (s *Scrobbler) Scrobble(form *url.Values, currentUser *models.BaseUser) (*m discovery := micropub.MicropubDiscoveryService{} - - mediaItem := models.MediaItem{} + post := models.Post{ + MediaItem: item, + User: *currentUser.UserRecord, + PostType: form.Get("type"), + Content: sql.NullString{String: form.Get("content"), Valid: true}, + Rating: sql.NullString{String: form.Get("rating"), Valid: true}, + } - post := models.Post{MediaItem: mediaItem, User: *currentUser.UserRecord, PostType: form.Get("type") } - discovery.SubmitScrobble(currentUser, &post) + time, err := time.Parse(config.BROWSER_TIME_FORMAT, form.Get("when")) + + if err == nil{ + post.ScrobbledAt = sql.NullTime{Time: time, Valid: true} + }else{ + fmt.Errorf("Failed to parse time %v because %v",form.Get("when"), err ) + } + + postBody, err := s.buildMicroPubPayload(&post) + + + fmt.Printf("Post body: %v\n", string(postBody)) + + if err != nil{ + return nil, err + } + + + resp, err := discovery.SubmitMicropub(currentUser, postBody) + + if err != nil{ + return nil, err + } + + loc, err := resp.Location() + + if err != nil{ + return nil, err + } + + post.URL = loc.String() + result = s.db.Create(&post) + + if result.Error != nil{ + return nil, result.Error + } return &post, nil -} \ No newline at end of file +} + + diff --git a/services/scrobble/types.go b/services/scrobble/types.go index e2af13f..3034a8a 100644 --- a/services/scrobble/types.go +++ b/services/scrobble/types.go @@ -14,6 +14,13 @@ var ScrobbleTypeNames = map[string]string { "read": "📖 Read", }; +var ScrobbleTypeVerbs = map[string]string { + "scrobble" : "listened to", + "tv" : "watched", + "movie": "watched", + "read": "read", +}; + var ScrobblePlaceholders = map[string]string { "scrobble" : "Jump Van Halen", "tv" : "Schitt's Creek", @@ -21,6 +28,13 @@ var ScrobblePlaceholders = map[string]string { "read": "Three Body Problem Cixin Liu", }; +var ScrobbleCitationProperties = map[string]string { + "scrobble" : "listen-of", + "tv" : "watch-of", + "movie": "watch-of", + "read": "read-of", +}; + type ScrobbleMetaRecord interface{ GetID() string diff --git a/templates/preview.tmpl b/templates/preview.tmpl index 108926a..2284bbc 100644 --- a/templates/preview.tmpl +++ b/templates/preview.tmpl @@ -13,7 +13,7 @@ {{end}} -
+

Add A Post > Add {{ .scrobbleTypeName }} > {{.item.GetDisplayName}}

@@ -50,6 +50,7 @@ +
diff --git a/templates/scrobbled.tmpl b/templates/scrobbled.tmpl new file mode 100644 index 0000000..260a40b --- /dev/null +++ b/templates/scrobbled.tmpl @@ -0,0 +1,24 @@ +{{ define "scrobbled.tmpl" }} + + + {{ template "head.tmpl" . }} + + {{ template "header.tmpl" . }} +
+ + {{ $scrobbleType := .scrobbleType }} + + {{ if .user }} + Logged in as {{.user.Me}} + {{end}} + + +

Add A Post > Add {{ .scrobbleTypeName }} > {{.post.MediaItem.DisplayName.String}}

+ +

Post Complete: {{.post.URL}}

+ +
+ {{ template "footer.tmpl" . }} + + +{{end}} \ No newline at end of file