diff --git a/Kyoo/ClientApp/src/app/player/player.component.html b/Kyoo/ClientApp/src/app/player/player.component.html index 6af97f18..71f00802 100644 --- a/Kyoo/ClientApp/src/app/player/player.component.html +++ b/Kyoo/ClientApp/src/app/player/player.component.html @@ -20,7 +20,7 @@
- +

S{{this.item.seasonNumber}}:E{{this.item.episodeNumber}} - {{this.item.title}}

diff --git a/Kyoo/ClientApp/src/app/player/player.component.ts b/Kyoo/ClientApp/src/app/player/player.component.ts index 5e4e4097..943ff377 100644 --- a/Kyoo/ClientApp/src/app/player/player.component.ts +++ b/Kyoo/ClientApp/src/app/player/player.component.ts @@ -207,7 +207,6 @@ export class PlayerComponent implements OnInit $(window).keydown((e) => { - console.log(e.keyCode); switch (e.keyCode) { case 32: //space @@ -313,6 +312,7 @@ export class PlayerComponent implements OnInit this.title.setTitle("Kyoo"); $(document).unbind(); + $(window).unbind(); $('[data-toggle="tooltip"]').hide(); } diff --git a/Kyoo/Controllers/SubtitleController.cs b/Kyoo/Controllers/SubtitleController.cs index 9ce91ec9..cb20049d 100644 --- a/Kyoo/Controllers/SubtitleController.cs +++ b/Kyoo/Controllers/SubtitleController.cs @@ -2,7 +2,12 @@ using Kyoo.Models; using Kyoo.Models.Watch; using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; namespace Kyoo.Controllers { @@ -19,8 +24,8 @@ namespace Kyoo.Controllers this.transcoder = transcoder; } - [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}.{identifier}.{codec?}")] - public IActionResult GetSubtitle(string showSlug, int seasonNumber, int episodeNumber, string identifier, string codec) + [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}.{identifier}.{extension?}")] + public IActionResult GetSubtitle(string showSlug, int seasonNumber, int episodeNumber, string identifier, string extension) { string languageTag = identifier.Substring(0, 3); bool forced = identifier.Length > 3 && identifier.Substring(4) == "forced"; @@ -30,10 +35,16 @@ namespace Kyoo.Controllers if (subtitle == null) return NotFound(); - string mime = "text/vtt"; + + if (subtitle.Codec == "subrip" && extension == "vtt") //The request wants a WebVTT from a Subrip subtitle, convert it on the fly and send it. + { + return new ConvertSubripToVtt(subtitle.Path); + } + + string mime; if (subtitle.Codec == "ass") mime = "text/x-ssa"; - else if (subtitle.Codec == "subrip") + else mime = "application/x-subrip"; //Should use appropriate mime type here @@ -44,9 +55,87 @@ namespace Kyoo.Controllers public string ExtractSubtitle(string showSlug, long seasonNumber, long episodeNumber) { Episode episode = libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); - transcoder.ExtractSubtitles(episode.Path); + libraryManager.ClearSubtitles(episode.id); - return "Processing..."; + Track[] tracks = transcoder.ExtractSubtitles(episode.Path); + foreach (Track track in tracks) + { + track.episodeID = episode.id; + libraryManager.RegisterTrack(track); + } + + return "Done. " + tracks.Length + " track(s) extracted."; + } + + [HttpGet("extract/{showSlug}")] + public string ExtractSubtitle(string showSlug) + { + List episodes = libraryManager.GetEpisodes(showSlug); + foreach (Episode episode in episodes) + { + libraryManager.ClearSubtitles(episode.id); + + Track[] tracks = transcoder.ExtractSubtitles(episode.Path); + foreach (Track track in tracks) + { + track.episodeID = episode.id; + libraryManager.RegisterTrack(track); + } + } + + return "Done."; + } + } + + + public class ConvertSubripToVtt : IActionResult + { + private string path; + private string lastLine = ""; + + public ConvertSubripToVtt(string subtitlePath) + { + path = subtitlePath; + } + + public async Task ExecuteResultAsync(ActionContext context) + { + context.HttpContext.Response.StatusCode = 200; + //HttpContext.Response.Headers.Add("Content-Disposition", "attachement"); + context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt"); + + using (StreamWriter writer = new StreamWriter(context.HttpContext.Response.Body)) + { + await writer.WriteLineAsync("WEBVTT"); + await writer.WriteLineAsync(""); + await writer.WriteLineAsync(""); + + string line; + using (StreamReader reader = new StreamReader(path)) + { + while ((line = await reader.ReadLineAsync()) != null) + { + string processedLine = ConvertLine(line); + if(processedLine != null) + await writer.WriteLineAsync(processedLine); + + lastLine = processedLine; + } + } + } + + await context.HttpContext.Response.Body.FlushAsync(); + } + + public string ConvertLine(string line) + { + if (lastLine == "") + line = null; + + if (lastLine == null) //The line is a timecode only if the last line is an index line and we already set it to null. + line = line.Replace(',', '.'); //This is never called. + + return line; } } } \ No newline at end of file diff --git a/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs index b5983f0b..150c9938 100644 --- a/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/ILibraryManager.cs @@ -24,6 +24,7 @@ namespace Kyoo.InternalAPI IEnumerable GetLibraries(); Show GetShowBySlug(string slug); Season GetSeason(string showSlug, long seasonNumber); + List GetEpisodes(string showSlug); List GetEpisodes(string showSlug, long seasonNumber); Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber); WatchItem GetWatchItem(string showSlug, long seasonNumber, long episodeNumber, bool complete = true); @@ -48,5 +49,7 @@ namespace Kyoo.InternalAPI long GetOrCreateStudio(Studio studio); void RegisterShowPeople(long showID, List actors); + + void ClearSubtitles(long episodeID); } } diff --git a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs index b1d0c967..2fa9fe0a 100644 --- a/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs +++ b/Kyoo/InternalAPI/LibraryManager/LibraryManager.cs @@ -318,6 +318,24 @@ namespace Kyoo.InternalAPI } } + public List GetEpisodes(string showSlug) + { + string query = "SELECT * FROM episodes JOIN shows ON shows.id = episodes.showID WHERE shows.slug = $showSlug ORDER BY episodeNumber;"; + + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$showSlug", showSlug); + SQLiteDataReader reader = cmd.ExecuteReader(); + + List episodes = new List(); + + while (reader.Read()) + episodes.Add(Episode.FromReader(reader).SetThumb(showSlug)); + + return episodes; + } + } + public List GetEpisodes(string showSlug, long seasonNumber) { string query = "SELECT * FROM episodes JOIN shows ON shows.id = episodes.showID WHERE shows.slug = $showSlug AND episodes.seasonNumber = $seasonNumber ORDER BY episodeNumber;"; @@ -735,6 +753,17 @@ namespace Kyoo.InternalAPI } } } + + public void ClearSubtitles(long episodeID) + { + string query = "DELETE FROM tracks WHERE episodeID = $episodeID;"; + + using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) + { + cmd.Parameters.AddWithValue("$episodeID", episodeID); + cmd.ExecuteNonQuery(); + } + } #endregion } } diff --git a/Kyoo/InternalAPI/Transcoder/Transcoder.cs b/Kyoo/InternalAPI/Transcoder/Transcoder.cs index 74e12b95..0a599601 100644 --- a/Kyoo/InternalAPI/Transcoder/Transcoder.cs +++ b/Kyoo/InternalAPI/Transcoder/Transcoder.cs @@ -1,8 +1,10 @@ using Kyoo.InternalAPI.TranscoderLink; using Kyoo.Models; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using System.Diagnostics; using System.IO; +using System.Threading.Tasks; namespace Kyoo.InternalAPI {