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 id="root">
<div class="player"> <div class="player">
<video id="player" poster="backdrop/{{this.item.showSlug}}" autoplay muted (click)="tooglePlayback()"> <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> </video>
</div> </div>

View File

@ -1,4 +1,5 @@
using Kyoo.InternalAPI; using Kyoo.InternalAPI;
using Kyoo.Models.Watch;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Controllers namespace Kyoo.Controllers
@ -14,10 +15,16 @@ namespace Kyoo.Controllers
this.libraryManager = libraryManager; this.libraryManager = libraryManager;
} }
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.ass")] [HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.{format?}")]
public IActionResult GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag) 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

@ -22,11 +22,35 @@ namespace Kyoo.Controllers
{ {
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber); 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)
{
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (System.IO.File.Exists(episode.Path)) if (System.IO.File.Exists(episode.Path))
{ {
//Should check if video is playable on the client and transcode if needed. string path = transcoder.Stream(episode.Path);
//Should use the right mime type return PhysicalFile(path, "video/mp4", true);
return PhysicalFile(episode.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 else
return NotFound(); return NotFound();

View File

@ -15,7 +15,10 @@ namespace Kyoo.InternalAPI
List<Genre> GetGenreForShow(long showID); List<Genre> GetGenreForShow(long showID);
List<Season> GetSeasons(long showID); List<Season> GetSeasons(long showID);
int GetSeasonCount(string showSlug, long seasonNumber); 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 //Public read
IEnumerable<Library> GetLibraries(); IEnumerable<Library> GetLibraries();

View File

@ -73,24 +73,14 @@ namespace Kyoo.InternalAPI
CREATE TABLE streams( CREATE TABLE streams(
id INTEGER PRIMARY KEY UNIQUE, id INTEGER PRIMARY KEY UNIQUE,
episodeID INTEGER, episodeID INTEGER,
streamIndex INTEGER,
streamType TEXT, streamType TEXT,
codec TEXT, tile TEXT,
language TEXT, language TEXT,
channelLayout TEXT, codec TEXT,
profile TEXT,
aspectRatio TEXT,
bitRate INTEGER,
sampleRate INTEGER,
isDefault BOOLEAN, isDefault BOOLEAN,
isForced BOOLEAN, isForced BOOLEAN,
isExternal BOOLEAN, isExternal BOOLEAN,
height INTEGER, path TEXT,
width INTEGER,
frameRate NUMBER,
level NUMBER,
pixelFormat TEXT,
bitDepth INTEGER,
FOREIGN KEY(episodeID) REFERENCES episodes(id) 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) public IEnumerable<Show> QueryShows(string selection)
{ {
string query = "SELECT * FROM shows ORDER BY title;"; 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) 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;"; 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 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); void GetVideo(string Path);
dynamic ScanVideo(string Path); dynamic ScanVideo(string Path);

View File

@ -22,5 +22,15 @@ namespace Kyoo.InternalAPI
{ {
return TranscoderAPI.ScanVideo(path); 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 Title;
public string Language; public string Language;
public bool IsDefault; public bool IsDefault;
public bool IsForced; public bool IsForced;
public string Format; public string Format;
}
public struct VideoStream [JsonIgnore] public bool IsExternal;
{ [JsonIgnore] public string Path;
public string Title;
public string Language; 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 static Stream FromReader(System.Data.SQLite.SQLiteDataReader reader)
{
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 string previousEpisode;
public Episode nextEpisode; public Episode nextEpisode;
[JsonIgnore] public VideoStream video;
public IEnumerable<Stream> audios; public IEnumerable<Stream> audios;
public IEnumerable<Stream> subtitles; public IEnumerable<Stream> subtitles;
@ -61,8 +60,7 @@ namespace Kyoo.Models
public WatchItem SetStreams(ILibraryManager libraryManager) public WatchItem SetStreams(ILibraryManager libraryManager)
{ {
(VideoStream video, IEnumerable<Stream> audios, IEnumerable<Stream> subtitles) streams = libraryManager.GetStreams(episodeID); (IEnumerable<Stream> audios, IEnumerable<Stream> subtitles) streams = libraryManager.GetStreams(episodeID);
video = streams.video;
audios = streams.audios; audios = streams.audios;
subtitles = streams.subtitles; subtitles = streams.subtitles;
return this; return this;