diff --git a/internal/server/media/get_video.go b/internal/server/media/get_video.go index 2cd793e..b4a2009 100644 --- a/internal/server/media/get_video.go +++ b/internal/server/media/get_video.go @@ -6,13 +6,14 @@ import ( "path/filepath" "connectrpc.com/connect" - "github.com/nandesh-dev/subtle/generated/proto/media" + media_proto "github.com/nandesh-dev/subtle/generated/proto/media" + subtitle_proto "github.com/nandesh-dev/subtle/generated/proto/subtitle" "github.com/nandesh-dev/subtle/pkgs/db" "github.com/nandesh-dev/subtle/pkgs/filemanager" "github.com/nandesh-dev/subtle/pkgs/subtitle" ) -func (s ServiceHandler) GetVideo(ctx context.Context, req *connect.Request[media.GetVideoRequest]) (*connect.Response[media.GetVideoResponse], error) { +func (s ServiceHandler) GetVideo(ctx context.Context, req *connect.Request[media_proto.GetVideoRequest]) (*connect.Response[media_proto.GetVideoResponse], error) { var videoEntry db.Video if err := db.DB().Where(&db.Video{DirectoryPath: req.Msg.DirectoryPath, Filename: req.Msg.Name + req.Msg.Extension}). @@ -28,22 +29,23 @@ func (s ServiceHandler) GetVideo(ctx context.Context, req *connect.Request[media return nil, fmt.Errorf("Error extracting available raw stream from video: %v", err) } - res := media.GetVideoResponse{ - Subtitles: make([]*media.Subtitle, len(videoEntry.Subtitles)), - RawStreams: make([]*media.RawStream, len(*rawStreams)), + res := media_proto.GetVideoResponse{ + Subtitles: make([]*subtitle_proto.Subtitle, len(videoEntry.Subtitles)), + RawStreams: make([]*media_proto.RawStream, len(*rawStreams)), } for i, subtitleEntry := range videoEntry.Subtitles { - res.Subtitles[i] = &media.Subtitle{ - Language: subtitleEntry.Language, - ImportIsExternal: subtitleEntry.ImportIsExternal, - ImportVideoStreamIndex: int32(subtitleEntry.ImportVideoStreamIndex), - ExportPath: subtitleEntry.ExportPath, + res.Subtitles[i] = &subtitle_proto.Subtitle{ + Id: int32(subtitleEntry.ID), + Title: "Hello World", + Language: subtitleEntry.Language, + ImportIsExternal: subtitleEntry.ImportIsExternal, + ExportPath: subtitleEntry.ExportPath, } } for i, rawStream := range *rawStreams { - res.RawStreams[i] = &media.RawStream{ + res.RawStreams[i] = &media_proto.RawStream{ Index: int32(rawStream.Index()), Format: subtitle.MapFormat(rawStream.Format()), Language: rawStream.Language().String(), diff --git a/internal/server/server.go b/internal/server/server.go index b20b2f4..f4c7433 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -8,7 +8,9 @@ import ( connectcors "connectrpc.com/cors" "connectrpc.com/grpcreflect" "github.com/nandesh-dev/subtle/generated/proto/media/mediaconnect" + "github.com/nandesh-dev/subtle/generated/proto/subtitle/subtitleconnect" "github.com/nandesh-dev/subtle/internal/server/media" + "github.com/nandesh-dev/subtle/internal/server/subtitle" "github.com/nandesh-dev/subtle/pkgs/config" "github.com/rs/cors" "golang.org/x/net/http2" @@ -31,6 +33,9 @@ func (s *server) Listen(port int, enableReflection bool) error { path, handler := mediaconnect.NewMediaServiceHandler(media.ServiceHandler{}) mux.Handle(path, handler) + path, handler = subtitleconnect.NewSubtitleServiceHandler(subtitle.ServiceHandler{}) + mux.Handle(path, handler) + if config.Config().Server.GRPCReflection { path, handler = grpcreflect.NewHandlerV1Alpha(grpcreflect.NewStaticReflector( mediaconnect.MediaServiceName)) diff --git a/internal/server/subtitle/export_subtitle.go b/internal/server/subtitle/export_subtitle.go new file mode 100644 index 0000000..9763c73 --- /dev/null +++ b/internal/server/subtitle/export_subtitle.go @@ -0,0 +1,40 @@ +package subtitle + +import ( + "context" + "fmt" + "os" + "path/filepath" + + "connectrpc.com/connect" + subtitle_proto "github.com/nandesh-dev/subtle/generated/proto/subtitle" + "github.com/nandesh-dev/subtle/pkgs/db" + "github.com/nandesh-dev/subtle/pkgs/srt" + "github.com/nandesh-dev/subtle/pkgs/subtitle" +) + +func (s ServiceHandler) ExportSubtitle(ctx context.Context, req *connect.Request[subtitle_proto.ExportSubtitleRequest]) (*connect.Response[subtitle_proto.ExportSubtitleResponse], error) { + var subtitleEntry db.Subtitle + if err := db.DB().Where(&db.Subtitle{ID: int(req.Msg.SubtitleId)}).Preload("Segments").First(&subtitleEntry).Error; err != nil { + return nil, fmt.Errorf("Error getting subtitle entry from database: %v", err) + } + + if req.Msg.ExportFormat != "srt" { + return nil, fmt.Errorf("Invalid export format: %v", req.Msg.ExportFormat) + } + + sub := subtitle.NewTextSubtitle() + + for _, segmentEntry := range subtitleEntry.Segments { + sub.AddSegment(*subtitle.NewTextSegment(segmentEntry.StartTime, segmentEntry.EndTime, segmentEntry.Text)) + } + + out := srt.EncodeSubtitle(*sub) + + path := filepath.Join(req.Msg.ExportDirectoryPath, req.Msg.ExportFilename+".srt") + if err := os.WriteFile(path, []byte(out), 0644); err != nil { + return nil, fmt.Errorf("Error writting file to disk: %v", err) + } + + return &connect.Response[subtitle_proto.ExportSubtitleResponse]{}, nil +} diff --git a/internal/server/subtitle/get_available_export_format.go b/internal/server/subtitle/get_available_export_format.go new file mode 100644 index 0000000..2c5f30a --- /dev/null +++ b/internal/server/subtitle/get_available_export_format.go @@ -0,0 +1,15 @@ +package subtitle + +import ( + "context" + + "connectrpc.com/connect" + subtitle_proto "github.com/nandesh-dev/subtle/generated/proto/subtitle" +) + +func (s ServiceHandler) GetAvailableExportFormats(ctx context.Context, req *connect.Request[subtitle_proto.GetAvailableExportFormatsRequest]) (*connect.Response[subtitle_proto.GetAvailableExportFormatsResponse], error) { + res := subtitle_proto.GetAvailableExportFormatsResponse{ + Formats: []string{"srt"}, + } + return connect.NewResponse(&res), nil +} diff --git a/internal/server/subtitle/subtitle.go b/internal/server/subtitle/subtitle.go new file mode 100644 index 0000000..a4a7f9a --- /dev/null +++ b/internal/server/subtitle/subtitle.go @@ -0,0 +1,7 @@ +package subtitle + +import "github.com/nandesh-dev/subtle/generated/proto/subtitle/subtitleconnect" + +type ServiceHandler struct { + subtitleconnect.UnimplementedSubtitleServiceHandler +} diff --git a/proto/media/media.proto b/proto/media/media.proto index 520ff59..9af987d 100644 --- a/proto/media/media.proto +++ b/proto/media/media.proto @@ -2,6 +2,8 @@ syntax = "proto3"; package media; +import 'proto/subtitle/subtitle.proto'; + option go_package = "github.com/nandesh-dev/subtle/generated/proto/media"; service MediaService { @@ -16,7 +18,7 @@ message GetVideoRequest { } message GetVideoResponse { - repeated Subtitle subtitles = 1; + repeated subtitle.Subtitle subtitles = 1; repeated RawStream raw_streams = 2; } @@ -27,15 +29,6 @@ message RawStream { string title = 4; } -message Subtitle { - string language = 1; - - bool importIsExternal = 2; - int32 importVideoStreamIndex = 3; - - string exportPath = 4; -} - message GetDirectoryRequest { string path = 1; } diff --git a/proto/subtitle/subtitle.proto b/proto/subtitle/subtitle.proto new file mode 100644 index 0000000..0248e74 --- /dev/null +++ b/proto/subtitle/subtitle.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +package subtitle; + +option go_package = "github.com/nandesh-dev/subtle/generated/proto/subtitle"; + +service SubtitleService { + rpc GetAvailableExportFormats(GetAvailableExportFormatsRequest) returns (GetAvailableExportFormatsResponse); + rpc ExportSubtitle(ExportSubtitleRequest) returns (ExportSubtitleResponse); +} + +message GetAvailableExportFormatsRequest {} + +message GetAvailableExportFormatsResponse { + repeated string formats = 1; +} + +message Subtitle { + int32 id = 1; + + string title = 2; + string language = 3; + + bool importIsExternal = 4; + string exportPath = 5; +} + +message ExportSubtitleRequest { + int32 subtitleId = 1; + + string exportDirectoryPath = 2; + string exportFilename = 3; + string exportFormat = 4; +} + +message ExportSubtitleResponse {} diff --git a/web/src/main.tsx b/web/src/main.tsx index bf107f2..43ab9e6 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -27,7 +27,7 @@ const router = createBrowserRouter([ element: , }, { - path: 'video', + path: 'media/video', element: