diff --git a/Models/ImageItem.vb b/Models/ImageItem.vb
index cf23c55..c746115 100644
--- a/Models/ImageItem.vb
+++ b/Models/ImageItem.vb
@@ -138,8 +138,6 @@ Namespace Models
''' ein Immich-Pseudo-Pfad ist.
Public Property ImmichLocalPath As String
- Private Shared ReadOnly _videoFormats As String() = {".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v"}
-
Public ReadOnly Property IsRawFile As Boolean
Get
Return Not IsFolder AndAlso RawPreviewService.IsSupportedRaw(FilePath)
@@ -150,13 +148,13 @@ Namespace Models
''' (Belichtung/Kurven/Filter usw.) ergibt dafür konzeptionell keinen Sinn.
Public ReadOnly Property IsVectorFile As Boolean
Get
- Return Not IsFolder AndAlso SvgPreviewService.IsSupportedSvg(FilePath)
+ Return Not IsFolder AndAlso MediaFormatService.IsSvg(FilePath)
End Get
End Property
Public ReadOnly Property IsVideoFile As Boolean
Get
- Return Not IsFolder AndAlso _videoFormats.Contains(IO.Path.GetExtension(FilePath).ToLowerInvariant())
+ Return Not IsFolder AndAlso MediaFormatService.IsVideo(FilePath)
End Get
End Property
diff --git a/Services/ImmichService.vb b/Services/ImmichService.vb
index e73674c..8b378c3 100644
--- a/Services/ImmichService.vb
+++ b/Services/ImmichService.vb
@@ -638,23 +638,7 @@ Namespace Services
End Sub
Private Shared Function GuessMimeType(filePath As String) As String
- Select Case IO.Path.GetExtension(filePath).ToLowerInvariant()
- Case ".jpg", ".jpeg" : Return "image/jpeg"
- Case ".png" : Return "image/png"
- Case ".gif" : Return "image/gif"
- Case ".webp" : Return "image/webp"
- Case ".bmp" : Return "image/bmp"
- Case ".tif", ".tiff" : Return "image/tiff"
- Case ".heic" : Return "image/heic"
- Case ".heif" : Return "image/heif"
- Case ".avif" : Return "image/avif"
- Case ".mp4" : Return "video/mp4"
- Case ".mov" : Return "video/quicktime"
- Case ".mkv" : Return "video/x-matroska"
- Case ".avi" : Return "video/x-msvideo"
- Case ".webm" : Return "video/webm"
- Case Else : Return "application/octet-stream"
- End Select
+ Return MediaFormatService.GuessMimeType(filePath)
End Function
''' Alle Alben des Servers, alphabetisch. Leere Liste bei Fehler/keiner Konfiguration.
diff --git a/Services/MediaFormatService.vb b/Services/MediaFormatService.vb
new file mode 100644
index 0000000..d727bf5
--- /dev/null
+++ b/Services/MediaFormatService.vb
@@ -0,0 +1,74 @@
+Imports System
+Imports System.Collections.Generic
+Imports System.IO
+Imports System.Linq
+
+Namespace Services
+
+ '''
+ ''' Canonical media classification shared by folder discovery, gallery items,
+ ''' the viewer, video previews, and upload MIME detection.
+ '''
+ Public NotInheritable Class MediaFormatService
+
+ Private Sub New()
+ End Sub
+
+ Public Shared ReadOnly DisplayImageExtensions As String() = {
+ ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff", ".tif", ".webp",
+ ".heic", ".avif",
+ ".ico", ".svg", ".fpx", ".psd", ".psb"
+ }
+
+ Public Shared ReadOnly VideoExtensions As String() = {
+ ".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v"
+ }
+
+ Public Shared ReadOnly DisplayMediaExtensions As String() =
+ DisplayImageExtensions.Concat(VideoExtensions).
+ Concat(RawPreviewService.SupportedExtensions).
+ Distinct(StringComparer.OrdinalIgnoreCase).
+ ToArray()
+
+ Public Shared Function IsVideo(filePath As String) As Boolean
+ If String.IsNullOrWhiteSpace(filePath) Then Return False
+ Return VideoExtensions.Contains(Path.GetExtension(filePath), StringComparer.OrdinalIgnoreCase)
+ End Function
+
+ Public Shared Function IsSvg(filePath As String) As Boolean
+ If String.IsNullOrWhiteSpace(filePath) Then Return False
+ Return String.Equals(Path.GetExtension(filePath), ".svg", StringComparison.OrdinalIgnoreCase)
+ End Function
+
+ Public Shared Function IsDisplayMedia(filePath As String) As Boolean
+ If String.IsNullOrWhiteSpace(filePath) Then Return False
+ Return DisplayMediaExtensions.Contains(Path.GetExtension(filePath), StringComparer.OrdinalIgnoreCase)
+ End Function
+
+ Public Shared Function VideoPickerPatterns() As IEnumerable(Of String)
+ Return VideoExtensions.Select(Function(extension) "*" & extension)
+ End Function
+
+ Public Shared Function GuessMimeType(filePath As String) As String
+ Select Case Path.GetExtension(filePath).ToLowerInvariant()
+ Case ".jpg", ".jpeg" : Return "image/jpeg"
+ Case ".png" : Return "image/png"
+ Case ".gif" : Return "image/gif"
+ Case ".webp" : Return "image/webp"
+ Case ".bmp" : Return "image/bmp"
+ Case ".tif", ".tiff" : Return "image/tiff"
+ Case ".heic" : Return "image/heic"
+ Case ".heif" : Return "image/heif"
+ Case ".avif" : Return "image/avif"
+ Case ".mp4" : Return "video/mp4"
+ Case ".mov" : Return "video/quicktime"
+ Case ".mkv" : Return "video/x-matroska"
+ Case ".avi" : Return "video/x-msvideo"
+ Case ".webm" : Return "video/webm"
+ Case Else : Return "application/octet-stream"
+ End Select
+ End Function
+
+ End Class
+
+End Namespace
diff --git a/Services/TransparencyBrushService.vb b/Services/TransparencyBrushService.vb
index 5cd7892..2b7e6e3 100644
--- a/Services/TransparencyBrushService.vb
+++ b/Services/TransparencyBrushService.vb
@@ -41,9 +41,9 @@ Namespace Services
''' RAW-Endungen kommen aus RawPreviewService.SupportedExtensions - eine eigene Kopie
''' wuerde beim naechsten neuen Format vergessen (siehe Kommentar dort).
Private Shared ReadOnly OpaqueOnlyExtensions As String() = {
- ".jpg", ".jpeg", ".jpe", ".jfif", ".bmp",
- ".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v"
- }.Concat(RawPreviewService.SupportedExtensions).ToArray()
+ ".jpg", ".jpeg", ".jpe", ".jfif", ".bmp"
+ }.Concat(MediaFormatService.VideoExtensions).
+ Concat(RawPreviewService.SupportedExtensions).ToArray()
Public Shared Function CanHaveTransparency(filePath As String) As Boolean
If String.IsNullOrEmpty(filePath) Then Return True
diff --git a/Services/VideoPreviewService.vb b/Services/VideoPreviewService.vb
index 18ee359..113bca3 100644
--- a/Services/VideoPreviewService.vb
+++ b/Services/VideoPreviewService.vb
@@ -12,12 +12,10 @@ Namespace Services
''' gehalten, weil mehrere parallele Decoder schnell mehr Last erzeugen als der Thumbnail-Cache spart.
Public Class VideoPreviewService
- Private Shared ReadOnly _videoExtensions As String() = {".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v"}
Private Shared ReadOnly _thumbnailGate As New SemaphoreSlim(1, 1)
Public Shared Function IsSupportedVideo(filePath As String) As Boolean
- If String.IsNullOrEmpty(filePath) Then Return False
- Return _videoExtensions.Contains(Path.GetExtension(filePath).ToLowerInvariant())
+ Return MediaFormatService.IsVideo(filePath)
End Function
Public Shared Function ExtractPreview(filePath As String, Optional maxDimension As Integer = 480, Optional timeoutMs As Integer = 4000) As MemoryStream
diff --git a/ViewModels/GalleryViewModel.vb b/ViewModels/GalleryViewModel.vb
index e0820ec..d880b23 100644
--- a/ViewModels/GalleryViewModel.vb
+++ b/ViewModels/GalleryViewModel.vb
@@ -3413,14 +3413,7 @@ Namespace ViewModels
Me.RaisePropertyChanged(NameOf(HasBreadcrumbParent))
End Sub
- ' ".fpx" gehört dazu: FerrumPix-Projekte erscheinen wie Bilder in Galerie und Filmstreifen
- ' (Thumbnail aus dem eingebetteten Composite, siehe ThumbnailCacheService).
- ' Anzeigbare Medien: feste Formate plus die kanonischen RAW-Endungen.
- Private ReadOnly _imageExtensions As String() = {
- ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff", ".tif", ".webp", ".heic", ".avif",
- ".ico", ".svg", ".fpx", ".psd", ".psb",
- ".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v"
- }.Concat(RawPreviewService.SupportedExtensions).ToArray()
+ Private ReadOnly _imageExtensions As String() = MediaFormatService.DisplayMediaExtensions
'''
''' Freier Speicherplatz des Laufwerks, auf dem der aktuelle Ordner liegt. Läuft im Hintergrund:
@@ -5254,7 +5247,6 @@ Namespace ViewModels
Return AppSettingsService.Load().DevelopRawInBatch
End Function
- Private Shared ReadOnly BatchConvertExcludedExtensions As String() = {".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v", ".svg"}
''' Kann die Stapel-Bildbearbeitung (Groesse/Wasserzeichen/Filter) diese Datei
''' schreiben? Die Menuesichtbarkeit MUSS dieselbe Frage stellen wie
''' GetSelectedBatchEditableImageItems - sonst stehen Eintraege da, die beim Klick still
@@ -5283,7 +5275,7 @@ Namespace ViewModels
''' nicht - deren Eintraege blieben sonst wirkungslos sichtbar.)
Public Shared Function IsBatchExportable(path As String) As Boolean
If String.IsNullOrWhiteSpace(path) Then Return False
- Return Not BatchConvertExcludedExtensions.Contains(IO.Path.GetExtension(path).ToLowerInvariant())
+ Return Not MediaFormatService.IsVideo(path) AndAlso Not MediaFormatService.IsSvg(path)
End Function
''' Formate, die die Stapel-Bildbearbeitung als QUELLE lesen kann. BMP/GIF (Skia)
@@ -6084,7 +6076,7 @@ Namespace ViewModels
Private Async Function ExportSelectedAsync() As Task
Dim targetItems = GetSelectedImageItems().
Where(Function(i) i IsNot Nothing AndAlso Not i.IsFolder).
- Where(Function(i) Not BatchConvertExcludedExtensions.Contains(IO.Path.GetExtension(i.FilePath).ToLowerInvariant())).
+ Where(Function(i) IsBatchExportable(i.FilePath)).
ToList()
DiagnosticLogService.LogAlways("Gallery.ExportTo", $"selected={GetSelectedImageItems().Count} exportable={targetItems.Count}")
If targetItems.Count = 0 Then Return
@@ -6242,7 +6234,7 @@ Namespace ViewModels
Private Async Sub BatchConvertSelected()
Dim targetItems = GetSelectedImageItems().
Where(Function(i) i IsNot Nothing AndAlso Not i.IsFolder).
- Where(Function(i) Not BatchConvertExcludedExtensions.Contains(IO.Path.GetExtension(i.FilePath).ToLowerInvariant())).
+ Where(Function(i) IsBatchExportable(i.FilePath)).
ToList()
DiagnosticLogService.LogAlways("Gallery.BatchConvert", $"selected={GetSelectedImageItems().Count} convertible={targetItems.Count}")
If targetItems.Count = 0 Then Return
diff --git a/ViewModels/MainWindowViewModel.vb b/ViewModels/MainWindowViewModel.vb
index f18a211..a518a16 100644
--- a/ViewModels/MainWindowViewModel.vb
+++ b/ViewModels/MainWindowViewModel.vb
@@ -458,7 +458,7 @@ Namespace ViewModels
Dim ignored = Gallery.OpenFolderForImage(imagePath)
CurrentMode = AppMode.Gallery
Case "Editor"
- If SvgPreviewService.IsSupportedSvg(imagePath) Then
+ If MediaFormatService.IsSvg(imagePath) Then
Viewer.OpenImage(imagePath)
CurrentMode = AppMode.Viewer
Else
diff --git a/ViewModels/ViewerViewModel.vb b/ViewModels/ViewerViewModel.vb
index c108185..1f2203e 100644
--- a/ViewModels/ViewerViewModel.vb
+++ b/ViewModels/ViewerViewModel.vb
@@ -2013,16 +2013,9 @@ Namespace ViewModels
End Sub
Private Sub LoadFolderContext(folder As String, currentPath As String)
- ' ".fpx" gehört dazu: Projekte blättern im Viewer/Vollbild mit (Anzeige aus dem Composite).
- ' Feste Formate plus die kanonischen RAW-Endungen (RawPreviewService.SupportedExtensions).
- Dim exts = {
- ".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff", ".tif", ".webp", ".heic", ".avif",
- ".ico", ".svg", ".fpx", ".psd", ".psb",
- ".mp4", ".mov", ".mkv", ".avi", ".webm", ".m4v"
- }.Concat(RawPreviewService.SupportedExtensions).ToArray()
Try
_folderPaths = Directory.GetFiles(folder).
- Where(Function(f) exts.Contains(IO.Path.GetExtension(f).ToLowerInvariant())).
+ Where(Function(f) MediaFormatService.IsDisplayMedia(f)).
OrderBy(Function(f) IO.Path.GetFileName(f)).
ToList()
_currentIndex = _folderPaths.FindIndex(Function(p) String.Equals(p, currentPath, StringComparison.OrdinalIgnoreCase))
diff --git a/Views/GalleryView.axaml.vb b/Views/GalleryView.axaml.vb
index 6c07073..f5b9f28 100644
--- a/Views/GalleryView.axaml.vb
+++ b/Views/GalleryView.axaml.vb
@@ -884,7 +884,8 @@ Namespace Views
Dim mediaType = New Avalonia.Platform.Storage.FilePickerFileType(LocalizationService.T("Bilder & Videos")) With {
.Patterns = New List(Of String) From {
"*.jpg", "*.jpeg", "*.png", "*.gif", "*.bmp", "*.tif", "*.tiff", "*.webp",
- "*.heic", "*.heif", "*.avif", "*.mp4", "*.mov", "*.mkv", "*.avi", "*.webm"}
+ "*.heic", "*.heif", "*.avif"
+ }.Concat(MediaFormatService.VideoPickerPatterns()).ToList()
}
Dim files = Await storageProvider.OpenFilePickerAsync(New Avalonia.Platform.Storage.FilePickerOpenOptions With {
.Title = LocalizationService.T("Bilder/Videos zum Hochladen wählen"),