mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-02 21:24:20 -04:00
Reworking the transcoder and creating a file manager
This commit is contained in:
parent
fdfd42c7c1
commit
57e49b7e83
16
Kyoo.Common/Controllers/IFileManager.cs
Normal file
16
Kyoo.Common/Controllers/IFileManager.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.IO;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Kyoo.Controllers
|
||||||
|
{
|
||||||
|
public interface IFileManager
|
||||||
|
{
|
||||||
|
public IActionResult FileResult([NotNull] string path, bool rangeSupport = false);
|
||||||
|
|
||||||
|
public StreamReader GetReader([NotNull] string path);
|
||||||
|
// TODO implement a List for directorys, a Exist to check existance and all.
|
||||||
|
// TODO replace every use of System.IO with this to allow custom paths (like uptobox://path)
|
||||||
|
// TODO find a way to handle Transmux/Transcode with this system.
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
using Kyoo.Models;
|
using Kyoo.Models;
|
||||||
using Kyoo.Models.Watch;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Kyoo.Controllers
|
namespace Kyoo.Controllers
|
||||||
{
|
{
|
||||||
public interface ITranscoder
|
public interface ITranscoder
|
||||||
{
|
{
|
||||||
Task<Track[]> ExtractInfos(string path);
|
Task<Track[]> ExtractInfos(string path, bool reextract);
|
||||||
Task<string> Transmux(Episode episode);
|
Task<string> Transmux(Episode episode);
|
||||||
Task<string> Transcode(Episode episode);
|
Task<string> Transcode(Episode episode);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
|
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0-beta-20204-02" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.0-beta-20204-02" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ namespace Kyoo.Models
|
|||||||
Video = 1,
|
Video = 1,
|
||||||
Audio = 2,
|
Audio = 2,
|
||||||
Subtitle = 3,
|
Subtitle = 3,
|
||||||
Font = 4
|
Attachment = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Watch
|
namespace Watch
|
||||||
@ -100,7 +100,7 @@ namespace Kyoo.Models
|
|||||||
StreamType.Subtitle => "",
|
StreamType.Subtitle => "",
|
||||||
StreamType.Video => "video.",
|
StreamType.Video => "video.",
|
||||||
StreamType.Audio => "audio.",
|
StreamType.Audio => "audio.",
|
||||||
StreamType.Font => "font.",
|
StreamType.Attachment => "font.",
|
||||||
_ => ""
|
_ => ""
|
||||||
};
|
};
|
||||||
string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty;
|
string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty;
|
||||||
|
41
Kyoo/Controllers/FileManager.cs
Normal file
41
Kyoo/Controllers/FileManager.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
|
|
||||||
|
namespace Kyoo.Controllers
|
||||||
|
{
|
||||||
|
public class FileManager : ControllerBase, IFileManager
|
||||||
|
{
|
||||||
|
private FileExtensionContentTypeProvider _provider;
|
||||||
|
|
||||||
|
private string _GetContentType(string path)
|
||||||
|
{
|
||||||
|
if (_provider == null)
|
||||||
|
{
|
||||||
|
_provider = new FileExtensionContentTypeProvider();
|
||||||
|
_provider.Mappings[".mkv"] = "video/x-matroska";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_provider.TryGetContentType(path, out string contentType))
|
||||||
|
return contentType;
|
||||||
|
return "video/mp4";
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult FileResult(string path, bool range)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
throw new ArgumentNullException(nameof(path));
|
||||||
|
if (!System.IO.File.Exists(path))
|
||||||
|
return NotFound();
|
||||||
|
return PhysicalFile(path, _GetContentType(path), range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamReader GetReader(string path)
|
||||||
|
{
|
||||||
|
if (path == null)
|
||||||
|
throw new ArgumentNullException(nameof(path));
|
||||||
|
return new StreamReader(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
134
Kyoo/Controllers/Transcoder.cs
Normal file
134
Kyoo/Controllers/Transcoder.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Kyoo.Models;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Stream = Kyoo.Models.Watch.Stream;
|
||||||
|
|
||||||
|
// We use threads so tasks are not always awaited.
|
||||||
|
#pragma warning disable 4014
|
||||||
|
|
||||||
|
namespace Kyoo.Controllers
|
||||||
|
{
|
||||||
|
public class BadTranscoderException : Exception {}
|
||||||
|
|
||||||
|
public class Transcoder : ITranscoder
|
||||||
|
{
|
||||||
|
private static class TranscoderAPI
|
||||||
|
{
|
||||||
|
private const string TranscoderPath = "libtranscoder.so";
|
||||||
|
|
||||||
|
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern int init();
|
||||||
|
|
||||||
|
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern int transmux(string path, string outpath, out float playableDuration);
|
||||||
|
|
||||||
|
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern int transcode(string path, string outpath, out float playableDuration);
|
||||||
|
|
||||||
|
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern IntPtr extract_infos(string path,
|
||||||
|
string outpath,
|
||||||
|
out int length,
|
||||||
|
out int trackCount,
|
||||||
|
bool reextracct);
|
||||||
|
|
||||||
|
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern void free(IntPtr ptr);
|
||||||
|
|
||||||
|
|
||||||
|
public static Track[] ExtractInfos(string path, string outPath, bool reextract)
|
||||||
|
{
|
||||||
|
int size = Marshal.SizeOf<Stream>();
|
||||||
|
IntPtr ptr = extract_infos(path, outPath, out int arrayLength, out int trackCount, reextract);
|
||||||
|
IntPtr streamsPtr = ptr;
|
||||||
|
Track[] tracks;
|
||||||
|
|
||||||
|
if (trackCount > 0 && ptr != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
tracks = new Track[trackCount];
|
||||||
|
|
||||||
|
int j = 0;
|
||||||
|
for (int i = 0; i < arrayLength; i++)
|
||||||
|
{
|
||||||
|
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
|
||||||
|
if (stream!.Type != StreamType.Unknown)
|
||||||
|
{
|
||||||
|
tracks[j] = new Track(stream);
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
streamsPtr += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
tracks = Array.Empty<Track>();
|
||||||
|
|
||||||
|
if (ptr != IntPtr.Zero)
|
||||||
|
free(ptr); // free_streams is not necesarry since the Marshal free the unmanaged pointers.
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly string _transmuxPath;
|
||||||
|
private readonly string _transcodePath;
|
||||||
|
|
||||||
|
public Transcoder(IConfiguration config)
|
||||||
|
{
|
||||||
|
_transmuxPath = Path.GetFullPath(config.GetValue<string>("transmuxTempPath"));
|
||||||
|
_transcodePath = Path.GetFullPath(config.GetValue<string>("transcodeTempPath"));
|
||||||
|
|
||||||
|
if (TranscoderAPI.init() != Marshal.SizeOf<Stream>())
|
||||||
|
throw new BadTranscoderException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Track[]> ExtractInfos(string path, bool reextract)
|
||||||
|
{
|
||||||
|
string dir = Path.GetDirectoryName(path);
|
||||||
|
if (dir == null)
|
||||||
|
throw new ArgumentException("Invalid path.");
|
||||||
|
dir = Path.Combine(dir, "Extra");
|
||||||
|
return Task.Factory.StartNew(
|
||||||
|
() => TranscoderAPI.ExtractInfos(path, dir, reextract),
|
||||||
|
TaskCreationOptions.LongRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> Transmux(Episode episode)
|
||||||
|
{
|
||||||
|
if (!File.Exists(episode.Path))
|
||||||
|
throw new ArgumentException("Path does not exists. Can't transcode.");
|
||||||
|
|
||||||
|
string folder = Path.Combine(_transmuxPath, episode.Slug);
|
||||||
|
string manifest = Path.Combine(folder, episode.Slug + ".m3u8");
|
||||||
|
float playableDuration = 0;
|
||||||
|
bool transmuxFailed = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
if (File.Exists(manifest))
|
||||||
|
return manifest;
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException)
|
||||||
|
{
|
||||||
|
await Console.Error.WriteLineAsync($"Access to the path {manifest} is denied. Please change your transmux path in the config.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
string cleanManifest = manifest.Replace('\\', '/');
|
||||||
|
transmuxFailed = TranscoderAPI.transmux(episode.Path, cleanManifest, out playableDuration) != 0;
|
||||||
|
}, TaskCreationOptions.LongRunning);
|
||||||
|
while (playableDuration < 10 || !File.Exists(manifest) && !transmuxFailed)
|
||||||
|
await Task.Delay(10);
|
||||||
|
return transmuxFailed ? null : manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<string> Transcode(Episode episode)
|
||||||
|
{
|
||||||
|
return Task.FromResult<string>(null); // Not implemented yet.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,70 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Kyoo.Models;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Kyoo.Controllers.TranscoderLink;
|
|
||||||
#pragma warning disable 4014
|
|
||||||
|
|
||||||
namespace Kyoo.Controllers
|
|
||||||
{
|
|
||||||
public class BadTranscoderException : Exception {}
|
|
||||||
|
|
||||||
public class Transcoder : ITranscoder
|
|
||||||
{
|
|
||||||
private readonly string _transmuxPath;
|
|
||||||
private readonly string _transcodePath;
|
|
||||||
|
|
||||||
public Transcoder(IConfiguration config)
|
|
||||||
{
|
|
||||||
_transmuxPath = Path.GetFullPath(config.GetValue<string>("transmuxTempPath"));
|
|
||||||
_transcodePath = Path.GetFullPath(config.GetValue<string>("transcodeTempPath"));
|
|
||||||
|
|
||||||
if (TranscoderAPI.init() != Marshal.SizeOf<Models.Watch.Stream>())
|
|
||||||
throw new BadTranscoderException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<Track[]> ExtractInfos(string path)
|
|
||||||
{
|
|
||||||
string dir = Path.GetDirectoryName(path);
|
|
||||||
if (dir == null)
|
|
||||||
throw new ArgumentException("Invalid path.");
|
|
||||||
|
|
||||||
return Task.Factory.StartNew(() => TranscoderAPI.ExtractInfos(path, dir), TaskCreationOptions.LongRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> Transmux(Episode episode)
|
|
||||||
{
|
|
||||||
string folder = Path.Combine(_transmuxPath, episode.Slug);
|
|
||||||
string manifest = Path.Combine(folder, episode.Slug + ".m3u8");
|
|
||||||
float playableDuration = 0;
|
|
||||||
bool transmuxFailed = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(folder);
|
|
||||||
if (File.Exists(manifest))
|
|
||||||
return manifest;
|
|
||||||
}
|
|
||||||
catch (UnauthorizedAccessException)
|
|
||||||
{
|
|
||||||
await Console.Error.WriteLineAsync($"Access to the path {manifest} is denied. Please change your transmux path in the config.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
transmuxFailed = TranscoderAPI.transmux(episode.Path, manifest.Replace('\\', '/'), out playableDuration) != 0;
|
|
||||||
}, TaskCreationOptions.LongRunning);
|
|
||||||
while (playableDuration < 10 || !File.Exists(manifest) && !transmuxFailed)
|
|
||||||
await Task.Delay(10);
|
|
||||||
return transmuxFailed ? null : manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<string> Transcode(Episode episode)
|
|
||||||
{
|
|
||||||
return Task.FromResult<string>(null); // Not implemented yet.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Kyoo.Models;
|
|
||||||
using Kyoo.Models.Watch;
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
|
|
||||||
namespace Kyoo.Controllers.TranscoderLink
|
|
||||||
{
|
|
||||||
public static class TranscoderAPI
|
|
||||||
{
|
|
||||||
private const string TranscoderPath = "libtranscoder.so";
|
|
||||||
|
|
||||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int init();
|
|
||||||
|
|
||||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int transmux(string path, string out_path, out float playableDuration);
|
|
||||||
|
|
||||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int transcode(string path, string out_path, out float playableDuration);
|
|
||||||
|
|
||||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern IntPtr extract_infos(string path, string outpath, out int length, out int track_count);
|
|
||||||
|
|
||||||
[DllImport(TranscoderPath, CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern void free(IntPtr stream_ptr);
|
|
||||||
|
|
||||||
|
|
||||||
public static Track[] ExtractInfos(string path, string outPath)
|
|
||||||
{
|
|
||||||
int size = Marshal.SizeOf<Stream>();
|
|
||||||
IntPtr ptr = extract_infos(path, outPath, out int arrayLength, out int trackCount);
|
|
||||||
IntPtr streamsPtr = ptr;
|
|
||||||
Track[] tracks;
|
|
||||||
|
|
||||||
if (trackCount > 0 && ptr != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
tracks = new Track[trackCount];
|
|
||||||
|
|
||||||
int j = 0;
|
|
||||||
for (int i = 0; i < arrayLength; i++)
|
|
||||||
{
|
|
||||||
Stream stream = Marshal.PtrToStructure<Stream>(streamsPtr);
|
|
||||||
if (stream!.Type != StreamType.Unknown)
|
|
||||||
{
|
|
||||||
tracks[j] = new Track(stream);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
streamsPtr += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
tracks = Array.Empty<Track>();
|
|
||||||
|
|
||||||
if (ptr != IntPtr.Zero)
|
|
||||||
free(ptr); // free_streams is not necesarry since the Marshal free the unmanaged pointers.
|
|
||||||
return tracks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -158,6 +158,7 @@ namespace Kyoo
|
|||||||
services.AddScoped<DbContext, DatabaseContext>();
|
services.AddScoped<DbContext, DatabaseContext>();
|
||||||
|
|
||||||
services.AddScoped<ILibraryManager, LibraryManager>();
|
services.AddScoped<ILibraryManager, LibraryManager>();
|
||||||
|
services.AddScoped<IFileManager, FileManager>();
|
||||||
services.AddSingleton<ITranscoder, Transcoder>();
|
services.AddSingleton<ITranscoder, Transcoder>();
|
||||||
services.AddSingleton<IThumbnailsManager, ThumbnailsManager>();
|
services.AddSingleton<IThumbnailsManager, ThumbnailsManager>();
|
||||||
services.AddSingleton<IProviderManager, ProviderManager>();
|
services.AddSingleton<IProviderManager, ProviderManager>();
|
||||||
|
@ -359,8 +359,8 @@ namespace Kyoo.Controllers
|
|||||||
|
|
||||||
private async Task<ICollection<Track>> GetTracks(Episode episode)
|
private async Task<ICollection<Track>> GetTracks(Episode episode)
|
||||||
{
|
{
|
||||||
episode.Tracks = (await _transcoder.ExtractInfos(episode.Path))
|
episode.Tracks = (await _transcoder.ExtractInfos(episode.Path, false))
|
||||||
.Where(x => x.Type != StreamType.Font)
|
.Where(x => x.Type != StreamType.Attachment)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
return episode.Tracks;
|
return episode.Tracks;
|
||||||
}
|
}
|
||||||
|
@ -101,8 +101,8 @@ namespace Kyoo.Tasks
|
|||||||
if (subs)
|
if (subs)
|
||||||
{
|
{
|
||||||
await _library.Load(episode, x => x.Tracks);
|
await _library.Load(episode, x => x.Tracks);
|
||||||
episode.Tracks = (await _transcoder!.ExtractInfos(episode.Path))
|
episode.Tracks = (await _transcoder!.ExtractInfos(episode.Path, true))
|
||||||
.Where(x => x.Type != StreamType.Font)
|
.Where(x => x.Type != StreamType.Attachment)
|
||||||
.Concat(episode.Tracks.Where(x => x.IsExternal))
|
.Concat(episode.Tracks.Where(x => x.IsExternal))
|
||||||
.ToList();
|
.ToList();
|
||||||
await _library.EditEpisode(episode, false);
|
await _library.EditEpisode(episode, false);
|
||||||
|
@ -14,10 +14,12 @@ namespace Kyoo.Api
|
|||||||
public class SubtitleApi : ControllerBase
|
public class SubtitleApi : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IFileManager _files;
|
||||||
|
|
||||||
public SubtitleApi(ILibraryManager libraryManager)
|
public SubtitleApi(ILibraryManager libraryManager, IFileManager files)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
|
_files = files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -39,9 +41,8 @@ namespace Kyoo.Api
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
if (subtitle.Codec == "subrip" && extension == "vtt")
|
if (subtitle.Codec == "subrip" && extension == "vtt")
|
||||||
return new ConvertSubripToVtt(subtitle.Path);
|
return new ConvertSubripToVtt(subtitle.Path, _files);
|
||||||
string mime = subtitle.Codec == "ass" ? "text/x-ssa" : "application/x-subrip";
|
return _files.FileResult(subtitle.Path);
|
||||||
return PhysicalFile(subtitle.Path, mime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,27 +50,29 @@ namespace Kyoo.Api
|
|||||||
public class ConvertSubripToVtt : IActionResult
|
public class ConvertSubripToVtt : IActionResult
|
||||||
{
|
{
|
||||||
private readonly string _path;
|
private readonly string _path;
|
||||||
|
private readonly IFileManager _files;
|
||||||
|
|
||||||
public ConvertSubripToVtt(string subtitlePath)
|
public ConvertSubripToVtt(string subtitlePath, IFileManager files)
|
||||||
{
|
{
|
||||||
_path = subtitlePath;
|
_path = subtitlePath;
|
||||||
|
_files = files;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExecuteResultAsync(ActionContext context)
|
public async Task ExecuteResultAsync(ActionContext context)
|
||||||
{
|
{
|
||||||
string line;
|
List<string> lines = new();
|
||||||
List<string> lines = new List<string>();
|
|
||||||
|
|
||||||
context.HttpContext.Response.StatusCode = 200;
|
context.HttpContext.Response.StatusCode = 200;
|
||||||
context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt");
|
context.HttpContext.Response.Headers.Add("Content-Type", "text/vtt");
|
||||||
|
|
||||||
await using (StreamWriter writer = new StreamWriter(context.HttpContext.Response.Body))
|
await using (StreamWriter writer = new(context.HttpContext.Response.Body))
|
||||||
{
|
{
|
||||||
await writer.WriteLineAsync("WEBVTT");
|
await writer.WriteLineAsync("WEBVTT");
|
||||||
await writer.WriteLineAsync("");
|
await writer.WriteLineAsync("");
|
||||||
await writer.WriteLineAsync("");
|
await writer.WriteLineAsync("");
|
||||||
|
|
||||||
using StreamReader reader = new StreamReader(_path);
|
using StreamReader reader = _files.GetReader(_path);
|
||||||
|
string line;
|
||||||
while ((line = await reader.ReadLineAsync()) != null)
|
while ((line = await reader.ReadLineAsync()) != null)
|
||||||
{
|
{
|
||||||
if (line == "")
|
if (line == "")
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
using Kyoo.Controllers;
|
using System.IO;
|
||||||
|
using Kyoo.Controllers;
|
||||||
using Kyoo.Models;
|
using Kyoo.Models;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
|
||||||
|
|
||||||
namespace Kyoo.Api
|
namespace Kyoo.Api
|
||||||
{
|
{
|
||||||
@ -16,14 +15,18 @@ namespace Kyoo.Api
|
|||||||
{
|
{
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly ITranscoder _transcoder;
|
private readonly ITranscoder _transcoder;
|
||||||
|
private readonly IFileManager _files;
|
||||||
private readonly string _transmuxPath;
|
private readonly string _transmuxPath;
|
||||||
private readonly string _transcodePath;
|
private readonly string _transcodePath;
|
||||||
private FileExtensionContentTypeProvider _provider;
|
|
||||||
|
|
||||||
public VideoApi(ILibraryManager libraryManager, ITranscoder transcoder, IConfiguration config)
|
public VideoApi(ILibraryManager libraryManager,
|
||||||
|
ITranscoder transcoder,
|
||||||
|
IConfiguration config,
|
||||||
|
IFileManager files)
|
||||||
{
|
{
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
_transcoder = transcoder;
|
_transcoder = transcoder;
|
||||||
|
_files = files;
|
||||||
_transmuxPath = config.GetValue<string>("transmuxTempPath");
|
_transmuxPath = config.GetValue<string>("transmuxTempPath");
|
||||||
_transcodePath = config.GetValue<string>("transcodeTempPath");
|
_transcodePath = config.GetValue<string>("transcodeTempPath");
|
||||||
}
|
}
|
||||||
@ -37,19 +40,6 @@ namespace Kyoo.Api
|
|||||||
ctx.HttpContext.Response.Headers.Add("Expires", "0");
|
ctx.HttpContext.Response.Headers.Add("Expires", "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _GetContentType(string path)
|
|
||||||
{
|
|
||||||
if (_provider == null)
|
|
||||||
{
|
|
||||||
_provider = new FileExtensionContentTypeProvider();
|
|
||||||
_provider.Mappings[".mkv"] = "video/x-matroska";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_provider.TryGetContentType(path, out string contentType))
|
|
||||||
return contentType;
|
|
||||||
return "video/mp4";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
[HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
||||||
[HttpGet("direct/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
[HttpGet("direct/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}")]
|
||||||
@ -60,9 +50,9 @@ namespace Kyoo.Api
|
|||||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||||
|
|
||||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||||
if (episode != null && System.IO.File.Exists(episode.Path))
|
if (episode == null)
|
||||||
return PhysicalFile(episode.Path, _GetContentType(episode.Path), true);
|
return NotFound();
|
||||||
return NotFound();
|
return _files.FileResult(episode.Path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{movieSlug}")]
|
[HttpGet("{movieSlug}")]
|
||||||
@ -72,9 +62,9 @@ namespace Kyoo.Api
|
|||||||
{
|
{
|
||||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||||
|
|
||||||
if (episode != null && System.IO.File.Exists(episode.Path))
|
if (episode == null)
|
||||||
return PhysicalFile(episode.Path, _GetContentType(episode.Path), true);
|
return NotFound();
|
||||||
return NotFound();
|
return _files.FileResult(episode.Path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -86,12 +76,12 @@ namespace Kyoo.Api
|
|||||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||||
|
|
||||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
if (episode == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
string path = await _transcoder.Transmux(episode);
|
string path = await _transcoder.Transmux(episode);
|
||||||
if (path == null)
|
if (path == null)
|
||||||
return StatusCode(500);
|
return StatusCode(500);
|
||||||
return PhysicalFile(path, "application/x-mpegurl", true);
|
return _files.FileResult(path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("transmux/{movieSlug}/master.m3u8")]
|
[HttpGet("transmux/{movieSlug}/master.m3u8")]
|
||||||
@ -100,12 +90,12 @@ namespace Kyoo.Api
|
|||||||
{
|
{
|
||||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||||
|
|
||||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
if (episode == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
string path = await _transcoder.Transmux(episode);
|
string path = await _transcoder.Transmux(episode);
|
||||||
if (path == null)
|
if (path == null)
|
||||||
return StatusCode(500);
|
return StatusCode(500);
|
||||||
return PhysicalFile(path, "application/x-mpegurl", true);
|
return _files.FileResult(path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("transcode/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/master.m3u8")]
|
[HttpGet("transcode/{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/master.m3u8")]
|
||||||
@ -116,12 +106,12 @@ namespace Kyoo.Api
|
|||||||
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
return BadRequest(new {error = "Season number or episode number can not be negative."});
|
||||||
|
|
||||||
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
Episode episode = await _libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber);
|
||||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
if (episode == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
string path = await _transcoder.Transcode(episode);
|
string path = await _transcoder.Transcode(episode);
|
||||||
if (path == null)
|
if (path == null)
|
||||||
return StatusCode(500);
|
return StatusCode(500);
|
||||||
return PhysicalFile(path, "application/x-mpegurl", true);
|
return _files.FileResult(path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("transcode/{movieSlug}/master.m3u8")]
|
[HttpGet("transcode/{movieSlug}/master.m3u8")]
|
||||||
@ -130,12 +120,12 @@ namespace Kyoo.Api
|
|||||||
{
|
{
|
||||||
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
Episode episode = await _libraryManager.GetMovieEpisode(movieSlug);
|
||||||
|
|
||||||
if (episode == null || !System.IO.File.Exists(episode.Path))
|
if (episode == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
string path = await _transcoder.Transcode(episode);
|
string path = await _transcoder.Transcode(episode);
|
||||||
if (path == null)
|
if (path == null)
|
||||||
return StatusCode(500);
|
return StatusCode(500);
|
||||||
return PhysicalFile(path, "application/x-mpegurl", true);
|
return _files.FileResult(path, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 3885dca743bbde5d83cb3816646455856fc5c316
|
Subproject commit 9acd635aca92ad81f1de562e34b2c7c270bade29
|
Loading…
x
Reference in New Issue
Block a user