mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-08 10:44:20 -04:00
Reworking player flow with subtitles and end user subtitles.
This commit is contained in:
parent
735c4098a3
commit
d17e598a18
@ -30,7 +30,7 @@ export class BrowseComponent implements OnInit
|
||||
|
||||
getThumb(slug: string)
|
||||
{
|
||||
return this.sanitizer.bypassSecurityTrustStyle("url(/thumb/" + slug + ")");
|
||||
return this.sanitizer.bypassSecurityTrustStyle("url(/poster/" + slug + ")");
|
||||
}
|
||||
|
||||
sort(type: string, order: boolean)
|
||||
|
@ -35,13 +35,13 @@
|
||||
|
||||
<div class="buttons">
|
||||
<div class="left">
|
||||
<button *ngIf="this.item.previousEpisode" mat-icon-button data-toggle="tooltip" data-placement="top" title="Previous" routerLink="/watch/{{this.item.previousEpisode}}">
|
||||
<a *ngIf="this.item.previousEpisode" mat-icon-button data-toggle="tooltip" data-placement="top" title="Previous" routerLink="/watch/{{this.item.previousEpisode}}" href="/watch/{{this.item.previousEpisode}}" queryParamsHandling="merge">
|
||||
<mat-icon>skip_previous</mat-icon>
|
||||
</button>
|
||||
</a>
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Play" id="play" (click)="tooglePlayback()">
|
||||
<mat-icon>{{this.playIcon}}</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button id="nextBtn" *ngIf="this.item.nextEpisode" routerLink="/watch/{{this.item.nextEpisode.link}}">
|
||||
<a mat-icon-button id="nextBtn" *ngIf="this.item.nextEpisode" routerLink="/watch/{{this.item.nextEpisode.link}}" href="/watch/{{this.item.nextEpisode.link}}" queryParamsHandling="merge">
|
||||
<mat-icon>skip_next</mat-icon>
|
||||
|
||||
<div id="next">
|
||||
@ -53,7 +53,7 @@
|
||||
<p>{{this.item.nextEpisode.overview}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</a>
|
||||
<div id="volume">
|
||||
<button mat-icon-button data-toggle="tooltip" data-placement="top" title="Volume" (click)="toogleMute()">
|
||||
<mat-icon>{{this.volumeIcon}}</mat-icon>
|
||||
@ -85,21 +85,23 @@
|
||||
</div>
|
||||
|
||||
<mat-menu #subtitles="matMenu">
|
||||
<button [ngClass]="{'selected': this.selectedSubtitle == null}" mat-menu-item (click)="selectSubtitle(null)">
|
||||
<span>None</span>
|
||||
</button>
|
||||
|
||||
<div *ngFor="let subtitle of this.item.subtitles">
|
||||
<button [ngClass]="{'selected': this.selectedSubtitle == subtitle}" mat-menu-item *ngIf="subtitle.codec == 'ass'; else elseBlock" (click)="selectSubtitle(subtitle)">
|
||||
<span>{{subtitle.displayName}}</span>
|
||||
<ng-template matMenuContent>
|
||||
<button [ngClass]="{'selected': this.selectedSubtitle == null}" mat-menu-item (click)="selectSubtitle(null)">
|
||||
<span>None</span>
|
||||
</button>
|
||||
|
||||
<ng-template #elseBlock>
|
||||
<button mat-menu-item disabled>
|
||||
<span>{{subtitle.displayName}} ({{subtitle.codec}})</span>
|
||||
<div *ngFor="let subtitle of this.item.subtitles">
|
||||
<button [ngClass]="{'selected': this.selectedSubtitle == subtitle}" mat-menu-item *ngIf="subtitle.codec == 'ass'; else elseBlock" (click)="selectSubtitle(subtitle)">
|
||||
<span>{{subtitle.displayName}}</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<ng-template #elseBlock>
|
||||
<button mat-menu-item disabled>
|
||||
<span>{{subtitle.displayName}} ({{subtitle.codec}})</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -120,6 +120,15 @@
|
||||
margin-right: .3rem;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
> a
|
||||
{
|
||||
margin-left: .3rem;
|
||||
margin-right: .3rem;
|
||||
outline: none;
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { WatchItem, Track } from "../../models/watch-item";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { DomSanitizer, Title } from "@angular/platform-browser";
|
||||
import { Location } from "@angular/common";
|
||||
import { MatSliderChange } from "@angular/material/slider";
|
||||
@ -39,7 +39,7 @@ export class PlayerComponent implements OnInit
|
||||
private progress: HTMLElement;
|
||||
private buffered: HTMLElement;
|
||||
|
||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private location: Location, private title: Title) { }
|
||||
constructor(private route: ActivatedRoute, private sanitizer: DomSanitizer, private location: Location, private title: Title, private router: Router) { }
|
||||
|
||||
ngOnInit()
|
||||
{
|
||||
@ -192,6 +192,16 @@ export class PlayerComponent implements OnInit
|
||||
}
|
||||
});
|
||||
|
||||
//Load sub selected from the url.
|
||||
let sub: string = this.route.snapshot.queryParams["sub"];
|
||||
if (sub != null)
|
||||
{
|
||||
let languageCode: string = sub.substring(0, 3);
|
||||
let forced: boolean = sub.length > 3 && sub.substring(4) == "for";
|
||||
|
||||
this.selectSubtitle(this.item.subtitles.find(x => x.language == languageCode && x.isForced == forced), false);
|
||||
}
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip({ trigger: "hover" });
|
||||
}
|
||||
|
||||
@ -228,11 +238,6 @@ export class PlayerComponent implements OnInit
|
||||
$('[data-toggle="tooltip"]').hide();
|
||||
}
|
||||
|
||||
back()
|
||||
{
|
||||
this.location.back();
|
||||
}
|
||||
|
||||
tooglePlayback()
|
||||
{
|
||||
if (this.player.paused)
|
||||
@ -293,37 +298,37 @@ export class PlayerComponent implements OnInit
|
||||
}
|
||||
}
|
||||
|
||||
selectSubtitle(subtitle: Track)
|
||||
selectSubtitle(subtitle: Track, changeUrl: boolean = true)
|
||||
{
|
||||
if (changeUrl)
|
||||
{
|
||||
let subSlug: string;
|
||||
if (subtitle != null)
|
||||
{
|
||||
subSlug = subtitle.language;
|
||||
if (subtitle.isForced)
|
||||
subSlug += "-for";
|
||||
}
|
||||
|
||||
this.router.navigate([], { relativeTo: this.route, queryParams: { sub: subSlug }, replaceUrl: true, queryParamsHandling: "merge" });
|
||||
}
|
||||
|
||||
this.selectedSubtitle = subtitle;
|
||||
|
||||
if (subtitle == null)
|
||||
{
|
||||
console.log("Removing subtitle");
|
||||
SubtitleManager.remove(this.player);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("Loading subtitle: " + subtitle.displayName);
|
||||
|
||||
if (subtitle.codec == "ass")
|
||||
SubtitleManager.add(this.player, this.getSubtitleLink(subtitle), true);
|
||||
SubtitleManager.add(this.player, subtitle.link, 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)
|
||||
{
|
||||
return this.sanitizer.bypassSecurityTrustStyle("url(" + url + ")");
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<div class="header container pt-sm-5">
|
||||
<div class="row">
|
||||
<img class="poster d-none d-sm-block" src="thumb/{{this.show.slug}}" />
|
||||
<img class="poster d-none d-sm-block" src="poster/{{this.show.slug}}" />
|
||||
<div class="main col">
|
||||
<div class="info">
|
||||
<h1 class="title">{{this.show.title}}</h1>
|
||||
|
@ -27,4 +27,5 @@ export interface Track
|
||||
isDefault: boolean;
|
||||
isForced: boolean;
|
||||
codec: string;
|
||||
link: string;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ namespace Kyoo.Controllers
|
||||
peoplePath = config.GetValue<string>("peoplePath");
|
||||
}
|
||||
|
||||
[HttpGet("thumb/{showSlug}")]
|
||||
[HttpGet("poster/{showSlug}")]
|
||||
public IActionResult GetShowThumb(string showSlug)
|
||||
{
|
||||
string path = libraryManager.GetShowBySlug(showSlug)?.Path;
|
||||
@ -72,7 +72,7 @@ namespace Kyoo.Controllers
|
||||
return new PhysicalFileResult(thumbPath, "image/jpg");
|
||||
}
|
||||
|
||||
[HttpGet("thumb/{showSlug}/s{seasonNumber}/e{episodeNumber}")]
|
||||
[HttpGet("thumb/{showSlug}-s{seasonNumber}e{episodeNumber}")]
|
||||
public IActionResult GetEpisodeThumb(string showSlug, long seasonNumber, long episodeNumber)
|
||||
{
|
||||
string path = libraryManager.GetEpisode(showSlug, seasonNumber, episodeNumber)?.Path;
|
||||
|
@ -17,7 +17,7 @@ namespace Kyoo.InternalAPI
|
||||
int GetSeasonCount(string showSlug, long seasonNumber);
|
||||
|
||||
//Internal HTML read
|
||||
(List<Track> audios, List<Track> subtitles) GetStreams(long episodeID);
|
||||
(List<Track> audios, List<Track> subtitles) GetStreams(long episodeID, string showSlug);
|
||||
Track GetSubtitle(string showSlug, long seasonNumber, long episodeNumber, string languageTag, bool forced);
|
||||
|
||||
//Public read
|
||||
|
@ -189,7 +189,7 @@ namespace Kyoo.InternalAPI
|
||||
}
|
||||
|
||||
|
||||
public (List<Track> audios, List<Track> subtitles) GetStreams(long episodeID)
|
||||
public (List<Track> audios, List<Track> subtitles) GetStreams(long episodeID, string episodeSlug)
|
||||
{
|
||||
string query = "SELECT * FROM tracks WHERE episodeID = $episodeID;";
|
||||
|
||||
@ -203,7 +203,7 @@ namespace Kyoo.InternalAPI
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
Track track = Track.FromReader(reader);
|
||||
Track track = Track.FromReader(reader).SetLink(episodeSlug);
|
||||
|
||||
if (track.type == StreamType.Audio)
|
||||
audios.Add(track);
|
||||
@ -229,7 +229,7 @@ namespace Kyoo.InternalAPI
|
||||
SQLiteDataReader reader = cmd.ExecuteReader();
|
||||
|
||||
if (reader.Read())
|
||||
return Track.FromReader(reader);
|
||||
return Track.FromReader(reader).SetLink(Episode.GetSlug(showSlug, seasonNumber, episodeNumber));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -77,9 +77,14 @@ namespace Kyoo.Models
|
||||
|
||||
public Episode SetThumb(string showSlug)
|
||||
{
|
||||
Thumb = "thumb/" + showSlug + "/s" + seasonNumber + "/e" + episodeNumber;
|
||||
Link = showSlug + "-s" + seasonNumber + "e" + episodeNumber;
|
||||
Link = GetSlug(showSlug, seasonNumber, episodeNumber);
|
||||
Thumb = "thumb/" + Link;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static string GetSlug(string showSlug, long seasonNumber, long episodeNumber)
|
||||
{
|
||||
return showSlug + "-s" + seasonNumber + "e" + episodeNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ namespace Kyoo.Models
|
||||
public class Track : Stream
|
||||
{
|
||||
public string DisplayName;
|
||||
public string Link;
|
||||
|
||||
[JsonIgnore] public readonly long id;
|
||||
[JsonIgnore] public long episodeID;
|
||||
[JsonIgnore] public StreamType type;
|
||||
@ -44,14 +46,6 @@ namespace Kyoo.Models
|
||||
Codec = codec;
|
||||
IsExternal = isExternal;
|
||||
Path = path;
|
||||
|
||||
//Converting mkv track language to c# system language tag.
|
||||
if (language == "fre")
|
||||
language = "fra";
|
||||
|
||||
DisplayName = CultureInfo.GetCultures(CultureTypes.NeutralCultures).Where(x => x.ThreeLetterISOLanguageName == language).FirstOrDefault()?.DisplayName ?? language;
|
||||
if (Title != null && Title.Length > 1)
|
||||
DisplayName += " - " + Title;
|
||||
}
|
||||
|
||||
public static Track FromReader(System.Data.SQLite.SQLiteDataReader reader)
|
||||
@ -75,5 +69,32 @@ namespace Kyoo.Models
|
||||
{
|
||||
return new Track(type, stream.Title, stream.Language, stream.IsDefault, stream.IsForced, stream.Codec, false, stream.Path);
|
||||
}
|
||||
|
||||
public Track SetLink(string episodeSlug)
|
||||
{
|
||||
string language = Language;
|
||||
//Converting mkv track language to c# system language tag.
|
||||
if (language == "fre")
|
||||
language = "fra";
|
||||
|
||||
DisplayName = CultureInfo.GetCultures(CultureTypes.NeutralCultures).Where(x => x.ThreeLetterISOLanguageName == language).FirstOrDefault()?.DisplayName ?? language;
|
||||
Link = "/api/subtitle/" + episodeSlug + "." + Language;
|
||||
|
||||
if (IsForced)
|
||||
{
|
||||
DisplayName += " Forced";
|
||||
Link += "-forced";
|
||||
}
|
||||
|
||||
if (Title != null && Title.Length > 1)
|
||||
DisplayName += " - " + Title;
|
||||
|
||||
if (Codec == "ass")
|
||||
Link += ".ass";
|
||||
else if (Codec == "subrip")
|
||||
Link += ".srt";
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ namespace Kyoo.Models
|
||||
ReleaseDate = releaseDate;
|
||||
Path = path;
|
||||
|
||||
Link = ShowSlug + "-s" + seasonNumber + "e" + episodeNumber;
|
||||
Link = Episode.GetSlug(ShowSlug, seasonNumber, episodeNumber);
|
||||
}
|
||||
|
||||
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)
|
||||
@ -60,7 +60,7 @@ namespace Kyoo.Models
|
||||
|
||||
public WatchItem SetStreams(ILibraryManager libraryManager)
|
||||
{
|
||||
(IEnumerable<Track> audios, IEnumerable<Track> subtitles) streams = libraryManager.GetStreams(episodeID);
|
||||
(IEnumerable<Track> audios, IEnumerable<Track> subtitles) streams = libraryManager.GetStreams(episodeID, Link);
|
||||
audios = streams.audios;
|
||||
subtitles = streams.subtitles;
|
||||
return this;
|
||||
|
Loading…
x
Reference in New Issue
Block a user