Creating controller for video transcoding and reworking the stream (subtitle/audio) section.

This commit is contained in:
Zoe Roux 2019-09-09 01:15:37 +02:00
parent e5d3ea1257
commit 8d1e8433fb
9 changed files with 146 additions and 51 deletions

View File

@ -1,7 +1,8 @@
<div id="root">
<div class="player">
<video id="player" poster="backdrop/{{this.item.showSlug}}" autoplay muted (click)="tooglePlayback()">
<source src="/api/video/{{this.item.link}}" type="video/mp4" />
<source src="/api/video/{{this.item.link}}" type="video/webm" />
<source src="/api/video/{{this.item.link}}/stream" type="video/mp4" />
</video>
</div>

View File

@ -1,4 +1,5 @@
using Kyoo.InternalAPI;
using Kyoo.Models.Watch;
using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Controllers
@ -14,10 +15,16 @@ namespace Kyoo.Controllers
this.libraryManager = libraryManager;
}
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.ass")]
public IActionResult GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag)
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.{format?}")]
public IActionResult GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, string format)
{
return PhysicalFile(@"D:\Videos\Devilman\Subtitles\fre\Devilman Crybaby S01E01.fre.ass", "text/x-ssa");
Stream subtitle = libraryManager.GetSubtitle(showSlug, seasonNumber, episodeNumber, languageTag);
if (subtitle == null)
return NotFound();
//Should use appropriate mime type here
return PhysicalFile(subtitle.Path, "text/x-ssa");
}
}
}

View File

@ -23,10 +23,34 @@ namespace Kyoo.Controllers
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (System.IO.File.Exists(episode.Path))
return PhysicalFile(episode.Path, "video/x-matroska", true);
else
return NotFound();
}
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}/stream")]
public IActionResult Stream(string showSlug, long seasonNumber, long episodeNumber)
{
//Should check if video is playable on the client and transcode if needed.
//Should use the right mime type
return PhysicalFile(episode.Path, "video/mp4", true);
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (System.IO.File.Exists(episode.Path))
{
string path = transcoder.Stream(episode.Path);
return PhysicalFile(path, "video/mp4", true);
}
else
return NotFound();
}
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}/transcode")]
public IActionResult Transcode(string showSlug, long seasonNumber, long episodeNumber)
{
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (System.IO.File.Exists(episode.Path))
{
string path = transcoder.Transcode(episode.Path);
return PhysicalFile(path, "video/mp4", true);
}
else
return NotFound();

View File

@ -15,7 +15,10 @@ namespace Kyoo.InternalAPI
List<Genre> GetGenreForShow(long showID);
List<Season> GetSeasons(long showID);
int GetSeasonCount(string showSlug, long seasonNumber);
(VideoStream video, List<Stream> audios, List<Stream> subtitles) GetStreams(long episodeID);
//Internal HTML read
(List<Stream> audios, List<Stream> subtitles) GetStreams(long episodeID);
Stream GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag);
//Public read
IEnumerable<Library> GetLibraries();

View File

