mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Cleaning up the video & the subtitle api
This commit is contained in:
parent
c07494c9e8
commit
2dd0806517
@ -32,6 +32,7 @@ namespace Kyoo.Controllers
|
||||
Task<Episode> GetEpisode(int id);
|
||||
Task<Episode> GetEpisode(int showID, int seasonNumber, int episodeNumber);
|
||||
Task<Genre> GetGenre(int id);
|
||||
Task<Track> GetTrack(int id);
|
||||
Task<Studio> GetStudio(int id);
|
||||
Task<People> GetPeople(int id);
|
||||
|
||||
@ -42,7 +43,7 @@ namespace Kyoo.Controllers
|
||||
Task<Season> GetSeason(string showSlug, int seasonNumber);
|
||||
Task<Episode> GetEpisode(string showSlug, int seasonNumber, int episodeNumber);
|
||||
Task<Episode> GetMovieEpisode(string movieSlug);
|
||||
Task<Track> GetTrack(int id);
|
||||
Task<Track> GetTrack(string slug);
|
||||
Task<Genre> GetGenre(string slug);
|
||||
Task<Studio> GetStudio(string slug);
|
||||
Task<People> GetPeople(string slug);
|
||||
|
@ -109,7 +109,12 @@ namespace Kyoo.Controllers
|
||||
{
|
||||
return EpisodeRepository.Get(showID, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
|
||||
public Task<Track> GetTrack(string slug)
|
||||
{
|
||||
return TrackRepository.Get(slug);
|
||||
}
|
||||
|
||||
public Task<Genre> GetGenre(int id)
|
||||
{
|
||||
return GenreRepository.Get(id);
|
||||
|
@ -93,7 +93,9 @@ namespace Kyoo.Models
|
||||
|
||||
public static string GetSlug(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return showSlug + "-s" + seasonNumber + "e" + episodeNumber;
|
||||
if (seasonNumber == -1)
|
||||
return showSlug;
|
||||
return $"{showSlug}-s{seasonNumber}e{episodeNumber}";
|
||||
}
|
||||
|
||||
public void OnMerge(object merged)
|
||||
|
@ -39,18 +39,21 @@ namespace Kyoo.Controllers
|
||||
public override Task<Track> Get(string slug)
|
||||
{
|
||||
Match match = Regex.Match(slug,
|
||||
@"(?<show>.*)-s(?<season>\d*)-e(?<episode>\d*).(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
||||
@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
if (int.TryParse(slug, out int id))
|
||||
return Get(id);
|
||||
throw new ArgumentException("Invalid track slug. Format: {episodeSlug}.{language}[-forced][.{extension}]");
|
||||
match = Regex.Match(slug, @"(?<show>.*)\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
||||
if (!match.Success)
|
||||
throw new ArgumentException("Invalid track slug. " +
|
||||
"Format: {episodeSlug}.{language}[-forced][.{extension}]");
|
||||
}
|
||||
|
||||
string showSlug = match.Groups["show"].Value;
|
||||
int seasonNumber = int.Parse(match.Groups["season"].Value);
|
||||
int episodeNumber = int.Parse(match.Groups["episode"].Value);
|
||||
int seasonNumber = match.Groups["season"].Success ? int.Parse(match.Groups["season"].Value) : -1;
|
||||
int episodeNumber = match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : -1;
|
||||
string language = match.Groups["language"].Value;
|
||||
bool forced = match.Groups["forced"].Success;
|
||||
return _database.Tracks.FirstOrDefaultAsync(x => x.Episode.Show.Slug == showSlug
|
||||
@ -59,6 +62,7 @@ namespace Kyoo.Controllers
|
||||
&& x.Language == language
|
||||
&& x.IsForced == forced);
|
||||
}
|
||||
|
||||
public Task<ICollection<Track>> Search(string query)
|
||||
{
|
||||
throw new InvalidOperationException("Tracks do not support the search method.");
|
||||
|
@ -79,7 +79,7 @@ namespace Kyoo.Controllers
|
||||
|
||||
public Task<string> Transcode(Episode episode)
|
||||
{
|
||||
return null; // Not implemented yet.
|
||||
return Task.FromResult<string>(null); // Not implemented yet.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ namespace Kyoo
|
||||
_loggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
@ -132,7 +131,7 @@ namespace Kyoo
|
||||
{
|
||||
AllowedOrigins = { new Uri(publicUrl).GetLeftPart(UriPartial.Authority) }
|
||||
});
|
||||
|
||||
|
||||
|
||||
services.AddScoped<ILibraryRepository, LibraryRepository>();
|
||||
services.AddScoped<ILibraryItemRepository, LibraryItemRepository>();
|
||||
|
@ -1,68 +1,46 @@
|
||||
using Kyoo.Models;
|
||||
using System;
|
||||
using Kyoo.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models.Watch;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Kyoo.Api
|
||||
{
|
||||
[Route("[controller]")]
|
||||
[Route("subtitle")]
|
||||
[ApiController]
|
||||
public class SubtitleController : ControllerBase
|
||||
public class SubtitleApi : ControllerBase
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
//private readonly ITranscoder _transcoder;
|
||||
|
||||
public SubtitleController(ILibraryManager libraryManager/*, ITranscoder transcoder*/)
|
||||
public SubtitleApi(ILibraryManager libraryManager)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
// _transcoder = transcoder;
|
||||
}
|
||||
|
||||
//TODO Create a real route for movie's subtitles.
|
||||
|
||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}.{identifier}.{extension?}")]
|
||||
|
||||
[HttpGet("{slug}.{extension?}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> GetSubtitle(string showSlug,
|
||||
int seasonNumber,
|
||||
int episodeNumber,
|
||||
string identifier,
|
||||
string extension)
|
||||
public async Task<IActionResult> GetSubtitle(string slug, string extension)
|
||||
{
|
||||
string languageTag = identifier.Length >= 3 ? identifier.Substring(0, 3) : null;
|
||||
bool forced = identifier.Length > 4 && identifier.Substring(4) == "forced";
|
||||
Track subtitle = null;
|
||||
|
||||
if (languageTag != null)
|
||||
subtitle = (await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber))?.Tracks
|
||||
.FirstOrDefault(x => x.Type == StreamType.Subtitle && x.Language == languageTag && x.IsForced == forced);
|
||||
|
||||
if (subtitle == null)
|
||||
Track subtitle;
|
||||
try
|
||||
{
|
||||
string idString = identifier.IndexOf('-') != -1
|
||||
? identifier.Substring(0, identifier.IndexOf('-'))
|
||||
: identifier;
|
||||
int.TryParse(idString, out int id);
|
||||
subtitle = await _libraryManager.GetTrack(id);
|
||||
subtitle = await _libraryManager.GetTrack(slug);
|
||||
}
|
||||
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
return BadRequest(new {error = ex.Message});
|
||||
}
|
||||
|
||||
if (subtitle == null)
|
||||
return NotFound();
|
||||
|
||||
if (subtitle.Codec == "subrip" && extension == "vtt")
|
||||
return new ConvertSubripToVtt(subtitle.Path);
|
||||
|
||||
string mime;
|
||||
if (subtitle.Codec == "ass")
|
||||
mime = "text/x-ssa";
|
||||
else
|
||||
mime = "application/x-subrip";
|
||||
|
||||
// TODO Should use appropriate mime type here
|
||||
string mime = subtitle.Codec == "ass" ? "text/x-ssa" : "application/x-subrip";
|
||||
return PhysicalFile(subtitle.Path, mime);
|
||||
}
|
||||
|
||||
@ -129,21 +107,19 @@ namespace Kyoo.Api
|
||||
await writer.WriteLineAsync("");
|
||||
await writer.WriteLineAsync("");
|
||||
|
||||
using (StreamReader reader = new StreamReader(_path))
|
||||
using StreamReader reader = new StreamReader(_path);
|
||||
while ((line = await reader.ReadLineAsync()) != null)
|
||||
{
|
||||
while ((line = await reader.ReadLineAsync()) != null)
|
||||
if (line == "")
|
||||
{
|
||||
if (line == "")
|
||||
{
|
||||
lines.Add("");
|
||||
IEnumerable<string> processedBlock = ConvertBlock(lines);
|
||||
foreach (string t in processedBlock)
|
||||
await writer.WriteLineAsync(t);
|
||||
lines.Clear();
|
||||
}
|
||||
else
|
||||
lines.Add(line);
|
||||
lines.Add("");
|
||||
IEnumerable<string> processedBlock = ConvertBlock(lines);
|
||||
foreach (string t in processedBlock)
|
||||
await writer.WriteLineAsync(t);
|
||||
lines.Clear();
|
||||
}
|
||||
else
|
||||
lines.Add(line);
|
||||
}
|
||||
}
|
||||
|
@ -5,19 +5,21 @@ using Microsoft.Extensions.Configuration;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
|
||||
namespace Kyoo.Api
|
||||
{
|
||||
[Route("[controller]")]
|
||||
[Route("video")]
|
||||
[ApiController]
|
||||
public class VideoController : ControllerBase
|
||||
public class VideoApi : ControllerBase
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly ITranscoder _transcoder;
|
||||
private readonly string _transmuxPath;
|
||||
private readonly string _transcodePath;
|
||||
private FileExtensionContentTypeProvider _provider;
|
||||
|
||||
public VideoController(ILibraryManager libraryManager, ITranscoder transcoder, IConfiguration config)
|
||||
public VideoApi(ILibraryManager libraryManager, ITranscoder transcoder, IConfiguration config)
|
||||
{
|
||||
_libraryManager = libraryManager;
|
||||
_transcoder = transcoder;
|
||||
@ -25,101 +27,124 @@ namespace Kyoo.Api
|
||||
_transcodePath = config.GetValue<string>("transcodeTempPath");
|
||||
}
|
||||
|
||||
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> Index(string showSlug, int seasonNumber, int episodeNumber)
|
||||
private string _GetContentType(string path)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (_provider == null)
|
||||
{
|
||||
_provider = new FileExtensionContentTypeProvider();
|
||||
_provider.Mappings[".mkv"] = "video/x-matroska";
|
||||
}
|
||||
|
||||
if (_provider.TryGetContentType(path, out string contentType))
|
||||
return contentType;
|
||||
return "video/mp4";
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")]
|
||||
[HttpGet("direct/{showSlug}-s{seasonNumber}e{episodeNumber}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> DirectEpisode(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
if (seasonNumber < 0 || episodeNumber < 0)
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode != null && System.IO.File.Exists(episode.Path))
|
||||
return PhysicalFile(episode.Path, "video/x-matroska", true);
|
||||
return PhysicalFile(episode.Path, _GetContentType(episode.Path), true);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("{movieSlug}")]
|
||||
[HttpGet("direct/{movieSlug}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> DirectMovie(string movieSlug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode != null && System.IO.File.Exists(episode.Path))
|
||||
return PhysicalFile(episode.Path, _GetContentType(episode.Path), true);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("transmux/{showSlug}-s{seasonNumber}e{episodeNumber}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> Transmux(string showSlug, int seasonNumber, int episodeNumber)
|
||||
public async Task<IActionResult> TransmuxEpisode(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
if (seasonNumber < 0 || episodeNumber < 0)
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transmux(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
}
|
||||
|
||||
[HttpGet("transmux/{movieSlug}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> TransmuxMovie(string movieSlug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transmux(episode);
|
||||
if (path != null)
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
return StatusCode(500);
|
||||
}
|
||||
|
||||
[HttpGet("transmux/{episodeLink}/segment/{chunk}")]
|
||||
public IActionResult GetTransmuxedChunk(string episodeLink, string chunk)
|
||||
{
|
||||
string path = Path.GetFullPath(Path.Combine(_transmuxPath, episodeLink));
|
||||
path = Path.Combine(path, "segments" + Path.DirectorySeparatorChar + chunk);
|
||||
|
||||
return PhysicalFile(path, "video/MP2T");
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{showSlug}-s{seasonNumber}e{episodeNumber}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> Transcode(string showSlug, int seasonNumber, int episodeNumber)
|
||||
public async Task<IActionResult> TranscodeEpisode(string showSlug, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
if (seasonNumber < 0 || episodeNumber < 0)
|
||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||
|
||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transcode(episode);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{movieSlug}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> TranscodeMovie(string movieSlug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transcode(episode);
|
||||
if (path != null)
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
return StatusCode(500);
|
||||
if (path == null)
|
||||
return StatusCode(500);
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{episodeLink}/segment/{chunk}")]
|
||||
public IActionResult GetTranscodedChunk(string episodeLink, string chunk)
|
||||
|
||||
[HttpGet("transmux/{episodeLink}/segment/{chunk}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public IActionResult GetTransmuxedChunk(string episodeLink, string chunk)
|
||||
{
|
||||
string path = Path.GetFullPath(Path.Combine(_transcodePath, episodeLink));
|
||||
path = Path.Combine(path, "segments" + Path.DirectorySeparatorChar + chunk);
|
||||
|
||||
string path = Path.GetFullPath(Path.Combine(_transmuxPath, episodeLink));
|
||||
path = Path.Combine(path, "segments", chunk);
|
||||
return PhysicalFile(path, "video/MP2T");
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("{movieSlug}")]
|
||||
[HttpGet("transcode/{episodeLink}/segment/{chunk}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> Index(string movieSlug)
|
||||
public IActionResult GetTranscodedChunk(string episodeLink, string chunk)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode != null && System.IO.File.Exists(episode.Path))
|
||||
return PhysicalFile(episode.Path, "video/webm", true);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpGet("transmux/{movieSlug}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> Transmux(string movieSlug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transmux(episode);
|
||||
if (path != null)
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
return StatusCode(500);
|
||||
}
|
||||
|
||||
[HttpGet("transcode/{movieSlug}")]
|
||||
[Authorize(Policy="Play")]
|
||||
public async Task<IActionResult> Transcode(string movieSlug)
|
||||
{
|
||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||
|
||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
||||
return NotFound();
|
||||
string path = await _transcoder.Transcode(episode);
|
||||
if (path != null)
|
||||
return PhysicalFile(path, "application/x-mpegURL ", true);
|
||||
return StatusCode(500);
|
||||
string path = Path.GetFullPath(Path.Combine(_transcodePath, episodeLink));
|
||||
path = Path.Combine(path, "segments", chunk);
|
||||
return PhysicalFile(path, "video/MP2T");
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
Subproject commit 59da0095a85914eabde9900973331a25a16088b3
|
||||
Subproject commit d2d90e28cd697a20fdd2e1c28fdfc2038c7f1d7c
|
Loading…
x
Reference in New Issue
Block a user