mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Start to remove transcoder dependence on kyoo
This commit is contained in:
parent
19485a110a
commit
0a0939fa3d
@ -44,9 +44,14 @@ namespace Kyoo.Abstractions.Models.Permissions
|
||||
Create,
|
||||
|
||||
/// <summary>
|
||||
/// Allow the user to delete this kind od data.
|
||||
/// Allow the user to delete this kind of data.
|
||||
/// </summary>
|
||||
Delete
|
||||
Delete,
|
||||
|
||||
/// <summary>
|
||||
/// Allow the user to play this file.
|
||||
/// </summary>
|
||||
Play,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -271,8 +271,8 @@ namespace Kyoo.Abstractions.Models
|
||||
public VideoLinks Links =>
|
||||
new()
|
||||
{
|
||||
Direct = $"/video/episode/{Slug}/direct",
|
||||
Hls = $"/video/episode/{Slug}/master.m3u8",
|
||||
Direct = $"/episode/{Slug}/direct",
|
||||
Hls = $"/episode/{Slug}/master.m3u8",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
@ -68,8 +68,7 @@ public class WatchStatusRepository : IWatchStatusRepository
|
||||
DatabaseContext db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
||||
WatchStatusRepository repo =
|
||||
scope.ServiceProvider.GetRequiredService<WatchStatusRepository>();
|
||||
List<Guid> users = await db
|
||||
.ShowWatchStatus.IgnoreQueryFilters()
|
||||
List<Guid> users = await db.ShowWatchStatus.IgnoreQueryFilters()
|
||||
.Where(x => x.ShowId == ep.ShowId && x.Status == WatchStatus.Completed)
|
||||
.Select(x => x.UserId)
|
||||
.ToListAsync();
|
||||
|
52
back/src/Kyoo.Core/Controllers/Transcoder.cs
Normal file
52
back/src/Kyoo.Core/Controllers/Transcoder.cs
Normal file
@ -0,0 +1,52 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using AspNetCore.Proxy;
|
||||
using AspNetCore.Proxy.Options;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Kyoo.Core.Controllers;
|
||||
|
||||
public class Transcoder : Controller
|
||||
{
|
||||
public Task Proxy(string route, string path)
|
||||
{
|
||||
HttpProxyOptions proxyOptions = HttpProxyOptionsBuilder
|
||||
.Instance.WithBeforeSend(
|
||||
(ctx, req) =>
|
||||
{
|
||||
req.Headers.Add("X-Path", path);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
)
|
||||
.WithHandleFailure(
|
||||
async (context, exception) =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
|
||||
await context.Response.WriteAsJsonAsync(
|
||||
new RequestError("Service unavailable")
|
||||
);
|
||||
}
|
||||
)
|
||||
.Build();
|
||||
return this.HttpProxyAsync($"http://transcoder:7666/{route}", proxyOptions);
|
||||
}
|
||||
}
|
@ -110,8 +110,7 @@ namespace Kyoo.Core
|
||||
options.SuppressMapClientErrors = true;
|
||||
options.InvalidModelStateResponseFactory = ctx =>
|
||||
{
|
||||
string[] errors = ctx
|
||||
.ModelState.SelectMany(x => x.Value!.Errors)
|
||||
string[] errors = ctx.ModelState.SelectMany(x => x.Value!.Errors)
|
||||
.Select(x => x.ErrorMessage)
|
||||
.ToArray();
|
||||
return new BadRequestObjectResult(new RequestError(errors));
|
||||
|
@ -24,6 +24,7 @@ using Kyoo.Abstractions.Models.Attributes;
|
||||
using Kyoo.Abstractions.Models.Permissions;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Kyoo.Authentication;
|
||||
using Kyoo.Core.Controllers;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using static Kyoo.Abstractions.Models.Utils.Constants;
|
||||
@ -38,26 +39,12 @@ namespace Kyoo.Core.Api
|
||||
[ApiController]
|
||||
[PartialPermission(nameof(Episode))]
|
||||
[ApiDefinition("Episodes", Group = ResourcesGroup)]
|
||||
public class EpisodeApi : CrudThumbsApi<Episode>
|
||||
public class EpisodeApi(
|
||||
ILibraryManager libraryManager,
|
||||
IThumbnailsManager thumbnails,
|
||||
Transcoder transcoder
|
||||
) : CrudThumbsApi<Episode>(libraryManager.Episodes, thumbnails)
|
||||
{
|
||||
/// <summary>
|
||||
/// The library manager used to modify or retrieve information in the data store.
|
||||
/// </summary>
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="EpisodeApi"/>.
|
||||
/// </summary>
|
||||
/// <param name="libraryManager">
|
||||
/// The library manager used to modify or retrieve information in the data store.
|
||||
/// </param>
|
||||
/// <param name="thumbnails">The thumbnail manager used to retrieve images paths.</param>
|
||||
public EpisodeApi(ILibraryManager libraryManager, IThumbnailsManager thumbnails)
|
||||
: base(libraryManager.Episodes, thumbnails)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get episode's show
|
||||
/// </summary>
|
||||
@ -77,7 +64,7 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Include<Show> fields
|
||||
)
|
||||
{
|
||||
return await _libraryManager.Shows.Get(
|
||||
return await libraryManager.Shows.Get(
|
||||
identifier.IsContainedIn<Show, Episode>(x => x.Episodes!),
|
||||
fields
|
||||
);
|
||||
@ -104,15 +91,15 @@ namespace Kyoo.Core.Api
|
||||
[FromQuery] Include<Season> fields
|
||||
)
|
||||
{
|
||||
Season? ret = await _libraryManager.Seasons.GetOrDefault(
|
||||
Season? ret = await libraryManager.Seasons.GetOrDefault(
|
||||
identifier.IsContainedIn<Season, Episode>(x => x.Episodes!),
|
||||
fields
|
||||
);
|
||||
if (ret != null)
|
||||
return ret;
|
||||
Episode? episode = await identifier.Match(
|
||||
id => _libraryManager.Episodes.GetOrDefault(id),
|
||||
slug => _libraryManager.Episodes.GetOrDefault(slug)
|
||||
id => libraryManager.Episodes.GetOrDefault(id),
|
||||
slug => libraryManager.Episodes.GetOrDefault(slug)
|
||||
);
|
||||
return episode == null ? NotFound() : NoContent();
|
||||
}
|
||||
@ -136,9 +123,9 @@ namespace Kyoo.Core.Api
|
||||
{
|
||||
Guid id = await identifier.Match(
|
||||
id => Task.FromResult(id),
|
||||
async slug => (await _libraryManager.Episodes.Get(slug)).Id
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Id
|
||||
);
|
||||
return await _libraryManager.WatchStatus.GetEpisodeStatus(id, User.GetIdOrThrow());
|
||||
return await libraryManager.WatchStatus.GetEpisodeStatus(id, User.GetIdOrThrow());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -170,9 +157,9 @@ namespace Kyoo.Core.Api
|
||||
{
|
||||
Guid id = await identifier.Match(
|
||||
id => Task.FromResult(id),
|
||||
async slug => (await _libraryManager.Episodes.Get(slug)).Id
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Id
|
||||
);
|
||||
return await _libraryManager.WatchStatus.SetEpisodeStatus(
|
||||
return await libraryManager.WatchStatus.SetEpisodeStatus(
|
||||
id,
|
||||
User.GetIdOrThrow(),
|
||||
status,
|
||||
@ -199,9 +186,100 @@ namespace Kyoo.Core.Api
|
||||
{
|
||||
Guid id = await identifier.Match(
|
||||
id => Task.FromResult(id),
|
||||
async slug => (await _libraryManager.Episodes.Get(slug)).Id
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Id
|
||||
);
|
||||
await _libraryManager.WatchStatus.DeleteEpisodeStatus(id, User.GetIdOrThrow());
|
||||
await libraryManager.WatchStatus.DeleteEpisodeStatus(id, User.GetIdOrThrow());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direct stream
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Retrieve the raw video stream, in the same container as the one on the server. No transcoding or
|
||||
/// transmuxing is done.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Episode"/>.</param>
|
||||
/// <returns>The video file of this episode.</returns>
|
||||
/// <response code="404">No episode with the given ID or slug could be found.</response>
|
||||
[HttpGet("{identifier:id}/direct")]
|
||||
[PartialPermission(Kind.Play)]
|
||||
[ProducesResponseType(StatusCodes.Status206PartialContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task GetDirectStream(Identifier identifier)
|
||||
{
|
||||
string path = await identifier.Match(
|
||||
async id => (await libraryManager.Episodes.Get(id)).Path,
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Path
|
||||
);
|
||||
await transcoder.Proxy("/direct", path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get master playlist
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get a master playlist containing all possible video qualities and audios available for this resource.
|
||||
/// Note that the direct stream is missing (since the direct is not an hls stream) and
|
||||
/// subtitles/fonts are not included to support more codecs than just webvtt.
|
||||
/// </remarks>
|
||||
/// <param name="identifier">The ID or slug of the <see cref="Episode"/>.</param>
|
||||
/// <returns>The master playlist of this episode.</returns>
|
||||
/// <response code="404">No episode with the given ID or slug could be found.</response>
|
||||
[HttpGet("{identifier:id}/master.m3u8")]
|
||||
[PartialPermission(Kind.Play)]
|
||||
[ProducesResponseType(StatusCodes.Status206PartialContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task GetMaster(Identifier identifier)
|
||||
{
|
||||
string path = await identifier.Match(
|
||||
async id => (await libraryManager.Episodes.Get(id)).Path,
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Path
|
||||
);
|
||||
await transcoder.Proxy("/master.m3u8", path);
|
||||
}
|
||||
|
||||
[HttpGet("{identifier:id}/{quality}/index.m3u8")]
|
||||
[PartialPermission(Kind.Play)]
|
||||
public async Task GetVideoIndex(Identifier identifier, string quality)
|
||||
{
|
||||
string path = await identifier.Match(
|
||||
async id => (await libraryManager.Episodes.Get(id)).Path,
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Path
|
||||
);
|
||||
await transcoder.Proxy($"/{quality}/index.m3u8", path);
|
||||
}
|
||||
|
||||
[HttpGet("{identifier:id}/audio/{audio}/index.m3u8")]
|
||||
[PartialPermission(Kind.Play)]
|
||||
public async Task GetAudioIndex(Identifier identifier, string audio)
|
||||
{
|
||||
string path = await identifier.Match(
|
||||
async id => (await libraryManager.Episodes.Get(id)).Path,
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Path
|
||||
);
|
||||
await transcoder.Proxy($"/audio/{audio}/index.m3u8", path);
|
||||
}
|
||||
|
||||
[HttpGet("{identifier:id}/audio/{audio}/{segment}")]
|
||||
[PartialPermission(Kind.Play)]
|
||||
public async Task GetAudioSegment(Identifier identifier, string audio, string segment)
|
||||
{
|
||||
string path = await identifier.Match(
|
||||
async id => (await libraryManager.Episodes.Get(id)).Path,
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Path
|
||||
);
|
||||
await transcoder.Proxy($"/audio/{audio}/{segment}", path);
|
||||
}
|
||||
|
||||
[HttpGet("{identifier:id}/info")]
|
||||
[PartialPermission(Kind.Play)]
|
||||
public async Task GetInfo(Identifier identifier)
|
||||
{
|
||||
string path = await identifier.Match(
|
||||
async id => (await libraryManager.Episodes.Get(id)).Path,
|
||||
async slug => (await libraryManager.Episodes.Get(slug)).Path
|
||||
);
|
||||
await transcoder.Proxy("/info", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,14 +16,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using AspNetCore.Proxy;
|
||||
using AspNetCore.Proxy.Options;
|
||||
using Kyoo.Abstractions.Controllers;
|
||||
using Kyoo.Abstractions.Models.Permissions;
|
||||
using Kyoo.Abstractions.Models.Utils;
|
||||
using Kyoo.Core.Controllers;
|
||||
using Kyoo.Utils;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Kyoo.Core.Api
|
||||
@ -32,20 +32,9 @@ namespace Kyoo.Core.Api
|
||||
/// Proxy to other services
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
public class ProxyApi : Controller
|
||||
[Obsolete("Use /episode/id/master.m3u8 or routes like that")]
|
||||
public class ProxyApi(ILibraryManager library, Transcoder transcoder) : Controller
|
||||
{
|
||||
private readonly HttpProxyOptions _proxyOptions = HttpProxyOptionsBuilder
|
||||
.Instance.WithHandleFailure(
|
||||
async (context, exception) =>
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
|
||||
await context.Response.WriteAsJsonAsync(
|
||||
new RequestError("Service unavailable")
|
||||
);
|
||||
}
|
||||
)
|
||||
.Build();
|
||||
|
||||
/// <summary>
|
||||
/// Transcoder proxy
|
||||
/// </summary>
|
||||
@ -54,15 +43,28 @@ namespace Kyoo.Core.Api
|
||||
/// </remarks>
|
||||
/// <param name="rest">The path of the transcoder.</param>
|
||||
/// <returns>The return value of the transcoder.</returns>
|
||||
[Route("video/{**rest}")]
|
||||
[Route("video/{type}/{id:id}/{**rest}")]
|
||||
[Permission("video", Kind.Read)]
|
||||
public Task Proxy(string rest, [FromQuery] Dictionary<string, string> query)
|
||||
[Obsolete("Use /episode/id/master.m3u8 or routes like that")]
|
||||
public async Task Proxy(
|
||||
string type,
|
||||
Identifier id,
|
||||
string rest,
|
||||
[FromQuery] Dictionary<string, string> query
|
||||
)
|
||||
{
|
||||
// TODO: Use an env var to configure transcoder:7666.
|
||||
return this.HttpProxyAsync(
|
||||
$"http://transcoder:7666/{rest}" + query.ToQueryString(),
|
||||
_proxyOptions
|
||||
string path = await (
|
||||
type is "movie" or "movies"
|
||||
? id.Match(
|
||||
async id => (await library.Movies.Get(id)).Path,
|
||||
async slug => (await library.Movies.Get(slug)).Path
|
||||
)
|
||||
: id.Match(
|
||||
async id => (await library.Episodes.Get(id)).Path,
|
||||
async slug => (await library.Episodes.Get(slug)).Path
|
||||
)
|
||||
);
|
||||
await transcoder.Proxy(rest + query.ToQueryString(), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,9 @@ import (
|
||||
// Retrieve the raw video stream, in the same container as the one on the server. No transcoding or
|
||||
// transmuxing is done.
|
||||
//
|
||||
// Path: /:resource/:slug/direct
|
||||
// Path: /direct
|
||||
func DirectStream(c echo.Context) error {
|
||||
resource := c.Param("resource")
|
||||
slug := c.Param("slug")
|
||||
|
||||
path, err := GetPath(resource, slug)
|
||||
path, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -34,17 +31,13 @@ func DirectStream(c echo.Context) error {
|
||||
// Note that the direct stream is missing (since the direct is not an hls stream) and
|
||||
// subtitles/fonts are not included to support more codecs than just webvtt.
|
||||
//
|
||||
// Path: /:resource/:slug/master.m3u8
|
||||
// Path: /master.m3u8
|
||||
func (h *Handler) GetMaster(c echo.Context) error {
|
||||
resource := c.Param("resource")
|
||||
slug := c.Param("slug")
|
||||
|
||||
client, err := GetClientId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path, err := GetPath(resource, slug)
|
||||
path, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -62,21 +55,17 @@ func (h *Handler) GetMaster(c echo.Context) error {
|
||||
// This route can take a few seconds to respond since it will way for at least one segment to be
|
||||
// available.
|
||||
//
|
||||
// Path: /:resource/:slug/:quality/index.m3u8
|
||||
// Path: /:quality/index.m3u8
|
||||
func (h *Handler) GetVideoIndex(c echo.Context) error {
|
||||
resource := c.Param("resource")
|
||||
slug := c.Param("slug")
|
||||
quality, err := src.QualityFromString(c.Param("quality"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := GetClientId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path, err := GetPath(resource, slug)
|
||||
path, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -94,21 +83,17 @@ func (h *Handler) GetVideoIndex(c echo.Context) error {
|
||||
// This route can take a few seconds to respond since it will way for at least one segment to be
|
||||
// available.
|
||||
//
|
||||
// Path: /:resource/:slug/audio/:audio/index.m3u8
|
||||
// Path: /audio/:audio/index.m3u8
|
||||
func (h *Handler) GetAudioIndex(c echo.Context) error {
|
||||
resource := c.Param("resource")
|
||||
slug := c.Param("slug")
|
||||
audio, err := strconv.ParseInt(c.Param("audio"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := GetClientId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path, err := GetPath(resource, slug)
|
||||
path, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -124,10 +109,8 @@ func (h *Handler) GetAudioIndex(c echo.Context) error {
|
||||
//
|
||||
// Retrieve a chunk of a transmuxed video.
|
||||
//
|
||||
// Path: /:resource/:slug/:quality/segments-:chunk.ts
|
||||
// Path: /:quality/segments-:chunk.ts
|
||||
func (h *Handler) GetVideoSegment(c echo.Context) error {
|
||||
resource := c.Param("resource")
|
||||
slug := c.Param("slug")
|
||||
quality, err := src.QualityFromString(c.Param("quality"))
|
||||
if err != nil {
|
||||
return err
|
||||
@ -136,13 +119,11 @@ func (h *Handler) GetVideoSegment(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := GetClientId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path, err := GetPath(resource, slug)
|
||||
path, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -158,10 +139,8 @@ func (h *Handler) GetVideoSegment(c echo.Context) error {
|
||||
//
|
||||
// Retrieve a chunk of a transcoded audio.
|
||||
//
|
||||
// Path: /:resource/:slug/audio/:audio/segments-:chunk.ts
|
||||
// Path: /audio/:audio/segments-:chunk.ts
|
||||
func (h *Handler) GetAudioSegment(c echo.Context) error {
|
||||
resource := c.Param("resource")
|
||||
slug := c.Param("slug")
|
||||
audio, err := strconv.ParseInt(c.Param("audio"), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -170,13 +149,11 @@ func (h *Handler) GetAudioSegment(c echo.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := GetClientId(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path, err := GetPath(resource, slug)
|
||||
path, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -192,12 +169,9 @@ func (h *Handler) GetAudioSegment(c echo.Context) error {
|
||||
//
|
||||
// Identify metadata about a file.
|
||||
//
|
||||
// Path: /:resource/:slug/info
|
||||
// Path: /info
|
||||
func (h *Handler) GetInfo(c echo.Context) error {
|
||||
resource := c.Param("resource")
|
||||
slug := c.Param("slug")
|
||||
|
||||
path, err := GetPath(resource, slug)
|
||||
path, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -208,10 +182,10 @@ func (h *Handler) GetInfo(c echo.Context) error {
|
||||
}
|
||||
// Run extractors to have them in cache
|
||||
h.extractor.RunExtractor(ret.Path, ret.Sha, &ret.Subtitles)
|
||||
go h.thumbnails.ExtractThumbnail(
|
||||
ret.Path,
|
||||
fmt.Sprintf("%s/%s/thumbnails.png", resource, slug),
|
||||
)
|
||||
// go h.thumbnails.ExtractThumbnail(
|
||||
// ret.Path,
|
||||
// fmt.Sprintf("%s/%s/thumbnails.png", resource, slug),
|
||||
// )
|
||||
return c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
@ -340,13 +314,13 @@ func main() {
|
||||
thumbnails: src.NewThumbnailsCreator(),
|
||||
}
|
||||
|
||||
e.GET("/:resource/:slug/direct", DirectStream)
|
||||
e.GET("/:resource/:slug/master.m3u8", h.GetMaster)
|
||||
e.GET("/:resource/:slug/:quality/index.m3u8", h.GetVideoIndex)
|
||||
e.GET("/:resource/:slug/audio/:audio/index.m3u8", h.GetAudioIndex)
|
||||
e.GET("/:resource/:slug/:quality/:chunk", h.GetVideoSegment)
|
||||
e.GET("/:resource/:slug/audio/:audio/:chunk", h.GetAudioSegment)
|
||||
e.GET("/:resource/:slug/info", h.GetInfo)
|
||||
e.GET("/direct", DirectStream)
|
||||
e.GET("/master.m3u8", h.GetMaster)
|
||||
e.GET("/:quality/index.m3u8", h.GetVideoIndex)
|
||||
e.GET("/audio/:audio/index.m3u8", h.GetAudioIndex)
|
||||
e.GET("/:quality/:chunk", h.GetVideoSegment)
|
||||
e.GET("/audio/:audio/:chunk", h.GetAudioSegment)
|
||||
e.GET("/info", h.GetInfo)
|
||||
e.GET("/:resource/:slug/thumbnails.png", h.GetThumbnails)
|
||||
e.GET("/:resource/:slug/thumbnails.vtt", h.GetThumbnailsVtt)
|
||||
e.GET("/:sha/attachment/:name", h.GetAttachment)
|
||||
|
@ -1,11 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -18,42 +15,12 @@ type Item struct {
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
func GetPath(resource string, slug string) (string, error) {
|
||||
url := os.Getenv("API_URL")
|
||||
if url == "" {
|
||||
url = "http://back:5000"
|
||||
}
|
||||
key := os.Getenv("KYOO_APIKEYS")
|
||||
func GetPath(c echo.Context) (string, error) {
|
||||
key := c.Request().Header.Get("X-Path")
|
||||
if key == "" {
|
||||
return "", errors.New("missing api keys")
|
||||
return "", echo.NewHTTPError(http.StatusBadRequest, "Missing resouce path. You need to set the X-Path header.")
|
||||
}
|
||||
key = strings.Split(key, ",")[0]
|
||||
|
||||
req, err := http.NewRequest("GET", strings.Join([]string{url, resource, slug}, "/"), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
req.Header.Set("X-API-KEY", key)
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return "", echo.NewHTTPError(
|
||||
http.StatusNotFound,
|
||||
fmt.Sprintf("No %s found with the slug %s.", resource, slug),
|
||||
)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
ret := Item{}
|
||||
err = json.NewDecoder(res.Body).Decode(&ret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ret.Path, nil
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func SanitizePath(path string) error {
|
||||
|
Loading…
x
Reference in New Issue
Block a user