diff --git a/frontend/src/Layout.tsx b/frontend/src/Layout.tsx index 95ac82e..8461f58 100644 --- a/frontend/src/Layout.tsx +++ b/frontend/src/Layout.tsx @@ -28,6 +28,7 @@ import FreeSpaceIndicator from './components/FreeSpaceIndicator' import Logout from './components/Logout' import SocketSubscriber from './components/SocketSubscriber' import ThemeToggler from './components/ThemeToggler' +import { useI18n } from './hooks/useI18n' import Toaster from './providers/ToasterProvider' export default function Layout() { @@ -50,6 +51,8 @@ export default function Layout() { const toggleDrawer = () => setOpen(state => !state) + const { i18n } = useI18n() + return ( @@ -88,10 +91,11 @@ export default function Layout() { display: 'flex', alignItems: 'center', flexWrap: 'wrap', + gap: 3, }}> -  {isConnected ? settings.serverAddr : 'not connected'} + {isConnected ? settings.serverAddr : i18n.t('notConnectedText')} @@ -121,7 +125,7 @@ export default function Layout() { - + - + - + diff --git a/frontend/src/assets/i18n.yaml b/frontend/src/assets/i18n.yaml index dd9659a..dc54998 100644 --- a/frontend/src/assets/i18n.yaml +++ b/frontend/src/assets/i18n.yaml @@ -35,6 +35,12 @@ languages: playlistCheckbox: Download playlist (it will take time, after submitting you may close this window) restartAppMessage: Needs a page reload to take effect servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App title savedTemplates: Saved templates templatesEditor: Templates editor @@ -75,6 +81,14 @@ languages: playlistCheckbox: Télécharger la liste de lecture (cela prendra du temps, vous pouvez fermer cette fenêtre après l'avoir validée) restartAppMessage: Nécessite un rechargement de la page pour prendre effet servedFromReverseProxyCheckbox: Est derrière un sous-dossier de proxy inverse + notConnectedText: not connected + settingsLabel: Settings + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: Nom de l'application savedTemplates: Saved templates templatesEditor: Templates editor @@ -114,6 +128,12 @@ languages: playlistCheckbox: Download playlist (richiederà tempo, puoi chiudere la finestra dopo l'inoltro) restartAppMessage: La finestra deve essere ricaricata perché abbia effetto servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: Nuovo download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: Titolo applicazione savedTemplates: Template salvati templatesEditor: Editor template @@ -154,6 +174,12 @@ languages: playlistCheckbox: 下载播放列表(可能需要一段时间,提交后可以关闭页面等待) restartAppMessage: 需要刷新页面才能生效 servedFromReverseProxyCheckbox: 处于反向代理的子目录后 + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App 标题 savedTemplates: Saved templates templatesEditor: Templates editor @@ -192,6 +218,12 @@ languages: clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App title savedTemplates: Saved templates templatesEditor: Templates editor @@ -230,6 +262,12 @@ languages: clipboardAction: URL скопирован в буфер обмена playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App title savedTemplates: Saved templates templatesEditor: Templates editor @@ -268,6 +306,12 @@ languages: clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App title savedTemplates: Saved templates templatesEditor: Templates editor @@ -307,6 +351,12 @@ languages: clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App title savedTemplates: Saved templates templatesEditor: Templates editor @@ -345,6 +395,12 @@ languages: clipboardAction: Copied URL to clipboard playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App title savedTemplates: Saved templates templatesEditor: Templates editor @@ -383,6 +439,12 @@ languages: clipboardAction: URL скопійовано в буфер обміну playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App title savedTemplates: Saved templates templatesEditor: Templates editor @@ -421,6 +483,12 @@ languages: clipboardAction: Adres URL zostanie skopiowany do schowka playlistCheckbox: Download playlist (it will take time, after submitting you may even close this window) servedFromReverseProxyCheckbox: Is behind a reverse proxy subfolder + newDownloadButton: New download + homeButtonLabel: Home + archiveButtonLabel: Archive + settingsButtonLabel: Settings + rpcAuthenticationLabel: RPC authentication + themeTogglerLabel: Theme toggler appTitle: App title savedTemplates: Saved templates templatesEditor: Templates editor diff --git a/frontend/src/components/HomeSpeedDial.tsx b/frontend/src/components/HomeSpeedDial.tsx index 0ff553a..3a68c37 100644 --- a/frontend/src/components/HomeSpeedDial.tsx +++ b/frontend/src/components/HomeSpeedDial.tsx @@ -48,7 +48,7 @@ const HomeSpeedDial: React.FC = ({ onDownloadOpen, onEditorOpen }) => { /> } - tooltipTitle={i18n.t('newDownload')} + tooltipTitle={i18n.t('newDownloadButton')} onClick={onDownloadOpen} /> diff --git a/frontend/src/components/Logout.tsx b/frontend/src/components/Logout.tsx index 463a950..2851ccd 100644 --- a/frontend/src/components/Logout.tsx +++ b/frontend/src/components/Logout.tsx @@ -1,8 +1,9 @@ -import { ListItemButton, ListItemIcon, ListItemText } from '@mui/material' import LogoutIcon from '@mui/icons-material/Logout' +import { ListItemButton, ListItemIcon, ListItemText } from '@mui/material' import { useNavigate } from 'react-router-dom' import { useRecoilValue } from 'recoil' import { serverURL } from '../atoms/settings' +import { useI18n } from '../hooks/useI18n' export default function Logout() { const navigate = useNavigate() @@ -15,12 +16,14 @@ export default function Logout() { } } + const { i18n } = useI18n() + return ( - + ) } \ No newline at end of file diff --git a/frontend/src/components/ThemeToggler.tsx b/frontend/src/components/ThemeToggler.tsx index 919c24b..2234f78 100644 --- a/frontend/src/components/ThemeToggler.tsx +++ b/frontend/src/components/ThemeToggler.tsx @@ -4,6 +4,7 @@ import BrightnessAuto from '@mui/icons-material/BrightnessAuto' import { ListItemButton, ListItemIcon, ListItemText } from '@mui/material' import { useRecoilState } from 'recoil' import { Theme, themeState } from '../atoms/settings' +import { useI18n } from '../hooks/useI18n' const ThemeToggler: React.FC = () => { const [theme, setTheme] = useRecoilState(themeState) @@ -17,6 +18,8 @@ const ThemeToggler: React.FC = () => { const themes: Theme[] = ['system', 'light', 'dark'] const currentTheme = themes.indexOf(theme) + const { i18n } = useI18n() + return ( { setTheme(themes[(currentTheme + 1) % themes.length]) @@ -24,7 +27,7 @@ const ThemeToggler: React.FC = () => { {actions[theme]} - + ) } diff --git a/frontend/src/lib/intl.ts b/frontend/src/lib/intl.ts index 629c741..0190b12 100644 --- a/frontend/src/lib/intl.ts +++ b/frontend/src/lib/intl.ts @@ -4,9 +4,10 @@ import i18n from "../assets/i18n.yaml" export default class I18nBuilder { private language: string private textMap = i18n.languages + private current: string[] constructor(language: string) { - this.language = language + this.setLanguage(language) } getLanguage(): string { @@ -15,13 +16,12 @@ export default class I18nBuilder { setLanguage(language: string): void { this.language = language + this.current = this.textMap[this.language] } t(key: string): string { - const map = this.textMap[this.language] - if (map) { - const translation = map[key] - return translation ?? 'caption not defined' + if (this.current) { + return this.current[key] ?? 'caption not defined' } return 'caption not defined' } diff --git a/server/handlers/archive.go b/server/handlers/archive.go index 49064a6..ea2c8b8 100644 --- a/server/handlers/archive.go +++ b/server/handlers/archive.go @@ -27,14 +27,22 @@ type DirectoryEntry struct { } func walkDir(root string) (*[]DirectoryEntry, error) { - files := []DirectoryEntry{} - dirs, err := os.ReadDir(root) if err != nil { return nil, err } + var validEntries int + for _, d := range dirs { + if utils.IsValidEntry(d) { + validEntries++ + } + } + + files := make([]DirectoryEntry, validEntries) + + for i, d := range dirs { if !utils.IsValidEntry(d) { continue } @@ -46,7 +54,7 @@ func walkDir(root string) (*[]DirectoryEntry, error) { return nil, err } - files = append(files, DirectoryEntry{ + files[i] = DirectoryEntry{ Path: path, Name: d.Name(), Size: info.Size(), @@ -54,7 +62,7 @@ func walkDir(root string) (*[]DirectoryEntry, error) { IsVideo: utils.IsVideo(d), IsDirectory: d.IsDir(), ModTime: info.ModTime(), - }) + } } return &files, err @@ -142,18 +150,18 @@ func SendFile(w http.ResponseWriter, r *http.Request) { return } - decodedStr := string(decoded) + filename := string(decoded) root := config.Instance().DownloadPath // TODO: further path / file validations - if strings.Contains(filepath.Dir(decodedStr), root) { + if strings.Contains(filepath.Dir(filename), root) { w.Header().Add( "Content-Disposition", - "inline; filename="+filepath.Base(decodedStr), + "inline; filename="+filepath.Base(filename), ) - http.ServeFile(w, r, decodedStr) + http.ServeFile(w, r, filename) } w.WriteHeader(http.StatusUnauthorized)