@@ -0,0 +1,109 @@
|
||||
package dramaser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/common/db"
|
||||
"91porn-server/models/v/media_buy_record_mod"
|
||||
"91porn-server/models/v/mediacontentmod"
|
||||
"91porn-server/models/v/mediamod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDramaEntitlementRequired = errors.New("active drama entitlement is required")
|
||||
ErrDownloadResourceInvalid = errors.New("short drama episode is unavailable")
|
||||
)
|
||||
|
||||
const downloadTicketTTL = 6 * time.Hour
|
||||
|
||||
type DownloadAuthorizeRequest struct {
|
||||
MediaID primitive.ObjectID `json:"mediaId" binding:"required"`
|
||||
ContentID primitive.ObjectID `json:"contentId" binding:"required"`
|
||||
}
|
||||
|
||||
type DownloadAuthorizeResponse struct {
|
||||
MediaID primitive.ObjectID `json:"mediaId"`
|
||||
ContentID primitive.ObjectID `json:"contentId"`
|
||||
EpisodeNumber int `json:"episodeNumber"`
|
||||
Name string `json:"name"`
|
||||
Cover string `json:"cover"`
|
||||
DownloadURL string `json:"downloadUrl"`
|
||||
H265DownloadURL string `json:"h265DownloadUrl"`
|
||||
MediaSize int64 `json:"mediaSize"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
RemainingDownloadCount int64 `json:"remainingDownloadCount"`
|
||||
Charged bool `json:"charged"`
|
||||
}
|
||||
|
||||
func AuthorizeDownload(ctx context.Context, uid uint64, requestID string, req DownloadAuthorizeRequest, now time.Time) (DownloadAuthorizeResponse, error) {
|
||||
requestID = strings.TrimSpace(requestID)
|
||||
if len(requestID) < 1 || len(requestID) > 128 {
|
||||
return DownloadAuthorizeResponse{}, errors.New("X-Request-ID长度必须为1到128")
|
||||
}
|
||||
media, err := mediamod.GetInfo(req.MediaID)
|
||||
if err != nil || media.MediaType != mediamod.MediaTypeDrama || media.Status != 1 || media.IsDelete {
|
||||
return DownloadAuthorizeResponse{}, ErrDownloadResourceInvalid
|
||||
}
|
||||
content, err := mediacontentmod.GetInfo(req.ContentID)
|
||||
if err != nil || content.MediaID != req.MediaID || content.MediaType != mediamod.MediaTypeDrama || content.Status != 1 || !content.IsActive || content.IsDelete {
|
||||
return DownloadAuthorizeResponse{}, ErrDownloadResourceInvalid
|
||||
}
|
||||
requestKey := downloadRequestKey(uid, requestID)
|
||||
fingerprint := req.MediaID.Hex() + ":" + req.ContentID.Hex()
|
||||
var wallet *walletmod.Wallet
|
||||
var charged bool
|
||||
for attempt := 0; attempt < 3; attempt++ {
|
||||
err = appg.VideoDB.TransCtx(ctx, func(t *db.MongoTool) error {
|
||||
user, userErr := usermod.FindUserByUIDTrans(t, uid)
|
||||
if userErr != nil {
|
||||
return userErr
|
||||
}
|
||||
bought := false
|
||||
if user != nil && !user.HasLocked && !user.DramaExpire.After(now) {
|
||||
bought, userErr = media_buy_record_mod.IsBuyWithTool(t, uid, req.MediaID, req.ContentID)
|
||||
if userErr != nil {
|
||||
return userErr
|
||||
}
|
||||
}
|
||||
if !canAuthorizeDramaDownload(user, bought, now) {
|
||||
return ErrDramaEntitlementRequired
|
||||
}
|
||||
wallet, charged, userErr = walletmod.AuthorizeDramaDownload(t, uid, requestKey, fingerprint, now)
|
||||
return userErr
|
||||
})
|
||||
if !errors.Is(err, walletmod.ErrDownloadAuthorizationRace) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return DownloadAuthorizeResponse{}, err
|
||||
}
|
||||
name := strings.TrimSpace(content.Name)
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("第%d集", content.EpisodeNumber)
|
||||
}
|
||||
return DownloadAuthorizeResponse{
|
||||
MediaID: req.MediaID, ContentID: req.ContentID, EpisodeNumber: content.EpisodeNumber,
|
||||
Name: name, Cover: content.Cover, DownloadURL: content.VideoUrl,
|
||||
H265DownloadURL: appg.H265URLForApp(content.H265Url), MediaSize: content.MediaSize,
|
||||
ExpiresAt: now.Add(downloadTicketTTL), RemainingDownloadCount: wallet.DownloadCount, Charged: charged,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func downloadRequestKey(uid uint64, requestID string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%d:%s", uid, requestID))))
|
||||
}
|
||||
|
||||
func canAuthorizeDramaDownload(user *usermod.User, bought bool, now time.Time) bool {
|
||||
return user != nil && !user.HasLocked && (user.DramaExpire.After(now) || bought)
|
||||
}
|
||||
Reference in New Issue
Block a user