Creating bases api calls for playback.

This commit is contained in:
Zoe Roux 2019-09-05 01:31:45 +02:00
parent 3096249eef
commit 2462d3ad7f
19 changed files with 226 additions and 74 deletions

View File

@ -159,6 +159,7 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="framework.h" /> <ClInclude Include="framework.h" />
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
<ClInclude Include="Stream.h" />
<ClInclude Include="Transcoder.h" /> <ClInclude Include="Transcoder.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -13,6 +13,9 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter> </Filter>
<Filter Include="Models">
<UniqueIdentifier>{a553acdb-cb65-47bc-8809-c5374fe91cae}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="framework.h"> <ClInclude Include="framework.h">
@ -24,6 +27,9 @@
<ClInclude Include="Transcoder.h"> <ClInclude Include="Transcoder.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="Stream.h">
<Filter>Models</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="dllmain.cpp"> <ClCompile Include="dllmain.cpp">

28
Kyoo.Transcoder/Stream.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#ifdef TRANSCODER_EXPORTS
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
#include <iostream>
extern "C" API struct Video
{
std::string title;
Audio* audios;
Subtitle* subtitles;
long duration;
};
extern "C" API struct Audio
{
std::string title;
std::string languageCode;
};
extern "C" API struct Subtitle
{
std::string title;
std::string languageCode;
};

View File

@ -4,4 +4,9 @@
int Init() int Init()
{ {
return 42; return 42;
}
Video ScanVideo(std::string path)
{
} }

View File

@ -1,9 +1,14 @@
#pragma once #pragma once
#ifdef TRANSCODER_EXPORTS #ifdef TRANSCODER_EXPORTS
#define API __declspec(dllexport) #define API __declspec(dllexport)
#else #else
#define API __declspec(dllimport) #define API __declspec(dllimport)
#endif #endif
#include <iostream>
#include "Stream.h"
extern "C" API int Init(); extern "C" API int Init();
extern "C" API Video ScanVideo(std::string path);

View File

@ -9,5 +9,6 @@
// add headers that you want to pre-compile here // add headers that you want to pre-compile here
#include "framework.h" #include "framework.h"
#include <iostream>
#endif //PCH_H #endif //PCH_H

View File

@ -0,0 +1,35 @@
using Kyoo.InternalAPI;
using Kyoo.Models;
using Microsoft.AspNetCore.Mvc;
namespace Kyoo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class VideoController : ControllerBase
{
private readonly ILibraryManager libraryManager;
private readonly ITranscoder transcoder;
public VideoController(ILibraryManager libraryManager, ITranscoder transcoder)
{
this.libraryManager = libraryManager;
this.transcoder = transcoder;
}
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")]
public IActionResult Index(string showSlug, long seasonNumber, long episodeNumber)
{
WatchItem episode = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
if (System.IO.File.Exists(episode.Path))
{
//Should check if video is playable on the client and transcode if needed.
//Should use the right mime type
return new PhysicalFileResult(episode.Path, "video/mp4");
}
else
return NotFound();
}
}
}

View File

@ -8,29 +8,25 @@ using System.Diagnostics;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
[Route("api/[controller]")] [Route("api/[controller]")]
public class WatchController : Controller [ApiController]
public class WatchController : ControllerBase
{ {
private readonly ILibraryManager libraryManager; private readonly ILibraryManager libraryManager;
private readonly ITranscoder transcoder;
public WatchController(ILibraryManager libraryManager, ITranscoder transcoder) public WatchController(ILibraryManager libraryManager)
{ {
this.libraryManager = libraryManager; this.libraryManager = libraryManager;
this.transcoder = transcoder;
} }
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")] [HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}")]
public IActionResult Index(string showSlug, long seasonNumber, long episodeNumber) public ActionResult<WatchItem> Index(string showSlug, long seasonNumber, long episodeNumber)
{ {
Debug.WriteLine("&Trying to watch " + showSlug + " season " + seasonNumber + " episode " + episodeNumber); WatchItem item = libraryManager.GetWatchItem(showSlug, seasonNumber, episodeNumber);
Episode episode = libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber); if(item == null)
return NotFound();
Debug.WriteLine("&Transcoding at: " + episode.Path); return item;
transcoder.GetVideo(episode.Path);
return NotFound();
} }
} }
} }

