diff --git a/backend/cmd/progress-test/main.go b/backend/cmd/progress-test/main.go index 3151ed4..f42ba44 100644 --- a/backend/cmd/progress-test/main.go +++ b/backend/cmd/progress-test/main.go @@ -44,9 +44,14 @@ func main() { // Create a MediaSetService mediaSetService := media.NewMediaSetService(store, &youtubeClient, s3Client) + mediaSet, err := mediaSetService.Get(ctx, videoID) + if err != nil { + log.Fatalf("error calling fetch service: %v", err) + } + // Create a progressReader - // TODO: fix - progressReader, err := mediaSetService.GetAudio(ctx, uuid.New(), 100) + id := uuid.MustParse(mediaSet.ID) + progressReader, err := mediaSetService.GetAudio(ctx, id, 100) if err != nil { log.Fatalf("error calling fetch service: %v", err) } diff --git a/backend/media/service.go b/backend/media/service.go index 184621b..f2a9701 100644 --- a/backend/media/service.go +++ b/backend/media/service.go @@ -22,6 +22,7 @@ const ( rawAudioCodec = "pcm_s16le" rawAudioFormat = "s16le" rawAudioSampleRate = 48_000 + rawAudioMimeType = "audio/raw" ) const ( @@ -50,6 +51,7 @@ type Store interface { GetMediaSet(ctx context.Context, id uuid.UUID) (store.MediaSet, error) GetMediaSetByYoutubeID(ctx context.Context, youtubeID string) (store.MediaSet, error) CreateMediaSet(ctx context.Context, arg store.CreateMediaSetParams) (store.MediaSet, error) + SetAudioUploaded(ctx context.Context, arg store.SetAudioUploadedParams) (store.MediaSet, error) } // S3Client wraps the AWS S3 service client. @@ -127,7 +129,7 @@ func (s *MediaSetService) createMediaSet(ctx context.Context, youtubeID string) AudioYoutubeItag: int32(audioMetadata.YoutubeItag), AudioChannels: int32(audioMetadata.Channels), AudioFramesApprox: audioMetadata.ApproxFrames, - AudioSampleRateRaw: int32(audioMetadata.SampleRate), + AudioSampleRate: int32(audioMetadata.SampleRate), AudioMimeTypeEncoded: audioMetadata.MimeType, VideoYoutubeItag: int32(videoMetadata.YoutubeItag), VideoMimeType: videoMetadata.MimeType, @@ -162,8 +164,8 @@ func (s *MediaSetService) findMediaSet(ctx context.Context, youtubeID string) (* Bytes: 0, // DEPRECATED Channels: int(mediaSet.AudioChannels), ApproxFrames: int64(mediaSet.AudioFramesApprox), - Frames: mediaSet.AudioFramesRaw.Int64, - SampleRate: int(mediaSet.AudioSampleRateRaw), + Frames: mediaSet.AudioFrames.Int64, + SampleRate: int(mediaSet.AudioSampleRate), MimeType: mediaSet.AudioMimeTypeEncoded, }, Video: Video{ @@ -258,13 +260,13 @@ func (s *MediaSetService) GetAudio(ctx context.Context, id uuid.UUID, numBins in return nil, fmt.Errorf("error creating ffmpegreader: %v", err) } - // set up uploader, this is writer 1 + s3Key := fmt.Sprintf("media_sets/%s/audio.webm", id) uploader, err := newMultipartUploadWriter( ctx, s.s3, s3Bucket, - fmt.Sprintf("media_sets/%s/audio.webm", id), - "application/octet-stream", + s3Key, + rawAudioMimeType, ) if err != nil { return nil, fmt.Errorf("error creating uploader: %v", err) @@ -276,24 +278,29 @@ func (s *MediaSetService) GetAudio(ctx context.Context, id uuid.UUID, numBins in 100, ) - state := fetchAudioState{ + state := getAudioState{ fetchAudioProgressReader: fetchAudioProgressReader, ffmpegReader: ffmpegReader, uploader: uploader, + s3Bucket: s3Bucket, + s3Key: s3Key, + store: s.store, } go state.run(ctx) return &state, nil } -type fetchAudioState struct { +type getAudioState struct { *fetchAudioProgressReader - ffmpegReader io.ReadCloser - uploader *multipartUploadWriter + ffmpegReader io.ReadCloser + uploader *multipartUploadWriter + s3Bucket, s3Key string + store Store } -func (s *fetchAudioState) run(ctx context.Context) { +func (s *getAudioState) run(ctx context.Context) { mw := io.MultiWriter(s, s.uploader) done := make(chan error) var err error @@ -326,6 +333,17 @@ outer: } } + if err == nil { + _, updateErr := s.store.SetAudioUploaded(ctx, store.SetAudioUploadedParams{ + AudioS3Bucket: sqlString(s.s3Bucket), + AudioS3Key: sqlString(s.s3Key), + }) + + if updateErr != nil { + err = updateErr + } + } + if err != nil { newCtx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() @@ -341,3 +359,7 @@ outer: log.Printf("error closing peak iterator: %v", iterErr) } } + +func sqlString(s string) sql.NullString { + return sql.NullString{String: s, Valid: true} +} diff --git a/backend/media/uploader.go b/backend/media/uploader.go index c48c58f..897bbfd 100644 --- a/backend/media/uploader.go +++ b/backend/media/uploader.go @@ -45,7 +45,8 @@ func newMultipartUploadWriter(ctx context.Context, s3Client S3Client, bucket, ke return nil, fmt.Errorf("error creating multipart upload: %v", err) } - b := make([]byte, 0, targetPartSizeBytes+16_384) + const bufferOverflowSize = 16_384 + b := make([]byte, 0, targetPartSizeBytes+bufferOverflowSize) return &multipartUploadWriter{ ctx: ctx, diff --git a/backend/server/server.go b/backend/server/server.go index 50fef6f..41a007e 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -65,20 +65,17 @@ func (c *mediaSetServiceController) Get(ctx context.Context, request *pbMediaSet } result := pbMediaSet.MediaSet{ - Id: mediaSet.YoutubeID, - Audio: &pbMediaSet.MediaSet_Audio{ - Channels: int32(mediaSet.Audio.Channels), - Frames: mediaSet.Audio.Frames, - ApproxFrames: mediaSet.Audio.ApproxFrames, - SampleRate: int32(mediaSet.Audio.SampleRate), - YoutubeItag: int32(mediaSet.Audio.YoutubeItag), - MimeType: mediaSet.Audio.MimeType, - }, - Video: &pbMediaSet.MediaSet_Video{ - Duration: durationpb.New(mediaSet.Video.Duration), - YoutubeItag: int32(mediaSet.Video.YoutubeItag), - MimeType: mediaSet.Video.MimeType, - }, + Id: mediaSet.ID, + YoutubeId: mediaSet.YoutubeID, + AudioChannels: int32(mediaSet.Audio.Channels), + AudioFrames: mediaSet.Audio.Frames, + AudioApproxFrames: mediaSet.Audio.ApproxFrames, + AudioSampleRate: int32(mediaSet.Audio.SampleRate), + AudioYoutubeItag: int32(mediaSet.Audio.YoutubeItag), + AudioMimeType: mediaSet.Audio.MimeType, + VideoDuration: durationpb.New(mediaSet.Video.Duration), + VideoYoutubeItag: int32(mediaSet.Video.YoutubeItag), + VideoMimeType: mediaSet.Video.MimeType, } return &result, nil diff --git a/backend/sql/queries.sql b/backend/sql/queries.sql index dc519bb..28d33e6 100644 --- a/backend/sql/queries.sql +++ b/backend/sql/queries.sql @@ -5,6 +5,11 @@ SELECT * FROM media_sets WHERE id = $1; SELECT * FROM media_sets WHERE youtube_id = $1; -- name: CreateMediaSet :one -INSERT INTO media_sets (youtube_id, audio_youtube_itag, audio_channels, audio_frames_approx, audio_sample_rate_raw, audio_mime_type_encoded, video_youtube_itag, video_mime_type, video_duration_nanos, created_at, updated_at) +INSERT INTO media_sets (youtube_id, audio_youtube_itag, audio_channels, audio_frames_approx, audio_sample_rate, audio_mime_type_encoded, video_youtube_itag, video_mime_type, video_duration_nanos, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW(), NOW()) RETURNING *; + +-- name: SetAudioUploaded :one +UPDATE media_sets + SET audio_s3_bucket = $1, audio_s3_key = $2, audio_s3_uploaded_at = NOW(), updated_at = NOW() + RETURNING *; diff --git a/frontend/package.json b/frontend/package.json index 6e8f320..d6a8596 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,6 +15,7 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "4.0.3", + "ts-proto": "^1.83.3", "typescript": "^4.1.2", "web-vitals": "^1.0.1" }, @@ -49,6 +50,6 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-react": "^7.25.1", "prettier": "2.4.0", - "ts-protoc-gen": "^0.15.0" + "rxjs": "^7.4.0" } } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 73deabb..d967e2c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,12 +1,15 @@ import { grpc } from '@improbable-eng/grpc-web'; +// import { +// MediaSet as MediaSetPb, +// GetRequest, +// GetAudioRequest, +// GetAudioProgress, +// } from './generated/media_set_pb'; import { - MediaSet as MediaSetPb, - GetRequest, - GetAudioRequest, - GetAudioProgress, -} from './generated/media_set_pb'; - -import { GetMediaSet } from './GrpcWrapper'; + MediaSet, + GrpcWebImpl, + MediaSetServiceClientImpl, +} from './generated/media_set'; import { useState, useEffect } from 'react'; import { VideoPreview } from './VideoPreview'; @@ -15,32 +18,13 @@ import { Waveform } from './Waveform'; import { ControlBar } from './ControlBar'; import { SeekBar } from './SeekBar'; import './App.css'; +import { Duration } from './generated/google/protobuf/duration'; const grpcHost = 'http://localhost:8888'; -// Audio corresponds to media.Audio. -export interface Audio { - bytes: number; - channels: number; - frames: number; - sampleRate: number; -} - -// Video corresponds to media.Video. -export interface Video { - bytes: number; - thumbnailWidth: number; - thumbnailHeight: number; - durationMillis: number; -} - -// MediaSet corresponds to media.MediaSet. -export interface MediaSet { - id: string; - source: string; - audio: Audio; - video: Video; -} +// ported from backend, where should they live? +const thumbnailWidth = 177; +const thumbnailHeight = 100; // Frames represents a selection of audio frames. export interface Frames { @@ -65,12 +49,14 @@ function App(): JSX.Element { // fetch mediaset on page load: useEffect(() => { (async function () { - const request = new GetRequest(); - request.setYoutubeId(videoID); + const rpc = new GrpcWebImpl('http://localhost:8888', {}); + const service = new MediaSetServiceClientImpl(rpc); + const mediaSet = await service.Get({ youtubeId: videoID }); - const mediaSet = await GetMediaSet(grpcHost, request); console.log('got media set:', mediaSet); + setMediaSet(mediaSet); + // const handleProgress = (progress: GetAudioProgress) => { // console.log('got progress', progress); // }; @@ -122,6 +108,8 @@ function App(): JSX.Element { return; } + return; + video.src = `http://localhost:8888/api/media_sets/${videoID}/video`; video.muted = false; video.volume = 1; @@ -134,7 +122,7 @@ function App(): JSX.Element { return; } - setViewport({ start: 0, end: mediaSet.audio.frames }); + setViewport({ start: 0, end: mediaSet.audioFrames }); }, [mediaSet]); useEffect(() => { @@ -150,7 +138,7 @@ function App(): JSX.Element { } if (selection.start >= selection.end) { - setViewport({ start: 0, end: mediaSet.audio.frames }); + setViewport({ start: 0, end: mediaSet.audioFrames }); return; } @@ -169,10 +157,7 @@ function App(): JSX.Element { flexDirection: 'column', } as React.CSSProperties; - let offsetPixels = 75; - if (mediaSet != null) { - offsetPixels = Math.floor(mediaSet.video.thumbnailWidth / 2); - } + const offsetPixels = Math.floor(thumbnailWidth / 2); if (mediaSet == null) { // TODO: improve @@ -212,7 +197,7 @@ function App(): JSX.Element { { video.currentTime = position; @@ -222,8 +207,8 @@ function App(): JSX.Element { @@ -232,3 +217,10 @@ function App(): JSX.Element { } export default App; + +function millisFromDuration(dur?: Duration): number { + if (dur == undefined) { + return 0; + } + return Math.floor(dur.seconds * 1000.0 + dur.nanos / 1000.0 / 1000.0); +} diff --git a/frontend/src/GrpcWrapper.tsx b/frontend/src/GrpcWrapper.tsx deleted file mode 100644 index ab02d9f..0000000 --- a/frontend/src/GrpcWrapper.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { grpc } from '@improbable-eng/grpc-web'; -import { MediaSetService } from './generated/media_set_pb_service'; -import { - MediaSet, - GetRequest, - GetAudioProgress, - GetAudioRequest, -} from './generated/media_set_pb'; - -export const GetMediaSet = ( - host: string, - request: GetRequest -): Promise => { - return new Promise((resolve, reject) => { - let result: MediaSet; - - grpc.invoke(MediaSetService.Get, { - host: host, - request: request, - onMessage: (mediaSet: MediaSet) => { - result = mediaSet; - }, - onEnd: ( - code: grpc.Code, - msg: string | undefined, - _trailers: grpc.Metadata - ) => { - if (code != 0) { - reject(new Error(`unexpected grpc code: ${code}, message: ${msg}`)); - return; - } - resolve(result); - }, - }); - }); -}; - -// export const etchMediaSetAudio = ( -// host: string, -// request: FetchAudioRequest, -// onProgress: { (progress: FetchAudioProgress): void } -// ) => { -// grpc.invoke(FetchService.FetchAudio, { -// host: 'http://localhost:8888', -// request: request, -// onMessage: onProgress, -// onEnd: ( -// code: grpc.Code, -// msg: string | undefined, -// trailers: grpc.Metadata -// ) => { -// console.log('fetch audio request ended'); -// }, -// }); -// }; diff --git a/frontend/src/Overview.tsx b/frontend/src/Overview.tsx index 0dcbb63..299d387 100644 --- a/frontend/src/Overview.tsx +++ b/frontend/src/Overview.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useRef, MouseEvent } from 'react'; -import { MediaSet, Frames } from './App'; +import { MediaSet } from './generated/media_set'; +import { Frames } from './App'; import { WaveformCanvas } from './WaveformCanvas'; import { mouseEventToCanvasX } from './Helpers'; import { secsToCanvasX } from './Helpers'; @@ -49,11 +50,11 @@ export const Overview: React.FC = ({ return; } - const resp = await fetch( - `http://localhost:8888/api/media_sets/${mediaSet.id}/peaks?start=0&end=${mediaSet.audio.frames}&bins=${CanvasLogicalWidth}` - ); - const peaks = await resp.json(); - setPeaks(peaks); + // const resp = await fetch( + // `http://localhost:8888/api/media_sets/${mediaSet.id}/peaks?start=0&end=${mediaSet.audioFrames}&bins=${CanvasLogicalWidth}` + // ); + // const peaks = await resp.json(); + // setPeaks(peaks); })(); }, [mediaSet]); @@ -84,9 +85,9 @@ export const Overview: React.FC = ({ if (currentSelection.start < currentSelection.end) { const x1 = - (currentSelection.start / mediaSet.audio.frames) * CanvasLogicalWidth; + (currentSelection.start / mediaSet.audioFrames) * CanvasLogicalWidth; const x2 = - (currentSelection.end / mediaSet.audio.frames) * CanvasLogicalWidth; + (currentSelection.end / mediaSet.audioFrames) * CanvasLogicalWidth; ctx.beginPath(); ctx.strokeStyle = 'red'; @@ -98,10 +99,10 @@ export const Overview: React.FC = ({ } // draw position marker: - const fullSelection = { start: 0, end: mediaSet.audio.frames }; // constantize? + const fullSelection = { start: 0, end: mediaSet.audioFrames }; // constantize? const x = secsToCanvasX( position, - mediaSet.audio.sampleRate, + mediaSet.audioSampleRate, fullSelection ); // should never happen: @@ -135,7 +136,7 @@ export const Overview: React.FC = ({ } const frame = Math.floor( - mediaSet.audio.frames * + mediaSet.audioFrames * (mouseEventToCanvasX(evt) / evt.currentTarget.width) ); @@ -155,7 +156,7 @@ export const Overview: React.FC = ({ } const frame = Math.floor( - mediaSet.audio.frames * + mediaSet.audioFrames * (mouseEventToCanvasX(evt) / evt.currentTarget.width) ); @@ -164,8 +165,8 @@ export const Overview: React.FC = ({ const frameCount = selection.end - selection.start; let start = Math.max(0, selection.start + diff); let end = start + frameCount; - if (end > mediaSet.audio.frames) { - end = mediaSet.audio.frames; + if (end > mediaSet.audioFrames) { + end = mediaSet.audioFrames; start = end - frameCount; } setNewSelection({ diff --git a/frontend/src/Waveform.tsx b/frontend/src/Waveform.tsx index e76785f..7673b35 100644 --- a/frontend/src/Waveform.tsx +++ b/frontend/src/Waveform.tsx @@ -1,5 +1,6 @@ import { useEffect, useState, useRef } from 'react'; -import { Frames, MediaSet } from './App'; +import { Frames } from './App'; +import { MediaSet } from './generated/media_set'; import { WaveformCanvas } from './WaveformCanvas'; import { secsToCanvasX } from './Helpers'; @@ -33,14 +34,14 @@ export const Waveform: React.FC = ({ let endFrame = viewport.end; if (endFrame <= viewport.start) { - endFrame = mediaSet.audio.frames; + endFrame = mediaSet.audioFrames; } - const resp = await fetch( - `http://localhost:8888/api/media_sets/${mediaSet.id}/peaks?start=${viewport.start}&end=${endFrame}&bins=${CanvasLogicalWidth}` - ); - const newPeaks = await resp.json(); - setPeaks(newPeaks); + // const resp = await fetch( + // `http://localhost:8888/api/media_sets/${mediaSet.id}/peaks?start=${viewport.start}&end=${endFrame}&bins=${CanvasLogicalWidth}` + // ); + // const newPeaks = await resp.json(); + // setPeaks(newPeaks); })(); }, [mediaSet, viewport]); @@ -63,7 +64,7 @@ export const Waveform: React.FC = ({ return; } - const x = secsToCanvasX(position, mediaSet.audio.sampleRate, viewport); + const x = secsToCanvasX(position, mediaSet.audioSampleRate, viewport); if (x == null) { return; } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 5e5e883..68306c0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1484,6 +1484,59 @@ schema-utils "^2.6.5" source-map "^0.7.3" +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + "@rollup/plugin-node-resolve@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz#80de384edfbd7bfc9101164910f86078151a3eca" @@ -1812,6 +1865,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/long@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" + integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + "@types/minimatch@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -1822,6 +1880,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== +"@types/node@>=13.7.0": + version "16.11.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" + integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== + "@types/node@^12.0.0": version "12.20.23" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.23.tgz#d0d5885bb885ee9b1ed114a04ea586540a1b2e2a" @@ -1832,11 +1895,21 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== +"@types/object-hash@^1.3.0": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" + integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== + "@types/prettier@^2.0.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.1.tgz#374e31645d58cb18a07b3ecd8e9dede4deb2cccd" @@ -4046,6 +4119,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +dataloader@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" + integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -5618,7 +5696,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -google-protobuf@^3.15.5, google-protobuf@^3.19.0: +google-protobuf@^3.19.0: version "3.19.0" resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.19.0.tgz#97f474323c92f19fd6737af1bb792e396991e0b8" integrity sha512-qXGAiv3OOlaJXJNeKOBKxbBAwjsxzhx+12ZdKOkZTsqsRkyiQRmr/nBkAkqnuQ8cmA9X5NVXvObQTpHVnXE2DQ== @@ -7365,6 +7443,11 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -7940,6 +8023,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" + integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== + object-inspect@^1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" @@ -9155,6 +9243,11 @@ prettier@2.4.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.0.tgz#85bdfe0f70c3e777cf13a4ffff39713ca6f64cba" integrity sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ== +prettier@^2.0.2: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" + integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== + pretty-bytes@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" @@ -9232,6 +9325,25 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +protobufjs@^6.8.8: + version "6.11.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" + integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -9981,6 +10093,13 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rxjs@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.4.0.tgz#a12a44d7eebf016f5ff2441b87f28c9a51cebc68" + integrity sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w== + dependencies: + tslib "~2.1.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -11060,12 +11179,34 @@ ts-pnp@1.2.0, ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -ts-protoc-gen@^0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/ts-protoc-gen/-/ts-protoc-gen-0.15.0.tgz#2fec5930b46def7dcc9fa73c060d770b7b076b7b" - integrity sha512-TycnzEyrdVDlATJ3bWFTtra3SCiEP0W0vySXReAuEygXCUr1j2uaVyL0DhzjwuUdQoW5oXPwk6oZWeA0955V+g== +ts-poet@^4.5.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/ts-poet/-/ts-poet-4.6.1.tgz#015dc823d726655af9f095c900f84ed7c60e2dd3" + integrity sha512-DXJ+mBJIDp+jiaUgB4N5I/sczHHDU2FWacdbDNVAVS4Mh4hb7ckpvUWVW7m7/nAOcjR0r4Wt+7AoO7FeJKExfA== dependencies: - google-protobuf "^3.15.5" + "@types/prettier" "^1.19.0" + lodash "^4.17.15" + prettier "^2.0.2" + +ts-proto-descriptors@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.3.1.tgz#760ebaaa19475b03662f7b358ffea45b9c5348f5" + integrity sha512-Cybb3fqceMwA6JzHdC32dIo8eVGVmXrM6TWhdk1XQVVHT/6OQqk0ioyX1dIdu3rCIBhRmWUhUE4HsyK+olmgMw== + dependencies: + long "^4.0.0" + protobufjs "^6.8.8" + +ts-proto@^1.83.3: + version "1.83.3" + resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.83.3.tgz#ada7483035ddc946aa686dad1049e4fe45ae1d0f" + integrity sha512-r6MKFjoc4Og2kB4cNJ/bddLebdIwhouG5plu0Rry1jJMEqp2GKA7AE4FrR/FnTCIGbNPYP4622lBqckZd7UHcQ== + dependencies: + "@types/object-hash" "^1.3.0" + dataloader "^1.4.0" + object-hash "^1.3.1" + protobufjs "^6.8.8" + ts-poet "^4.5.0" + ts-proto-descriptors "^1.2.1" tsconfig-paths@^3.9.0: version "3.9.0" @@ -11082,7 +11223,7 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3: +tslib@^2.0.3, tslib@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== diff --git a/proto/media_set.proto b/proto/media_set.proto index a272aba..8f9205a 100644 --- a/proto/media_set.proto +++ b/proto/media_set.proto @@ -6,25 +6,19 @@ option go_package = "pb/media_set"; import "google/protobuf/duration.proto"; message MediaSet { - message Audio { - int32 channels = 1; - int64 approx_frames = 2; - int64 frames = 3; - int32 sample_rate = 4; - int32 youtube_itag = 5; - string mime_type = 6; - }; - - message Video { - google.protobuf.Duration duration = 1; - int32 youtube_itag = 2; - string mime_type = 3; - }; - string id = 1; string youtube_id = 2; - Audio audio = 3; - Video video = 4; + + int32 audio_channels = 3; + int64 audio_approx_frames = 4; + int64 audio_frames = 5; + int32 audio_sample_rate = 6; + int32 audio_youtube_itag = 7; + string audio_mime_type = 8; + + google.protobuf.Duration video_duration = 9; + int32 video_youtube_itag = 10; + string video_mime_type = 11; }; message GetAudioProgress { diff --git a/protogen.sh b/protogen.sh index 664c56f..ab5bac3 100755 --- a/protogen.sh +++ b/protogen.sh @@ -5,7 +5,7 @@ # protobuf (pacman -S protobuf) # protoc-gen-go (go install google.golang.org/protobuf/cmd/protoc-gen-go@latest) # protoc-gen-go-grpc (go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest) -# protoc-gen-ts (uses local copy from node_modules) +# ts-proto (uses local copy from node_modules) # set -ex @@ -17,9 +17,9 @@ mkdir -p $TARGET_FRONTEND protoc \ -I./proto/ \ - --plugin="protoc-gen-ts=./frontend/node_modules/.bin/protoc-gen-ts" \ + --plugin="./frontend/node_modules/.bin/protoc-gen-ts_proto" \ --go_out="$TARGET_BACKEND" \ --go-grpc_out="$TARGET_BACKEND" \ --js_out="import_style=commonjs,binary:$TARGET_FRONTEND" \ - --ts_out="service=grpc-web:$TARGET_FRONTEND" \ + --ts_proto_out="outputClientImpl=grpc-web,useOptionals=true,esModuleInterop=true:$TARGET_FRONTEND" \ ./proto/*