1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2024-12-29 16:07:40 +01:00

Modal is almost ready after bookmark button

This commit is contained in:
Captain Jack Sparrow 2024-07-06 22:04:32 +00:00
commit fae66c8db5
15 changed files with 1750 additions and 1479 deletions

View file

@ -16,7 +16,7 @@
## Referrers ## Referrers
- [Priacy Subreddit Megathread](https://www.reddit.com/r/Piracy/s/iymSloEpXn) - [Piracy Subreddit Megathread](https://www.reddit.com/r/Piracy/s/iymSloEpXn)
- [Toon's Instances](https://erynith.github.io/movie-web-instances) - [Toon's Instances](https://erynith.github.io/movie-web-instances)
- [Sudo-Flix docs](https://sussy-code.github.io/docs) - [Sudo-Flix docs](https://sussy-code.github.io/docs)
- [Sudo-Flix Discord](https://discord.gg/r5cYshWM4G) - [Sudo-Flix Discord](https://discord.gg/r5cYshWM4G)

View file

@ -41,15 +41,15 @@
"@sozialhelden/ietf-language-tags": "^5.4.2", "@sozialhelden/ietf-language-tags": "^5.4.2",
"@types/node-forge": "^1.3.11", "@types/node-forge": "^1.3.11",
"classnames": "^2.5.1", "classnames": "^2.5.1",
"core-js": "^3.37.0", "core-js": "^3.37.1",
"detect-browser": "^5.3.0", "detect-browser": "^5.3.0",
"dompurify": "^3.1.1", "dompurify": "^3.1.6",
"flag-icons": "^7.2.1", "flag-icons": "^7.2.3",
"focus-trap-react": "^10.2.3", "focus-trap-react": "^10.2.3",
"fscreen": "^1.2.0", "fscreen": "^1.2.0",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"hls.js": "^1.5.8", "hls.js": "^1.5.13",
"i18next": "^23.11.2", "i18next": "^23.11.5",
"immer": "^10.1.1", "immer": "^10.1.1",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
@ -62,33 +62,33 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-ga4": "^2.1.0", "react-ga4": "^2.1.0",
"react-google-recaptcha-v3": "^1.10.1", "react-google-recaptcha-v3": "^1.10.1",
"react-helmet-async": "^2.0.4", "react-helmet-async": "^2.0.5",
"react-i18next": "^14.1.1", "react-i18next": "^14.1.2",
"react-lazy-with-preload": "^2.2.1", "react-lazy-with-preload": "^2.2.1",
"react-loading-skeleton": "^3.4.0", "react-loading-skeleton": "^3.4.0",
"react-router-dom": "^6.23.0", "react-router-dom": "^6.24.1",
"react-sticky-el": "^2.1.0", "react-sticky-el": "^2.1.0",
"react-use": "^17.5.0", "react-use": "^17.5.0",
"semver": "^7.6.0", "semver": "^7.6.2",
"slugify": "^1.6.6", "slugify": "^1.6.6",
"subsrt-ts": "^2.1.2", "subsrt-ts": "^2.1.2",
"zustand": "^4.5.2" "zustand": "^4.5.4"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.24.4", "@babel/core": "^7.24.7",
"@babel/preset-env": "^7.24.4", "@babel/preset-env": "^7.24.7",
"@babel/preset-typescript": "^7.24.1", "@babel/preset-typescript": "^7.24.7",
"@rollup/wasm-node": "^4.17.0", "@rollup/wasm-node": "^4.18.0",
"@types/chromecast-caf-sender": "^1.0.9", "@types/chromecast-caf-sender": "^1.0.10",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
"@types/fscreen": "^1.0.4", "@types/fscreen": "^1.0.4",
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
"@types/lodash.merge": "^4.6.9", "@types/lodash.merge": "^4.6.9",
"@types/lodash.throttle": "^4.1.9", "@types/lodash.throttle": "^4.1.9",
"@types/node": "^20.12.7", "@types/node": "^20.14.10",
"@types/pako": "^2.0.3", "@types/pako": "^2.0.3",
"@types/react": "^18.3.1", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@types/react-helmet": "^6.1.11", "@types/react-helmet": "^6.1.11",
"@types/react-lazy-load-image-component": "^1.6.4", "@types/react-lazy-load-image-component": "^1.6.4",
@ -99,7 +99,7 @@
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0", "@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"eslint": "^8.57.0", "eslint": "^8.57.0",
@ -107,30 +107,30 @@
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1", "eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsx-a11y": "^6.8.0", "eslint-plugin-jsx-a11y": "^6.9.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "7.33.2", "eslint-plugin-react": "7.33.2",
"eslint-plugin-react-hooks": "4.6.0", "eslint-plugin-react-hooks": "4.6.0",
"glob": "^10.3.12", "glob": "^10.4.3",
"handlebars": "^4.7.8", "handlebars": "^4.7.8",
"jsdom": "^23.2.0", "jsdom": "^23.2.0",
"postcss": "^8.4.38", "postcss": "^8.4.39",
"postcss-rtl": "^2.0.0", "postcss-rtl": "^2.0.0",
"postcss-rtlcss": "^4.0.9", "postcss-rtlcss": "^4.0.9",
"prettier": "^3.2.5", "prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.5.14", "prettier-plugin-tailwindcss": "^0.5.14",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.12.0",
"tailwind-scrollbar": "^3.1.0", "tailwind-scrollbar": "^3.1.0",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.4",
"tailwindcss-themer": "^4.0.0", "tailwindcss-themer": "^4.0.0",
"type-fest": "^4.17.0", "type-fest": "^4.21.0",
"typescript": "^5.4.5", "typescript": "^5.5.3",
"vite": "^5.2.10", "vite": "^5.3.3",
"vite-plugin-checker": "^0.6.4", "vite-plugin-checker": "^0.6.4",
"vite-plugin-package-version": "^1.1.0", "vite-plugin-package-version": "^1.1.0",
"vite-plugin-pwa": "^0.17.5", "vite-plugin-pwa": "^0.17.5",
"vite-plugin-static-copy": "^1.0.3", "vite-plugin-static-copy": "^1.0.6",
"vitest": "^1.5.2", "vitest": "^1.6.0",
"workbox-window": "^7.1.0" "workbox-window": "^7.1.0"
}, },
"pnpm": { "pnpm": {

File diff suppressed because it is too large Load diff

View file

@ -1,286 +1,359 @@
{ {
"about": { "about": {
"description": "sudo-flix е уеб приложение, което търси в интернет за стриймове. Екипът се стреми към предимно минималистичен подход при консумирането на съдържание.", "description": "sudo-flix е уеб приложение, което търси в интернет за стриймове. Екипът се стреми към предимно минималистичен подход при консумирането на съдържание.",
"faqTitle": "Общи въпроси", "faqTitle": "Общи въпроси",
"q1": { "q1": {
"body": "sudo-flix не хоства никакво съдържание. Когато кликнете върху нещо, за да го гледате, се търси в интернет избраното медийно съдържание (На екрана за зареждане и в раздела \"Източници на видео\" можете да видите кой източник използвате). Медийното съдържание никога не се качва от sudo-flix, всичко става чрез този механизъм за търсене.", "body": "sudo-flix не хоства никакво съдържание. Когато кликнете върху нещо, за да го гледате, се търси в интернет избраното медийно съдържание (На екрана за зареждане и в раздела \"Източници на видео\" можете да видите кой източник използвате). Медийното съдържание никога не се качва от sudo-flix, всичко става чрез този механизъм за търсене.",
"title": "От къде идва съдържанието?" "title": "От къде идва съдържанието?"
},
"q2": {
"body": "Невъзможно е да поискате сериал или филм, sudo-flix не управлява никакво съдържание. Всичко се гледа чрез източници в интернет.",
"title": "Къде мога да поискам определен сериал или филм?"
},
"q3": {
"body": "Резултатите от търсенето ни се поддържат от базата данни за филми (The Movie Database - TMDB) и се показват, независимо дали източниците ни реално разполагат със съдържанието.",
"title": "Резултатите от търсенето показват сериала или филма, защо не мога да го пусна?"
},
"title": "Относно sudo-flix"
},
"actions": {
"copied": "Копиране",
"copy": "Копирай"
},
"auth": {
"createAccount": "Все още нямате акаунт? <0>Създайте акаунт.</0>",
"deviceNameLabel": "Име на устройството",
"deviceNamePlaceholder": "Личен телефон",
"generate": {
"description": "Вашата паролна фраза служи като потребителско име и парола. Уверете се, че я пазите в безопасност, тъй като ще трябва да я въведете, за да влезете в своя акаунт.",
"next": "Запазих паролната си фраза",
"passphraseFrameLabel": "Паролна фраза",
"title": "Вашата паролна фраза"
},
"hasAccount": "Вече имате акаунт? <0>Влезте тук.</0>",
"login": {
"description": "Моля, въведете своята паролна фраза, за да влезете в своя акаунт",
"deviceLengthError": "Моля, въведете името на устройството",
"passphraseLabel": "12-думена паролна фраза",
"passphrasePlaceholder": "Паролна фраза",
"submit": "Вход",
"title": "Влезте в своя акаунт",
"validationError": "Некоректна или непълна паролна фраза"
},
"register": {
"information": {
"color1": "Профилен цвят едно",
"color2": "Профилен цвят две",
"header": "Въведете име за устройството си и изберете цветове и потребителска икона по ваш избор",
"icon": "Потребителска икона",
"next": "Напред",
"title": "Информация за акаунта"
}
},
"trust": {
"failed": {
"text": "Конфигурирахте ли го правилно?",
"title": "Неуспешно свързване със сървъра"
},
"host": "Вие се свързвате към <0>{{hostname}}</0> - моля, потвърдете, че му вярвате, преди да създадете акаунт",
"no": "Върнете се назад",
"title": "Вярвате ли на този сървър?",
"yes": "Вярвам на този сървър"
},
"verify": {
"description": "Моля, въведете паролната фраза от по-рано, за да потвърдите, че сте я запазили, и за да създадете своя акаунт",
"invalidData": "Данните не са валидни",
"noMatch": "Паролната фраза не съвпада",
"passphraseLabel": "Вашата 12-думена паролна фраза",
"recaptchaFailed": "Валидацията на ReCaptcha не беше успешна",
"register": "Създаване на акаунт",
"title": "Потвърдете вашата паролна фраза"
}
},
"errors": {
"badge": "Счупи се",
"details": "Детайли за грешката",
"reloadPage": "Презаредете страницата",
"showError": "Покажи детайлите за грешката",
"title": "Сблъскахме се с грешка!"
},
"footer": {
"legal": {
"disclaimer": "Внимание",
"disclaimerText": "sudo-flix не хоства никакви файлове, а просто предоставя връзки към услуги от трета страна. Правните въпроси следва да се разглеждат с доставчиците на файлове. sudo-flix не носи отговорност за никакви медийни файлове, предоставени от видео доставчици."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "Github"
},
"tagline": "Гледайте любимите си предавания и филми с това отворено-код стрийминг приложение."
},
"global": {
"name": "sudo-flix",
"pages": {
"about": "За нас",
"dmca": "DMCA",
"login": "Вход",
"pagetitle": "{{title}} - sudo-flix",
"register": "Регистрация",
"settings": "Настройки"
}
},
"home": {
"bookmarks": {
"sectionTitle": "Отметки"
},
"continueWatching": {
"sectionTitle": "Продължи гледането"
},
"mediaList": {
"stopEditing": "Спри редактирането"
},
"search": {
"allResults": "Това е всичко, което имаме!",
"failed": "Неуспешно намиране на медия, опитайте отново!",
"loading": "Зареждане...",
"noResults": "Не успяхме да намерим нищо!",
"placeholder": {
"default": "Какво искате да гледате?",
"extra": []
},
"sectionTitle": "Резултати от търсенето"
},
"titles": {
"day": {
"default": "Какво бихте искали да гледате този следобед?",
"extra": [
"Чувствате ли се авантюристично? \"Джурасик Парк\" може да бъде перфектният избор."
]
},
"morning": {
"default": "Какво бихте искали да гледате тази сутрин?",
"extra": ["Чух, че Before Sunrise е добър"]
},
"night": {
"default": "Какво бихте искали да гледате тази вечер?",
"extra": ["Изморен? Чух, че Екзорсистът е добър."]
}
}
},
"media": {
"episodeDisplay": "S{{season}} E{{episode}}",
"types": {
"movie": "Филм",
"show": "Сериал"
}
},
"navigation": {
"banner": {
"offline": "Проверете вашата интернет връзка"
},
"menu": {
"about": "За нас",
"logout": "Излез",
"register": "Синхронизиране с облак",
"settings": "Настройки",
"support": "поддържа"
}
},
"notFound": {
"badge": "Не е намерено",
"goHome": "Обратно към началото",
"message": "Търсихме навсякъде: под кошчетата, в килера, зад проксито, но в крайна сметка не успяхме да намерим страницата, която търсите.",
"title": "Тази страница не можа да бъде намерена"
},
"overlays": {
"close": "Затвори"
},
"player": {
"back": {
"default": "Обратно към началото",
"short": "Назад"
},
"casting": {
"enabled": "Предава се към устройството..."
},
"menus": {
"downloads": {
"disclaimer": "Изтеглянията се вземат директно от доставчика. sudo-flix няма контрол върху това как се предоставят изтеглянията.",
"downloadSubtitle": "Изтеглете текущите субтитри",
"downloadVideo": "Изтегляне на видео",
"hlsDisclaimer": "Изтеглянията се вземат директно от доставчика. sudo-flix няма контрол върху това как се предоставят изтеглянията. Моля, имайте предвид, че изтегляте HLS плейлист, това е предназначено за потребители, запознати с усъвършенствано мултимедийно поточно предаване.",
"onAndroid": {
"1": "За да изтеглите на Android, щракнете върху бутона за изтегляне, след което на новата страница <bold>докоснете и задръжте</bold> видеоклипа, след което изберете <bold>запазване</bold>.",
"shortTitle": "Изтегляне / Android",
"title": "Изтегляне на Android"
}, },
"onIos": { "q2": {
"1": "За да изтеглите на iOS, щракнете върху бутона за изтегляне, след което на новата страница щракнете върху <bold><ios_share /></bold>, след което върху <bold>Запазване във файлове <ios_files /></bold>.", "body": "Невъзможно е да поискате сериал или филм, sudo-flix не управлява никакво съдържание. Всичко се гледа чрез източници в интернет.",
"shortTitle": "Изтегляне / iOS", "title": "Къде мога да поискам определен сериал или филм?"
"title": "Изтегляне на iOS"
}, },
"onPc": { "q3": {
"1": "На компютър щракнете върху бутона за изтегляне, след което на новата страница щракнете с десния бутон върху видеоклипа и изберете <bold>Запазване на видеоклипа като</bold>", "body": "Резултатите от търсенето ни се поддържат от базата данни за филми (The Movie Database - TMDB) и се показват, независимо дали източниците ни реално разполагат със съдържанието.",
"shortTitle": "Изтегляне / компютър", "title": "Резултатите от търсенето показват сериала или филма, защо не мога да го пусна?"
"title": "Изтегляне на компютър"
}, },
"title": "Изтегли" "title": "Относно sudo-flix",
}, "q5": {
"episodes": { "title": "Как мога да разбера повече?",
"button": "епизоди", "body": "Е, sudo-flix има акаунт в Twitter, който може да бъде намерен в долния колонтитул на тази страница, както и връзка към изходния код в Github."
"emptyState": "Няма епизоди в този сезон, проверете отново по-късно!",
"episodeBadge": "E{{episode}}",
"loadingError": "Грешка при зареждането на сезона",
"loadingList": "Зареждане...",
"loadingTitle": "Зареждане..."
},
"playback": {
"speedLabel": "Скорост на възпроизвеждане",
"title": "Настройки за възпроизвеждане"
},
"quality": {
"automaticLabel": "Автоматично качество",
"hint": "Можете да опитате <0>да превключите източника</0>, за да получите различни опции за качество.",
"iosNoQuality": "Поради ограниченията, определени от Apple, изборът на качество не е наличен в iOS за този източник. Можете да опитате <0>да превключите към друг източник</0>, за да получите различни опции за качество.",
"title": "качество"
},
"settings": {
"downloadItem": "Изтегли",
"enableSubtitles": "включи субтитрите",
"experienceSection": "Изживяване при гледане",
"playbackItem": "Настройки за възпроизвеждане",
"qualityItem": "качество",
"sourceItem": "Видео източници",
"subtitleItem": "Настройки на субтитрите",
"videoSection": "Видео настройки"
},
"sources": {
"failed": {
"text": "Възникна грешка при опит за намиране на видеоклипове, моля, опитайте с друг източник.",
"title": "Неуспешно изстъргване"
}, },
"noEmbeds": { "q4": {
"text": "Не можахме да намерим никакви вграждания, моля, опитайте с друг източник.", "title": "Какво ще кажете за моите данни и други неща?",
"title": "Няма намерени вграждания" "body": "Всички данни се синхронизират със sudo-бекенда, който е различен от бекенда на общността и се хоства независимо от мен, всеки може да използва и това."
}, }
"noStream": {
"text": "Този източник няма потоци за този филм или шоу.",
"title": "Няма поток"
},
"title": "Източници",
"unknownOption": "неизвестен"
},
"subtitles": {
"customChoice": "Качете субтитри",
"customizeLabel": "Персонализиране",
"offChoice": "изключи",
"settings": {
"backlink": "Персонализирани субтитри",
"delay": "Забавяне на субтитрите",
"fixCapitals": "Коригиране на главни букви"
},
"title": "субтитри",
"unknownLanguage": "неизвестен"
}
}, },
"metadata": { "actions": {
"failed": { "copied": "Копиране",
"badge": "Провал", "copy": "Копирай"
"homeButton": "Обратно към началото", },
"text": "Неуспешно зареждане на метаданните на носителя от TMDB. Моля, проверете дали TMDB не работи или е блокиран във вашата интернет връзка.", "auth": {
"title": "Неуспешно зареждане на метаданните" "createAccount": "Все още нямате акаунт? <0>Създайте акаунт.</0>",
}, "deviceNameLabel": "Име на устройството",
"notFound": { "deviceNamePlaceholder": "Личен телефон",
"generate": {
"description": "Вашата паролна фраза служи като потребителско име и парола. Уверете се, че я пазите в безопасност, тъй като ще трябва да я въведете, за да влезете в своя акаунт.",
"next": "Запазих паролната си фраза",
"passphraseFrameLabel": "Паролна фраза",
"title": "Вашата паролна фраза"
},
"hasAccount": "Вече имате акаунт? <0>Влезте тук.</0>",
"login": {
"description": "Моля, въведете своята паролна фраза, за да влезете в своя акаунт",
"deviceLengthError": "Моля, въведете името на устройството",
"passphraseLabel": "12-думена паролна фраза",
"passphrasePlaceholder": "Паролна фраза",
"submit": "Вход",
"title": "Влезте в своя акаунт",
"validationError": "Некоректна или непълна паролна фраза"
},
"register": {
"information": {
"color1": "Профилен цвят едно",
"color2": "Профилен цвят две",
"header": "Въведете име за устройството си и изберете цветове и потребителска икона по ваш избор",
"icon": "Потребителска икона",
"next": "Напред",
"title": "Информация за акаунта"
}
},
"trust": {
"failed": {
"text": "Конфигурирахте ли го правилно?",
"title": "Неуспешно свързване със сървъра"
},
"host": "Вие се свързвате към <0>{{hostname}}</0> - моля, потвърдете, че му вярвате, преди да създадете акаунт",
"no": "Върнете се назад",
"title": "Вярвате ли на този сървър?",
"yes": "Вярвам на този сървър",
"noHostTitle": "Сървърът не е конфигуриран!",
"noHost": "Сървърът не е конфигуриран, следователно не можете да създадете акаунт"
},
"verify": {
"description": "Моля, въведете паролната фраза от по-рано, за да потвърдите, че сте я запазили, и за да създадете своя акаунт",
"invalidData": "Данните не са валидни",
"noMatch": "Паролната фраза не съвпада",
"passphraseLabel": "Вашата 12-думена паролна фраза",
"recaptchaFailed": "Валидацията на ReCaptcha не беше успешна",
"register": "Създаване на акаунт",
"title": "Потвърдете вашата паролна фраза"
}
},
"errors": {
"badge": "Счупи се",
"details": "Детайли за грешката",
"reloadPage": "Презаредете страницата",
"showError": "Покажи детайлите за грешката",
"title": "Сблъскахме се с грешка!"
},
"footer": {
"legal": {
"disclaimer": "Внимание",
"disclaimerText": "sudo-flix не хоства никакви файлове, а просто предоставя връзки към услуги от трета страна. Правните въпроси следва да се разглеждат с доставчиците на файлове. sudo-flix не носи отговорност за никакви медийни файлове, предоставени от видео доставчици."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "Github",
"twitter": "Twitter"
},
"tagline": "Гледайте любимите си предавания и филми с това отворено-код стрийминг приложение."
},
"global": {
"name": "sudo-flix",
"pages": {
"about": "За нас",
"dmca": "DMCA",
"login": "Вход",
"pagetitle": "{{title}} - sudo-flix",
"register": "Регистрация",
"settings": "Настройки",
"discover": "Открийте",
"support": "поддържа",
"onboarding": "Настройвам"
}
},
"home": {
"bookmarks": {
"sectionTitle": "Отметки"
},
"continueWatching": {
"sectionTitle": "Продължи гледането"
},
"mediaList": {
"stopEditing": "Спри редактирането"
},
"search": {
"allResults": "Това е всичко, което имаме!",
"failed": "Неуспешно намиране на медия, опитайте отново!",
"loading": "Зареждане...",
"noResults": "Не успяхме да намерим нищо!",
"placeholder": {
"default": "Какво искате да гледате?",
"extra": [
"За какво имаш настроение?",
"Трябва ли да изтрием историята на браузъра ви?",
"Какво искате да предавате?",
"Sudo-Flix е най-добрият сайт досега!",
"Какво е в списъка ви за гледане днес?",
"Как беше денят ти?",
"Лошото ми е, че сайтът никога не работи...",
"Не е ли sudo-flix най-добрият?",
">ᴗ<"
]
},
"sectionTitle": "Резултати от търсенето",
"empty": {
"default": "Добре дошли, намерете медии за гледане тук!",
"extra": [
"Тук няма нищо :(",
"Толкова празно...",
"Такава празнота.",
"Здравей нов потребител :3"
]
},
"discoverMore": "Открий повече",
"discover": "Открийте"
},
"titles": {
"day": {
"default": "Какво бихте искали да гледате този следобед?",
"extra": [
"Viva la sudo-flix!"
]
},
"morning": {
"default": "Какво бихте искали да гледате тази сутрин?",
"extra": [
"Viva la sudo-flix!"
]
},
"night": {
"default": "Какво бихте искали да гледате тази вечер?",
"extra": [
"Viva la sudo-flix!"
]
},
"69": {
"default": "За нещо пикантно?",
"extra": [
"Честит 69 ден 😘!"
]
},
"420": {
"extra": [
"Честит 4/20 🥳!"
],
"default": "Какво бихте искали да гледате този 4/20?"
}
}
},
"media": {
"episodeDisplay": "S{{season}} E{{episode}}",
"types": {
"movie": "Филм",
"show": "Сериал"
},
"unreleased": "Неиздавана"
},
"navigation": {
"banner": {
"offline": "Проверете вашата интернет връзка"
},
"menu": {
"about": "За нас",
"logout": "Излез",
"register": "Синхронизиране с облак",
"settings": "Настройки",
"support": "поддържа",
"discover": "Открийте"
}
},
"notFound": {
"badge": "Не е намерено", "badge": "Не е намерено",
"homeButton": "Обратно към началото", "goHome": "Обратно към началото",
"text": "Не успяхме да намерим медията, която поискахте. Или е бил премахнат, или сте променили URL адреса.", "message": "Търсихме навсякъде: под кошчетата, в килера, зад проксито, но в крайна сметка не успяхме да намерим страницата, която търсите. (ಥ﹏ಥ)",
"title": "Тази медия не можа да бъде намерена." "title": "Тази страница не можа да бъде намерена",
} "reloadButton": "Опитай пак"
}, },
"nextEpisode": { "overlays": {
"cancel": "Отказ", "close": "Затвори"
"next": "Следващ епизод"
}, },
"playbackError": { "player": {
"badge": "Грешка при възпроизвеждане", "back": {
"errors": { "default": "Обратно към началото",
"errorAborted": "Извличането на медията беше прекратено по искане на потребителя.", "short": "Назад"
"errorDecode": "Въпреки че преди това беше определено като използваем, възникна грешка при опит за декодиране на медийния ресурс, което доведе до грешка.", },
"errorGenericMedia": "Възникна неизвестна медийна грешка.", "casting": {
"errorNetwork": "Възникна някакъв вид мрежова грешка, която попречи на медията да бъде успешно извлечена, въпреки че преди това е била достъпна.", "enabled": "Предава се към устройството..."
"errorNotSupported": "Обектът медия или доставчик на медия не се поддържа." },
} "menus": {
"downloads": {
"disclaimer": "Изтеглянията се вземат директно от доставчика. sudo-flix няма контрол върху това как се предоставят изтеглянията.",
"downloadSubtitle": "Изтеглете текущите субтитри",
"downloadVideo": "Изтегляне на видео",
"hlsDisclaimer": "Изтеглянията се вземат директно от доставчика. sudo-flix няма контрол върху това как се предоставят изтеглянията. Моля, имайте предвид, че изтегляте HLS плейлист, това е предназначено за потребители, запознати с усъвършенствано мултимедийно поточно предаване.",
"onAndroid": {
"1": "За да изтеглите на Android, щракнете върху бутона за изтегляне, след което на новата страница <bold>докоснете и задръжте</bold> видеоклипа, след което изберете <bold>запазване</bold>.",
"shortTitle": "Изтегляне / Android",
"title": "Изтегляне на Android"
},
"onIos": {
"1": "За да изтеглите на iOS, щракнете върху бутона за изтегляне, след което на новата страница щракнете върху <bold><ios_share /></bold>, след което върху <bold>Запазване във файлове <ios_files /></bold>.",
"shortTitle": "Изтегляне / iOS",
"title": "Изтегляне на iOS"
},
"onPc": {
"1": "На компютър щракнете върху бутона за изтегляне, след което на новата страница щракнете с десния бутон върху видеоклипа и изберете <bold>Запазване на видеоклипа като</bold>",
"shortTitle": "Изтегляне / компютър",
"title": "Изтегляне на компютър"
},
"title": "Изтегли"
},
"episodes": {
"button": "епизоди",
"emptyState": "Няма епизоди в този сезон, проверете отново по-късно!",
"episodeBadge": "E{{episode}}",
"loadingError": "Грешка при зареждането на сезона",
"loadingList": "Зареждане...",
"loadingTitle": "Зареждане..."
},
"playback": {
"speedLabel": "Скорост на възпроизвеждане",
"title": "Настройки за възпроизвеждане"
},
"quality": {
"automaticLabel": "Автоматично качество",
"hint": "Можете да опитате <0>да превключите източника</0>, за да получите различни опции за качество.",
"iosNoQuality": "Поради ограниченията, определени от Apple, изборът на качество не е наличен в iOS за този източник. Можете да опитате <0>да превключите към друг източник</0>, за да получите различни опции за качество.",
"title": "качество"
},
"settings": {
"downloadItem": "Изтегли",
"enableSubtitles": "включи субтитрите",
"experienceSection": "Изживяване при гледане",
"playbackItem": "Настройки за възпроизвеждане",
"qualityItem": "качество",
"sourceItem": "Видео източници",
"subtitleItem": "Настройки на субтитрите",
"videoSection": "Видео настройки"
},
"sources": {
"failed": {
"text": "Възникна грешка при опит за намиране на видеоклипове, моля, опитайте с друг източник.",
"title": "Неуспешно изстъргване"
},
"noEmbeds": {
"text": "Не можахме да намерим никакви вграждания, моля, опитайте с друг източник.",
"title": "Няма намерени вграждания"
},
"noStream": {
"text": "Този източник няма потоци за този филм или шоу.",
"title": "Няма поток"
},
"title": "Източници",
"unknownOption": "неизвестен"
},
"subtitles": {
"customChoice": "Качете субтитри",
"customizeLabel": "Персонализиране",
"offChoice": "изключи",
"settings": {
"backlink": "Персонализирани субтитри",
"delay": "Забавяне на субтитрите",
"fixCapitals": "Коригиране на главни букви"
},
"title": "субтитри",
"unknownLanguage": "неизвестен"
}
},
"metadata": {
"failed": {
"badge": "Провал",
"homeButton": "Обратно към началото",
"text": "Неуспешно зареждане на метаданните на носителя от TMDB. Моля, проверете дали TMDB не работи или е блокиран във вашата интернет връзка.",
"title": "Неуспешно зареждане на метаданните"
},
"notFound": {
"badge": "Не е намерено",
"homeButton": "Обратно към началото",
"text": "Не успяхме да намерим медията, която поискахте. Или е бил премахнат, или сте променили URL адреса.",
"title": "Тази медия не можа да бъде намерена."
}
},
"nextEpisode": {
"cancel": "Отказ",
"next": "Следващ епизод"
},
"playbackError": {
"badge": "Грешка при възпроизвеждане",
"errors": {
"errorAborted": "Извличането на медията беше прекратено по искане на потребителя.",
"errorDecode": "Въпреки че преди това беше определено като използваем, възникна грешка при опит за декодиране на медийния ресурс, което доведе до грешка.",
"errorGenericMedia": "Възникна неизвестна медийна грешка.",
"errorNetwork": "Възникна някакъв вид мрежова грешка, която попречи на медията да бъде успешно извлечена, въпреки че преди това е била достъпна.",
"errorNotSupported": "Обектът медия или доставчик на медия не се поддържа."
}
}
},
"downtimeNotice": {
"badge": "Проблеми",
"goHome": "Прибирай се",
"timeFrame": "<bold>March 31th 11:00 PM - 5:00 AM EST</bold>",
"title": "Проблеми с доставчика",
"message": "sudo-flix.lol отново има проблеми с някои доставчици, ако не можете да намерите или пуснете шоу, моля, променете източника. Очаквайте тази грешка да продължи през времената по-долу."
},
"onboarding": {
"defaultConfirm": {
"cancel": "Отказ",
"confirm": "Използвайте настройките по подразбиране",
"title": "Сигурен ли си?",
"description": "Настройката по подразбиране няма най-добрите потоци и може да бъде непоносимо бавна. ( ͠° ͟ʖ ͡°)"
},
"extension": {
"back": "Върни се",
"explainer": "С помощта на разширението на браузъра можете да получите най-добрите потоци, които предлагаме. Само с проста инсталация. 👌"
}
} }
}
} }

View file

@ -180,6 +180,12 @@
"extra": [ "extra": [
"Glædelig 4/20 🥳!" "Glædelig 4/20 🥳!"
] ]
},
"69": {
"default": "Har du lyst til at \"hygge\"?",
"extra": [
"Glædelig 69-dag 😘!"
]
} }
} }
}, },
@ -456,7 +462,7 @@
"shortRemaining": "-{{timeLeft}}" "shortRemaining": "-{{timeLeft}}"
}, },
"turnstile": { "turnstile": {
"description": "Bevis at du er et menneske ved at udfylde Captcha'en til højre. Dette er for at holde sudo-flix.lol sikker!", "description": "Bevis at du er et menneske ved at udføre den hurtige opgave, det er for at holde sudo-flix sikker.",
"verifyingHumanity": "Bekræfter at du er et menneske... (^▽^)👍", "verifyingHumanity": "Bekræfter at du er et menneske... (^▽^)👍",
"title": "Er du en robot 🤖?", "title": "Er du en robot 🤖?",
"error": "Kunne ikke bekræfte at du er et menneske! Prøv venligst igen." "error": "Kunne ikke bekræfte at du er et menneske! Prøv venligst igen."
@ -544,7 +550,8 @@
"default": "Standard", "default": "Standard",
"gray": "Grå", "gray": "Grå",
"red": "Rød", "red": "Rød",
"teal": "Blågrøn" "teal": "Blågrøn",
"blck": "BLCK"
}, },
"title": "Udseende" "title": "Udseende"
}, },