View File

@ -1,4 +1,5 @@
using Kyoo.Models; using Kyoo.Models;
using Kyoo.Models.Watch;
using System.Collections.Generic; using System.Collections.Generic;
namespace Kyoo.InternalAPI namespace Kyoo.InternalAPI
@ -13,6 +14,7 @@ namespace Kyoo.InternalAPI
List<People> GetPeople(long showID); List<People> GetPeople(long showID);
List<Genre> GetGenreForShow(long showID); List<Genre> GetGenreForShow(long showID);
List<Season> GetSeasons(long showID); List<Season> GetSeasons(long showID);
(VideoStream video, List<Stream> audios, List<Stream> subtitles) GetStreams(long episodeID);
//Public read //Public read
IEnumerable<Library> GetLibraries(); IEnumerable<Library> GetLibraries();
@ -20,6 +22,7 @@ namespace Kyoo.InternalAPI
Season GetSeason(string showSlug, long seasonNumber); Season GetSeason(string showSlug, long seasonNumber);
List<Episode> GetEpisodes(string showSlug, long seasonNumber); List<Episode> GetEpisodes(string showSlug, long seasonNumber);
Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber); Episode GetEpisode(string showSlug, long seasonNumber, long episodeNumber);
WatchItem GetWatchItem(string showSlug, long seasonNumber, long episodeNumber);
People GetPeopleBySlug(string slug); People GetPeopleBySlug(string slug);
Genre GetGenreBySlug(string slug); Genre GetGenreBySlug(string slug);
Studio GetStudioBySlug(string slug); Studio GetStudioBySlug(string slug);

View File

@ -1,9 +1,9 @@
using Kyoo.Models; using Kyoo.Models;
using Kyoo.Models.Watch;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.SQLite; using System.Data.SQLite;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
namespace Kyoo.InternalAPI namespace Kyoo.InternalAPI
{ {
@ -17,7 +17,7 @@ namespace Kyoo.InternalAPI
string databasePath = configuration.GetValue<string>("databasePath"); string databasePath = configuration.GetValue<string>("databasePath");
Debug.WriteLine("&Library Manager init, databasePath: " + databasePath); Debug.WriteLine("&Library Manager init, databasePath: " + databasePath);
if (!File.Exists(databasePath)) if (!System.IO.File.Exists(databasePath))
{ {
Debug.WriteLine("&Database doesn't exist, creating one."); Debug.WriteLine("&Database doesn't exist, creating one.");
@ -303,6 +303,43 @@ namespace Kyoo.InternalAPI
} }
} }
public WatchItem GetWatchItem(string showSlug, long seasonNumber, long episodeNumber)
{
string query = "SELECT episodes.id, shows.title as showTitle, seasonNumber, episodeNumber, episodes.title, releaseDate, episodes.path FROM episodes JOIN shows ON shows.id = episodes.showID WHERE shows.slug = $showSlug AND episodes.seasonNumber = $seasonNumber AND episodes.episodeNumber = $episodeNumber;";
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
{
cmd.Parameters.AddWithValue("$showSlug", showSlug);
cmd.Parameters.AddWithValue("$seasonNumber", seasonNumber);
cmd.Parameters.AddWithValue("$episodeNumber", episodeNumber);
SQLiteDataReader reader = cmd.ExecuteReader();
if (reader.Read())
return WatchItem.FromReader(reader).SetStreams(this);
else
return null;
}
}
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

@ -3,5 +3,7 @@ namespace Kyoo.InternalAPI
public interface ITranscoder public interface ITranscoder
{ {
void GetVideo(string Path); void GetVideo(string Path);
dynamic ScanVideo(string Path);
} }
} }

View File

