mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Creating controller for video transcoding and reworking the stream (subtitle/audio) section.
This commit is contained in:
parent
e5d3ea1257
commit
8d1e8433fb
@ -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>
|
||||||
|
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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;";
|
||||||
|
@ -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);
|
||||||
|
@ -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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user