Skip to content
Merged
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
2 changes: 1 addition & 1 deletion iOSClient/Media/Cell/NCMediaCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class NCMediaCell: UICollectionViewCell {
@IBOutlet weak var imageStatus: UIImageView!

var ocId: String = ""
var datePhotosOriginal: Date?
var date: Date?

override func awakeFromNib() {
super.awakeFromNib()
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/Media/NCMedia+CollectionViewDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ extension NCMedia: UICollectionViewDataSource {
let imageCache = imageCache.getImageCache(ocId: metadata.ocId, etag: metadata.etag, ext: ext)

cell.imageItem.image = imageCache
cell.datePhotosOriginal = metadata.datePhotosOriginal as Date
cell.date = metadata.date
cell.ocId = metadata.ocId
cell.imageStatus.image = nil

Expand Down
2 changes: 1 addition & 1 deletion iOSClient/Media/NCMedia+Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ extension NCMedia {
let sortedAttributes = layoutAttributes.sorted { $0.frame.minY < $1.frame.minY || ($0.frame.minY == $1.frame.minY && $0.frame.minX < $1.frame.minX) }

if let firstAttribute = sortedAttributes.first, let metadata = dataSource.getMetadata(indexPath: firstAttribute.indexPath) {
titleDate?.text = utility.getTitleFromDate(metadata.datePhotosOriginal as Date)
titleDate?.text = utility.getTitleFromDate(metadata.date)
return
}
}
Expand Down
131 changes: 87 additions & 44 deletions iOSClient/Media/NCMedia+Netwoking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import Alamofire

extension NCMedia {
func searchMediaAsync(path: String = "",
lessDate: Any,
greaterDate: Any,
elementDate: String,
lessDate: Date,
greaterDate: Date,
limit: Int,
account: String,
options: NKRequestOptions = NKRequestOptions(),
Expand All @@ -19,25 +18,33 @@ extension NCMedia {
guard let nkSession = NextcloudKit.shared.nkCommonInstance.nksessions.session(forAccount: account) else {
return (account, nil, .urlError)
}
let capabilities = await NKCapabilities.shared.getCapabilities(for: account)
let files: [NKFile] = []
let elementDate = elementDate + "/"
var greaterDateString: String?, lessDateString: String?
let href = "/files/" + nkSession.userId + path
if let lessDate = lessDate as? Date {

let elementDate: String
var lessDateString: String
var greaterDateString: String

if capabilities.serverVersionMajor >= self.global.nextcloudVersionFuture {
elementDate = "nc:metadata-photos-original_date_time"
lessDateString = String(lessDate.timeIntervalSince1970)
greaterDateString = String(greaterDate.timeIntervalSince1970)
} else {
elementDate = "d:getlastmodified"
lessDateString = lessDate.formatted(using: "yyyy-MM-dd'T'HH:mm:ssZZZZZ")
} else if let lessDate = lessDate as? Int {
lessDateString = String(lessDate)
}
if let greaterDate = greaterDate as? Date {
greaterDateString = greaterDate.formatted(using: "yyyy-MM-dd'T'HH:mm:ssZZZZZ")
} else if let greaterDate = greaterDate as? Int {
greaterDateString = String(greaterDate)
}
guard let lessDateString, let greaterDateString else {
return (account, files, .invalidDate)
}

let httpBodyString = String(format: getRequestBodySearchMedia(createProperties: options.createProperties, removeProperties: options.removeProperties), href, elementDate, elementDate, lessDateString, elementDate, greaterDateString, String(limit))
let httpBodyString = String(format: getRequestBodySearchMedia(
createProperties: options.createProperties,
removeProperties: options.removeProperties,
href: href,
elementDate: elementDate,
lessDate: lessDateString,
greaterDate: greaterDateString,
limit: String(limit))
)

guard let httpBody = httpBodyString.data(using: .utf8) else {
return (account, files, .invalidData)
Expand All @@ -48,65 +55,101 @@ extension NCMedia {
return(results.account, results.files, results.error)
}

func getRequestBodySearchMedia(createProperties: [NKProperties]?, removeProperties: [NKProperties] = []) -> String {
func getRequestBodySearchMedia(createProperties: [NKProperties]?,
removeProperties: [NKProperties] = [],
href: String,
elementDate: String,
lessDate: String,
greaterDate: String,
limit: String) -> String {
// Build the DAV property list (merged create/remove rules)
let properties = NKProperties.properties(createProperties: createProperties, removeProperties: removeProperties)

let request = """
<?xml version=\"1.0\"?>
<d:searchrequest xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\" xmlns:nc=\"http://nextcloud.org/ns\">
<d:basicsearch>
<d:select>
<d:prop>
""" + NKProperties.properties(createProperties: createProperties, removeProperties: removeProperties) + """
</d:prop>
</d:select>
<d:basicsearch>

<!-- ====================================================== -->
<!-- SELECT: properties returned for each matching resource -->
<!-- ====================================================== -->

<d:select>
<d:prop>\(properties)</d:prop>
</d:select>

<!-- ===================================================== -->
<!-- FROM: recursive search starting from the given href -->
<!-- ===================================================== -->
<d:from>
<d:scope>
<d:href>%@</d:href>
<d:href>\(href)</d:href>
<d:depth>infinity</d:depth>
</d:scope>
</d:from>

<!-- ===================================================== -->
<!-- ORDER BY: -->
<!-- Primary sort on elementDate (descending) -->
<!-- Secondary sort on displayname for deterministic order -->
<!-- ===================================================== -->
<d:orderby>
<d:order>
<d:prop><%@></d:prop>
<d:prop><\(elementDate)/></d:prop>
<d:descending/>
</d:order>
<d:order>
<d:prop><d:displayname/></d:prop>
<d:descending/>
</d:order>
</d:orderby>

<!-- ===================================================== -->
<!-- WHERE: -->
<!-- 1) Filter only image and video content types -->
<!-- 2) Apply a numeric/date range on elementDate -->
<!-- ===================================================== -->
<d:where>
<d:and>
<d:or>
<d:like>
<d:prop><d:getcontenttype/></d:prop>
<d:literal>image/%%</d:literal>
</d:like>
<d:like>
<d:prop><d:getcontenttype/></d:prop>
<d:literal>video/%%</d:literal>
</d:like>
</d:or>
<d:or>

<!-- Media type filter -->
<d:or>
<d:like>
<d:prop><d:getcontenttype/></d:prop>
<d:literal>image/%%</d:literal>
</d:like>
<d:like>
<d:prop><d:getcontenttype/></d:prop>
<d:literal>video/%%</d:literal>
</d:like>
</d:or>

<!-- Date / numeric range filter -->
<d:and>
<d:lt>
<d:prop><%@></d:prop>
<d:literal>%@</d:literal>
<d:prop><\(elementDate)/></d:prop>
<d:literal>\(lessDate)</d:literal>
</d:lt>
<d:gt>
<d:prop><%@></d:prop>
<d:literal>%@</d:literal>
<d:prop><\(elementDate)/></d:prop>
<d:literal>\(greaterDate)</d:literal>
</d:gt>
</d:and>
</d:or>

</d:and>
</d:where>

<!-- ===================================================== -->
<!-- LIMIT: maximum number of results returned -->
<!-- ===================================================== -->
<d:limit>
<d:nresults>%@</d:nresults>
<d:nresults>\(limit)</d:nresults>
</d:limit>
</d:basicsearch>

</d:basicsearch>
</d:searchrequest>
"""
return request
return request
}
}

Expand Down
67 changes: 38 additions & 29 deletions iOSClient/Media/NCMediaDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@ extension NCMedia {
guard let tblAccount = await self.database.getTableAccountAsync(predicate: NSPredicate(format: "account == %@", self.session.account)) else {
return
}
let capabilities = await NKCapabilities.shared.getCapabilities(for: self.session.account)
let mediaPredicate = self.imageCache.getMediaPredicate(session: self.session,
mediaPath: tblAccount.mediaPath,
showOnlyImages: self.showOnlyImages,
showOnlyVideos: self.showOnlyVideos)
if let metadatas = await self.database.getMetadatasAsync(predicate: mediaPredicate, sortedByKeyPath: "datePhotosOriginal", ascending: false) {
var sortedByKeyPath: String
if capabilities.serverVersionMajor >= self.global.nextcloudVersionFuture {
sortedByKeyPath = "datePhotosOriginal"
} else {
sortedByKeyPath = "date"
}

if let metadatas = await self.database.getMetadatasAsync(predicate: mediaPredicate, sortedByKeyPath: sortedByKeyPath, ascending: false) {
self.database.filterAndNormalizeLivePhotos(from: metadatas) { metadatas in
Task { @MainActor in
self.dataSource = NCMediaDataSource(metadatas: metadatas)
Expand Down Expand Up @@ -97,54 +105,39 @@ extension NCMedia {
}

visibleCells = visibleCells.sorted {
guard let date1 = $0.datePhotosOriginal, let date2 = $1.datePhotosOriginal else {
guard let date1 = $0.date, let date2 = $1.date else {
return false
}
return date1 > date2
}

if !visibleCells.isEmpty, !distant {
let firstCellDate = visibleCells.first?.datePhotosOriginal
let lastCellDate = visibleCells.last?.datePhotosOriginal
let firstCellDate = visibleCells.first?.date
let lastCellDate = visibleCells.last?.date

if collectionView.contentOffset.y <= 0 {
lessDate = .distantFuture
} else {
lessDate = Calendar.current.date(byAdding: .second, value: 1, to: firstCellDate ?? .distantFuture) ?? .distantFuture
}

if lastCellDate == self.dataSource.metadatas.last?.datePhotosOriginal {
if lastCellDate == self.dataSource.metadatas.last?.date {
greaterDate = .distantPast
} else {
greaterDate = Calendar.current.date(byAdding: .second, value: -1, to: lastCellDate ?? .distantPast) ?? .distantPast
}
}
}

let elementDate: String
var lessDateAny: Any
var greaterDateAny: Any

if capabilities.serverVersionMajor >= self.global.nextcloudVersion31 {
elementDate = "nc:metadata-photos-original_date_time"
lessDateAny = Int(lessDate.timeIntervalSince1970)
greaterDateAny = Int(greaterDate.timeIntervalSince1970)
} else {
elementDate = "d:getlastmodified"
lessDateAny = lessDate
greaterDateAny = greaterDate
}

let limit = await MainActor.run {
max(self.collectionView.visibleCells.count * 3, 300)
}

let options = NKRequestOptions(timeout: 180, taskDescription: self.global.taskDescriptionRetrievesProperties, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)

let result = await searchMediaAsync(path: tblAccount.mediaPath,
lessDate: lessDateAny,
greaterDate: greaterDateAny,
elementDate: elementDate,
lessDate: lessDate,
greaterDate: greaterDate,
limit: limit,
account: self.session.account,
options: options) { task in
Expand Down Expand Up @@ -181,10 +174,19 @@ extension NCMedia {
mediaPath: tblAccount.mediaPath,
showOnlyImages: self.showOnlyImages,
showOnlyVideos: self.showOnlyVideos)
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "datePhotosOriginal >= %@ AND datePhotosOriginal <= %@ AND mediaSearch == true", greaterDate as NSDate, lessDate as NSDate),

var predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "date >= %@ AND date <= %@ AND mediaSearch == true", greaterDate as NSDate, lessDate as NSDate),
mediaPredicate
])

if capabilities.serverVersionMajor >= self.global.nextcloudVersionFuture {
predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
NSPredicate(format: "datePhotosOriginal >= %@ AND datePhotosOriginal <= %@ AND mediaSearch == true", greaterDate as NSDate, lessDate as NSDate),
mediaPredicate
])
}

let localMetadatas = await self.database.getMetadatasAsync(predicate: predicate)

await MainActor.run {
Expand All @@ -206,22 +208,22 @@ extension NCMedia {
@MainActor
public class NCMediaDataSource: NSObject {
public class Metadata: NSObject {
let datePhotosOriginal: Date
let date: Date
let etag: String
let imageSize: CGSize
let isImage: Bool
let isLivePhoto: Bool
let isVideo: Bool
let ocId: String

init(datePhotosOriginal: Date,
init(date: Date,
etag: String,
imageSize: CGSize,
isImage: Bool,
isLivePhoto: Bool,
isVideo: Bool,
ocId: String) {
self.datePhotosOriginal = datePhotosOriginal
self.date = date
self.etag = etag
self.imageSize = imageSize
self.isImage = isImage
Expand All @@ -245,7 +247,7 @@ public class NCMediaDataSource: NSObject {

private func insertInMetadatas(metadata: Metadata) {
for i in 0..<self.metadatas.count {
if (metadata.datePhotosOriginal as Date) > self.metadatas[i].datePhotosOriginal {
if (metadata.date) > self.metadatas[i].date {
self.metadatas.insert(metadata, at: i)
return
}
Expand All @@ -255,7 +257,14 @@ public class NCMediaDataSource: NSObject {
}

private func getMetadataFromTableMetadata(_ metadata: tableMetadata) -> Metadata {
return Metadata(datePhotosOriginal: metadata.datePhotosOriginal as Date,
let capabilities = NCNetworking.shared.capabilities[metadata.account] ?? NKCapabilities.Capabilities()
let date: Date
if capabilities.serverVersionMajor >= self.global.nextcloudVersionFuture {
date = metadata.datePhotosOriginal as Date
} else {
date = metadata.date as Date
}
return Metadata(date: date,
etag: metadata.etag,
imageSize: CGSize(width: metadata.width, height: metadata.height),
isImage: metadata.classFile == NKTypeClassFile.image.rawValue,
Expand Down
2 changes: 2 additions & 0 deletions iOSClient/NCGlobal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ final class NCGlobal: Sendable {
let nextcloudVersion28: Int = 28
let nextcloudVersion30: Int = 30
let nextcloudVersion31: Int = 31
let nextcloudVersionFuture: Int = 99999


// Nextcloud unsupported
//
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/NCImageCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ final class NCImageCache: @unchecked Sendable {

// MEDIA
let predicate = self.getMediaPredicate(session: session, mediaPath: tblAccount.mediaPath, showOnlyImages: false, showOnlyVideos: false)
guard let metadatas = await self.database.getMetadatasAsync(predicate: predicate, sortedByKeyPath: "datePhotosOriginal", limit: self.countLimit) else {
guard let metadatas = await self.database.getMetadatasAsync(predicate: predicate, sortedByKeyPath: "date", limit: self.countLimit) else {
return
}

Expand Down
Loading