@ -17,5 +17,10 @@ namespace Kyoo.InternalAPI
{ {
Debug.WriteLine("&Getting video..."); Debug.WriteLine("&Getting video...");
} }
public dynamic ScanVideo(string path)
{
return TranscoderAPI.ScanVideo(path);
}
} }
} }

View File

@ -4,7 +4,12 @@ namespace Kyoo.InternalAPI.TranscoderLink
{ {
public class TranscoderAPI public class TranscoderAPI
{ {
[DllImport(@"C:\Projects\Kyoo\Debug\Kyoo.Transcoder.dll", CallingConvention = CallingConvention.Cdecl)] private const string TranscoderPath = @"C:\Projects\Kyoo\Debug\Kyoo.Transcoder.dll";
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public extern static int Init(); public extern static int Init();
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
public extern static dynamic ScanVideo(string path);
} }
} }

View File

@ -16,21 +16,13 @@ namespace Kyoo.Models
public string Overview; public string Overview;
public DateTime? ReleaseDate; public DateTime? ReleaseDate;
public long Runtime; //This runtime variable should be in seconds (used by the video manager so we need precisions) public long Runtime; //This runtime variable should be in minutes
[JsonIgnore] public string ImgPrimary; [JsonIgnore] public string ImgPrimary;
public string ExternalIDs; public string ExternalIDs;
public string Thumb; //Used in the API response only public string Thumb; //Used in the API response only
public long RuntimeInMinutes
{
get
{
return Runtime / 60;
}
}
public Episode() { } public Episode() { }

17
Kyoo/Models/Stream.cs Normal file
View File

@ -0,0 +1,17 @@
namespace Kyoo.Models.Watch
{
public struct Stream
{
public string Title;
public string Language;
public bool IsDefault;
public bool IsForced;
public string Format;
}
public struct VideoStream
{
public string Title;
public string Language;
}
}

63
Kyoo/Models/WatchItem.cs Normal file
View File

@ -0,0 +1,63 @@
using Kyoo.InternalAPI;
using Kyoo.Models.Watch;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace Kyoo.Models
{
public class WatchItem
{
[JsonIgnore] public readonly long episodeID;
public string ShowTitle;
public long seasonNumber;
public long episodeNumber;
public string Title;
public DateTime? ReleaseDate;
[JsonIgnore] public string Path;
[JsonIgnore] public VideoStream video;
public IEnumerable<Stream> audios;
public IEnumerable<Stream> subtitles;
public WatchItem() { }
public WatchItem(long episodeID, string showTitle, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path)
{
this.episodeID = episodeID;
ShowTitle = showTitle;
this.seasonNumber = seasonNumber;
this.episodeNumber = episodeNumber;
Title = title;
ReleaseDate = releaseDate;
Path = path;
}
public WatchItem(long episodeID, string showTitle, long seasonNumber, long episodeNumber, string title, DateTime? releaseDate, string path, Stream[] audios, Stream[] subtitles) : this(episodeID, showTitle, seasonNumber, episodeNumber, title, releaseDate, path)
{
this.audios = audios;
this.subtitles = subtitles;
}
public static WatchItem FromReader(System.Data.SQLite.SQLiteDataReader reader)
{
return new WatchItem((long)reader["id"],
reader["showTitle"] as string,
(long)reader["seasonNumber"],
(long)reader["episodeNumber"],
reader["title"] as string,
reader["releaseDate"] as DateTime?,
reader["path"] as string);
}
public WatchItem SetStreams(ILibraryManager libraryManager)
{
(VideoStream video, IEnumerable<Stream> audios, IEnumerable<Stream> subtitles) streams = libraryManager.GetStreams(episodeID);
video = streams.video;
audios = streams.audios;
subtitles = streams.subtitles;
return this;
}
}
}

View File

@ -1,23 +0,0 @@
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
</p>

View File

@ -1,23 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Kyoo.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}

View File

@ -1,3 +0,0 @@
@using Kyoo
@namespace Kyoo.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers