Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions Models/ImageItem.vb
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,6 @@ Namespace Models
''' ein Immich-Pseudo-Pfad ist.</summary>
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)
Expand All @@ -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

Expand Down
18 changes: 1 addition & 17 deletions Services/ImmichService.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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

''' <summary>Alle Alben des Servers, alphabetisch. Leere Liste bei Fehler/keiner Konfiguration.</summary>
Expand Down
74 changes: 74 additions & 0 deletions Services/MediaFormatService.vb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Linq

Namespace Services

''' <summary>
''' Canonical media classification shared by folder discovery, gallery items,
''' the viewer, video previews, and upload MIME detection.
''' </summary>
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
6 changes: 3 additions & 3 deletions Services/TransparencyBrushService.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 1 addition & 3 deletions Services/VideoPreviewService.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 4 additions & 12 deletions ViewModels/GalleryViewModel.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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

''' <summary>
''' Freier Speicherplatz des Laufwerks, auf dem der aktuelle Ordner liegt. Läuft im Hintergrund:
Expand Down Expand Up @@ -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"}
''' <summary>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
Expand Down Expand Up @@ -5283,7 +5275,7 @@ Namespace ViewModels
''' nicht - deren Eintraege blieben sonst wirkungslos sichtbar.)</summary>
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

''' <summary>Formate, die die Stapel-Bildbearbeitung als QUELLE lesen kann. BMP/GIF (Skia)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion ViewModels/MainWindowViewModel.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 1 addition & 8 deletions ViewModels/ViewerViewModel.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
3 changes: 2 additions & 1 deletion Views/GalleryView.axaml.vb
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down