v.0.0.2 Убран duration из клипов
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
10466568d4
commit
7bfc7eb9a8
@ -7,7 +7,6 @@ type Clip struct {
|
|||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
VideoURL string `json:"video_url"`
|
VideoURL string `json:"video_url"`
|
||||||
ThumbnailURL string `json:"thumbnail_url"`
|
ThumbnailURL string `json:"thumbnail_url"`
|
||||||
Duration int `json:"duration"` // seconds
|
|
||||||
AuthorID int `json:"author_id"`
|
AuthorID int `json:"author_id"`
|
||||||
LikesCount int `json:"likes_count"`
|
LikesCount int `json:"likes_count"`
|
||||||
CommentsCount int `json:"comments_count"`
|
CommentsCount int `json:"comments_count"`
|
||||||
|
|||||||
@ -4,13 +4,10 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type VideoProcessor interface {
|
type VideoProcessor interface {
|
||||||
GenerateThumbnail(videoData []byte) ([]byte, error)
|
GenerateThumbnail(videoData []byte) ([]byte, error)
|
||||||
GetDuration(videoData []byte) (int, error)
|
|
||||||
TrimVideo(videoData []byte, maxDuration int) ([]byte, error)
|
TrimVideo(videoData []byte, maxDuration int) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,23 +20,24 @@ func NewVideoProcessor() VideoProcessor {
|
|||||||
|
|
||||||
func (vp *videoProcessor) GenerateThumbnail(videoData []byte) ([]byte, error) {
|
func (vp *videoProcessor) GenerateThumbnail(videoData []byte) ([]byte, error) {
|
||||||
cmd := exec.Command("ffmpeg",
|
cmd := exec.Command("ffmpeg",
|
||||||
"-i", "pipe:0", // читаем из stdin
|
"-i", "pipe:0",
|
||||||
"-ss", "00:00:01",
|
"-ss", "00:00:01",
|
||||||
"-vframes", "1",
|
"-vframes", "1",
|
||||||
"-q:v", "2",
|
"-q:v", "2",
|
||||||
"-f", "image2pipe", // вывод в pipe
|
"-f", "image2pipe",
|
||||||
"-c:v", "mjpeg",
|
"-c:v", "mjpeg",
|
||||||
"pipe:1", // пишем в stdout
|
"pipe:1",
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd.Stdin = bytes.NewReader(videoData)
|
cmd.Stdin = bytes.NewReader(videoData)
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
cmd.Stdout = &output
|
cmd.Stdout = &output
|
||||||
cmd.Stderr = &output // capture stderr for error messages
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ffmpeg failed: %s, error: %w", output.String(), err)
|
return nil, fmt.Errorf("ffmpeg failed: %s, error: %w", stderr.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if output.Len() == 0 {
|
if output.Len() == 0 {
|
||||||
@ -49,48 +47,11 @@ func (vp *videoProcessor) GenerateThumbnail(videoData []byte) ([]byte, error) {
|
|||||||
return output.Bytes(), nil
|
return output.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vp *videoProcessor) GetDuration(videoData []byte) (int, error) {
|
|
||||||
cmd := exec.Command("ffprobe",
|
|
||||||
"-i", "pipe:0",
|
|
||||||
"-show_entries", "format=duration",
|
|
||||||
"-v", "quiet",
|
|
||||||
"-of", "csv=p=0",
|
|
||||||
)
|
|
||||||
|
|
||||||
cmd.Stdin = bytes.NewReader(videoData)
|
|
||||||
output, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
debugCmd := exec.Command("ffprobe", "-i", "pipe:0")
|
|
||||||
debugCmd.Stdin = bytes.NewReader(videoData)
|
|
||||||
debugOutput, _ := debugCmd.CombinedOutput()
|
|
||||||
return 0, fmt.Errorf("ffprobe failed: %w, debug output: %s", err, string(debugOutput))
|
|
||||||
}
|
|
||||||
|
|
||||||
durationStr := strings.TrimSpace(string(output))
|
|
||||||
duration, err := strconv.ParseFloat(durationStr, 64)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("failed to parse duration '%s': %w", durationStr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(duration), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (vp *videoProcessor) TrimVideo(videoData []byte, maxDuration int) ([]byte, error) {
|
func (vp *videoProcessor) TrimVideo(videoData []byte, maxDuration int) ([]byte, error) {
|
||||||
// Сначала получаем длительность
|
// Просто обрезаем видео без проверки длительности
|
||||||
duration, err := vp.GetDuration(videoData)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get video duration: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если видео короче или равно лимиту, возвращаем оригинал
|
|
||||||
if duration <= maxDuration {
|
|
||||||
return videoData, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обрезаем видео
|
|
||||||
cmd := exec.Command("ffmpeg",
|
cmd := exec.Command("ffmpeg",
|
||||||
"-i", "pipe:0",
|
"-i", "pipe:0",
|
||||||
"-t", strconv.Itoa(maxDuration),
|
"-t", fmt.Sprintf("%d", maxDuration),
|
||||||
"-c", "copy",
|
"-c", "copy",
|
||||||
"-f", "mp4",
|
"-f", "mp4",
|
||||||
"pipe:1",
|
"pipe:1",
|
||||||
@ -98,12 +59,13 @@ func (vp *videoProcessor) TrimVideo(videoData []byte, maxDuration int) ([]byte,
|
|||||||
|
|
||||||
cmd.Stdin = bytes.NewReader(videoData)
|
cmd.Stdin = bytes.NewReader(videoData)
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
|
var stderr bytes.Buffer
|
||||||
cmd.Stdout = &output
|
cmd.Stdout = &output
|
||||||
cmd.Stderr = &output
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
err = cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ffmpeg trim failed: %s, error: %w", output.String(), err)
|
return nil, fmt.Errorf("ffmpeg trim failed: %s, error: %w", stderr.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if output.Len() == 0 {
|
if output.Len() == 0 {
|
||||||
|
|||||||
@ -181,7 +181,6 @@ func (h *GRPCHandler) clipToProto(clip *domain.Clip) *proto.Clip {
|
|||||||
Title: clip.Title,
|
Title: clip.Title,
|
||||||
VideoUrl: clip.VideoURL,
|
VideoUrl: clip.VideoURL,
|
||||||
ThumbnailUrl: clip.ThumbnailURL,
|
ThumbnailUrl: clip.ThumbnailURL,
|
||||||
Duration: int32(clip.Duration),
|
|
||||||
AuthorId: int32(clip.AuthorID),
|
AuthorId: int32(clip.AuthorID),
|
||||||
LikesCount: int32(clip.LikesCount),
|
LikesCount: int32(clip.LikesCount),
|
||||||
CommentsCount: int32(clip.CommentsCount),
|
CommentsCount: int32(clip.CommentsCount),
|
||||||
|
|||||||
@ -37,8 +37,8 @@ func NewClipRepository(db *sql.DB) ClipRepository {
|
|||||||
|
|
||||||
func (r *clipRepository) Create(ctx context.Context, clip *domain.Clip) error {
|
func (r *clipRepository) Create(ctx context.Context, clip *domain.Clip) error {
|
||||||
query := `
|
query := `
|
||||||
INSERT INTO clips (title, video_url, thumbnail_url, duration, author_id, likes_count, comments_count, created_at, updated_at)
|
INSERT INTO clips (title, video_url, thumbnail_url, author_id, likes_count, comments_count, created_at, updated_at)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||||
RETURNING id
|
RETURNING id
|
||||||
`
|
`
|
||||||
|
|
||||||
@ -46,7 +46,6 @@ func (r *clipRepository) Create(ctx context.Context, clip *domain.Clip) error {
|
|||||||
clip.Title,
|
clip.Title,
|
||||||
clip.VideoURL,
|
clip.VideoURL,
|
||||||
clip.ThumbnailURL,
|
clip.ThumbnailURL,
|
||||||
clip.Duration,
|
|
||||||
clip.AuthorID,
|
clip.AuthorID,
|
||||||
clip.LikesCount,
|
clip.LikesCount,
|
||||||
clip.CommentsCount,
|
clip.CommentsCount,
|
||||||
@ -63,7 +62,7 @@ func (r *clipRepository) Create(ctx context.Context, clip *domain.Clip) error {
|
|||||||
|
|
||||||
func (r *clipRepository) GetByID(ctx context.Context, id int) (*domain.Clip, error) {
|
func (r *clipRepository) GetByID(ctx context.Context, id int) (*domain.Clip, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, title, video_url, thumbnail_url, duration, author_id,
|
SELECT id, title, video_url, thumbnail_url, author_id,
|
||||||
likes_count, comments_count, created_at, updated_at
|
likes_count, comments_count, created_at, updated_at
|
||||||
FROM clips
|
FROM clips
|
||||||
WHERE id = $1 AND deleted_at IS NULL
|
WHERE id = $1 AND deleted_at IS NULL
|
||||||
@ -75,7 +74,6 @@ func (r *clipRepository) GetByID(ctx context.Context, id int) (*domain.Clip, err
|
|||||||
&clip.Title,
|
&clip.Title,
|
||||||
&clip.VideoURL,
|
&clip.VideoURL,
|
||||||
&clip.ThumbnailURL,
|
&clip.ThumbnailURL,
|
||||||
&clip.Duration,
|
|
||||||
&clip.AuthorID,
|
&clip.AuthorID,
|
||||||
&clip.LikesCount,
|
&clip.LikesCount,
|
||||||
&clip.CommentsCount,
|
&clip.CommentsCount,
|
||||||
@ -104,7 +102,7 @@ func (r *clipRepository) GetByAuthorID(ctx context.Context, authorID, limit, off
|
|||||||
|
|
||||||
// Получаем клипы
|
// Получаем клипы
|
||||||
query := `
|
query := `
|
||||||
SELECT id, title, video_url, thumbnail_url, duration, author_id,
|
SELECT id, title, video_url, thumbnail_url, author_id,
|
||||||
likes_count, comments_count, created_at, updated_at
|
likes_count, comments_count, created_at, updated_at
|
||||||
FROM clips
|
FROM clips
|
||||||
WHERE author_id = $1 AND deleted_at IS NULL
|
WHERE author_id = $1 AND deleted_at IS NULL
|
||||||
@ -126,7 +124,6 @@ func (r *clipRepository) GetByAuthorID(ctx context.Context, authorID, limit, off
|
|||||||
&clip.Title,
|
&clip.Title,
|
||||||
&clip.VideoURL,
|
&clip.VideoURL,
|
||||||
&clip.ThumbnailURL,
|
&clip.ThumbnailURL,
|
||||||
&clip.Duration,
|
|
||||||
&clip.AuthorID,
|
&clip.AuthorID,
|
||||||
&clip.LikesCount,
|
&clip.LikesCount,
|
||||||
&clip.CommentsCount,
|
&clip.CommentsCount,
|
||||||
@ -157,7 +154,7 @@ func (r *clipRepository) GetAll(ctx context.Context, limit, offset int) ([]*doma
|
|||||||
|
|
||||||
// Получаем клипы
|
// Получаем клипы
|
||||||
query := `
|
query := `
|
||||||
SELECT id, title, video_url, thumbnail_url, duration, author_id,
|
SELECT id, title, video_url, thumbnail_url, author_id,
|
||||||
likes_count, comments_count, created_at, updated_at
|
likes_count, comments_count, created_at, updated_at
|
||||||
FROM clips
|
FROM clips
|
||||||
WHERE deleted_at IS NULL
|
WHERE deleted_at IS NULL
|
||||||
@ -179,7 +176,6 @@ func (r *clipRepository) GetAll(ctx context.Context, limit, offset int) ([]*doma
|
|||||||
&clip.Title,
|
&clip.Title,
|
||||||
&clip.VideoURL,
|
&clip.VideoURL,
|
||||||
&clip.ThumbnailURL,
|
&clip.ThumbnailURL,
|
||||||
&clip.Duration,
|
|
||||||
&clip.AuthorID,
|
&clip.AuthorID,
|
||||||
&clip.LikesCount,
|
&clip.LikesCount,
|
||||||
&clip.CommentsCount,
|
&clip.CommentsCount,
|
||||||
@ -282,6 +278,7 @@ func (r *clipRepository) DecrementCommentsCount(ctx context.Context, clipID int)
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *clipRepository) GetClipURLs(ctx context.Context, clipID int) (string, string, error) {
|
func (r *clipRepository) GetClipURLs(ctx context.Context, clipID int) (string, string, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT video_url, thumbnail_url
|
SELECT video_url, thumbnail_url
|
||||||
@ -301,10 +298,9 @@ func (r *clipRepository) GetClipURLs(ctx context.Context, clipID int) (string, s
|
|||||||
return videoURL, thumbnailURL, nil
|
return videoURL, thumbnailURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClipWithURLs возвращает клип с URL
|
|
||||||
func (r *clipRepository) GetClipWithURLs(ctx context.Context, clipID int) (*domain.Clip, error) {
|
func (r *clipRepository) GetClipWithURLs(ctx context.Context, clipID int) (*domain.Clip, error) {
|
||||||
query := `
|
query := `
|
||||||
SELECT id, title, video_url, thumbnail_url, duration, author_id,
|
SELECT id, title, video_url, thumbnail_url, author_id,
|
||||||
likes_count, comments_count, created_at, updated_at
|
likes_count, comments_count, created_at, updated_at
|
||||||
FROM clips
|
FROM clips
|
||||||
WHERE id = $1 AND deleted_at IS NULL
|
WHERE id = $1 AND deleted_at IS NULL
|
||||||
@ -316,7 +312,6 @@ func (r *clipRepository) GetClipWithURLs(ctx context.Context, clipID int) (*doma
|
|||||||
&clip.Title,
|
&clip.Title,
|
||||||
&clip.VideoURL,
|
&clip.VideoURL,
|
||||||
&clip.ThumbnailURL,
|
&clip.ThumbnailURL,
|
||||||
&clip.Duration,
|
|
||||||
&clip.AuthorID,
|
&clip.AuthorID,
|
||||||
&clip.LikesCount,
|
&clip.LikesCount,
|
||||||
&clip.CommentsCount,
|
&clip.CommentsCount,
|
||||||
|
|||||||
@ -54,8 +54,6 @@ func (s *clipService) CreateClip(ctx context.Context, req domain.CreateClipReque
|
|||||||
videoErrChan := make(chan error, 1)
|
videoErrChan := make(chan error, 1)
|
||||||
thumbnailChan := make(chan string, 1)
|
thumbnailChan := make(chan string, 1)
|
||||||
thumbnailErrChan := make(chan error, 1)
|
thumbnailErrChan := make(chan error, 1)
|
||||||
durationChan := make(chan int, 1)
|
|
||||||
durationErrChan := make(chan error, 1)
|
|
||||||
|
|
||||||
// Горутина для загрузки видео в S3
|
// Горутина для загрузки видео в S3
|
||||||
go func() {
|
go func() {
|
||||||
@ -83,19 +81,8 @@ func (s *clipService) CreateClip(ctx context.Context, req domain.CreateClipReque
|
|||||||
thumbnailChan <- thumbnailURL
|
thumbnailChan <- thumbnailURL
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Горутина для получения длительности
|
// 3. Ждем результаты операций
|
||||||
go func() {
|
|
||||||
duration, err := s.videoProcessor.GetDuration(trimmedVideoData)
|
|
||||||
if err != nil {
|
|
||||||
durationErrChan <- fmt.Errorf("%s: failed to get duration: %w", op, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
durationChan <- duration
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 3. Ждем результаты всех операций
|
|
||||||
var videoURL, thumbnailURL string
|
var videoURL, thumbnailURL string
|
||||||
var duration int
|
|
||||||
|
|
||||||
// Ждем загрузки видео
|
// Ждем загрузки видео
|
||||||
select {
|
select {
|
||||||
@ -117,19 +104,6 @@ func (s *clipService) CreateClip(ctx context.Context, req domain.CreateClipReque
|
|||||||
return nil, fmt.Errorf("%s: operation cancelled", op)
|
return nil, fmt.Errorf("%s: operation cancelled", op)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ждем получения длительности
|
|
||||||
select {
|
|
||||||
case duration = <-durationChan:
|
|
||||||
case err := <-durationErrChan:
|
|
||||||
s.storage.DeleteVideo(ctx, videoURL)
|
|
||||||
s.storage.DeleteThumbnail(ctx, thumbnailURL)
|
|
||||||
return nil, err
|
|
||||||
case <-ctx.Done():
|
|
||||||
s.storage.DeleteVideo(ctx, videoURL)
|
|
||||||
s.storage.DeleteThumbnail(ctx, thumbnailURL)
|
|
||||||
return nil, fmt.Errorf("%s: operation cancelled", op)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Модерируем обрезанное видео по URL
|
// 4. Модерируем обрезанное видео по URL
|
||||||
videoAllowed, err := s.modClient.CheckVideoUrl(ctx, videoURL)
|
videoAllowed, err := s.modClient.CheckVideoUrl(ctx, videoURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -148,7 +122,6 @@ func (s *clipService) CreateClip(ctx context.Context, req domain.CreateClipReque
|
|||||||
Title: req.Title,
|
Title: req.Title,
|
||||||
VideoURL: videoURL,
|
VideoURL: videoURL,
|
||||||
ThumbnailURL: thumbnailURL,
|
ThumbnailURL: thumbnailURL,
|
||||||
Duration: duration,
|
|
||||||
AuthorID: req.UserID,
|
AuthorID: req.UserID,
|
||||||
LikesCount: 0,
|
LikesCount: 0,
|
||||||
CommentsCount: 0,
|
CommentsCount: 0,
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
-- Клипы
|
-- Клипы
|
||||||
CREATE TABLE clips (
|
CREATE TABLE clips (
|
||||||
id SERIAL PRIMARY KEY,
|
id SERIAL PRIMARY KEY,
|
||||||
title VARCHAR(255) NOT NULL,
|
title VARCHAR(255),
|
||||||
video_url TEXT NOT NULL,
|
video_url TEXT NOT NULL,
|
||||||
thumbnail_url TEXT NOT NULL,
|
thumbnail_url TEXT NOT NULL,
|
||||||
duration INT NOT NULL,
|
|
||||||
author_id INTEGER NOT NULL,
|
author_id INTEGER NOT NULL,
|
||||||
likes_count INTEGER DEFAULT 0,
|
likes_count INTEGER DEFAULT 0,
|
||||||
comments_count INTEGER DEFAULT 0,
|
comments_count INTEGER DEFAULT 0,
|
||||||
|
|||||||
@ -1129,12 +1129,11 @@ type Clip struct {
|
|||||||
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
|
Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`
|
||||||
VideoUrl string `protobuf:"bytes,3,opt,name=video_url,json=videoUrl,proto3" json:"video_url,omitempty"`
|
VideoUrl string `protobuf:"bytes,3,opt,name=video_url,json=videoUrl,proto3" json:"video_url,omitempty"`
|
||||||
ThumbnailUrl string `protobuf:"bytes,4,opt,name=thumbnail_url,json=thumbnailUrl,proto3" json:"thumbnail_url,omitempty"`
|
ThumbnailUrl string `protobuf:"bytes,4,opt,name=thumbnail_url,json=thumbnailUrl,proto3" json:"thumbnail_url,omitempty"`
|
||||||
Duration int32 `protobuf:"varint,5,opt,name=duration,proto3" json:"duration,omitempty"`
|
AuthorId int32 `protobuf:"varint,5,opt,name=author_id,json=authorId,proto3" json:"author_id,omitempty"`
|
||||||
AuthorId int32 `protobuf:"varint,6,opt,name=author_id,json=authorId,proto3" json:"author_id,omitempty"`
|
LikesCount int32 `protobuf:"varint,6,opt,name=likes_count,json=likesCount,proto3" json:"likes_count,omitempty"`
|
||||||
LikesCount int32 `protobuf:"varint,7,opt,name=likes_count,json=likesCount,proto3" json:"likes_count,omitempty"`
|
CommentsCount int32 `protobuf:"varint,7,opt,name=comments_count,json=commentsCount,proto3" json:"comments_count,omitempty"`
|
||||||
CommentsCount int32 `protobuf:"varint,8,opt,name=comments_count,json=commentsCount,proto3" json:"comments_count,omitempty"`
|
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
||||||
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
|
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
|
||||||
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
|
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -1197,13 +1196,6 @@ func (x *Clip) GetThumbnailUrl() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Clip) GetDuration() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.Duration
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Clip) GetAuthorId() int32 {
|
func (x *Clip) GetAuthorId() int32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.AuthorId
|
return x.AuthorId
|
||||||
@ -1466,22 +1458,20 @@ const file_clip_proto_rawDesc = "" +
|
|||||||
"\x14DeleteCommentRequest\x12\x1d\n" +
|
"\x14DeleteCommentRequest\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"comment_id\x18\x01 \x01(\x05R\tcommentId\x12\x17\n" +
|
"comment_id\x18\x01 \x01(\x05R\tcommentId\x12\x17\n" +
|
||||||
"\auser_id\x18\x02 \x01(\x05R\x06userId\"\xe5\x02\n" +
|
"\auser_id\x18\x02 \x01(\x05R\x06userId\"\xc9\x02\n" +
|
||||||
"\x04Clip\x12\x0e\n" +
|
"\x04Clip\x12\x0e\n" +
|
||||||
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x14\n" +
|
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x14\n" +
|
||||||
"\x05title\x18\x02 \x01(\tR\x05title\x12\x1b\n" +
|
"\x05title\x18\x02 \x01(\tR\x05title\x12\x1b\n" +
|
||||||
"\tvideo_url\x18\x03 \x01(\tR\bvideoUrl\x12#\n" +
|
"\tvideo_url\x18\x03 \x01(\tR\bvideoUrl\x12#\n" +
|
||||||
"\rthumbnail_url\x18\x04 \x01(\tR\fthumbnailUrl\x12\x1a\n" +
|
"\rthumbnail_url\x18\x04 \x01(\tR\fthumbnailUrl\x12\x1b\n" +
|
||||||
"\bduration\x18\x05 \x01(\x05R\bduration\x12\x1b\n" +
|
"\tauthor_id\x18\x05 \x01(\x05R\bauthorId\x12\x1f\n" +
|
||||||
"\tauthor_id\x18\x06 \x01(\x05R\bauthorId\x12\x1f\n" +
|
"\vlikes_count\x18\x06 \x01(\x05R\n" +
|
||||||
"\vlikes_count\x18\a \x01(\x05R\n" +
|
|
||||||
"likesCount\x12%\n" +
|
"likesCount\x12%\n" +
|
||||||
"\x0ecomments_count\x18\b \x01(\x05R\rcommentsCount\x129\n" +
|
"\x0ecomments_count\x18\a \x01(\x05R\rcommentsCount\x129\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"created_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" +
|
"created_at\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"updated_at\x18\n" +
|
"updated_at\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\"\x87\x01\n" +
|
||||||
" \x01(\v2\x1a.google.protobuf.TimestampR\tupdatedAt\"\x87\x01\n" +
|
|
||||||
"\bClipLike\x12\x0e\n" +
|
"\bClipLike\x12\x0e\n" +
|
||||||
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x17\n" +
|
"\x02id\x18\x01 \x01(\x05R\x02id\x12\x17\n" +
|
||||||
"\aclip_id\x18\x02 \x01(\x05R\x06clipId\x12\x17\n" +
|
"\aclip_id\x18\x02 \x01(\x05R\x06clipId\x12\x17\n" +
|
||||||
|
|||||||
@ -138,12 +138,11 @@ message Clip {
|
|||||||
string title = 2;
|
string title = 2;
|
||||||
string video_url = 3;
|
string video_url = 3;
|
||||||
string thumbnail_url = 4;
|
string thumbnail_url = 4;
|
||||||
int32 duration = 5;
|
int32 author_id = 5;
|
||||||
int32 author_id = 6;
|
int32 likes_count = 6;
|
||||||
int32 likes_count = 7;
|
int32 comments_count = 7;
|
||||||
int32 comments_count = 8;
|
google.protobuf.Timestamp created_at = 8;
|
||||||
google.protobuf.Timestamp created_at = 9;
|
google.protobuf.Timestamp updated_at = 9;
|
||||||
google.protobuf.Timestamp updated_at = 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ClipLike {
|
message ClipLike {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user