Piped icon indicating copy to clipboard operation
Piped copied to clipboard

autoplay playlist only

Open Chrysanthemum536 opened this issue 1 year ago • 8 comments

Describe the feature

only autoplay the next video in a playlist do not autoplay if it is the end of the playlist or not in a playlist

Why would this be useful to add?

to watch a playlist without getting random videos I didn't ask for

Concept(s)

No response

Additional context

No response

Acknowledgements

  • [X] I have searched the existing issues and this is NOT a duplicate or related to another open issue.
  • [X] I have written a short but informative title.
  • [X] I filled out all of the requested information in this form.

Chrysanthemum536 avatar May 22 '24 06:05 Chrysanthemum536

Codecov Report

Attention: Patch coverage is 3.03030% with 64 lines in your changes missing coverage. Please review.

Project coverage is 94.31%. Comparing base (60d68ca) to head (36267e2). Report is 203 commits behind head on main.

Files with missing lines Patch % Lines
src/middleware/hook/index.ts 0.00% 62 Missing :warning:
src/request.ts 50.00% 2 Missing :warning:
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3025      +/-   ##
==========================================
- Coverage   94.76%   94.31%   -0.46%     
==========================================
  Files         136      137       +1     
  Lines       13369    13435      +66     
  Branches     2264     2297      +33     
==========================================
+ Hits        12669    12671       +2     
- Misses        700      764      +64     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov[bot] avatar Jun 23 '24 12:06 codecov[bot]

Hey @Code-Hex!

It is still in draft stage and I am not even sure if I will do it or not, but I would like to know your opinion if you like. Would your expectations of middleware traceability be achieved with such a middlewara?

usualoma avatar Jun 23 '24 12:06 usualoma

Updated 36267e2

usualoma avatar Jun 23 '24 13:06 usualoma

Quite Interesting! Some thoughts I have regarding the API -

  1. Isn't afterNext and after functions getting called one after the other, so to simplify can't we just have an after function?
  2. Not sure about beforeNext as I don't like some random code running between my code which might alter the response. I believe beforeNext func and before func of the next middleware will have the same params, so what's the point of beforeNext?
  3. Can we simplify the hook API and make it similar to the middleware function?
app.use(
  hook(async (c, handler) => {
    console.log("Before")
    await handler();
    console.log("After")
  })
)

These are just my viewpoints, correct me if I am wrong but I love the idea of a function running between middleware, already thinking about a lot of stuff to build with it 😂

MathurAditya724 avatar Jun 27 '24 19:06 MathurAditya724

@MathurAditya724

Thank you. I understand your argument for more simplicity.

But if you write the following, the handler can't be a handler as it is in the original. It becomes a wrapped function. Here we have to ask, "What function are you hooking?" so we need to know, so we need to pass a bit more information.

app.use(
  hook(async (c, handler) => {
    console.log("Before")
    await handler();
    console.log("After")
  })
)

Why beforeNext / afterNext

BeforeNext and afterNext are provided to allow the processing time of hardWorkBeforeNext() and hardWorkAfterNext() to be measured in the following code.

app.use(async (c, next) => {

  // before hook

  hardWorkBeforeNext()

  // beforeNext hook

  await next()

  // afterNext hook

  hardWorkAfterNext()

  // after hook

})

usualoma avatar Jun 29 '24 06:06 usualoma

@usualoma Thank you very much for your prompt creation of a PoC based on the ideas you presented at the Hono Conference! The image is very close.

The idea originally came from my comment that it would be nice to have a mechanism in place to help Hono users visualize how their applications are being used. The idea on which this idea is based is stats.Handler in grpc-go. As the name implies, if we implement an object that satisfies the stats.Handler interface, you can aggregate the usage by the users themselves.

HandleRPC is the easiest to imagine. It takes an interface called RPCStats as a parameter.

type RPCStats interface {
	// IsClient returns true if this RPCStats is from client side.
	IsClient() bool
}

The reason IsClient is a method is because gRPC allows client and server middleware and stats Handler to use the same interface.

8 implementations are provided to satisfy this interface. They are Begin, InHeader, InPayload, InTrailer, OutHeader, OutPayload, OutTrailer, End.

Using these, users can create a HandleRPC in this way to collect statistical information.

// HandleRPC implements per RPC tracing and stats implementation.
func (h *serverStatsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
	ri := getRPCInfo(ctx)
	if ri == nil {
		logger.Error("ctx passed into server side stats handler metrics event handling has no server call data present")
		return
	}
	h.processRPCData(ctx, rs, ri.ai)
}

