Adding metadata to the stream object and getting back data from c++ to the managed code.

This commit is contained in:
Zoe Roux 2019-09-16 03:25:03 +02:00
parent 5459e3cde6
commit 2835f75c69
12 changed files with 144 additions and 97 deletions

View File

@ -11,11 +11,11 @@ extern "C" struct Stream
{ {
const char* title; const char* title;
const char* language; const char* language;
const char* format; const char* codec;
bool isDefault; bool isDefault;
bool isForced; bool isForced;
const char* path; const char* path;
Stream(const char* title, const char* languageCode, const char* format, bool isDefault, bool isForced, const char* path) Stream(const char* title, const char* languageCode, const char* codec, bool isDefault, bool isForced, const char* path)
: title(title), language(languageCode), format(format), isDefault(isDefault), isForced(isForced), path(path) { } : title(title), language(languageCode), codec(codec), isDefault(isDefault), isForced(isForced), path(path) { }
}; };

View File

@ -56,14 +56,14 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
//Get metadata for file name //Get metadata for file name
Stream stream("title", //title Stream stream("title", //title
av_dict_get(inputStream->metadata, "language", NULL, 0)->value, //language av_dict_get(inputStream->metadata, "language", NULL, 0)->value, //language
"format", //format avcodec_get_name(inputCodecpar->codec_id), //format
false, //isDefault inputStream->disposition & AV_DISPOSITION_DEFAULT, //isDefault
false, //isForced inputStream->disposition & AV_DISPOSITION_FORCED, //isForced
"path"); //path NULL); //The path is assigned afterward.
subtitleStreams->push_back(stream); subtitleStreams->push_back(stream);
std::cout << "Stream #" << i << "(" << stream.language << "), stream type: " << inputCodecpar->codec_type << " codec: " << inputCodecpar->codec_tag << std::endl; std::cout << "Stream #" << i << "(" << stream.language << "), stream type: " << inputCodecpar->codec_type << " codec: " << stream.codec << std::endl;
//Create output folder //Create output folder
std::stringstream outStream; std::stringstream outStream;
@ -77,12 +77,23 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
//Construct output file name //Construct output file name
outStream << fileName << "." << stream.language; outStream << fileName << "." << stream.language;
if (stream.isDefault)
outStream << ".default";
if (stream.isForced)
outStream << ".forced";
if (stream.codec == "subrip")
outStream << ".srt";
else if(stream.codec == "ass")
outStream << ".ass"; outStream << ".ass";
std::string outStr = outStream.str(); std::string outStr = outStream.str();
const char* output = outStr.c_str(); stream.path = outStr.c_str();
AVFormatContext* outputContext = NULL; AVFormatContext* outputContext = NULL;
if (avformat_alloc_output_context2(&outputContext, NULL, NULL, output) < 0) if (avformat_alloc_output_context2(&outputContext, NULL, NULL, stream.path) < 0)
{ {
std::cout << "Error: Couldn't create an output file." << std::endl; std::cout << "Error: Couldn't create an output file." << std::endl;
continue; continue;
@ -129,13 +140,13 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
// } // }
//} //}
av_dump_format(outputContext, 0, output, true); av_dump_format(outputContext, 0, stream.path, true);
if (!(outputContext->flags & AVFMT_NOFILE)) if (!(outputContext->flags & AVFMT_NOFILE))
{ {
if (avio_open(&outputContext->pb, output, AVIO_FLAG_WRITE) < 0) if (avio_open(&outputContext->pb, stream.path, AVIO_FLAG_WRITE) < 0)
{ {
std::cout << "Error: Couldn't open file at " << output << std::endl; std::cout << "Error: Couldn't open file at " << stream.path << std::endl;
goto end; goto end;
} }
} }
@ -144,7 +155,7 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
if (avformat_write_header(outputContext, NULL) < 0) if (avformat_write_header(outputContext, NULL) < 0)
{ {
std::cout << "Error: Couldn't write headers to file at " << output << std::endl; std::cout << "Error: Couldn't write headers to file at " << stream.path << std::endl;
goto end; goto end;
} }

View File

@ -21,7 +21,7 @@ namespace Kyoo.Controllers
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.{format?}")] [HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.{format?}")]
public IActionResult GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, string format) public IActionResult GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, string format)
{ {
Stream subtitle = libraryManager.GetSubtitle(showSlug, seasonNumber, episodeNumber, languageTag); Track subtitle = libraryManager.GetSubtitle(showSlug, seasonNumber, episodeNumber, languageTag);
if (subtitle == null) if (subtitle == null)
return NotFound(); return NotFound();

View File

@ -8,7 +8,6 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Stream = Kyoo.Models.Watch.Stream;
namespace Kyoo.InternalAPI namespace Kyoo.InternalAPI
{ {
@ -145,8 +144,15 @@ namespace Kyoo.InternalAPI
episode.SeasonID = seasonID; episode.SeasonID = seasonID;
long episodeID = libraryManager.RegisterEpisode(episode); long episodeID = libraryManager.RegisterEpisode(episode);
Stream[] streams = transcoder.ExtractSubtitles(episode.Path); if (episode.Path.EndsWith(".mkv"))
//libraryManager.RegisterStreams(episodeID, streams); {
Track[] tracks = transcoder.ExtractSubtitles(episode.Path);
foreach (Track track in tracks)
{
track.episodeID = episodeID;
libraryManager.RegisterTrack(track);
}
}
} }
} }

