Added arn to the main repository
This commit is contained in:
267
arn/AMV.go
Normal file
267
arn/AMV.go
Normal file
@ -0,0 +1,267 @@
|
||||
package arn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
"github.com/aerogo/nano"
|
||||
"github.com/animenotifier/notify.moe/arn/video"
|
||||
)
|
||||
|
||||
// AMV is an anime music video.
|
||||
type AMV struct {
|
||||
File string `json:"file" editable:"true" type:"upload" filetype:"video" endpoint:"/api/upload/amv/:id/file"`
|
||||
Title AMVTitle `json:"title" editable:"true"`
|
||||
MainAnimeID string `json:"mainAnimeId" editable:"true"`
|
||||
ExtraAnimeIDs []string `json:"extraAnimeIds" editable:"true"`
|
||||
VideoEditorIDs []string `json:"videoEditorIds" editable:"true"`
|
||||
Links []Link `json:"links" editable:"true"`
|
||||
Tags []string `json:"tags" editable:"true"`
|
||||
Info video.Info `json:"info"`
|
||||
|
||||
hasID
|
||||
hasPosts
|
||||
hasCreator
|
||||
hasEditor
|
||||
hasLikes
|
||||
hasDraft
|
||||
}
|
||||
|
||||
// Link returns the permalink for the AMV.
|
||||
func (amv *AMV) Link() string {
|
||||
return "/amv/" + amv.ID
|
||||
}
|
||||
|
||||
// TitleByUser returns the preferred title for the given user.
|
||||
func (amv *AMV) TitleByUser(user *User) string {
|
||||
return amv.Title.ByUser(user)
|
||||
}
|
||||
|
||||
// SetVideoBytes sets the bytes for the video file.
|
||||
func (amv *AMV) SetVideoBytes(data []byte) error {
|
||||
fileName := amv.ID + ".webm"
|
||||
filePath := path.Join(Root, "videos", "amvs", fileName)
|
||||
err := ioutil.WriteFile(filePath, data, 0644)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Run mkclean
|
||||
optimizedFile := filePath + ".optimized"
|
||||
|
||||
cmd := exec.Command(
|
||||
"mkclean",
|
||||
"--doctype", "4",
|
||||
"--keep-cues",
|
||||
"--optimize",
|
||||
filePath,
|
||||
optimizedFile,
|
||||
)
|
||||
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
err = cmd.Start()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Now delete the original file and replace it with the optimized file
|
||||
err = os.Remove(filePath)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Rename(optimizedFile, filePath)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Refresh video file info
|
||||
amv.File = fileName
|
||||
return amv.RefreshInfo()
|
||||
}
|
||||
|
||||
// RefreshInfo refreshes the information about the video file.
|
||||
func (amv *AMV) RefreshInfo() error {
|
||||
if amv.File == "" {
|
||||
return fmt.Errorf("Video file has not been uploaded yet for AMV %s", amv.ID)
|
||||
}
|
||||
|
||||
info, err := video.GetInfo(path.Join(Root, "videos", "amvs", amv.File))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
amv.Info = *info
|
||||
return nil
|
||||
}
|
||||
|
||||
// MainAnime returns main anime for the AMV, if available.
|
||||
func (amv *AMV) MainAnime() *Anime {
|
||||
mainAnime, _ := GetAnime(amv.MainAnimeID)
|
||||
return mainAnime
|
||||
}
|
||||
|
||||
// ExtraAnime returns main anime for the AMV, if available.
|
||||
func (amv *AMV) ExtraAnime() []*Anime {
|
||||
objects := DB.GetMany("Anime", amv.ExtraAnimeIDs)
|
||||
animes := []*Anime{}
|
||||
|
||||
for _, obj := range objects {
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
animes = append(animes, obj.(*Anime))
|
||||
}
|
||||
|
||||
return animes
|
||||
}
|
||||
|
||||
// VideoEditors returns a slice of all the users involved in creating the AMV.
|
||||
func (amv *AMV) VideoEditors() []*User {
|
||||
objects := DB.GetMany("User", amv.VideoEditorIDs)
|
||||
editors := []*User{}
|
||||
|
||||
for _, obj := range objects {
|
||||
if obj == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
editors = append(editors, obj.(*User))
|
||||
}
|
||||
|
||||
return editors
|
||||
}
|
||||
|
||||
// Publish turns the draft into a published object.
|
||||
func (amv *AMV) Publish() error {
|
||||
// No title
|
||||
if amv.Title.String() == "" {
|
||||
return errors.New("AMV doesn't have a title")
|
||||
}
|
||||
|
||||
// No anime found
|
||||
if amv.MainAnimeID == "" && len(amv.ExtraAnimeIDs) == 0 {
|
||||
return errors.New("Need to specify at least one anime")
|
||||
}
|
||||
|
||||
// No file uploaded
|
||||
if amv.File == "" {
|
||||
return errors.New("You need to upload a WebM file for this AMV")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path.Join(Root, "videos", "amvs", amv.File)); os.IsNotExist(err) {
|
||||
return errors.New("You need to upload a WebM file for this AMV")
|
||||
}
|
||||
|
||||
return publish(amv)
|
||||
}
|
||||
|
||||
// Unpublish turns the object back into a draft.
|
||||
func (amv *AMV) Unpublish() error {
|
||||
return unpublish(amv)
|
||||
}
|
||||
|
||||
// OnLike is called when the AMV receives a like.
|
||||
func (amv *AMV) OnLike(likedBy *User) {
|
||||
if likedBy.ID == amv.CreatedBy {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
amv.Creator().SendNotification(&PushNotification{
|
||||
Title: likedBy.Nick + " liked your AMV " + amv.Title.ByUser(amv.Creator()),
|
||||
Message: likedBy.Nick + " liked your AMV " + amv.Title.ByUser(amv.Creator()) + ".",
|
||||
Icon: "https:" + likedBy.AvatarLink("large"),
|
||||
Link: "https://notify.moe" + likedBy.Link(),
|
||||
Type: NotificationTypeLike,
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
// String implements the default string serialization.
|
||||
func (amv *AMV) String() string {
|
||||
return amv.Title.ByUser(nil)
|
||||
}
|
||||
|
||||
// TypeName returns the type name.
|
||||
func (amv *AMV) TypeName() string {
|
||||
return "AMV"
|
||||
}
|
||||
|
||||
// Self returns the object itself.
|
||||
func (amv *AMV) Self() Loggable {
|
||||
return amv
|
||||
}
|
||||
|
||||
// GetAMV returns the AMV with the given ID.
|
||||
func GetAMV(id string) (*AMV, error) {
|
||||
obj, err := DB.Get("AMV", id)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*AMV), nil
|
||||
}
|
||||
|
||||
// StreamAMVs returns a stream of all AMVs.
|
||||
func StreamAMVs() <-chan *AMV {
|
||||
channel := make(chan *AMV, nano.ChannelBufferSize)
|
||||
|
||||
go func() {
|
||||
for obj := range DB.All("AMV") {
|
||||
channel <- obj.(*AMV)
|
||||
}
|
||||
|
||||
close(channel)
|
||||
}()
|
||||
|
||||
return channel
|
||||
}
|
||||
|
||||
// AllAMVs returns a slice of all AMVs.
|
||||
func AllAMVs() []*AMV {
|
||||
all := make([]*AMV, 0, DB.Collection("AMV").Count())
|
||||
|
||||
stream := StreamAMVs()
|
||||
|
||||
for obj := range stream {
|
||||
all = append(all, obj)
|
||||
}
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
// FilterAMVs filters all AMVs by a custom function.
|
||||
func FilterAMVs(filter func(*AMV) bool) []*AMV {
|
||||
var filtered []*AMV
|
||||
|
||||
for obj := range DB.All("AMV") {
|
||||
realObject := obj.(*AMV)
|
||||
|
||||
if filter(realObject) {
|
||||
filtered = append(filtered, realObject)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
Reference in New Issue
Block a user