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* language;
const char* format;
const char* codec;
bool isDefault;
bool isForced;
const char* path;
Stream(const char* title, const char* languageCode, const char* format, bool isDefault, bool isForced, const char* path)
: title(title), language(languageCode), format(format), isDefault(isDefault), isForced(isForced), path(path) { }
Stream(const char* title, const char* languageCode, const char* codec, bool isDefault, bool isForced, const char* 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
Stream stream("title", //title
av_dict_get(inputStream->metadata, "language", NULL, 0)->value, //language
"format", //format
false, //isDefault
false, //isForced
"path"); //path
avcodec_get_name(inputCodecpar->codec_id), //format
inputStream->disposition & AV_DISPOSITION_DEFAULT, //isDefault
inputStream->disposition & AV_DISPOSITION_FORCED, //isForced
NULL); //The path is assigned afterward.
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
std::stringstream outStream;
@ -77,12 +77,23 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
//Construct output file name
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";
std::string outStr = outStream.str();
const char* output = outStr.c_str();
stream.path = outStr.c_str();
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;
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 (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;
}
}
@ -144,7 +155,7 @@ Stream* ExtractSubtitles(const char* path, const char* outPath, int* streamCount
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;
}

View File

@ -21,7 +21,7 @@ namespace Kyoo.Controllers
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.{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)
return NotFound();

View File

@ -8,7 +8,6 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Stream = Kyoo.Models.Watch.Stream;
namespace Kyoo.InternalAPI
{
@ -145,8 +144,15 @@ namespace Kyoo.InternalAPI
episode.SeasonID = seasonID;
long episodeID = libraryManager.RegisterEpisode(episode);
Stream[] streams = transcoder.ExtractSubtitles(episode.Path);
//libraryManager.RegisterStreams(episodeID, streams);
if (episode.Path.EndsWith(".mkv"))
{
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);
//Internal HTML read
(List<Stream> audios, List<Stream> subtitles) GetStreams(long episodeID);
Stream GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag);
(List<Track> audios, List<Track> subtitles) GetStreams(long episodeID);
Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag);
//Public read
IEnumerable<Library> GetLibraries();
@ -42,6 +42,7 @@ namespace Kyoo.InternalAPI
long RegisterShow(Show show);
long RegisterSeason(Season season);
long RegisterEpisode(Episode episode);
void RegisterTrack(Track track);
long GetOrCreateGenre(Genre genre);
long GetOrCreateStudio(Studio studio);

View File

@ -70,11 +70,11 @@ namespace Kyoo.InternalAPI
FOREIGN KEY(showID) REFERENCES shows(id),
FOREIGN KEY(seasonID) REFERENCES seasons(id)
);
CREATE TABLE streams(
CREATE TABLE tracks(
id INTEGER PRIMARY KEY UNIQUE,
episodeID INTEGER,
streamType TEXT,
tile TEXT,
title TEXT,
language TEXT,
codec TEXT,
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))
{
cmd.Parameters.AddWithValue("$episodeID", episodeID);
SQLiteDataReader reader = cmd.ExecuteReader();
List<Stream> audios = new List<Stream>();
List<Stream> subtitles = new List<Stream>();
List<Track> audios = new List<Track>();
List<Track> subtitles = new List<Track>();
while (reader.Read())
{
Stream stream = Stream.FromReader(reader);
Track stream = Track.FromReader(reader);
//if (stream.type == StreamType.Audio)
// audios.Add(stream);
//else if (stream.type == StreamType.Subtitle)
// subtitles.Add(stream);
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)
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))
{
@ -228,7 +228,7 @@ namespace Kyoo.InternalAPI
SQLiteDataReader reader = cmd.ExecuteReader();
if (reader.Read())
return Stream.FromReader(reader);
return Track.FromReader(reader);
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)
{
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;
namespace Kyoo.InternalAPI
@ -11,6 +12,6 @@ namespace Kyoo.InternalAPI
string Transcode(string path);
//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.Models;
using Microsoft.Extensions.Configuration;
using System.Diagnostics;
using System.IO;
using Stream = Kyoo.Models.Watch.Stream;
namespace Kyoo.InternalAPI
{
@ -13,13 +13,13 @@ namespace Kyoo.InternalAPI
Debug.WriteLine("&Api INIT: " + TranscoderAPI.Init());
}
public Stream[] ExtractSubtitles(string path)
public Track[] ExtractSubtitles(string path)
{
string output = Path.Combine(Path.GetDirectoryName(path), "Subtitles");
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)

View File

@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Kyoo.Models;
using Kyoo.Models.Watch;
namespace Kyoo.InternalAPI.TranscoderLink
@ -14,23 +15,24 @@ namespace Kyoo.InternalAPI.TranscoderLink
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
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>();
IntPtr streamsPtr = ExtractSubtitles(path, outPath, out int length);
if (length > 0)
{
streams = new Stream[length];
tracks = new Track[length];
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;
}
}
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 Episode nextEpisode;
public IEnumerable<Stream> audios;
public IEnumerable<Stream> subtitles;
public IEnumerable<Track> audios;
public IEnumerable<Track> subtitles;
public WatchItem() { }
@ -40,7 +40,7 @@ namespace Kyoo.Models
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.subtitles = subtitles;
@ -60,7 +60,7 @@ namespace Kyoo.Models
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;
subtitles = streams.subtitles;
return this;