mirror of
https://github.com/yattee/yattee.git
synced 2025-08-09 20:24:06 +00:00
81
Shared/HTTPStatus.swift
Normal file
81
Shared/HTTPStatus.swift
Normal file
@@ -0,0 +1,81 @@
|
||||
// HTTP response status codes
|
||||
|
||||
enum HTTPStatus {
|
||||
// Informational responses (100 - 199)
|
||||
|
||||
static let Continue = 100
|
||||
static let SwitchingProtocols = 101
|
||||
static let Processing = 102
|
||||
static let EarlyHints = 103
|
||||
|
||||
// Successful responses (200 - 299)
|
||||
|
||||
static let OK = 200
|
||||
static let Created = 201
|
||||
static let Accepted = 202
|
||||
static let NonAuthoritativeInformation = 203
|
||||
static let NoContent = 204
|
||||
static let ResetContent = 205
|
||||
static let PartialContent = 206
|
||||
static let MultiStatus = 207
|
||||
static let AlreadyReported = 208
|
||||
static let IMUsed = 226
|
||||
|
||||
// Redirection messages (300 - 399)
|
||||
|
||||
static let MultipleChoices = 300
|
||||
static let MovedPermanently = 301
|
||||
static let Found = 302
|
||||
static let SeeOther = 303
|
||||
static let NotModified = 304
|
||||
static let UseProxy = 305
|
||||
static let SwitchProxy = 306
|
||||
static let TemporaryRedirect = 307
|
||||
static let PermanentRedirect = 308
|
||||
|
||||
// Client error responses (400 - 499)
|
||||
|
||||
static let BadRequest = 400
|
||||
static let Unauthorized = 401
|
||||
static let PaymentRequired = 402
|
||||
static let Forbidden = 403
|
||||
static let NotFound = 404
|
||||
static let MethodNotAllowed = 405
|
||||
static let NotAcceptable = 406
|
||||
static let ProxyAuthenticationRequired = 407
|
||||
static let RequestTimeout = 408
|
||||
static let Conflict = 409
|
||||
static let Gone = 410
|
||||
static let LengthRequired = 411
|
||||
static let PreconditionFailed = 412
|
||||
static let PayloadTooLarge = 413
|
||||
static let URITooLong = 414
|
||||
static let UnsupportedMediaType = 415
|
||||
static let RangeNotSatisfiable = 416
|
||||
static let ExpectationFailed = 417
|
||||
static let IAmATeapot = 418
|
||||
static let MisdirectedRequest = 421
|
||||
static let UnprocessableEntity = 422
|
||||
static let Locked = 423
|
||||
static let FailedDependency = 424
|
||||
static let TooEarly = 425
|
||||
static let UpgradeRequired = 426
|
||||
static let PreconditionRequired = 428
|
||||
static let TooManyRequests = 429
|
||||
static let RequestHeaderFieldsTooLarge = 431
|
||||
static let UnavailableForLegalReasons = 451
|
||||
|
||||
// Server error responses (500 - 599)
|
||||
|
||||
static let InternalServerError = 500
|
||||
static let NotImplemented = 501
|
||||
static let BadGateway = 502
|
||||
static let ServiceUnavailable = 503
|
||||
static let GatewayTimeout = 504
|
||||
static let HTTPVersionNotSupported = 505
|
||||
static let VariantAlsoNegotiates = 506
|
||||
static let InsufficientStorage = 507
|
||||
static let LoopDetected = 508
|
||||
static let NotExtended = 510
|
||||
static let NetworkAuthenticationRequired = 511
|
||||
}
|
@@ -10,26 +10,28 @@ struct OpeningStream: View {
|
||||
}
|
||||
|
||||
var visible: Bool {
|
||||
(!player.currentItem.isNil && !player.videoBeingOpened.isNil) || (player.isLoadingVideo && !model.pausedForCache && !player.isSeeking)
|
||||
(!player.currentItem.isNil && !player.videoBeingOpened.isNil) ||
|
||||
(player.isLoadingVideo && !model.pausedForCache && !player.isSeeking) ||
|
||||
!player.hasStarted
|
||||
}
|
||||
|
||||
var reason: String {
|
||||
guard player.videoBeingOpened == nil else {
|
||||
return "Loading streams...".localized()
|
||||
return "Loading streams…".localized()
|
||||
}
|
||||
|
||||
if player.musicMode {
|
||||
return "Opening audio stream...".localized()
|
||||
return "Opening audio stream…".localized()
|
||||
}
|
||||
|
||||
if let selection = player.streamSelection {
|
||||
if selection.isLocal {
|
||||
return "Opening file...".localized()
|
||||
return "Opening file…".localized()
|
||||
}
|
||||
return String(format: "Opening %@ stream...".localized(), selection.shortQuality)
|
||||
return String(format: "Opening %@ stream…".localized(), selection.shortQuality)
|
||||
}
|
||||
|
||||
return "Loading streams...".localized()
|
||||
return "Loading streams…".localized()
|
||||
}
|
||||
|
||||
var state: String? {
|
||||
|
@@ -75,16 +75,20 @@ struct PlayerControls: View {
|
||||
}
|
||||
|
||||
VStack {
|
||||
Spacer()
|
||||
ZStack {
|
||||
VStack(spacing: 0) {
|
||||
ZStack {
|
||||
OpeningStream()
|
||||
NetworkState()
|
||||
GeometryReader { geometry in
|
||||
VStack(spacing: 0) {
|
||||
ZStack {
|
||||
OpeningStream()
|
||||
NetworkState()
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
.position(
|
||||
x: geometry.size.width / 2,
|
||||
y: geometry.size.height / 2
|
||||
)
|
||||
}
|
||||
.offset(y: playerControlsLayout.osdVerticalOffset + 5)
|
||||
|
||||
if showControls {
|
||||
Section {
|
||||
|
@@ -278,10 +278,6 @@ enum PlayerControlsLayout: String, CaseIterable, Defaults.Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
var osdVerticalOffset: Double {
|
||||
buttonSize
|
||||
}
|
||||
|
||||
var osdProgressBarHeight: Double {
|
||||
switch self {
|
||||
case .tvRegular:
|
||||
|
109
Shared/URLTester.swift
Normal file
109
Shared/URLTester.swift
Normal file
@@ -0,0 +1,109 @@
|
||||
import Foundation
|
||||
import Logging
|
||||
|
||||
enum URLTester {
|
||||
private static let hlsMediaPrefix = "#EXT-X-MEDIA:"
|
||||
private static let hlsInfPrefix = "#EXTINF:"
|
||||
private static let uriRegex = "(?<=URI=\")(.*?)(?=\")"
|
||||
|
||||
static func testURLResponse(url: URL, range: String, isHLS: Bool, completion: @escaping (Int) -> Void) {
|
||||
if isHLS {
|
||||
parseAndTestHLSManifest(manifestUrl: url, range: range, completion: completion)
|
||||
} else {
|
||||
httpRequest(url: url, range: range) { statusCode, _ in
|
||||
completion(statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func httpRequest(url: URL, range: String, completion: @escaping (Int, URLSessionDataTask?) -> Void) {
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "HEAD"
|
||||
request.setValue("bytes=\(range)", forHTTPHeaderField: "Range")
|
||||
|
||||
var dataTask: URLSessionDataTask?
|
||||
dataTask = URLSession.shared.dataTask(with: request) { _, response, _ in
|
||||
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? HTTPStatus.Forbidden
|
||||
Logger(label: "stream.yattee.httpRequest").info("URL: \(url) | Status Code: \(statusCode)")
|
||||
completion(statusCode, dataTask)
|
||||
}
|
||||
dataTask?.resume()
|
||||
}
|
||||
|
||||
static func parseAndTestHLSManifest(manifestUrl: URL, range: String, completion: @escaping (Int) -> Void) {
|
||||
recursivelyParseManifest(manifestUrl: manifestUrl) { allURLs in
|
||||
if let url = allURLs.randomElement() {
|
||||
httpRequest(url: url, range: range) { statusCode, _ in
|
||||
completion(statusCode)
|
||||
}
|
||||
} else {
|
||||
completion(HTTPStatus.NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func recursivelyParseManifest(manifestUrl: URL, fullyParsed: @escaping ([URL]) -> Void) {
|
||||
parseHLSManifest(manifestUrl: manifestUrl) { urls in
|
||||
var allURLs = [URL]()
|
||||
let group = DispatchGroup()
|
||||
for url in urls {
|
||||
if url.pathExtension == "m3u8" {
|
||||
group.enter()
|
||||
recursivelyParseManifest(manifestUrl: url) { subUrls in
|
||||
allURLs += subUrls
|
||||
group.leave()
|
||||
}
|
||||
} else {
|
||||
allURLs.append(url)
|
||||
}
|
||||
}
|
||||
group.notify(queue: .main) {
|
||||
fullyParsed(allURLs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func parseHLSManifest(manifestUrl: URL, completion: @escaping ([URL]) -> Void) {
|
||||
URLSession.shared.dataTask(with: manifestUrl) { data, _, _ in
|
||||
// swiftlint:disable:next shorthand_optional_binding
|
||||
guard let data = data else {
|
||||
Logger(label: "stream.yattee.httpRequest").error("Data is nil")
|
||||
completion([])
|
||||
return
|
||||
}
|
||||
|
||||
// swiftlint:disable:next non_optional_string_data_conversion
|
||||
guard let manifest = String(data: data, encoding: .utf8), !manifest.isEmpty else {
|
||||
Logger(label: "stream.yattee.httpRequest").error("Cannot read or empty HLS manifest")
|
||||
completion([])
|
||||
return
|
||||
}
|
||||
|
||||
let lines = manifest.split(separator: "\n")
|
||||
var mediaURLs: [URL] = []
|
||||
|
||||
for index in 0 ..< lines.count {
|
||||
let lineString = String(lines[index])
|
||||
|
||||
if lineString.hasPrefix(hlsMediaPrefix),
|
||||
let uriRange = lineString.range(of: uriRegex, options: .regularExpression)
|
||||
{
|
||||
let uri = lineString[uriRange]
|
||||
if let url = URL(string: String(uri)) {
|
||||
mediaURLs.append(url)
|
||||
}
|
||||
} else if lineString.hasPrefix(hlsInfPrefix), index < lines.count - 1 {
|
||||
let possibleURL = String(lines[index + 1])
|
||||
let baseURL = manifestUrl.deletingLastPathComponent()
|
||||
if let relativeURL = URL(string: possibleURL, relativeTo: baseURL),
|
||||
relativeURL.scheme != nil
|
||||
{
|
||||
mediaURLs.append(relativeURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
completion(mediaURLs)
|
||||
}
|
||||
.resume()
|
||||
}
|
||||
}
|
@@ -129,7 +129,7 @@
|
||||
"LIVE" = "مباشر";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "تحميل بثوث...";
|
||||
"Loading streams…" = "تحميل بثوث…";
|
||||
"Loading..." = "تحميل...";
|
||||
|
||||
/* Video duration filter in search */
|
||||
@@ -163,8 +163,8 @@
|
||||
"Open Settings" = "فتح الإعدادات";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "فتح بث %@ ...";
|
||||
"Opening audio stream..." = "فتح بث صوتي...";
|
||||
"Opening %@ stream…" = "فتح بث %@ …";
|
||||
"Opening audio stream…" = "فتح بث صوتي…";
|
||||
"Orientation" = "اتجاه";
|
||||
"Play in PiP" = "تشغيل في الفيديو المصغر";
|
||||
"Play Last" = "تشغيل الأخير";
|
||||
@@ -558,7 +558,7 @@
|
||||
"Are you sure you want to clear cache?" = "هل أنت متأكد من أنك تريد مسح ذاكرة التخزين المؤقت؟";
|
||||
"Gesture settings control skipping interval for remote arrow buttons (for 2nd generation Siri Remote or newer). Changing system controls settings requires restart." = "التحكم في إعدادات إيماءة الفاصل الزمني للتخطي لأزرار الأسهم عن بعد (للجيل الثاني من Siri Remote أو أحدث). تغيير إعدادات نظام عناصر التحكم يتطلب إعادة بدء التشغيل.";
|
||||
"Opened File" = "ملف مفتوح";
|
||||
"Opening file..." = "فتح الملف...";
|
||||
"Opening file…" = "فتح الملف…";
|
||||
"Short videos: hidden" = "مقاطع الفيديو القصيرة: مخفية";
|
||||
"Mark channel feed as watched" = "وضع علامة تمت المشاهدة على محتوى القناة";
|
||||
"Gesture settings control skipping interval for double click on left/right side of the player. Changing system controls settings requires restart." = "التحكم في إعدادات إيماءة فترة التخطي للنقر المزدوج على الجانب الأيسر/الأيمن من المشغل. تغيير إعدادات نظام عناصر التحكم يتطلب إعادة بدء التشغيل.";
|
||||
|
@@ -277,11 +277,11 @@
|
||||
"Large" = "Böyük";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Yayımlar yüklənilir...";
|
||||
"Loading streams…" = "Yayımlar yüklənilir…";
|
||||
"Only when signed in" = "Yalnız daxil olduqda";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "%@ yayımı açılır...";
|
||||
"Opening %@ stream…" = "%@ yayımı açılır…";
|
||||
"Matrix Chat" = "Matrix Söhbət";
|
||||
|
||||
/* Player controls layout size */
|
||||
@@ -296,7 +296,7 @@
|
||||
"Open Settings" = "Tənzimləmələri Aç";
|
||||
"Movies" = "Filmlər";
|
||||
"No description" = "Açıqlama yoxdur";
|
||||
"Opening audio stream..." = "Səs yayımı açılır...";
|
||||
"Opening audio stream…" = "Səs yayımı açılır…";
|
||||
"Password" = "Şifrə";
|
||||
"Preferred Formats" = "Üstünlük Verilən Formatlar";
|
||||
"Quality Profile" = "Profil Keyfiyyəti";
|
||||
|
@@ -113,7 +113,7 @@
|
||||
"LIVE" = "EN VIU";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "S'estan carregant els fluxos...";
|
||||
"Loading streams…" = "S'estan carregant els fluxos…";
|
||||
"Loading..." = "Carregant...";
|
||||
"Locations" = "Ubicacions";
|
||||
"Lock portrait mode" = "Bloqueja el mode vertical";
|
||||
@@ -147,7 +147,7 @@
|
||||
"Offtopic in Music Videos" = "Offtopic als vídeos musicals";
|
||||
"Open \"Playlists\" tab to create new one" = "Obriu la pestanya \"Llistes de reproducció\" per crear-ne una de nova";
|
||||
"Open Settings" = "Obriu Configuració";
|
||||
"Opening audio stream..." = "Obrint la reproducció d'àudio...";
|
||||
"Opening audio stream…" = "Obrint la reproducció d'àudio…";
|
||||
"Orientation" = "Orientació";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -454,7 +454,7 @@
|
||||
"Low" = "Baix";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "S'està obrint %@...";
|
||||
"Opening %@ stream…" = "S'està obrint %@…";
|
||||
"Only when signed in" = "Només quan s'ha iniciat la sessió";
|
||||
"Password" = "Contrasenya";
|
||||
"Part of a video promoting a product or service not directly related to the creator. The creator will receive payment or compensation in the form of money or free products." = "Part d'un vídeo que promociona un producte o servei no relacionat directament amb el creador. El creador rebrà un pagament o compensació en forma de diners o productes gratuïts.";
|
||||
|
@@ -166,7 +166,7 @@
|
||||
"Only when signed in" = "Pouze když přihlášený";
|
||||
"Open \"Playlists\" tab to create new one" = "Otevřete kartu \"Playlisty\", aby jste vytvořili nový";
|
||||
"Open Settings" = "Otevřete Nastavení";
|
||||
"Opening audio stream..." = "Otevírám audio stream...";
|
||||
"Opening audio stream…" = "Otevírám audio stream…";
|
||||
"Orientation" = "Orientace";
|
||||
"Password" = "Heslo";
|
||||
"Pause" = "Pauza";
|
||||
@@ -401,11 +401,11 @@
|
||||
"Promoting a product or service that is directly related to the creator themselves. This usually includes merchandise or promotion of monetized platforms." = "Propagace produktu nebo služby, která přímo souvisí s tvůrcem samotným. Obvykle se jedná o zboží nebo zpeněžené platformy.";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Načítaní streamu...";
|
||||
"Loading streams…" = "Načítaní streamu…";
|
||||
"Matrix Chat" = "Matrix Chat";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Otevírám %@ stream...";
|
||||
"Opening %@ stream…" = "Otevírám %@ stream…";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
"Outro" = "Zakončení";
|
||||
@@ -543,7 +543,7 @@
|
||||
"Available" = "Dostupné";
|
||||
"Are you sure you want to remove %@ from Favorites?" = "Opravdu chcete odstranit %@ z oblíbených položek?";
|
||||
"Use system controls with AVPlayer" = "Použití systémových ovládacích prvků s AVPlayerem";
|
||||
"Opening file..." = "Otvírání souboru...";
|
||||
"Opening file…" = "Otvírání souboru…";
|
||||
"No videos to show" = "Žádná videa k zobrazení";
|
||||
"Autoplay next" = "Automaticky přehrát další";
|
||||
"Inspector" = "Inspektor";
|
||||
|
@@ -182,7 +182,7 @@
|
||||
"Large" = "Groß";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Lädt Streams …";
|
||||
"Loading streams…" = "Lädt Streams …";
|
||||
|
||||
/* Video duration filter in search */
|
||||
"Long" = "Lang";
|
||||
@@ -210,7 +210,7 @@
|
||||
"Only when signed in" = "Nur wenn Sie eingeloggt sind";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Öffne %@-stream …";
|
||||
"Opening %@ stream…" = "Öffne %@-Stream …";
|
||||
"Connection failed" = "Verbindung fehlgeschlagen";
|
||||
"Continue from %@" = "Ab %@ fortsetzen";
|
||||
"Contributing" = "Beitragen";
|
||||
@@ -227,7 +227,7 @@
|
||||
"I want to ask a question" = "Ich möchte eine Frage stellen";
|
||||
"If you are interested what's coming in future updates, you can track project Milestones." = "Wenn Sie sich für künftige Updates interessieren, können Sie die Meilensteine des Projekts verfolgen.";
|
||||
"Large layout is not suitable for all devices and using it may cause controls not to fit on the screen." = "Das große Layout ist nicht für alle Geräte geeignet und kann dazu führen, dass die Bedienelemente nicht auf den Bildschirm passen.";
|
||||
"Opening audio stream..." = "Audiostream wird geöffnet …";
|
||||
"Opening audio stream…" = "Audiostream wird geöffnet …";
|
||||
"Orientation" = "Ausrichtung";
|
||||
"Playlist \"%@\" will be deleted.\nIt cannot be reverted." = "Die Wiedergabeliste \"%@\" wird gelöscht.\nDies kann nicht rückgängig gemacht werden.";
|
||||
"Preferred Formats" = "Bevorzugte Formate";
|
||||
@@ -569,7 +569,7 @@
|
||||
"Enter location address to connect..." = "Geben Sie die Internetadresse ein, um eine Verbindung herzustellen …";
|
||||
"Opened File" = "Geöffnete Datei";
|
||||
"File Extension" = "Dateierweiterung";
|
||||
"Opening file..." = "Datei öffnen …";
|
||||
"Opening file…" = "Datei öffnen …";
|
||||
"Close video and player on end" = "Video und Player am Ende beenden";
|
||||
"Use system controls with AVPlayer" = "Systemsteuerung mit AVPlayer verwenden";
|
||||
"Public account" = "Öffentliches Konto";
|
||||
|
@@ -157,7 +157,7 @@
|
||||
"LIVE" = "LIVE";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Loading streams...";
|
||||
"Loading streams…" = "Loading streams…";
|
||||
"Loading..." = "Loading...";
|
||||
"Locations" = "Locations";
|
||||
"Lock portrait mode" = "Lock portrait mode";
|
||||
@@ -202,8 +202,8 @@
|
||||
"Open Settings" = "Open Settings";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Opening %@ stream...";
|
||||
"Opening audio stream..." = "Opening audio stream...";
|
||||
"Opening %@ stream…" = "Opening %@ stream…";
|
||||
"Opening audio stream…" = "Opening audio stream…";
|
||||
"Orientation" = "Orientation";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -567,7 +567,7 @@
|
||||
"Seek" = "Seek";
|
||||
"Opened File" = "Opened File";
|
||||
"File Extension" = "File Extension";
|
||||
"Opening file..." = "Opening file...";
|
||||
"Opening file…" = "Opening file…";
|
||||
"Public account" = "Public account";
|
||||
"Your Accounts" = "Your Accounts";
|
||||
"Browse without account" = "Browse without account";
|
||||
|
@@ -138,7 +138,7 @@
|
||||
|
||||
/* Video date filter in search */
|
||||
"Today" = "Hoy";
|
||||
"Opening audio stream..." = "Abriendo transmisión de audio...";
|
||||
"Opening audio stream…" = "Abriendo transmisión de audio…";
|
||||
"Open Video" = "Abrir Video";
|
||||
"I want to ask a question" = "Quiero hacer una pregunta";
|
||||
"Save history of played videos" = "Guardar historial de videos reproducidos";
|
||||
@@ -208,7 +208,7 @@
|
||||
"No documents" = "Sin documentos";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Abriendo %@ emisión...";
|
||||
"Opening %@ stream…" = "Abriendo %@ emisión…";
|
||||
"Documents" = "Documentos";
|
||||
"Thumbnails" = "Miniaturas";
|
||||
"Password" = "Contraseña";
|
||||
@@ -265,7 +265,7 @@
|
||||
"Shuffle" = "Mezclar";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Cargando secuencias...";
|
||||
"Loading streams…" = "Cargando secuencias…";
|
||||
"Public Locations" = "Ubicaciones públicas";
|
||||
"Yattee" = "Yattee";
|
||||
"No results" = "No hay resultados";
|
||||
@@ -558,7 +558,7 @@
|
||||
"Available" = "Disponible";
|
||||
"Loop one" = "Bucle uno";
|
||||
"Use system controls with AVPlayer" = "Utilizar los controles del sistema con AVPlayer";
|
||||
"Opening file..." = "Abriendo el archivo...";
|
||||
"Opening file…" = "Abriendo el archivo…";
|
||||
"No videos to show" = "No hay vídeos que mostrar";
|
||||
"Autoplay next" = "Reproducir automáticamente la siguiente";
|
||||
"Home Settings" = "Ajustes iniciales";
|
||||
|
@@ -83,7 +83,7 @@
|
||||
"Profiles" = "نمایهها";
|
||||
"New Playlist" = "فهرست پخش جدید";
|
||||
"Automatic" = "خودکار";
|
||||
"Opening file..." = "در حال باز کردن فایل…";
|
||||
"Opening file…" = "در حال باز کردن فایل…";
|
||||
"Add Quality Profile" = "افزودن نمایهٔ کیفیت";
|
||||
"Close video after playing last in the queue" = "ویدیو را پس از پخش آخرین مورد فهرست ببند";
|
||||
|
||||
@@ -279,14 +279,14 @@
|
||||
"Controls" = "کنترلها";
|
||||
"This URL could not be opened" = "این نشانی باز نمیشود";
|
||||
"Trending" = "پرطرفدار";
|
||||
"Opening audio stream..." = "باز کردن استریم صوتی…";
|
||||
"Opening audio stream…" = "باز کردن استریم صوتی…";
|
||||
"Statistics" = "آمار";
|
||||
"Pause when player is closed" = "پس از بسته شدن پخشکننده مکث کن";
|
||||
"Play All" = "همه را پخش کن";
|
||||
"Sort: %@" = "ترتیب: %@";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "باز کردن استریم %@…";
|
||||
"Opening %@ stream…" = "باز کردن استریم %@…";
|
||||
"Next in Queue" = "مورد بعد در صف";
|
||||
"Honor orientation lock" = "قفل چرخش صفحه را در نظر بگیر";
|
||||
"Rate" = "امتیاز";
|
||||
@@ -405,7 +405,7 @@
|
||||
"Info" = "اطلاعات";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "درحال دریافت استریم…";
|
||||
"Loading streams…" = "درحال دریافت استریم…";
|
||||
"No rotation" = "بدون چرخش";
|
||||
"Codec" = "کدک (Codec)";
|
||||
"Startup section" = "بخش آغازین";
|
||||
|
@@ -282,11 +282,11 @@
|
||||
"Large layout is not suitable for all devices and using it may cause controls not to fit on the screen." = "Le grand format n'est pas adapté à tous les appareils et son utilisation peut empêcher certains contrôles de s'afficher à l'écran.";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Chargement des flux...";
|
||||
"Loading streams…" = "Chargement des flux…";
|
||||
"Lock portrait mode" = "Verrouille l'orientation en mode portrait";
|
||||
"Matrix Channel" = "Salon Matrix";
|
||||
"Only when signed in" = "Uniquement lorsque vous êtes connecté";
|
||||
"Opening audio stream..." = "Ouverture du flux audio…";
|
||||
"Opening audio stream…" = "Ouverture du flux audio…";
|
||||
"Orientation" = "Orientation";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -424,7 +424,7 @@
|
||||
"Milestones" = "Étapes";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Ouverture du flux %@…";
|
||||
"Opening %@ stream…" = "Ouverture du flux %@…";
|
||||
"Regular size" = "Taille normale";
|
||||
"Regular Size" = "Taille normale";
|
||||
"Related" = "En relation";
|
||||
@@ -567,7 +567,7 @@
|
||||
"Seek" = "Recherche";
|
||||
"Show scroll to top button in comments" = "Afficher le bouton de retour en haut de la page dans les commentaires";
|
||||
"Opened File" = "Fichier ouvert";
|
||||
"Opening file..." = "Ouverture du fichier...";
|
||||
"Opening file…" = "Ouverture du fichier…";
|
||||
"Enter location address to connect..." = "Entrez l'adresse de l'instance pour se connecter...";
|
||||
"File Extension" = "Extension de fichier";
|
||||
"Public account" = "Compte publique";
|
||||
|
@@ -84,8 +84,8 @@
|
||||
"Open Settings" = "सेटिंग खोलें";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "%@ स्ट्रीम खुल रहा…";
|
||||
"Opening audio stream..." = "ऑडियो स्ट्रीम खुल रहा…";
|
||||
"Opening %@ stream…" = "%@ स्ट्रीम खुल रहा…";
|
||||
"Opening audio stream…" = "ऑडियो स्ट्रीम खुल रहा…";
|
||||
"If you are reporting a bug, include all relevant details (especially: app version, used device and system version, steps to reproduce)." = "यदि आप किसी बग की रिपोर्ट कर रहे हैं, तो सभी प्रासंगिक विवरण शामिल करें (विशेषकर: ऐप संस्करण, प्रयुक्त डिवाइस और सिस्टम संस्करण, पुन: पेश करने के चरण)।";
|
||||
"Increase rate" = "दर बढ़ाएँ";
|
||||
"Info" = "जानकारी";
|
||||
@@ -104,7 +104,7 @@
|
||||
"LIVE" = "लाइव";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "स्ट्रीम लोड हो रहें…";
|
||||
"Loading streams…" = "स्ट्रीम लोड हो रहें…";
|
||||
"Loading..." = "लोड हो रहा…";
|
||||
"Locations" = "स्थान";
|
||||
"Lock portrait mode" = "पोर्ट्रेट मोड लॉक करें";
|
||||
|
@@ -158,8 +158,8 @@
|
||||
"Open Settings" = "Apri Impostazioni";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Apertura stream %@...";
|
||||
"Opening audio stream..." = "Apertura stream audio...";
|
||||
"Opening %@ stream…" = "Apertura stream %@…";
|
||||
"Opening audio stream…" = "Apertura stream audio…";
|
||||
"Orientation" = "Orientamento";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -170,7 +170,7 @@
|
||||
"LIVE" = "DIRETTA";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Caricamento stream...";
|
||||
"Loading streams…" = "Caricamento stream…";
|
||||
"Loading..." = "Caricamento...";
|
||||
"Locations" = "Posizioni";
|
||||
"Low quality" = "Qualità bassa";
|
||||
@@ -569,7 +569,7 @@
|
||||
"Enter location address to connect..." = "Inserisci posizione per connetterti...";
|
||||
"Opened File" = "File aperto";
|
||||
"File Extension" = "Estensione file";
|
||||
"Opening file..." = "Apro file...";
|
||||
"Opening file…" = "Apro file…";
|
||||
"Close video and player on end" = "Chiudi video e riproduttore alla fine";
|
||||
"Use system controls with AVPlayer" = "Usa controlli di sistema con AVPlayer";
|
||||
"Public account" = "Account pubblico";
|
||||
|
@@ -87,7 +87,7 @@
|
||||
"Large" = "大";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "ストリーム読込中...";
|
||||
"Loading streams…" = "ストリーム読込中…";
|
||||
"Lock portrait mode" = "縦モードをロック";
|
||||
"LIVE" = "ライブ";
|
||||
"Locations" = "場所";
|
||||
@@ -108,10 +108,10 @@
|
||||
"Offtopic in Music Videos" = "音楽動画の非音楽部分";
|
||||
"Only when signed in" = "ログイン時のみ";
|
||||
"Orientation" = "向き";
|
||||
"Opening audio stream..." = "音声ストリーム 開始中...";
|
||||
"Opening audio stream…" = "音声ストリーム 開始中…";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "%@ ストリーム 開始中...";
|
||||
"Opening %@ stream…" = "%@ ストリーム 開始中…";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
"Outro" = "終了シーン";
|
||||
@@ -566,7 +566,7 @@
|
||||
"Show scroll to top button in comments" = "コメント欄に「上に戻る」表示";
|
||||
"Opened File" = "開いたファイル";
|
||||
"File Extension" = "ファイル拡張子";
|
||||
"Opening file..." = "ファイルを読み込み中...";
|
||||
"Opening file…" = "ファイルを読み込み中…";
|
||||
"Your Accounts" = "アカウントを使用";
|
||||
"Public account" = "公開アカウント";
|
||||
"Browse without account" = "アカウントなしで閲覧";
|
||||
|
@@ -82,7 +82,7 @@
|
||||
"LIVE" = "Direkte";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Laster inn strømmer …";
|
||||
"Loading streams…" = "Laster inn strømmer …";
|
||||
|
||||
/* Video duration filter in search */
|
||||
"Long" = "Lang";
|
||||
@@ -223,7 +223,7 @@
|
||||
"Rate" = "Takt";
|
||||
"Not Playing" = "Spiller ikke";
|
||||
"Open \"Playlists\" tab to create new one" = "Åpne «Spillelister»-fanen for å opprette ny";
|
||||
"Opening audio stream..." = "Åpner lydstrøm …";
|
||||
"Opening audio stream…" = "Åpner lydstrøm …";
|
||||
"Password" = "Passord";
|
||||
"Nothing" = "Ingenting";
|
||||
"Picture in Picture" = "Bilde-i-bilde";
|
||||
@@ -303,7 +303,7 @@
|
||||
"Offtopic in Music Videos" = "Urelaterte ting i musikkvideoer";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Åpner %@-strøm …";
|
||||
"Opening %@ stream…" = "Åpner %@-strøm …";
|
||||
"Play in PiP" = "Bilde-i-bilde";
|
||||
"Pause when entering background" = "Pause ved forsendelse til bakgrunnen";
|
||||
"Promoting a product or service that is directly related to the creator themselves. This usually includes merchandise or promotion of monetized platforms." = "Promoterer noe som har å gjøre med skaperen direkte. Vanligvis effekter eller betalte plattformer.";
|
||||
|
@@ -157,7 +157,7 @@
|
||||
"LIVE" = "LIVE";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Ładowanie strumieni...";
|
||||
"Loading streams…" = "Ładowanie strumieni…";
|
||||
"Loading..." = "Ładowanie…";
|
||||
"Locations" = "Lokalizacje";
|
||||
"Lock portrait mode" = "Zablokuj tryb portretowy";
|
||||
@@ -202,8 +202,8 @@
|
||||
"Open Settings" = "Otwórz Ustawienia";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Otwieranie strumienia %@…";
|
||||
"Opening audio stream..." = "Otwieranie strumienia audio...";
|
||||
"Opening %@ stream…" = "Otwieranie strumienia %@…";
|
||||
"Opening audio stream…" = "Otwieranie strumienia audio…";
|
||||
"Orientation" = "Orientacja";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -570,7 +570,7 @@
|
||||
"Enter location address to connect..." = "Wprowadź adres lokalizacji, aby połączyć...";
|
||||
"Opened File" = "Otwarty plik";
|
||||
"File Extension" = "Rozszerzenie pliku";
|
||||
"Opening file..." = "Otwieranie pliku...";
|
||||
"Opening file…" = "Otwieranie pliku…";
|
||||
"Public account" = "Konto publiczne";
|
||||
"Your Accounts" = "Twoje konta";
|
||||
"Browse without account" = "Przeglądanie bez konta";
|
||||
|
@@ -102,7 +102,7 @@
|
||||
"Just watched" = "Acabou de assistir";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Carregando streams…";
|
||||
"Loading streams…" = "Carregando streams…";
|
||||
"Medium quality" = "Qualidade média";
|
||||
"No description" = "Sem descrição";
|
||||
"No Playlists" = "Sem playlists";
|
||||
@@ -114,8 +114,8 @@
|
||||
"Open Settings" = "Abrir Ajustes";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Abrindo stream %@…";
|
||||
"Opening audio stream..." = "Abrindo stream de áudio…";
|
||||
"Opening %@ stream…" = "Abrindo stream %@…";
|
||||
"Opening audio stream…" = "Abrindo stream de áudio…";
|
||||
"Orientation" = "Orientação";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -569,7 +569,7 @@
|
||||
"Enter account credentials to connect..." = "Insira as credenciais da conta para conectar…";
|
||||
"Opened File" = "Arquivo Aberto";
|
||||
"File Extension" = "Extensão do Arquivo";
|
||||
"Opening file..." = "Abrindo arquivo…";
|
||||
"Opening file…" = "Abrindo arquivo…";
|
||||
"Browse without account" = "Navegar sem uma conta";
|
||||
"Rotate when entering fullscreen on landscape video" = "Girar quando entrar no modo tela cheia em vídeo em paisagem";
|
||||
"Landscape left" = "Paisagem à esquerda";
|
||||
|
@@ -196,7 +196,7 @@
|
||||
"LIVE" = "AO VIVO";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Carregando streams…";
|
||||
"Loading streams…" = "Carregando streams…";
|
||||
"Loading..." = "Carregando…";
|
||||
"Locations" = "Localizações";
|
||||
"Lock portrait mode" = "Travar modo retrato";
|
||||
@@ -237,8 +237,8 @@
|
||||
"Open Settings" = "Abrir Ajustes";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Abrindo stream %@…";
|
||||
"Opening audio stream..." = "Abrindo stream de áudio…";
|
||||
"Opening %@ stream…" = "Abrindo stream %@…";
|
||||
"Opening audio stream…" = "Abrindo stream de áudio…";
|
||||
"Orientation" = "Orientação";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -557,7 +557,7 @@
|
||||
"Keep channels with unwatched videos on top of subscriptions list" = "Manter canais com vídeos não vistos no topo da lista de inscrições";
|
||||
"Opened File" = "Ficheiro Aberto";
|
||||
"File Extension" = "Extensão do Ficheiro";
|
||||
"Opening file..." = "A abrir ficheiro…";
|
||||
"Opening file…" = "A abrir ficheiro…";
|
||||
"Close video and player on end" = "Fechar vídeo e player ao final";
|
||||
"Use system controls with AVPlayer" = "Usar controles do sistema com o AVPlayer";
|
||||
"Public account" = "Conta pública";
|
||||
|
@@ -89,7 +89,7 @@
|
||||
"LIVE" = "LIVE";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Se încarcă fluxurile...";
|
||||
"Loading streams…" = "Se încarcă fluxurile…";
|
||||
"Locations" = "Locații";
|
||||
"Mark watched videos with" = "Marcați videoclipurile vizionate cu";
|
||||
"Matrix Channel" = "Canal Matrix";
|
||||
@@ -100,7 +100,7 @@
|
||||
"Nothing" = "Nimic";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Se deschide %@ flux...";
|
||||
"Opening %@ stream…" = "Se deschide %@ flux…";
|
||||
"Play Last" = "Reda ultimul";
|
||||
"Player" = "Player";
|
||||
"Playlist" = "Playlist";
|
||||
@@ -243,7 +243,7 @@
|
||||
/* SponsorBlock category name */
|
||||
"Outro" = "Outro";
|
||||
"Orientation" = "Orientare";
|
||||
"Opening audio stream..." = "Se deschide fluxul audio...";
|
||||
"Opening audio stream…" = "Se deschide fluxul audio…";
|
||||
"Password" = "Parolă";
|
||||
"Pause" = "Pauză";
|
||||
"Pause when entering background" = "Pauză când intrați în fundal";
|
||||
@@ -568,7 +568,7 @@
|
||||
"Enter account credentials to connect..." = "Introduceți acreditările contului pentru a vă conecta...";
|
||||
"Enter location address to connect..." = "Introdu adresa locației pentru a te conecta...";
|
||||
"Opened File" = "Fișier deschis";
|
||||
"Opening file..." = "Deschiderea fișierului...";
|
||||
"Opening file…" = "Deschiderea fișierului…";
|
||||
"File Extension" = "Extensie fișier";
|
||||
"Use system controls with AVPlayer" = "Utilizați controalele de sistem cu AVPlayer";
|
||||
"Rotate when entering fullscreen on landscape video" = "Rotiți când intrați pe ecran complet în videoclipul peisaj";
|
||||
|
@@ -370,7 +370,7 @@
|
||||
"LIVE" = "ПРЯМАЯ ТРАНСЛЯЦИЯ";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Загрузка прямой трансляции...";
|
||||
"Loading streams…" = "Загрузка прямой трансляции…";
|
||||
"Loading..." = "Загрузка...";
|
||||
"Lock portrait mode" = "Блокировка портретного режима";
|
||||
"Low" = "Низкое";
|
||||
@@ -405,8 +405,8 @@
|
||||
"Open Settings" = "Отрыть настройки";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Открытие %@ прямой трансляции...";
|
||||
"Opening audio stream..." = "Открытие прямой трансляции аудио...";
|
||||
"Opening %@ stream…" = "Открытие %@ прямой трансляции…";
|
||||
"Opening audio stream…" = "Открытие прямой трансляции аудио…";
|
||||
"Orientation" = "Ориентация";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -591,7 +591,7 @@
|
||||
"Actions buttons" = "Кнопки действия";
|
||||
"Show sidebar" = "Показать боковую панель";
|
||||
"Browse without account" = "Искать без аккаунта";
|
||||
"Opening file..." = "Отрытие файла...";
|
||||
"Opening file…" = "Отрытие файла…";
|
||||
"Public account" = "Публичный аккаунт";
|
||||
"Your Accounts" = "Ваши аккаунты";
|
||||
"Close video and player on end" = "Закрыть видео и плеер в конце";
|
||||
|
@@ -110,7 +110,7 @@
|
||||
"I want to ask a question" = "Bir soru sormak istiyorum";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Akışlar yükleniyor...";
|
||||
"Loading streams…" = "Akışlar yükleniyor…";
|
||||
"Edit Quality Profile" = "Kalite Profilini Düzenle";
|
||||
"Frontend URL" = "Ön uç URL'si";
|
||||
"Close player when starting PiP" = "Resim içinde Resim modu başlatılırken oynatıcıyı kapat";
|
||||
@@ -227,7 +227,7 @@
|
||||
"No description" = "Açıklama yok";
|
||||
"Normal" = "Normal";
|
||||
"Open \"Playlists\" tab to create new one" = "Yeni bir tane oluşturmak için \"Oynatma Listeleri\" sekmesini açın";
|
||||
"Opening audio stream..." = "Ses akışı açılıyor...";
|
||||
"Opening audio stream…" = "Ses akışı açılıyor…";
|
||||
"Rate" = "Derecelendir";
|
||||
"Orientation" = "Yönlendirme";
|
||||
"No results" = "Sonuç yok";
|
||||
@@ -396,7 +396,7 @@
|
||||
"Open" = "Aç";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "%@ akışı açılıyor...";
|
||||
"Opening %@ stream…" = "%@ akışı açılıyor…";
|
||||
"Clear Queue before opening" = "Açmadan önce Bekleme Listesini temizle";
|
||||
"Copy %@ link with time" = "Bağlantıyı %@ zaman ile kopyala";
|
||||
"Show Inspector" = "Denetleyiciyi Göster";
|
||||
|
@@ -259,7 +259,7 @@
|
||||
"LIVE" = "В ЕФІРІ";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "Завантаження трансляції...";
|
||||
"Loading streams…" = "Завантаження трансляції…";
|
||||
"Loading..." = "Завантаження...";
|
||||
"Locations" = "Локації";
|
||||
"Lock portrait mode" = "Заблокувати портретний режим";
|
||||
@@ -298,8 +298,8 @@
|
||||
"Open Settings" = "Відрити налаштування";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "Запуск трансляції %@...";
|
||||
"Opening audio stream..." = "Запуск аудіо трансляції...";
|
||||
"Opening %@ stream…" = "Запуск трансляції %@…";
|
||||
"Opening audio stream…" = "Запуск аудіо трансляції…";
|
||||
"Orientation" = "Орієнтація";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
|
@@ -152,7 +152,7 @@
|
||||
"Loading..." = "加载中...";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "加载流中...";
|
||||
"Loading streams…" = "加载流中…";
|
||||
"Locations" = "地址";
|
||||
"Lock portrait mode" = "锁定竖屏模式";
|
||||
|
||||
@@ -191,8 +191,8 @@
|
||||
"Open Settings" = "打开设置";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "正在打开 %@ 的流...";
|
||||
"Opening audio stream..." = "正在打开音频流...";
|
||||
"Opening %@ stream…" = "正在打开 %@ 的流…";
|
||||
"Opening audio stream…" = "正在打开音频流…";
|
||||
"Orientation" = "方向";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
@@ -530,7 +530,7 @@
|
||||
"Show scroll to top button in comments" = "在评论中显示“滚动到顶部”按钮";
|
||||
"Opened File" = "打开的文件";
|
||||
"File Extension" = "文件扩展";
|
||||
"Opening file..." = "打开文件中...";
|
||||
"Opening file…" = "打开文件中…";
|
||||
"Single tap gesture" = "单击手势";
|
||||
"Right click channel thumbnail to open context menu with more actions" = "右键单击频道缩略图以打开具有更多操作的上下文菜单";
|
||||
"Show unwatched feed badges" = "显示未观看的 Feed 标志";
|
||||
|
@@ -252,7 +252,7 @@
|
||||
"Interface" = "介面";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Loading streams..." = "加載中...";
|
||||
"Loading streams…" = "加載中…";
|
||||
"Loading..." = "加載中...";
|
||||
"Locations" = "地址";
|
||||
"Lock portrait mode" = "鎖定直屏";
|
||||
@@ -292,8 +292,8 @@
|
||||
"Open Settings" = "打開設置";
|
||||
|
||||
/* Loading stream OSD */
|
||||
"Opening %@ stream..." = "正在打開 %@ ...";
|
||||
"Opening audio stream..." = "正在打開音訊...";
|
||||
"Opening %@ stream…" = "正在打開 %@ …";
|
||||
"Opening audio stream…" = "正在打開音訊…";
|
||||
|
||||
/* SponsorBlock category name */
|
||||
"Outro" = "結尾";
|
||||
@@ -554,7 +554,7 @@
|
||||
"Queue - shuffled" = "隊列 - 隨機";
|
||||
"Loop one" = "單個循環";
|
||||
"File Extension" = "副檔名";
|
||||
"Opening file..." = "正在打開文件...";
|
||||
"Opening file…" = "正在打開文件…";
|
||||
"Public account" = "公共帳戶";
|
||||
"Enter account credentials to connect..." = "輸入帳號密碼來連接...";
|
||||
"Enter location address to connect..." = "輸入站台地址來連接...";
|
||||
|
Reference in New Issue
Block a user