mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add earing Impaired suptitle flag support (#754)
This commit is contained in:
parent
cd89e757c1
commit
2ee313d5f6
@ -95,6 +95,10 @@ export const SubtitleP = TrackP.extend({
|
||||
* Is this an external subtitle (as in stored in a different file)
|
||||
*/
|
||||
isExternal: z.boolean(),
|
||||
/**
|
||||
* Is this a hearing impaired subtitle?
|
||||
*/
|
||||
isHearingImpaired: z.boolean(),
|
||||
});
|
||||
export type Subtitle = z.infer<typeof SubtitleP>;
|
||||
|
||||
|
@ -59,6 +59,9 @@ const MediaInfoTable = ({
|
||||
// Only show it if there is more than one track
|
||||
track.isDefault && !singleTrack ? t("mediainfo.default") : undefined,
|
||||
track.isForced ? t("mediainfo.forced") : undefined,
|
||||
"isHearingImpaired" in track && track.isHearingImpaired
|
||||
? t("mediainfo.hearing-impaired")
|
||||
: undefined,
|
||||
"isExternal" in track && track.isExternal ? t("mediainfo.external") : undefined,
|
||||
track.codec,
|
||||
]
|
||||
|
@ -29,7 +29,7 @@ import { useAtom } from "jotai";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Platform, View } from "react-native";
|
||||
import { type Stylable, useYoshiki } from "yoshiki/native";
|
||||
import { useDisplayName } from "../../utils";
|
||||
import { useSubtitleName } from "../../utils";
|
||||
import { fullscreenAtom, subtitleAtom } from "../state";
|
||||
import { AudiosMenu, QualitiesMenu } from "../video";
|
||||
|
||||
@ -49,7 +49,7 @@ export const RightButtons = ({
|
||||
} & Stylable) => {
|
||||
const { css } = useYoshiki();
|
||||
const { t } = useTranslation();
|
||||
const getDisplayName = useDisplayName();
|
||||
const getSubtitleName = useSubtitleName();
|
||||
const [isFullscreen, setFullscreen] = useAtom(fullscreenAtom);
|
||||
const [selectedSubtitle, setSubtitle] = useAtom(subtitleAtom);
|
||||
|
||||
@ -74,7 +74,7 @@ export const RightButtons = ({
|
||||
{subtitles.map((x, i) => (
|
||||
<Menu.Item
|
||||
key={x.index ?? i}
|
||||
label={x.link ? getDisplayName(x) : `${getDisplayName(x)} (${x.codec})`}
|
||||
label={x.link ? getSubtitleName(x) : `${getSubtitleName(x)} (${x.codec})`}
|
||||
selected={selectedSubtitle === x}
|
||||
disabled={!x.link}
|
||||
onSelect={() => setSubtitle(x)}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import type { Track } from "@kyoo/models";
|
||||
import type { Subtitle, Track } from "@kyoo/models";
|
||||
|
||||
import intl from "langmap";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const useLanguageName = () => {
|
||||
return (lang: string) => intl[lang]?.nativeName;
|
||||
@ -7,6 +9,7 @@ export const useLanguageName = () => {
|
||||
|
||||
export const useDisplayName = () => {
|
||||
const getLanguageName = useLanguageName();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (sub: Track) => {
|
||||
const lng = sub.language ? getLanguageName(sub.language) : null;
|
||||
@ -14,8 +17,25 @@ export const useDisplayName = () => {
|
||||
if (lng && sub.title && sub.title !== lng) return `${lng} - ${sub.title}`;
|
||||
if (lng) return lng;
|
||||
if (sub.title) return sub.title;
|
||||
if (sub.index !== null) return `Unknown (${sub.index})`;
|
||||
return "Unknown";
|
||||
if (sub.index !== null) return `${t("mediainfo.unknown")} (${sub.index})`;
|
||||
return t("mediainfo.unknown");
|
||||
};
|
||||
};
|
||||
|
||||
export const useSubtitleName = () => {
|
||||
const getDisplayName = useDisplayName();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (sub: Subtitle) => {
|
||||
const name = getDisplayName(sub);
|
||||
const attributes = [name];
|
||||
|
||||
if (sub.isDefault) attributes.push(t("mediainfo.default"));
|
||||
if (sub.isForced) attributes.push(t("mediainfo.forced"));
|
||||
if (sub.isHearingImpaired) attributes.push(t("mediainfo.hearing-impaired"));
|
||||
if (sub.isExternal) attributes.push(t("mediainfo.external"));
|
||||
|
||||
return attributes.join(" - ");
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -259,12 +259,14 @@
|
||||
"audio": "Audio",
|
||||
"subtitles": "Subtitles",
|
||||
"forced": "Forced",
|
||||
"hearing-impaired": "CC",
|
||||
"default": "Default",
|
||||
"external": "External",
|
||||
"duration": "Duration",
|
||||
"size": "Size",
|
||||
"novideo": "No video",
|
||||
"nocontainer": "Invalid container"
|
||||
"nocontainer": "Invalid container",
|
||||
"unknown": "Unknown"
|
||||
},
|
||||
"admin": {
|
||||
"users": {
|
||||
|
@ -259,12 +259,14 @@
|
||||
"audio": "Áudio",
|
||||
"subtitles": "Legendas",
|
||||
"forced": "Forçado",
|
||||
"hearing-impaired": "CC",
|
||||
"default": "Padrão",
|
||||
"duration": "Duração",
|
||||
"size": "Tamanho",
|
||||
"novideo": "Sem vídeo",
|
||||
"nocontainer": "Contêiner inválido",
|
||||
"external": "Externo"
|
||||
"external": "Externo",
|
||||
"unknown": "Desconhecido"
|
||||
},
|
||||
"admin": {
|
||||
"users": {
|
||||
|
@ -0,0 +1,5 @@
|
||||
begin;
|
||||
|
||||
alter table subtitles drop column is_hearing_impaired;
|
||||
|
||||
commit;
|
@ -0,0 +1,5 @@
|
||||
begin;
|
||||
|
||||
alter table subtitles add column is_hearing_impaired boolean not null default false;
|
||||
|
||||
commit;
|
@ -123,6 +123,8 @@ type Subtitle struct {
|
||||
IsDefault bool `json:"isDefault"`
|
||||
/// Is this stream tagged as forced?
|
||||
IsForced bool `json:"isForced"`
|
||||
/// Is this stream tagged as hearing impaired?
|
||||
IsHearingImpaired bool `json:"isHearingImpaired"`
|
||||
/// Is this an external subtitle (as in stored in a different file)
|
||||
IsExternal bool `json:"isExternal"`
|
||||
/// Where the subtitle is stored (null if stored inside the video)
|
||||
@ -287,14 +289,15 @@ func RetriveMediaInfo(path string, sha string) (*MediaInfo, error) {
|
||||
lang, _ := language.Parse(stream.Tags.Language)
|
||||
idx := uint32(i)
|
||||
return Subtitle{
|
||||
Index: &idx,
|
||||
Title: OrNull(stream.Tags.Title),
|
||||
Language: NullIfUnd(lang.String()),
|
||||
Codec: stream.CodecName,
|
||||
Extension: extension,
|
||||
IsDefault: stream.Disposition.Default != 0,
|
||||
IsForced: stream.Disposition.Forced != 0,
|
||||
Link: &link,
|
||||
Index: &idx,
|
||||
Title: OrNull(stream.Tags.Title),
|
||||
Language: NullIfUnd(lang.String()),
|
||||
Codec: stream.CodecName,
|
||||
Extension: extension,
|
||||
IsDefault: stream.Disposition.Default != 0,
|
||||
IsForced: stream.Disposition.Forced != 0,
|
||||
IsHearingImpaired: stream.Disposition.HearingImpaired != 0,
|
||||
Link: &link,
|
||||
}
|
||||
}),
|
||||
Chapters: Map(mi.Chapters, func(c *ffprobe.Chapter, _ int) Chapter {
|
||||
|
@ -162,7 +162,7 @@ func (s *MetadataService) getMetadata(path string, sha string) (*MediaInfo, erro
|
||||
}
|
||||
|
||||
rows, err = s.database.Query(
|
||||
`select s.idx, s.title, s.language, s.codec, s.extension, s.is_default, s.is_forced
|
||||
`select s.idx, s.title, s.language, s.codec, s.extension, s.is_default, s.is_forced, s.is_hearing_impaired
|
||||
from subtitles as s where s.sha=$1`,
|
||||
sha,
|
||||
)
|
||||
@ -171,7 +171,7 @@ func (s *MetadataService) getMetadata(path string, sha string) (*MediaInfo, erro
|
||||
}
|
||||
for rows.Next() {
|
||||
var s Subtitle
|
||||
err := rows.Scan(&s.Index, &s.Title, &s.Language, &s.Codec, &s.Extension, &s.IsDefault, &s.IsForced)
|
||||
err := rows.Scan(&s.Index, &s.Title, &s.Language, &s.Codec, &s.Extension, &s.IsDefault, &s.IsForced, &s.IsHearingImpaired)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -273,8 +273,8 @@ func (s *MetadataService) storeFreshMetadata(path string, sha string) (*MediaInf
|
||||
}
|
||||
for _, s := range ret.Subtitles {
|
||||
tx.Exec(`
|
||||
insert into subtitles(sha, idx, title, language, codec, extension, is_default, is_forced)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
insert into subtitles(sha, idx, title, language, codec, extension, is_default, is_forced, is_hearing_impaired)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
on conflict (sha, idx) do update set
|
||||
sha = excluded.sha,
|
||||
idx = excluded.idx,
|
||||
@ -283,9 +283,10 @@ func (s *MetadataService) storeFreshMetadata(path string, sha string) (*MediaInf
|
||||
codec = excluded.codec,
|
||||
extension = excluded.extension,
|
||||
is_default = excluded.is_default,
|
||||
is_forced = excluded.is_forced
|
||||
is_forced = excluded.is_forced,
|
||||
is_hearing_impaired = excluded.is_hearing_impaired
|
||||
`,
|
||||
ret.Sha, s.Index, s.Title, s.Language, s.Codec, s.Extension, s.IsDefault, s.IsForced,
|
||||
ret.Sha, s.Index, s.Title, s.Language, s.Codec, s.Extension, s.IsDefault, s.IsForced, s.IsHearingImpaired,
|
||||
)
|
||||
}
|
||||
for _, c := range ret.Chapters {
|
||||
|
@ -43,26 +43,45 @@ outer:
|
||||
Path: &match,
|
||||
Link: &link,
|
||||
}
|
||||
flags := separator.Split(match[len(base_path):], -1)
|
||||
flags_str := strings.ToLower(match[len(base_path):])
|
||||
flags := separator.Split(flags_str, -1)
|
||||
|
||||
// remove extension from flags
|
||||
flags = flags[:len(flags)-1]
|
||||
|
||||
for _, flag := range flags {
|
||||
switch strings.ToLower(flag) {
|
||||
switch flag {
|
||||
case "default":
|
||||
sub.IsDefault = true
|
||||
case "forced":
|
||||
sub.IsForced = true
|
||||
case "hi", "sdh", "cc":
|
||||
sub.IsHearingImpaired = true
|
||||
default:
|
||||
lang, err := language.Parse(flag)
|
||||
if err == nil && lang != language.Und {
|
||||
lang := lang.String()
|
||||
sub.Language = &lang
|
||||
langStr := lang.String()
|
||||
sub.Language = &langStr
|
||||
} else {
|
||||
sub.Title = &flag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Hindi (hi) collision with Hearing Impaired (hi):
|
||||
// "hi" by itself means a language code, but when combined with other lang flags it means Hearing Impaired.
|
||||
// In case Hindi was not detected before, but "hi" is present, assume it is Hindi.
|
||||
if sub.Language == nil {
|
||||
hiCount := Count(flags, "hi")
|
||||
if hiCount > 0 {
|
||||
languageStr := language.Hindi.String()
|
||||
sub.Language = &languageStr
|
||||
}
|
||||
if hiCount == 1 {
|
||||
sub.IsHearingImpaired = false
|
||||
}
|
||||
}
|
||||
|
||||
mi.Subtitles = append(mi.Subtitles, sub)
|
||||
continue outer
|
||||
}
|
||||
|
@ -25,3 +25,14 @@ func Filter[E any](s []E, f func(E) bool) []E {
|
||||
}
|
||||
return s2
|
||||
}
|
||||
|
||||
// Count returns the number of elements in s that are equal to e.
|
||||
func Count[S []E, E comparable](s S, e E) int {
|
||||
var n int
|
||||
for _, v := range s {
|
||||
if v == e {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user