View file

@ -14,7 +14,15 @@
"body": "Nos résultats de recherche sont alimentés par The Movie Database (TMDB) et s'affichent indépendamment de la disponibilité réelle du contenu dans nos sources.", "body": "Nos résultats de recherche sont alimentés par The Movie Database (TMDB) et s'affichent indépendamment de la disponibilité réelle du contenu dans nos sources.",
"title": "Les résultats de la recherche affichent la série ou le film, pourquoi ne puis-je pas le lire ?" "title": "Les résultats de la recherche affichent la série ou le film, pourquoi ne puis-je pas le lire ?"
}, },
"title": "À propos de sudo-flix" "title": "À propos de sudo-flix",
"q4": {
"body": "La synchronisation de toutes les données se fait sur la sudo-backend, qui diffère de la backend de communauté et est hébergé indépendamment par moi. Tout le monde peut également l'utiliser.",
"title": "Qu'en est-il de mes données ?"
},
"q5": {
"body": "Sudo-flix possède un compte twitter qui peut être trouvé en bas de cette page ainsi qu'un lien vers le code source sur Github.",
"title": "Comment puis-je en savoir plus ?"
}
}, },
"actions": { "actions": {
"copied": "Copié", "copied": "Copié",
@ -87,7 +95,8 @@
"links": { "links": {
"discord": "Discord", "discord": "Discord",
"dmca": "DMCA", "dmca": "DMCA",
"github": "GitHub" "github": "GitHub",
"twitter": "Twitter"
}, },
"tagline": "Regardez vos séries et films préférés avec cette application de streaming open source." "tagline": "Regardez vos séries et films préférés avec cette application de streaming open source."
}, },
@ -100,7 +109,9 @@
"onboarding": "Mise en place", "onboarding": "Mise en place",
"pagetitle": "{{title}} - sudo-flix", "pagetitle": "{{title}} - sudo-flix",
"register": "Créer un compte", "register": "Créer un compte",
"settings": "Paramètres" "settings": "Paramètres",
"discover": "Découvrir",
"support": "Support"
} }
}, },
"home": { "home": {

View file

@ -29,6 +29,8 @@ export enum Icons {
EXPAND = "expand", EXPAND = "expand",
COMPRESS = "compress", COMPRESS = "compress",
VOLUME = "volume", VOLUME = "volume",
VOLUME_MED = "volume_med",
VOLUME_LOW = "volume_low",
VOLUME_X = "volume_x", VOLUME_X = "volume_x",
X = "x", X = "x",
EDIT = "edit", EDIT = "edit",
@ -102,7 +104,9 @@ const iconList: Record<Icons, string> = {
expand: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M32 32C14.3 32 0 46.3 0 64v96c0 17.7 14.3 32 32 32s32-14.3 32-32V96h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H32zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7 14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H64V352zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32h64v64c0 17.7 14.3 32 32 32s32-14.3 32-32V64c0-17.7-14.3-32-32-32H320zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v64H320c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32V352z"/></svg>`, expand: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M32 32C14.3 32 0 46.3 0 64v96c0 17.7 14.3 32 32 32s32-14.3 32-32V96h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H32zM64 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7 14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H64V352zM320 32c-17.7 0-32 14.3-32 32s14.3 32 32 32h64v64c0 17.7 14.3 32 32 32s32-14.3 32-32V64c0-17.7-14.3-32-32-32H320zM448 352c0-17.7-14.3-32-32-32s-32 14.3-32 32v64H320c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32V352z"/></svg>`,
compress: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M160 64c0-17.7-14.3-32-32-32s-32 14.3-32 32v64H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32V64zM32 320c-17.7 0-32 14.3-32 32s14.3 32 32 32H96v64c0 17.7 14.3 32 32 32s32-14.3 32-32V352c0-17.7-14.3-32-32-32H32zM352 64c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7 14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H352V64zM320 320c-17.7 0-32 14.3-32 32v96c0 17.7 14.3 32 32 32s32-14.3 32-32V384h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H320z"/></svg>`, compress: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M160 64c0-17.7-14.3-32-32-32s-32 14.3-32 32v64H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h96c17.7 0 32-14.3 32-32V64zM32 320c-17.7 0-32 14.3-32 32s14.3 32 32 32H96v64c0 17.7 14.3 32 32 32s32-14.3 32-32V352c0-17.7-14.3-32-32-32H32zM352 64c0-17.7-14.3-32-32-32s-32 14.3-32 32v96c0 17.7 14.3 32 32 32h96c17.7 0 32-14.3 32-32s-14.3-32-32-32H352V64zM320 320c-17.7 0-32 14.3-32 32v96c0 17.7 14.3 32 32 32s32-14.3 32-32V384h64c17.7 0 32-14.3 32-32s-14.3-32-32-32H320z"/></svg>`,
volume: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M533.6 32.5C598.5 85.3 640 165.8 640 256s-41.5 170.8-106.4 223.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C557.5 398.2 592 331.2 592 256s-34.5-142.2-88.7-186.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM473.1 107c43.2 35.2 70.9 88.9 70.9 149s-27.7 113.8-70.9 149c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C475.3 341.3 496 301.1 496 256s-20.7-85.3-53.2-111.8c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zm-60.5 74.5C434.1 199.1 448 225.9 448 256s-13.9 56.9-35.4 74.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C393.1 284.4 400 271 400 256s-6.9-28.4-17.7-37.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3z"/></svg>`, volume: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M533.6 32.5C598.5 85.3 640 165.8 640 256s-41.5 170.8-106.4 223.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C557.5 398.2 592 331.2 592 256s-34.5-142.2-88.7-186.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM473.1 107c43.2 35.2 70.9 88.9 70.9 149s-27.7 113.8-70.9 149c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C475.3 341.3 496 301.1 496 256s-20.7-85.3-53.2-111.8c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zm-60.5 74.5C434.1 199.1 448 225.9 448 256s-13.9 56.9-35.4 74.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C393.1 284.4 400 271 400 256s-6.9-28.4-17.7-37.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3z"/></svg>`,
volume_x: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3zM425 167l55 55 55-55c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-55 55 55 55c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-55-55-55 55c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l55-55-55-55c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0z"/></svg>`, volume_med: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M473.1 107c43.2 35.2 70.9 88.9 70.9 149s-27.7 113.8-70.9 149c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C475.3 341.3 496 301.1 496 256s-20.7-85.3-53.2-111.8c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zm-60.5 74.5C434.1 199.1 448 225.9 448 256s-13.9 56.9-35.4 74.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C393.1 284.4 400 271 400 256s-6.9-28.4-17.7-37.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5zM301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3z"/></svg>`,
volume_low: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3zm105.5 145.2C434.1 199.1 448 225.9 448 256s-13.9 56.9-35.4 74.5c-10.3 8.4-25.4 6.8-33.8-3.5s-6.8-25.4 3.5-33.8C393.1 284.4 400 271 400 256s-6.9-28.4-17.7-37.3c-10.3-8.4-11.8-23.5-3.5-33.8s23.5-11.8 33.8-3.5z"/></svg>`,
volume_x: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M301.1 34.8C312.6 40 320 51.4 320 64V448c0 12.6-7.4 24-18.9 29.2s-25 3.1-34.4-5.3L131.8 352H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h67.8L266.7 40.1c9.4-8.4 22.9-10.4 34.4-5.3zM425 167l55 55 55-55c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-55 55 55 55c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-55-55-55 55c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l55-55-55-55c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0z"/></svg>`,
x: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"/></svg>`, x: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z"/></svg>`,
edit: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z"/></svg>`, edit: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z"/></svg>`,
bookmark_outline: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M336 0h-288C21.49 0 0 21.49 0 48v431.9c0 24.7 26.79 40.08 48.12 27.64L192 423.6l143.9 83.93C357.2 519.1 384 504.6 384 479.9V48C384 21.49 362.5 0 336 0zM336 452L192 368l-144 84V54C48 50.63 50.63 48 53.1 48h276C333.4 48 336 50.63 336 54V452z"/></svg>`, bookmark_outline: `<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path fill="currentColor" d="M336 0h-288C21.49 0 0 21.49 0 48v431.9c0 24.7 26.79 40.08 48.12 27.64L192 423.6l143.9 83.93C357.2 519.1 384 504.6 384 479.9V48C384 21.49 362.5 0 336 0zM336 452L192 368l-144 84V54C48 50.63 50.63 48 53.1 48h276C333.4 48 336 50.63 336 54V452z"/></svg>`,

View file

@ -1,7 +1,7 @@
import classNames from "classnames"; import classNames from "classnames";
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom"; import { To, useNavigate } from "react-router-dom";
import { base64ToBuffer, decryptData } from "@/backend/accounts/crypto"; import { base64ToBuffer, decryptData } from "@/backend/accounts/crypto";
import { UserAvatar } from "@/components/Avatar"; import { UserAvatar } from "@/components/Avatar";
@ -24,8 +24,12 @@ function GoToLink(props: {
const navigate = useNavigate(); const navigate = useNavigate();
const goTo = (href: string) => { const goTo = (href: string) => {
if (href.startsWith("http")) window.open(href, "_blank"); if (href.startsWith("http")) {
else navigate(href); window.open(href, "_blank");
} else {
window.scrollTo(0, 0);
navigate(href);
}
}; };
return ( return (

View file

@ -1,5 +1,5 @@
import classNames from "classnames"; import classNames from "classnames";
import { Link, useNavigate } from "react-router-dom"; import { Link, To, useNavigate } from "react-router-dom";
import { NoUserAvatar, UserAvatar } from "@/components/Avatar"; import { NoUserAvatar, UserAvatar } from "@/components/Avatar";
import { IconPatch } from "@/components/buttons/IconPatch"; import { IconPatch } from "@/components/buttons/IconPatch";
@ -24,6 +24,11 @@ export function Navigation(props: NavigationProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const { loggedIn } = useAuth(); const { loggedIn } = useAuth();
const handleClick = (path: To) => {
window.scrollTo(0, 0);
navigate(path);
};
return ( return (
<> <>
{/* lightbar */} {/* lightbar */}
@ -79,11 +84,12 @@ export function Navigation(props: NavigationProps) {
}} }}
> >
<div className={classNames("fixed left-0 right-0 flex items-center")}> <div className={classNames("fixed left-0 right-0 flex items-center")}>
<div className="px-7 py-5 relative z-[60] flex flex-1 items-center justify-between"> <div className="px-7 py-3 relative z-[60] flex flex-1 items-center justify-between">
<div className="flex items-center space-x-1.5 ssm:space-x-3 pointer-events-auto"> <div className="flex items-center space-x-1.5 ssm:space-x-3 pointer-events-auto">
<Link <Link
className="block tabbable rounded-full text-xs ssm:text-base" className="block tabbable rounded-full text-xs ssm:text-base"
to="/" to="/"
onClick={() => window.scrollTo(0, 0)}
> >
<BrandPill clickable header /> <BrandPill clickable header />
</Link> </Link>
@ -104,7 +110,7 @@ export function Navigation(props: NavigationProps) {
<IconPatch icon={Icons.GITHUB} clickable downsized /> <IconPatch icon={Icons.GITHUB} clickable downsized />
</a> </a>
<a <a
onClick={() => navigate("/discover")} onClick={() => handleClick("/discover")}
rel="noreferrer" rel="noreferrer"
className="text-xl text-white tabbable rounded-full" className="text-xl text-white tabbable rounded-full"
> >

View file

@ -61,7 +61,7 @@ export function EpisodeSelector({
return ( return (
<div className="flex flex-row relative"> <div className="flex flex-row relative">
<div className="w-24 sm:w-96 cursor-pointer overflow-y-auto overflow-x-hidden max-h-60 z-10"> <div className="w-24 min-w-20 cursor-pointer overflow-y-auto overflow-x-hidden max-h-56 z-10 scrollbar-hide">
{seasonsData.map((season) => ( {seasonsData.map((season) => (
<div <div
key={season.season_number} key={season.season_number}
@ -79,8 +79,8 @@ export function EpisodeSelector({
</div> </div>
))} ))}
</div> </div>
<div className="flex-auto mt-4 sm:mt-0 sm:ml-4 cursor-pointer overflow-x-auto overflow-y-hidden sm:overflow-y-auto sm:overflow-x-hidden max-h-60 max-w-[70vw] z-0"> <div className="flex-auto sm:mt-0 sm:ml-4 cursor-pointer overflow-x-auto overflow-y-hidden sm:overflow-y-auto sm:overflow-x-hidden max-h-56 max-w-[70vw]">
<div className="flex sm:grid sm:grid-cols-3 sm:gap-2"> <div className="flex sm:grid sm:grid-cols-3 sm:gap-2 gap-1">
{selectedSeason ? ( {selectedSeason ? (
selectedSeason.episodes.map( selectedSeason.episodes.map(
(episode: { (episode: {
@ -92,11 +92,11 @@ export function EpisodeSelector({
<Flare.Base <Flare.Base
key={episode.episode_number} key={episode.episode_number}
onClick={() => { onClick={() => {
const navigateUrl = `/media/tmdb-tv-${tmdbId}-${mediaTitle}/${selectedSeason.id}/${episode.id}`; navigate(
// console.log("Navigating to:", navigateUrl); `/media/tmdb-tv-${tmdbId}-${mediaTitle}/${selectedSeason.id}/${episode.id}`,
navigate(navigateUrl); );
}} }}
className="group cursor-pointer rounded-xl relative p-[0.65em] bg-background-main transition-colors duration-[0.28s] flex-shrink-0 w-48 sm:w-auto mr-2 sm:mr-0" className="group cursor-pointer rounded-xl relative p-[0.6em] bg-background-secondary transition-colors duration-[0.25s] flex-shrink-0 w-44 max-h-48 sm:w-auto mr-2 sm:mr-0"
> >
<Flare.Light <Flare.Light
flareSize={300} flareSize={300}
@ -109,9 +109,11 @@ export function EpisodeSelector({
src={`https://image.tmdb.org/t/p/w500/${episode.still_path}`} src={`https://image.tmdb.org/t/p/w500/${episode.still_path}`}
className="w-full h-auto rounded" className="w-full h-auto rounded"
/> />
<p className="text-center text-[0.95em] mt-2"> <div className="text-center my-2 overflow-hidden">
E{episode.episode_number}: {episode.name} <p className="text-[0.95em] overflow-ellipsis whitespace-wrap">
</p> E{episode.episode_number}: {episode.name}
</p>
</div>
</div> </div>
</Flare.Base> </Flare.Base>
), ),

View file

@ -60,8 +60,6 @@ function formatRuntime(runtime: number) {
export function PopupModal({ export function PopupModal({
isVisible, isVisible,
onClose, onClose,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// idk just needed to add this line for commit
playingTitle, playingTitle,
media, media,
}: PopupModalProps) { }: PopupModalProps) {
@ -171,28 +169,25 @@ export function PopupModal({
return ( return (
<div <div
className="fixed inset-0 bg-black bg-opacity-40 flex justify-center items-center z-50 transition-opacity duration-100" className="fixed inset-0 flex justify-center items-center z-50 transition-opacity duration-100"
style={{ opacity: style.opacity, visibility: style.visibility }} style={{ opacity: style.opacity, visibility: style.visibility }}
> >
<div <div
ref={modalRef} ref={modalRef}
className="rounded-xl bg-modal-background flex flex-col justify-center items-center transition-opacity duration-100 max-w-full sm:max-w-xl w-full sm:w-auto sm:h-auto overflow-y-auto p-4" className="rounded-xl mx-4 min-w-[23em] overflow-x-hidden bg-modal-background flex flex-col justify-center items-center transition-opacity duration-100 sm:max-w-xl sm:w-auto sm:h-auto overflow-y-auto p-4"
style={{ opacity: style.opacity, maxHeight: "90vh", height: "auto" }} style={{ opacity: style.opacity, height: "auto" }}
> >
<div className="aspect-w-16 aspect-h-9 w-full sm:w-auto"> <div className="aspect-w-16 aspect-h-9 w-full sm:w-auto rounded-xl">
<div className="rounded-xl"> {data?.backdrop_path ? (
{data?.backdrop_path ? ( <img
<img src={`https://image.tmdb.org/t/p/original/${data.backdrop_path}`}
src={`https://image.tmdb.org/t/p/original/${data.backdrop_path}`} alt={media.poster ? "" : "failed to fetch :("}
alt={media.poster ? "" : "failed to fetch :("} className="rounded-xl object-cover"
className="rounded-xl object-cover w-full h-full" loading="lazy"
loading="lazy" />
style={{ maxHeight: "60vh" }} ) : (
/> <Skeleton />
) : ( )}
<Skeleton />
)}
</div>
<div className="flex pt-3 items-center gap-4"> <div className="flex pt-3 items-center gap-4">
<h1 className="relative text-xl sm:text-2xl whitespace-normal font-bold text-white"> <h1 className="relative text-xl sm:text-2xl whitespace-normal font-bold text-white">
{data?.title || data?.name ? ( {data?.title || data?.name ? (
@ -262,22 +257,24 @@ export function PopupModal({
</span> </span>
))} ))}
</div> </div>
{data?.genres && data.genres.length > 0 <div className="flex flex-row gap-3 flex-wrap">
? data.genres.map((genre: { name: string }) => ( {data?.genres && data.genres.length > 0
<div key={genre.name} className="inline-block"> ? data.genres.map((genre: { name: string }) => (
<div className="px-2 py-1 bg-mediaCard-hoverBackground rounded hover:bg-search-background cursor-default duration-200 transition-colors transform hover:scale-110"> <div key={genre.name}>
{genre.name} <div className="px-2 py-1 text-[0.95em] bg-mediaCard-hoverBackground rounded hover:bg-search-background cursor-default duration-200 transition-colors transform hover:scale-110">
{genre.name}
</div>
</div> </div>
</div> ))
)) : Array.from({ length: 3 }).map((_, idx) => (
: Array.from({ length: 3 }).map((_, idx) => ( // eslint-disable-next-line react/no-array-index-key
// eslint-disable-next-line react/no-array-index-key <div key={idx}>
<div className="inline-block" key={idx}> <Skeleton />
<Skeleton /> </div>
</div> ))}
))} </div>
</div> </div>
<div className="relative whitespace-normal font-medium overflow-y-auto max-h-32"> <div className="relative whitespace-normal font-medium overflow-y-auto max-h-28">
{data?.overview} {data?.overview}
</div> </div>
<div className="pt-3"> <div className="pt-3">
@ -285,7 +282,7 @@ export function PopupModal({
<EpisodeSelector tmdbId={media.id} mediaTitle={media.title} /> <EpisodeSelector tmdbId={media.id} mediaTitle={media.title} />
) : null} ) : null}
</div> </div>
<div className="flex justify-center items-center mt-4 mb-1"> <div className="flex justify-center items-center mt-3 mb-1">
<Button <Button
theme="purple" theme="purple"
onClick={() => onClick={() =>

View file

@ -62,6 +62,19 @@ export function Volume(props: Props) {
[volume, setVolume], [volume, setVolume],
); );
const getVolumeIcon = (volumeLevel: number) => {
if (volumeLevel === 0) {
return Icons.VOLUME_X;
}
if (volumeLevel > 0 && volumeLevel <= 0.33) {
return Icons.VOLUME_LOW;
}
if (volumeLevel > 0.33 && volumeLevel <= 0.66) {
return Icons.VOLUME_MED;
}
return Icons.VOLUME;
};
return ( return (
<div <div
className={props.className} className={props.className}
@ -71,7 +84,7 @@ export function Volume(props: Props) {
> >
<div className="pointer-events-auto flex cursor-pointer items-center py-0 touch-none"> <div className="pointer-events-auto flex cursor-pointer items-center py-0 touch-none">
<div className="px-4 text-2xl text-white" onClick={handleClick}> <div className="px-4 text-2xl text-white" onClick={handleClick}>
<Icon icon={percentage > 0 ? Icons.VOLUME : Icons.VOLUME_X} /> <Icon icon={getVolumeIcon(percentage / 100)} />
</div> </div>
<div <div
className={`linear -ml-2 w-0 overflow-hidden transition-[width,opacity] duration-300 ${ className={`linear -ml-2 w-0 overflow-hidden transition-[width,opacity] duration-300 ${

View file

@ -118,10 +118,17 @@ export function KeyboardEvents() {
// Utils // Utils
if (keyL === "f") dataRef.current.display?.toggleFullscreen(); if (keyL === "f") dataRef.current.display?.toggleFullscreen();
if (k === " " || keyL === "k") if (k === " " || keyL === "k") {
dataRef.current.display?.[ if (
dataRef.current.mediaPlaying.isPaused ? "play" : "pause" evt.target &&
](); (evt.target as HTMLInputElement).nodeName === "BUTTON"
) {
return;
}
const action = dataRef.current.mediaPlaying.isPaused ? "play" : "pause";
dataRef.current.display?.[action]();
}
if (k === "Escape") dataRef.current.router.close(); if (k === "Escape") dataRef.current.router.close();
// captions // captions

View file

@ -1,7 +1,7 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Helmet } from "react-helmet-async"; import { Helmet } from "react-helmet-async";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom"; import { To, useNavigate } from "react-router-dom";
import { WideContainer } from "@/components/layout/WideContainer"; import { WideContainer } from "@/components/layout/WideContainer";
import { useDebounce } from "@/hooks/useDebounce"; import { useDebounce } from "@/hooks/useDebounce";
@ -48,6 +48,11 @@ export function HomePage() {
const [showBookmarks, setShowBookmarks] = useState(false); const [showBookmarks, setShowBookmarks] = useState(false);
const [showWatching, setShowWatching] = useState(false); const [showWatching, setShowWatching] = useState(false);
const handleClick = (path: To) => {
window.scrollTo(0, 0);
navigate(path);
};
return ( return (
<HomeLayout showBg={showBg}> <HomeLayout showBg={showBg}>
<div className="mb-16 sm:mb-24"> <div className="mb-16 sm:mb-24">
@ -77,7 +82,7 @@ export function HomePage() {
<p className="text-[18.5px] pb-3">{emptyText}</p> <p className="text-[18.5px] pb-3">{emptyText}</p>
<Button <Button
className="px-py p-[0.35em] mt-3 rounded-xl text-type-dimmed box-content text-[18px] bg-largeCard-background text-buttons-secondaryText justify-center items-center" className="px-py p-[0.35em] mt-3 rounded-xl text-type-dimmed box-content text-[18px] bg-largeCard-background text-buttons-secondaryText justify-center items-center"
onClick={() => navigate("/discover")} onClick={() => handleClick("/discover")}
> >
{t("home.search.discover")} {t("home.search.discover")}
</Button> </Button>

View file

@ -59,7 +59,7 @@ export default defineConfig(({ mode }) => {
registerType: "autoUpdate", registerType: "autoUpdate",
workbox: { workbox: {
maximumFileSizeToCacheInBytes: 4000000, // 4mb maximumFileSizeToCacheInBytes: 4000000, // 4mb
globIgnores: ["**ping.txt**"], globIgnores: ["!assets/**/*"],
}, },
includeAssets: [ includeAssets: [
"favicon.ico", "favicon.ico",