mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Adding subtitle swap fonctionality inside the web app player.
This commit is contained in:
parent
1ce584d087
commit
7cf9a6fe6b
@ -64,7 +64,7 @@
|
|||||||
<button *ngIf="this.item.audios.length > 0" mat-icon-button data-toggle="tooltip" data-placement="top" title="Select audio track">
|
<button *ngIf="this.item.audios.length > 0" mat-icon-button data-toggle="tooltip" data-placement="top" title="Select audio track">
|
||||||
<mat-icon>music_note</mat-icon>
|
<mat-icon>music_note</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button *ngIf="this.item.subtitles.length > 0" mat-icon-button data-toggle="tooltip" data-placement="top" title="Select subtitle track">
|
<button *ngIf="this.item.subtitles.length > 0" mat-icon-button [matMenuTriggerFor]="subtitles" data-toggle="tooltip" data-placement="top" title="Select subtitle track">
|
||||||
<mat-icon>closed_caption</mat-icon>
|
<mat-icon>closed_caption</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Cast">
|
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Cast">
|
||||||
@ -79,6 +79,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<mat-menu #subtitles="matMenu">
|
||||||
|
<button mat-menu-item (click)="selectSubtitle(null)">
|
||||||
|
<span>None</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div *ngFor="let subtitle of this.item.subtitles">
|
||||||
|
<button mat-menu-item *ngIf="subtitle.codec == 'ass'; else elseBlock" (click)="selectSubtitle(subtitle)">
|
||||||
|
<span>{{subtitle.language}}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ng-template #elseBlock>
|
||||||
|
<button mat-menu-item disabled>
|
||||||
|
<span>{{subtitle.language}}</span>
|
||||||
|
</button>
|
||||||
|
</ng-template>
|
||||||
|
</div>
|
||||||
|
</mat-menu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||||
import { WatchItem } from "../../models/watch-item";
|
import { WatchItem, Track } from "../../models/watch-item";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { DomSanitizer, Title } from "@angular/platform-browser";
|
import { DomSanitizer, Title } from "@angular/platform-browser";
|
||||||
import { Location } from "@angular/common";
|
import { Location } from "@angular/common";
|
||||||
@ -20,6 +20,7 @@ export class PlayerComponent implements OnInit
|
|||||||
volume: number = 100;
|
volume: number = 100;
|
||||||
seeking: boolean = false;
|
seeking: boolean = false;
|
||||||
videoHider;
|
videoHider;
|
||||||
|
selectedSubtitle: Track;
|
||||||
|
|
||||||
hours: number;
|
hours: number;
|
||||||
minutes: number = 0;
|
minutes: number = 0;
|
||||||
@ -100,7 +101,6 @@ export class PlayerComponent implements OnInit
|
|||||||
let progressBar: HTMLElement = document.getElementById("progress-bar") as HTMLElement;
|
let progressBar: HTMLElement = document.getElementById("progress-bar") as HTMLElement;
|
||||||
$(progressBar).click((event) =>
|
$(progressBar).click((event) =>
|
||||||
{
|
{
|
||||||
console.log("Duration: " + this.player.duration);
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let time: number = this.getTimeFromSeekbar(progressBar, event.pageX);
|
let time: number = this.getTimeFromSeekbar(progressBar, event.pageX);
|
||||||
this.player.currentTime = time;
|
this.player.currentTime = time;
|
||||||
@ -182,8 +182,6 @@ export class PlayerComponent implements OnInit
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('[data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
$('[data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||||
|
|
||||||
SubtitleManager.add(this.player, "/api/subtitle/" + this.item.link + "-fre.ass", true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTimeFromSeekbar(progressBar: HTMLElement, pageX: number)
|
getTimeFromSeekbar(progressBar: HTMLElement, pageX: number)
|
||||||
@ -281,6 +279,36 @@ export class PlayerComponent implements OnInit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectSubtitle(subtitle: Track)
|
||||||
|
{
|
||||||
|
this.selectedSubtitle = subtitle;
|
||||||
|
|
||||||
|
if (subtitle == null)
|
||||||
|
{
|
||||||
|
SubtitleManager.remove(this.player);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (subtitle.codec == "ass")
|
||||||
|
SubtitleManager.add(this.player, this.getSubtitleLink(subtitle), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubtitleLink(subtitle: Track): string
|
||||||
|
{
|
||||||
|
let link: string = "/api/subtitle/" + this.item.link + "-" + subtitle.language;
|
||||||
|
|
||||||
|
if (subtitle.isForced)
|
||||||
|
link += "-forced";
|
||||||
|
|
||||||
|
//The extension is not necesarry but we add this because it allow the user to quickly download the file in the good format if he wants.
|
||||||
|
if (subtitle.codec == "ass")
|
||||||
|
link += ".ass";
|
||||||
|
else if (subtitle.codec == "subrip")
|
||||||
|
link += ".srt"
|
||||||
|
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
getThumb(url: string)
|
getThumb(url: string)
|
||||||
{
|
{
|
||||||
|
@ -15,15 +15,15 @@ export interface WatchItem
|
|||||||
previousEpisode: string;
|
previousEpisode: string;
|
||||||
nextEpisode: Episode;
|
nextEpisode: Episode;
|
||||||
|
|
||||||
audio: Stream[];
|
audio: Track[];
|
||||||
subtitles: Stream[];
|
subtitles: Track[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Stream
|
export interface Track
|
||||||
{
|
{
|
||||||
title: string;
|
title: string;
|
||||||
language: string;
|
language: string;
|
||||||
isDefault: boolean;
|
isDefault: boolean;
|
||||||
isForced: boolean;
|
isForced: boolean;
|
||||||
format: string;
|
codec: string;
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,23 @@ namespace Kyoo.Controllers
|
|||||||
this.transcoder = transcoder;
|
this.transcoder = transcoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.{format?}")]
|
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}.{codec?}")]
|
||||||
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 codec)
|
||||||
{
|
{
|
||||||
Track subtitle = libraryManager.GetSubtitle(showSlug, seasonNumber, episodeNumber, languageTag);
|
Track subtitle = libraryManager.GetSubtitle(showSlug, seasonNumber, episodeNumber, languageTag, false);
|
||||||
|
|
||||||
|
if (subtitle == null)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
//Should use appropriate mime type here
|
||||||
|
return PhysicalFile(subtitle.Path, "text/x-ssa");
|
||||||
|
}
|
||||||
|
|
||||||
|
//This one is never called.
|
||||||
|
[HttpGet("{showSlug}-s{seasonNumber}e{episodeNumber}-{languageTag}-{disposition}.{codec?}")] //Disposition can't be tagged as optional because there is a parametter after him.
|
||||||
|
public IActionResult GetForcedSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, string disposition, string codec)
|
||||||
|
{
|
||||||
|
Track subtitle = libraryManager.GetSubtitle(showSlug, seasonNumber, episodeNumber, languageTag, disposition == "forced");
|
||||||
|
|
||||||
if (subtitle == null)
|
if (subtitle == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
@ -18,7 +18,7 @@ namespace Kyoo.InternalAPI
|
|||||||
|
|
||||||
//Internal HTML read
|
//Internal HTML read
|
||||||
(List<Track> audios, List<Track> subtitles) GetStreams(long episodeID);
|
(List<Track> audios, List<Track> subtitles) GetStreams(long episodeID);
|
||||||
Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag);
|
Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, bool forced);
|
||||||
|
|
||||||
//Public read
|
//Public read
|
||||||
IEnumerable<Library> GetLibraries();
|
IEnumerable<Library> GetLibraries();
|
||||||
|
@ -73,7 +73,7 @@ namespace Kyoo.InternalAPI
|
|||||||
CREATE TABLE tracks(
|
CREATE TABLE tracks(
|
||||||
id INTEGER PRIMARY KEY UNIQUE,
|
id INTEGER PRIMARY KEY UNIQUE,
|
||||||
episodeID INTEGER,
|
episodeID INTEGER,
|
||||||
streamType TEXT,
|
streamType INTEGER,
|
||||||
title TEXT,
|
title TEXT,
|
||||||
language TEXT,
|
language TEXT,
|
||||||
codec TEXT,
|
codec TEXT,
|
||||||
@ -203,21 +203,21 @@ namespace Kyoo.InternalAPI
|
|||||||
|
|
||||||
while (reader.Read())
|
while (reader.Read())
|
||||||
{
|
{
|
||||||
Track stream = Track.FromReader(reader);
|
Track track = Track.FromReader(reader);
|
||||||
|
|
||||||
if (stream.type == StreamType.Audio)
|
if (track.type == StreamType.Audio)
|
||||||
audios.Add(stream);
|
audios.Add(track);
|
||||||
else if (stream.type == StreamType.Subtitle)
|
else if (track.type == StreamType.Subtitle)
|
||||||
subtitles.Add(stream);
|
subtitles.Add(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (audios, subtitles);
|
return (audios, subtitles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag)
|
public Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, bool forced)
|
||||||
{
|
{
|
||||||
string query = "SELECT tracks.* FROM tracks JOIN episodes ON tracks.episodeID = episodes.id JOIN shows ON episodes.showID = shows.id WHERE shows.slug = $showSlug AND episodes.seasonNumber = $seasonNumber AND episodes.episodeNumber = $episodeNumber AND tracks.language = $languageTag;";
|
string query = "SELECT tracks.* FROM tracks JOIN episodes ON tracks.episodeID = episodes.id JOIN shows ON episodes.showID = shows.id WHERE shows.slug = $showSlug AND episodes.seasonNumber = $seasonNumber AND episodes.episodeNumber = $episodeNumber AND tracks.language = $languageTag AND tracks.isForced = $forced;";
|
||||||
|
|
||||||
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
using (SQLiteCommand cmd = new SQLiteCommand(query, sqlConnection))
|
||||||
{
|
{
|
||||||
@ -225,6 +225,7 @@ namespace Kyoo.InternalAPI
|
|||||||
cmd.Parameters.AddWithValue("$seasonNumber", seasonNumber);
|
cmd.Parameters.AddWithValue("$seasonNumber", seasonNumber);
|
||||||
cmd.Parameters.AddWithValue("$episodeNumber", episodeNumber);
|
cmd.Parameters.AddWithValue("$episodeNumber", episodeNumber);
|
||||||
cmd.Parameters.AddWithValue("$languageTag", languageTag);
|
cmd.Parameters.AddWithValue("$languageTag", languageTag);
|
||||||
|
cmd.Parameters.AddWithValue("$forced", forced);
|
||||||
SQLiteDataReader reader = cmd.ExecuteReader();
|
SQLiteDataReader reader = cmd.ExecuteReader();
|
||||||
|
|
||||||
if (reader.Read())
|
if (reader.Read())
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Kyoo.Models.Watch;
|
using Kyoo.Models.Watch;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Kyoo.Models
|
namespace Kyoo.Models
|
||||||
@ -44,7 +45,7 @@ namespace Kyoo.Models
|
|||||||
|
|
||||||
public static Track FromReader(System.Data.SQLite.SQLiteDataReader reader)
|
public static Track FromReader(System.Data.SQLite.SQLiteDataReader reader)
|
||||||
{
|
{
|
||||||
return new Track(reader["streamType"] as StreamType? ?? StreamType.Unknow,
|
return new Track((StreamType)Enum.ToObject(typeof(StreamType), reader["streamType"]),
|
||||||
reader["title"] as string,
|
reader["title"] as string,
|
||||||
reader["language"] as string,
|
reader["language"] as string,
|
||||||
reader["isDefault"] as bool? ?? false,
|
reader["isDefault"] as bool? ?? false,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user