func (h *serverStatsHandler) processRPCData(ctx context.Context, s stats.RPCStats, ai *attemptInfo) {
	switch st := s.(type) {
	case *stats.InHeader:
		if ai.pluginOptionLabels == nil && h.options.MetricsOptions.pluginOption != nil {
			labels := h.options.MetricsOptions.pluginOption.GetLabels(st.Header)
			if labels == nil {
				labels = map[string]string{} // Shouldn't return a nil map. Make it empty if so to ignore future Get Calls for this Attempt.
			}
			ai.pluginOptionLabels = labels
		}
		h.serverMetrics.callStarted.Add(ctx, 1, otelmetric.WithAttributes(otelattribute.String("grpc.method", ai.method)))
	case *stats.OutPayload:
		atomic.AddInt64(&ai.sentCompressedBytes, int64(st.CompressedLength))
	case *stats.InPayload:
		atomic.AddInt64(&ai.recvCompressedBytes, int64(st.CompressedLength))
	case *stats.End:
		h.processRPCEnd(ctx, ai, st)
	default:
	}
}

https://github.com/grpc/grpc-go/blob/f199062ef31ddda54152e1ca5e3d15fb63903dc3/stats/opentelemetry/server_metrics.go#L203-L232

If these handlers were actually passed as options inside the framework, they would be called this way.

func (s *Server) processUnaryRPC(ctx context.Context, t transport.ServerTransport, stream *transport.Stream, info *serviceInfo, md *MethodDesc, trInfo *traceInfo) (err error) {
	shs := s.opts.statsHandlers
	if len(shs) != 0 || trInfo != nil || channelz.IsOn() {
		// ...
		var statsBegin *stats.Begin
		for _, sh := range shs {
			beginTime := time.Now()
			statsBegin = &stats.Begin{
				BeginTime:      beginTime,
				IsClientStream: false,
				IsServerStream: false,
			}
			sh.HandleRPC(ctx, statsBegin)
		}
	}

	// Processing to call handler
	// after called

	for _, sh := range shs {
		end := &stats.End{
			BeginTime: statsBegin.BeginTime,
			EndTime:   time.Now(),
		}
		if err != nil && err != io.EOF {
			end.Error = toRPCErr(err)
		}
		sh.HandleRPC(ctx, end)
	}

In the same file, InPayload is called immediately after parsing the request body and OutPayload is called immediately after writing the response body. The other InHeader, OutHeader, InTrailer, and OutTrailer call HandleRPC at similar times.

https://github.com/grpc/grpc-go/blob/f199062ef31ddda54152e1ca5e3d15fb63903dc3/server.go#L1206-L1246


Based on the above, I think that TagRPC (routing information) and HandleRPC (handling information) hooks are the parts that can be used for Hono. I think users can hook the middleware themselves if they work hard enough, so it may not be necessary to do so.

Since these are hooks for monitoring, they are defined on the assumption that no errors are thrown.

And I do not believe they should be used to hack complex business logic. In other words, I believe this should be available as something completely different from existing middleware.

I personally thought it would be nice if we could pass it on as an option for Hono rather than as middleware.

stats({
    tagHandler(c, handlerInfo) { // tagRouter is better??
        // We can handle w/ handler information or inject to HonoContext
        // We may have router information at here??
    },
    handleHandler(c, handlerStats) {
        // Type switch and handle them
    },
})

Code-Hex avatar Jun 30 '24 06:06 Code-Hex

@Code-Hex

Thank you. That is very helpful information.

I understand that "grpc" provides the following eight,

Begin, InHeader, InPayload, InTrailer, OutHeader, OutPayload, OutTrailer, End.

What would a generic framework, such as Hono, be able to provide? I still don't understand, when should handleHandler(c, handlerStats) be called?

Is there anything we can do by 'providing it as a core feature rather than middleware'?

usualoma avatar Jul 01 '24 23:07 usualoma

@usualoma Thanks for reply me.

I'm writing just focus for handleHandler.

start and end are mandatory (metrics or trace usecase). I think this should be called immediately after Hono receives the request, and at the last layer when all processing is done and the response is returned.

Start, End

It is right after the request headers are received and right after the response headers to be returned are finalized. Since multiple middleware can modify the request and response, I believe that only the core functionality of the framework can retrieve the finalized information.

  • Why are headers important?
    • This is because many of the tracing and request context mechanisms are based on HTTP headers.
      • e.g. https://www.w3.org/TR/trace-context/#trace-context-http-headers-format
    • Often included in request headers via cloud vendors such as GCP.
      • https://cloud.google.com/trace/docs/trace-context

Header

Payload is a concern of mine as well. Hono has a mechanism to validate payloads such as path parameters and form bodies by using validator as middleware. InPayload is exactly what I thought it would be nice to call c.req.json() or c.req.valid() when the request payload is successfully parsed.

I thought that OutPayload should be similar to the timing of End, a fixed endpoint where the response body is not changed any more.

Payload

Personally, I consider the Trailer to be optional. gRPC uses it to express Response Status details, so it is important, but the HTTP Web Framework in general does not consider it that important.

Code-Hex avatar Jul 03 '24 14:07 Code-Hex