@ -73,24 +73,14 @@ namespace Kyoo.InternalAPI
CREATE TABLE streams(
id INTEGER PRIMARY KEY UNIQUE,
episodeID INTEGER,
streamIndex INTEGER,
streamType TEXT,
codec TEXT,
tile TEXT,
language TEXT,
channelLayout TEXT,
profile TEXT,
aspectRatio TEXT,
bitRate INTEGER,
sampleRate INTEGER,
codec TEXT,
isDefault BOOLEAN,
isForced BOOLEAN,
isExternal BOOLEAN,
height INTEGER,
width INTEGER,
frameRate NUMBER,
level NUMBER,
pixelFormat TEXT,
bitDepth INTEGER,
path TEXT,
FOREIGN KEY(episodeID) REFERENCES episodes(id)
);
@ -199,6 +189,52 @@ namespace Kyoo.InternalAPI
}
public (List<Stream> audios, List<Stream> subtitles) GetStreams(long episodeID)
{
string query = "SELECT * FROM streams WHERE episodeID = $episodeID;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$episodeID", episodeID);
SQLiteDataReader reader = cmd.ExecuteReader();
List<Stream> audios = new List<Stream>();
List<Stream> subtitles = new List<Stream>();
while (reader.Read())
{
Stream stream = Stream.FromReader(reader);
if (stream.type == StreamType.Audio)
audios.Add(stream);
else if (stream.type == StreamType.Subtitle)
subtitles.Add(stream);
}
return (audios, subtitles);
}
}
public Stream GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag)
{
string query = "SELECT streams.* FROM streams JOIN episodes ON streams.episodeID = episodes.id JOIN shows ON episodes.showID = shows.id WHERE shows.showSlug = $showSlug, episodes.seasonNumber = $seasonNumber, episodes.episodeNumber = $episodeNumber, streams.language = $languageTag;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$showSlug", showSlug);
cmd.Parameters.AddWithValue("$seasonNumber", seasonNumber);
cmd.Parameters.AddWithValue("$episodeNumber", episodeNumber);
cmd.Parameters.AddWithValue("$languageTag", languageTag);
SQLiteDataReader reader = cmd.ExecuteReader();
if (reader.Read())
return Stream.FromReader(reader);
return null;
}
}
public IEnumerable<Show> QueryShows(string selection)
{
string query = "SELECT * FROM shows ORDER BY title;";
@ -341,25 +377,6 @@ namespace Kyoo.InternalAPI
}
}
public (VideoStream video, List<Stream> audios, List<Stream> subtitles) GetStreams(long episodeID)
{
return (new VideoStream(), null, null);
//string query = "SELECT genres.id, genres.slug, genres.name FROM genres JOIN genresLinks l ON l.genreID = genres.id WHERE l.showID = $showID;";
//using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
//{
// cmd.Parameters.AddWithValue("$episodeID", episodeID);
// SQLiteDataReader reader = cmd.ExecuteReader();
// List<Genre> genres = new List<Genre>();
// while (reader.Read())
// genres.Add(Stream.FromReader(reader));
// return genres;
//}
}
public List<People> GetPeople(long showID)
{
string query = "SELECT people.id, people.slug, people.name, people.imgPrimary, people.externalIDs, l.role, l.type FROM people JOIN peopleLinks l ON l.peopleID = people.id WHERE l.showID = $showID;";

View File

@ -2,6 +2,12 @@ namespace Kyoo.InternalAPI
{
public interface ITranscoder
{
//Should transcode to a mp4 container (same video/audio format if possible, no subtitles).
string Stream(string path);
//Should transcode to a mp4 container with a h264 video format and a AAC audio format, no subtitles.
string Transcode(string path);
void GetVideo(string Path);
dynamic ScanVideo(string Path);

View File

@ -22,5 +22,15 @@ namespace Kyoo.InternalAPI
{
return TranscoderAPI.ScanVideo(path);
}
public string Stream(string path)
{
return @"D:\Videos\Anohana\AnoHana S01E01.mp4";
}
public string Transcode(string path)
{
return @"D:\Videos\Anohana\AnoHana S01E01.mp4";
}
}
}

View File

@ -1,17 +1,46 @@
namespace Kyoo.Models.Watch
using Newtonsoft.Json;
namespace Kyoo.Models.Watch
{
public struct Stream
public enum StreamType
{
Audio, Subtitle, Unknow
}
public class Stream
{
[JsonIgnore] public StreamType type;
public string Title;
public string Language;
public bool IsDefault;
public bool IsForced;
public string Format;
[JsonIgnore] public bool IsExternal;
[JsonIgnore] public string Path;
public Stream(StreamType type, string title, string language, bool isDefault, bool isForced, string format, bool isExternal, string path)
{
this.type = type;
Title = title;
Language = language;
IsDefault = isDefault;
IsForced = isForced;
Format = format;
IsExternal = isExternal;
Path = path;
}
public struct VideoStream
public static Stream FromReader(System.Data.SQLite.SQLiteDataReader reader)
{
public string Title;
public string Language;
return new Stream(reader["streamType"] as StreamType? ?? StreamType.Unknow,
reader["title"] as string,
reader["language"] as string,
reader["isDefault"] as bool? ?? false,
reader["isForced"] as bool? ?? false,
reader["codec"] as string,
reader["isExternal"] as bool? ?? false,
reader["path"] as string);
}
}
}

View File

@ -21,7 +21,6 @@ namespace Kyoo.Models
public string previousEpisode;
public Episode nextEpisode;
[JsonIgnore] public VideoStream video;
public IEnumerable<Stream> audios;
public IEnumerable<Stream> subtitles;
@ -61,8 +60,7 @@ namespace Kyoo.Models
public WatchItem SetStreams(ILibraryManager libraryManager)
{
(VideoStream video, IEnumerable<Stream> audios, IEnumerable<Stream> subtitles) streams = libraryManager.GetStreams(episodeID);
video = streams.video;
(IEnumerable<Stream> audios, IEnumerable<Stream> subtitles) streams = libraryManager.GetStreams(episodeID);
audios = streams.audios;
subtitles = streams.subtitles;
return this;