View File

@ -17,8 +17,8 @@ namespace Kyoo.InternalAPI
int GetSeasonCount(string showSlug, long seasonNumber); int GetSeasonCount(string showSlug, long seasonNumber);
//Internal HTML read //Internal HTML read
(List<Stream> audios, List<Stream> subtitles) GetStreams(long episodeID); (List<Track> audios, List<Track> subtitles) GetStreams(long episodeID);
Stream GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag); Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag);
//Public read //Public read
IEnumerable<Library> GetLibraries(); IEnumerable<Library> GetLibraries();
@ -42,6 +42,7 @@ namespace Kyoo.InternalAPI
long RegisterShow(Show show); long RegisterShow(Show show);
long RegisterSeason(Season season); long RegisterSeason(Season season);
long RegisterEpisode(Episode episode); long RegisterEpisode(Episode episode);
void RegisterTrack(Track track);
long GetOrCreateGenre(Genre genre); long GetOrCreateGenre(Genre genre);
long GetOrCreateStudio(Studio studio); long GetOrCreateStudio(Studio studio);

View File

@ -70,11 +70,11 @@ namespace Kyoo.InternalAPI
FOREIGN KEY(showID) REFERENCES shows(id), FOREIGN KEY(showID) REFERENCES shows(id),
FOREIGN KEY(seasonID) REFERENCES seasons(id) FOREIGN KEY(seasonID) REFERENCES seasons(id)
); );
CREATE TABLE streams( CREATE TABLE tracks(
id INTEGER PRIMARY KEY UNIQUE, id INTEGER PRIMARY KEY UNIQUE,
episodeID INTEGER, episodeID INTEGER,
streamType TEXT, streamType TEXT,
tile TEXT, title TEXT,
language TEXT, language TEXT,
codec TEXT, codec TEXT,
isDefault BOOLEAN, isDefault BOOLEAN,
@ -189,35 +189,35 @@ namespace Kyoo.InternalAPI
} }
public (List<Stream> audios, List<Stream> subtitles) GetStreams(long episodeID) public (List<Track> audios, List<Track> subtitles) GetStreams(long episodeID)
{ {
string query = "SELECT * FROM streams WHERE episodeID = $episodeID;"; string query = "SELECT * FROM tracks WHERE episodeID = $episodeID;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{ {
cmd.Parameters.AddWithValue("$episodeID", episodeID); cmd.Parameters.AddWithValue("$episodeID", episodeID);
SQLiteDataReader reader = cmd.ExecuteReader(); SQLiteDataReader reader = cmd.ExecuteReader();
List<Stream> audios = new List<Stream>(); List<Track> audios = new List<Track>();
List<Stream> subtitles = new List<Stream>(); List<Track> subtitles = new List<Track>();
while (reader.Read()) while (reader.Read())
{ {
Stream stream = Stream.FromReader(reader); Track stream = Track.FromReader(reader);
//if (stream.type == StreamType.Audio) if (stream.type == StreamType.Audio)
// audios.Add(stream); audios.Add(stream);
//else if (stream.type == StreamType.Subtitle) else if (stream.type == StreamType.Subtitle)
// subtitles.Add(stream); subtitles.Add(stream);
} }
return (audios, subtitles); return (audios, subtitles);
} }
} }
public Stream GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag) public Track 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 AND episodes.seasonNumber = $seasonNumber AND episodes.episodeNumber = $episodeNumber AND streams.language = $languageTag;"; string query = "SELECT tracks.* FROM tracks JOIN episodes ON tracks.episodeID = episodes.id JOIN shows ON episodes.showID = shows.id WHERE shows.showSlug = $showSlug AND episodes.seasonNumber = $seasonNumber AND episodes.episodeNumber = $episodeNumber AND tracks.language = $languageTag;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection)) using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{ {
@ -228,7 +228,7 @@ namespace Kyoo.InternalAPI
SQLiteDataReader reader = cmd.ExecuteReader(); SQLiteDataReader reader = cmd.ExecuteReader();
if (reader.Read()) if (reader.Read())
return Stream.FromReader(reader); return Track.FromReader(reader);
return null; return null;
} }
@ -685,6 +685,24 @@ namespace Kyoo.InternalAPI
} }
} }
public void RegisterTrack(Track track)
{
string query = "INSERT INTO tracks (episodeID, streamType, title, language, codec, isDefault, isForced, isExternal, path) VALUES($episodeID, $streamType, $title, $language, $codec, $isDefault, $isForced, $isExternal, $path);";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$episodeID", track.episodeID);
cmd.Parameters.AddWithValue("$streamType", track.type);
cmd.Parameters.AddWithValue("$title", track.Title);
cmd.Parameters.AddWithValue("$language", track.Language);
cmd.Parameters.AddWithValue("$codec", track.Codec);
cmd.Parameters.AddWithValue("$isDefault", track.IsDefault);
cmd.Parameters.AddWithValue("$isForced", track.IsForced);
cmd.Parameters.AddWithValue("$isExternal", track.IsDefault);
cmd.Parameters.AddWithValue("$path", track.Path);
cmd.ExecuteNonQuery();
}
}
public void RegisterShowPeople(long showID, List<People> people) public void RegisterShowPeople(long showID, List<People> people)
{ {
string query = "INSERT INTO people (slug, name, imgPrimary, externalIDs) VALUES($slug, $name, $imgPrimary, $externalIDs);"; string query = "INSERT INTO people (slug, name, imgPrimary, externalIDs) VALUES($slug, $name, $imgPrimary, $externalIDs);";

View File

@ -1,3 +1,4 @@
using Kyoo.Models;
using Kyoo.Models.Watch; using Kyoo.Models.Watch;
namespace Kyoo.InternalAPI namespace Kyoo.InternalAPI
@ -11,6 +12,6 @@ namespace Kyoo.InternalAPI
string Transcode(string path); string Transcode(string path);
//Extract all subtitles of a video and save them in the subtitles sub-folder. //Extract all subtitles of a video and save them in the subtitles sub-folder.
Stream[] ExtractSubtitles(string path); Track[] ExtractSubtitles(string path);
} }
} }

