feat: finish the upload, playback, and test gaps from the docs
ci / api / api (push) Successful in 40s
ci / db / db (push) Successful in 1m3s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Failing after 1m48s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 46s
ci / ts / ts (packages + web) (push) Failing after 4m57s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 5m7s
ci / api / api (push) Successful in 40s
ci / db / db (push) Successful in 1m3s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Failing after 1m48s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 46s
ci / ts / ts (packages + web) (push) Failing after 4m57s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 5m7s
Quality upgrades, batch covers, staging preview, and resume after reload are in the queue. Loudness and crossfade work on web and Android, and the documented checks now have suites. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
2b76060ea3
commit
066ba24834
61 files changed
+2818
-463
No files matched your search
@@ -10,17 +10,22 @@ import (
|
||||
"pipu/api/internal/api/gen"
|
||||
"pipu/api/internal/auth"
|
||||
"pipu/api/internal/platform/apperr"
|
||||
"pipu/api/internal/platform/sign"
|
||||
)
|
||||
|
||||
// Handler — эндпоинты очереди на добавление (часть gen.StrictServerInterface).
|
||||
type Handler struct {
|
||||
svc *Service
|
||||
svc *Service
|
||||
signer *sign.Signer
|
||||
xAccel bool
|
||||
}
|
||||
|
||||
// NewHandler создаёт обработчик.
|
||||
func NewHandler(svc *Service) *Handler { return &Handler{svc: svc} }
|
||||
// NewHandler создаёт обработчик. signer подписывает ссылки прослушивания из staging.
|
||||
func NewHandler(svc *Service, signer *sign.Signer, xAccel bool) *Handler {
|
||||
return &Handler{svc: svc, signer: signer, xAccel: xAccel}
|
||||
}
|
||||
|
||||
func fileDTO(ctx context.Context, f File) gen.UploadFile {
|
||||
func (h *Handler) fileDTO(ctx context.Context, f File) gen.UploadFile {
|
||||
out := gen.UploadFile{
|
||||
Id: f.ID, BatchId: f.BatchID, BatchComment: f.BatchComment,
|
||||
Author: gen.UserRef{Id: f.AuthorID, Username: f.AuthorName},
|
||||
@@ -28,7 +33,7 @@ func fileDTO(ctx context.Context, f File) gen.UploadFile {
|
||||
SizeBytes: f.Size, ReceivedBytes: f.Received, Format: f.Format, Codec: f.Codec, Bitrate: f.Bitrate,
|
||||
SampleRate: f.SampleRate, BitDepth: f.BitDepth, DurationMs: f.DurationMs, Tags: f.Tags,
|
||||
MetadataEdited: f.MetadataEdited, MatchedArtistId: f.MatchedArtistID, MatchedAlbumId: f.MatchedAlbumID,
|
||||
DuplicateOfTrackId: f.DuplicateOfTrackID, PossibleDuplicate: f.PossibleDuplicate,
|
||||
DuplicateOfTrackId: f.DuplicateOfTrackID, PossibleDuplicate: f.PossibleDuplicate, QualityUpgrade: f.QualityUpgrade,
|
||||
TargetSourceId: f.TargetSourceID, TargetRelPath: f.TargetRelPath, StatusReason: f.StatusReason,
|
||||
ReviewedAt: f.ReviewedAt, CreatedAt: f.CreatedAt, UpdatedAt: f.UpdatedAt,
|
||||
}
|
||||
@@ -47,6 +52,12 @@ func fileDTO(ctx context.Context, f File) gen.UploadFile {
|
||||
u := TusURL(f.ID)
|
||||
out.TusUrl = &u
|
||||
}
|
||||
if h.signer != nil && f.StagingPath != "" && f.Status.KeepsStaging() {
|
||||
if p, ok := auth.PrincipalFrom(ctx); ok && auth.Can(p.Role, auth.PermReviewUploads) {
|
||||
u := h.streamURL(f.ID)
|
||||
out.StreamUrl = &u
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -64,10 +75,10 @@ func metaFromDTO(m gen.UploadMeta) Meta {
|
||||
}
|
||||
}
|
||||
|
||||
func pageDTO(ctx context.Context, files []File, next string) gen.UploadFilePage {
|
||||
func (h *Handler) pageDTO(ctx context.Context, files []File, next string) gen.UploadFilePage {
|
||||
out := gen.UploadFilePage{Items: make([]gen.UploadFile, 0, len(files))}
|
||||
for _, f := range files {
|
||||
out.Items = append(out.Items, fileDTO(ctx, f))
|
||||
out.Items = append(out.Items, h.fileDTO(ctx, f))
|
||||
}
|
||||
if next != "" {
|
||||
out.NextCursor = &next
|
||||
@@ -96,7 +107,7 @@ func deref[T any](p *T, def T) T {
|
||||
func settingsDTO(s Settings) gen.UploadSettings {
|
||||
return gen.UploadSettings{
|
||||
QueueLimitBytes: s.QueueLimitBytes, PerUserLimitBytes: s.PerUserLimitBytes, MaxFileBytes: s.MaxFileBytes,
|
||||
DefaultSourceId: s.DefaultSourceID,
|
||||
DefaultSourceId: s.DefaultSourceID, AllowQualityUpgrade: s.AllowQualityUpgrade,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +185,7 @@ func (h *Handler) ListMyUploads(ctx context.Context, req gen.ListMyUploadsReques
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.ListMyUploads200JSONResponse(pageDTO(ctx, files, next)), nil
|
||||
return gen.ListMyUploads200JSONResponse(h.pageDTO(ctx, files, next)), nil
|
||||
}
|
||||
|
||||
// WithdrawUpload реализует gen.StrictServerInterface.
|
||||
@@ -195,7 +206,7 @@ func (h *Handler) ListUploads(ctx context.Context, req gen.ListUploadsRequestObj
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.ListUploads200JSONResponse(pageDTO(ctx, files, next)), nil
|
||||
return gen.ListUploads200JSONResponse(h.pageDTO(ctx, files, next)), nil
|
||||
}
|
||||
|
||||
// UpdateUpload реализует gen.StrictServerInterface.
|
||||
@@ -209,7 +220,7 @@ func (h *Handler) UpdateUpload(ctx context.Context, req gen.UpdateUploadRequestO
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.UpdateUpload200JSONResponse(fileDTO(ctx, f)), nil
|
||||
return gen.UpdateUpload200JSONResponse(h.fileDTO(ctx, f)), nil
|
||||
}
|
||||
|
||||
// RetryUploadImport реализует gen.StrictServerInterface.
|
||||
@@ -218,7 +229,7 @@ func (h *Handler) RetryUploadImport(ctx context.Context, req gen.RetryUploadImpo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.RetryUploadImport200JSONResponse(fileDTO(ctx, f)), nil
|
||||
return gen.RetryUploadImport200JSONResponse(h.fileDTO(ctx, f)), nil
|
||||
}
|
||||
|
||||
// ReviewUploads реализует gen.StrictServerInterface.
|
||||
@@ -272,9 +283,59 @@ func (h *Handler) UpdateUploadSettings(ctx context.Context, req gen.UpdateUpload
|
||||
QueueLimitBytes: b.QueueLimitBytes, MaxFileBytes: b.MaxFileBytes,
|
||||
PerUserLimitBytes: b.PerUserLimitBytes, ClearPerUser: b.PerUserLimitBytes == nil,
|
||||
DefaultSourceID: b.DefaultSourceId, ClearDefault: b.DefaultSourceId == nil,
|
||||
AllowQualityUpgrade: b.AllowQualityUpgrade,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.UpdateUploadSettings200JSONResponse(settingsDTO(s)), nil
|
||||
}
|
||||
|
||||
// UnseenUploadDecisions реализует gen.StrictServerInterface.
|
||||
func (h *Handler) UnseenUploadDecisions(ctx context.Context, _ gen.UnseenUploadDecisionsRequestObject) (gen.UnseenUploadDecisionsResponseObject, error) {
|
||||
n, err := h.svc.Unseen(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.UnseenUploadDecisions200JSONResponse{Count: n}, nil
|
||||
}
|
||||
|
||||
// MarkUploadsSeen реализует gen.StrictServerInterface.
|
||||
func (h *Handler) MarkUploadsSeen(ctx context.Context, _ gen.MarkUploadsSeenRequestObject) (gen.MarkUploadsSeenResponseObject, error) {
|
||||
if err := h.svc.MarkSeen(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.MarkUploadsSeen204Response{}, nil
|
||||
}
|
||||
|
||||
// UpdateUploadBatch реализует gen.StrictServerInterface.
|
||||
func (h *Handler) UpdateUploadBatch(ctx context.Context, req gen.UpdateUploadBatchRequestObject) (gen.UpdateUploadBatchResponseObject, error) {
|
||||
b := req.Body
|
||||
in := BatchPatch{}
|
||||
if b.Artist != nil {
|
||||
in.Artist = b.Artist
|
||||
}
|
||||
if b.Album != nil {
|
||||
in.Album = b.Album
|
||||
}
|
||||
if b.Year != nil {
|
||||
in.SetYear = true
|
||||
in.Year = b.Year
|
||||
}
|
||||
if b.CoverBase64 != nil {
|
||||
if *b.CoverBase64 == "" {
|
||||
in.ClearCover = true
|
||||
} else {
|
||||
raw, err := DecodeCover(*b.CoverBase64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
in.Cover = raw
|
||||
}
|
||||
}
|
||||
files, err := h.svc.UpdateBatch(ctx, req.Id, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return gen.UpdateUploadBatch200JSONResponse{Items: h.pageDTO(ctx, files, "").Items}, nil
|
||||
}
|
||||
Reference in new issue
Block a user