View File

@ -1,8 +1,8 @@
using Kyoo.InternalAPI.TranscoderLink; using Kyoo.InternalAPI.TranscoderLink;
using Kyoo.Models;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using Stream = Kyoo.Models.Watch.Stream;
namespace Kyoo.InternalAPI namespace Kyoo.InternalAPI
{ {
@ -13,13 +13,13 @@ namespace Kyoo.InternalAPI
Debug.WriteLine("&Api INIT: " + TranscoderAPI.Init()); Debug.WriteLine("&Api INIT: " + TranscoderAPI.Init());
} }
public Stream[] ExtractSubtitles(string path) public Track[] ExtractSubtitles(string path)
{ {
string output = Path.Combine(Path.GetDirectoryName(path), "Subtitles"); string output = Path.Combine(Path.GetDirectoryName(path), "Subtitles");
Directory.CreateDirectory(output); Directory.CreateDirectory(output);
TranscoderAPI.ExtractSubtitles(path, output, out Stream[] streams); TranscoderAPI.ExtractSubtitles(path, output, out Track[] tracks);
return streams; return tracks;
} }
public void GetVideo(string path) public void GetVideo(string path)

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Kyoo.Models;
using Kyoo.Models.Watch; using Kyoo.Models.Watch;
namespace Kyoo.InternalAPI.TranscoderLink namespace Kyoo.InternalAPI.TranscoderLink
@ -14,23 +15,24 @@ namespace Kyoo.InternalAPI.TranscoderLink
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)] [DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
private extern static IntPtr ExtractSubtitles(string path, string outPath, out int streams); private extern static IntPtr ExtractSubtitles(string path, string outPath, out int streams);
public static void ExtractSubtitles(string path, string outPath, out Stream[] streams) public static void ExtractSubtitles(string path, string outPath, out Track[] tracks)
{ {
int size = Marshal.SizeOf<Stream>(); int size = Marshal.SizeOf<Stream>();
IntPtr streamsPtr = ExtractSubtitles(path, outPath, out int length); IntPtr streamsPtr = ExtractSubtitles(path, outPath, out int length);
if (length > 0) if (length > 0)
{ {
streams = new Stream[length]; tracks = new Track[length];
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
streams[i] = Marshal.PtrToStructure<Stream>(streamsPtr); tracks[i] = (Track)Marshal.PtrToStructure<Stream>(streamsPtr);
tracks[i].type = StreamType.Subtitle;
streamsPtr += size; streamsPtr += size;
} }
} }
else else
streams = null; tracks = null;
} }
} }
} }

View File

@ -1,49 +0,0 @@
using Newtonsoft.Json;
using System.Runtime.InteropServices;
namespace Kyoo.Models.Watch
{
public enum StreamType
{
Audio, Subtitle, Unknow
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Stream
{
public string Title;
public string Language;
public string Format;
[MarshalAs(UnmanagedType.I1)] public bool IsDefault;
[MarshalAs(UnmanagedType.I1)] public bool IsForced;
[JsonIgnore] public string Path;
//[JsonIgnore] public StreamType type;
//[JsonIgnore] public bool IsExternal;
//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();
//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);
}
}
}

57
Kyoo/Models/Track.cs Normal file
View File

@ -0,0 +1,57 @@
using Kyoo.Models.Watch;
using Newtonsoft.Json;
using System.Runtime.InteropServices;
namespace Kyoo.Models
{
namespace Watch
{
public enum StreamType
{
Audio, Subtitle, Unknow
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Stream
{
public string Title;
public string Language;
public string Codec;
[MarshalAs(UnmanagedType.I1)] public bool IsDefault;
[MarshalAs(UnmanagedType.I1)] public bool IsForced;
[JsonIgnore] public string Path;
}
}
public class Track : Stream
{
[JsonIgnore] public readonly long id;
[JsonIgnore] public long episodeID;
[JsonIgnore] public StreamType type;
[JsonIgnore] public bool IsExternal;
public Track(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;
Codec = format;
IsExternal = isExternal;
Path = path;
}
public static Track FromReader(System.Data.SQLite.SQLiteDataReader reader)
{
return new Track(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,8 +21,8 @@ namespace Kyoo.Models
public string previousEpisode; public string previousEpisode;
public Episode nextEpisode; public Episode nextEpisode;
public IEnumerable<Stream> audios; public IEnumerable<Track> audios;
public IEnumerable<Stream> subtitles; public IEnumerable<Track> subtitles;
public WatchItem() { } public WatchItem() { }
@ -40,7 +40,7 @@ namespace Kyoo.Models
Link = ShowSlug + "-s" + seasonNumber + "e" + episodeNumber; Link = ShowSlug + "-s" + seasonNumber + "e" + episodeNumber;
} }
public WatchItem(long episodeID, string showTitle, string showSlug, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path, Stream[] audios, Stream[] subtitles) : this(episodeID, showTitle, showSlug, seasonNumber, episodeNumber, title, releaseDate, path) public WatchItem(long episodeID, string showTitle, string showSlug, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path, Track[] audios, Track[] subtitles) : this(episodeID, showTitle, showSlug, seasonNumber, episodeNumber, title, releaseDate, path)
{ {
this.audios = audios; this.audios = audios;
this.subtitles = subtitles; this.subtitles = subtitles;
@ -60,7 +60,7 @@ namespace Kyoo.Models
public WatchItem SetStreams(ILibraryManager libraryManager) public WatchItem SetStreams(ILibraryManager libraryManager)
{ {
(IEnumerable<Stream> audios, IEnumerable<Stream> subtitles) streams = libraryManager.GetStreams(episodeID); (IEnumerable<Track> audios, IEnumerable<Track> subtitles) streams = libraryManager.GetStreams(episodeID);
audios = streams.audios; audios = streams.audios;
subtitles = streams.subtitles; subtitles = streams.subtitles;
return this; return this;