mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-08 01:40:09 +00:00
Refactor RangeSet for 32-bit support
This commit is contained in:
parent
3f6dabad1e
commit
fc89536b5c
@ -20,7 +20,8 @@ swift_library(
|
|||||||
"//submodules/Postbox:Postbox",
|
"//submodules/Postbox:Postbox",
|
||||||
"//submodules/TelegramCore:TelegramCore",
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
|
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
|
||||||
"//submodules/MeshAnimationCache:MeshAnimationCache"
|
"//submodules/MeshAnimationCache:MeshAnimationCache",
|
||||||
|
"//submodules/Utils/RangeSet:RangeSet",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import UIKit
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import TelegramAudio
|
import TelegramAudio
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
public enum PeerMessagesMediaPlaylistId: Equatable, SharedMediaPlaylistId {
|
public enum PeerMessagesMediaPlaylistId: Equatable, SharedMediaPlaylistId {
|
||||||
case peer(PeerId)
|
case peer(PeerId)
|
||||||
@ -200,7 +201,7 @@ public protocol UniversalVideoManager: AnyObject {
|
|||||||
func addPlaybackCompleted(id: AnyHashable, _ f: @escaping () -> Void) -> Int
|
func addPlaybackCompleted(id: AnyHashable, _ f: @escaping () -> Void) -> Int
|
||||||
func removePlaybackCompleted(id: AnyHashable, index: Int)
|
func removePlaybackCompleted(id: AnyHashable, index: Int)
|
||||||
func statusSignal(content: UniversalVideoContent) -> Signal<MediaPlayerStatus?, NoError>
|
func statusSignal(content: UniversalVideoContent) -> Signal<MediaPlayerStatus?, NoError>
|
||||||
func bufferingStatusSignal(content: UniversalVideoContent) -> Signal<(IndexSet, Int64)?, NoError>
|
func bufferingStatusSignal(content: UniversalVideoContent) -> Signal<(RangeSet<Int64>, Int64)?, NoError>
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AudioRecordingState: Equatable {
|
public enum AudioRecordingState: Equatable {
|
||||||
|
|||||||
@ -8,11 +8,12 @@ import Display
|
|||||||
import TelegramAudio
|
import TelegramAudio
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
public protocol UniversalVideoContentNode: AnyObject {
|
public protocol UniversalVideoContentNode: AnyObject {
|
||||||
var ready: Signal<Void, NoError> { get }
|
var ready: Signal<Void, NoError> { get }
|
||||||
var status: Signal<MediaPlayerStatus, NoError> { get }
|
var status: Signal<MediaPlayerStatus, NoError> { get }
|
||||||
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> { get }
|
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> { get }
|
||||||
|
|
||||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
|
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition)
|
||||||
|
|
||||||
@ -105,8 +106,8 @@ public final class UniversalVideoNode: ASDisplayNode {
|
|||||||
return self._status.get()
|
return self._status.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
|
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
|
||||||
public var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
|
public var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
|
||||||
return self._bufferingStatus.get()
|
return self._bufferingStatus.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,7 @@ swift_library(
|
|||||||
"//submodules/UndoUI:UndoUI",
|
"//submodules/UndoUI:UndoUI",
|
||||||
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||||
"//submodules/TranslateUI:TranslateUI",
|
"//submodules/TranslateUI:TranslateUI",
|
||||||
|
"//submodules/Utils/RangeSet:RangeSet",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import Postbox
|
|||||||
import Display
|
import Display
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
private let textFont = Font.with(size: 13.0, design: .regular, weight: .regular, traits: [.monospacedNumbers])
|
private let textFont = Font.with(size: 13.0, design: .regular, weight: .regular, traits: [.monospacedNumbers])
|
||||||
|
|
||||||
@ -255,7 +256,7 @@ final class ChatVideoGalleryItemScrubberView: UIView {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func setBufferingStatusSignal(_ status: Signal<(IndexSet, Int64)?, NoError>?) {
|
func setBufferingStatusSignal(_ status: Signal<(RangeSet<Int64>, Int64)?, NoError>?) {
|
||||||
self.scrubberNode.bufferingStatus = status
|
self.scrubberNode.bufferingStatus = status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ swift_library(
|
|||||||
"//submodules/FFMpegBinding:FFMpegBinding",
|
"//submodules/FFMpegBinding:FFMpegBinding",
|
||||||
"//submodules/RingBuffer:RingBuffer",
|
"//submodules/RingBuffer:RingBuffer",
|
||||||
"//submodules/YuvConversion:YuvConversion",
|
"//submodules/YuvConversion:YuvConversion",
|
||||||
|
"//submodules/Utils/RangeSet:RangeSet",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import Foundation
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
public enum MediaPlayerScrubbingNodeCap {
|
public enum MediaPlayerScrubbingNodeCap {
|
||||||
case square
|
case square
|
||||||
@ -218,7 +219,7 @@ private final class MediaPlayerScrubbingBufferingNode: ASDisplayNode {
|
|||||||
private let containerNode: ASDisplayNode
|
private let containerNode: ASDisplayNode
|
||||||
private let foregroundNode: ASImageNode
|
private let foregroundNode: ASImageNode
|
||||||
|
|
||||||
private var ranges: (IndexSet, Int64)?
|
private var ranges: (RangeSet<Int64>, Int64)?
|
||||||
|
|
||||||
init(color: UIColor, lineCap: MediaPlayerScrubbingNodeCap, lineHeight: CGFloat) {
|
init(color: UIColor, lineCap: MediaPlayerScrubbingNodeCap, lineHeight: CGFloat) {
|
||||||
self.color = color
|
self.color = color
|
||||||
@ -239,7 +240,7 @@ private final class MediaPlayerScrubbingBufferingNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.containerNode)
|
self.addSubnode(self.containerNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStatus(_ ranges: IndexSet, _ size: Int64) {
|
func updateStatus(_ ranges: RangeSet<Int64>, _ size: Int64) {
|
||||||
self.ranges = (ranges, size)
|
self.ranges = (ranges, size)
|
||||||
if !self.bounds.width.isZero {
|
if !self.bounds.width.isZero {
|
||||||
self.updateLayout(size: self.bounds.size, transition: .animated(duration: 0.15, curve: .easeInOut))
|
self.updateLayout(size: self.bounds.size, transition: .animated(duration: 0.15, curve: .easeInOut))
|
||||||
@ -249,7 +250,7 @@ private final class MediaPlayerScrubbingBufferingNode: ASDisplayNode {
|
|||||||
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
|
||||||
transition.updateFrame(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height)))
|
transition.updateFrame(node: self.foregroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: size.height)))
|
||||||
if let ranges = self.ranges, !ranges.0.isEmpty, ranges.1 != 0 {
|
if let ranges = self.ranges, !ranges.0.isEmpty, ranges.1 != 0 {
|
||||||
for range in ranges.0.rangeView {
|
for range in ranges.0.ranges {
|
||||||
let rangeWidth = min(size.width, (CGFloat(range.count) / CGFloat(ranges.1)) * size.width)
|
let rangeWidth = min(size.width, (CGFloat(range.count) / CGFloat(ranges.1)) * size.width)
|
||||||
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: rangeWidth, height: size.height)))
|
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: rangeWidth, height: size.height)))
|
||||||
transition.updateAlpha(node: self.foregroundNode, alpha: abs(size.width - rangeWidth) < 1.0 ? 0.0 : 1.0)
|
transition.updateAlpha(node: self.foregroundNode, alpha: abs(size.width - rangeWidth) < 1.0 ? 0.0 : 1.0)
|
||||||
@ -358,9 +359,9 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var bufferingStatusDisposable: Disposable?
|
private var bufferingStatusDisposable: Disposable?
|
||||||
private var bufferingStatusValuePromise = Promise<(IndexSet, Int64)?>()
|
private var bufferingStatusValuePromise = Promise<(RangeSet<Int64>, Int64)?>()
|
||||||
|
|
||||||
public var bufferingStatus: Signal<(IndexSet, Int64)?, NoError>? {
|
public var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError>? {
|
||||||
didSet {
|
didSet {
|
||||||
if let bufferingStatus = self.bufferingStatus {
|
if let bufferingStatus = self.bufferingStatus {
|
||||||
self.bufferingStatusValuePromise.set(bufferingStatus)
|
self.bufferingStatusValuePromise.set(bufferingStatus)
|
||||||
|
|||||||
@ -25,6 +25,7 @@ swift_library(
|
|||||||
"//submodules/AppBundle:AppBundle",
|
"//submodules/AppBundle:AppBundle",
|
||||||
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
|
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
|
||||||
"//submodules/Svg:Svg",
|
"//submodules/Svg:Svg",
|
||||||
|
"//submodules/Utils/RangeSet:RangeSet",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import ImageTransparency
|
|||||||
import AppBundle
|
import AppBundle
|
||||||
import MusicAlbumArtResources
|
import MusicAlbumArtResources
|
||||||
import Svg
|
import Svg
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
private enum ResourceFileData {
|
private enum ResourceFileData {
|
||||||
case data(Data)
|
case data(Data)
|
||||||
@ -1671,7 +1672,7 @@ public func chatMessagePhotoStatus(context: AccountContext, messageId: MessageId
|
|||||||
context.account.postbox.mediaBox.resourceRangesStatus(largestRepresentation.resource)
|
context.account.postbox.mediaBox.resourceRangesStatus(largestRepresentation.resource)
|
||||||
)
|
)
|
||||||
|> map { status, rangeStatus -> MediaResourceStatus in
|
|> map { status, rangeStatus -> MediaResourceStatus in
|
||||||
if rangeStatus.contains(integersIn: Int(range.lowerBound) ..< Int(range.upperBound)) {
|
if rangeStatus.isSuperset(of: RangeSet<Int64>(range)) {
|
||||||
return .Local
|
return .Local
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import ManagedFile
|
import ManagedFile
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
private final class ResourceStatusContext {
|
private final class ResourceStatusContext {
|
||||||
var status: MediaResourceStatus?
|
var status: MediaResourceStatus?
|
||||||
@ -685,7 +686,7 @@ public final class MediaBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func resourceRangesStatus(_ resource: MediaResource) -> Signal<IndexSet, NoError> {
|
public func resourceRangesStatus(_ resource: MediaResource) -> Signal<RangeSet<Int64>, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
let disposable = MetaDisposable()
|
let disposable = MetaDisposable()
|
||||||
|
|
||||||
|
|||||||
@ -2,16 +2,17 @@ import Foundation
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Crc32
|
import Crc32
|
||||||
import ManagedFile
|
import ManagedFile
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
private final class MediaBoxFileMap {
|
private final class MediaBoxFileMap {
|
||||||
fileprivate(set) var sum: Int64
|
fileprivate(set) var sum: Int64
|
||||||
private(set) var ranges: IndexSet
|
private(set) var ranges: RangeSet<Int64>
|
||||||
private(set) var truncationSize: Int64?
|
private(set) var truncationSize: Int64?
|
||||||
private(set) var progress: Float?
|
private(set) var progress: Float?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.sum = 0
|
self.sum = 0
|
||||||
self.ranges = IndexSet()
|
self.ranges = RangeSet<Int64>()
|
||||||
self.truncationSize = nil
|
self.truncationSize = nil
|
||||||
self.progress = nil
|
self.progress = nil
|
||||||
}
|
}
|
||||||
@ -34,7 +35,7 @@ private final class MediaBoxFileMap {
|
|||||||
|
|
||||||
var count: Int32 = 0
|
var count: Int32 = 0
|
||||||
var sum: Int64 = 0
|
var sum: Int64 = 0
|
||||||
var ranges: IndexSet = IndexSet()
|
var ranges = RangeSet<Int64>()
|
||||||
|
|
||||||
guard fd.read(&count, 4) == 4 else {
|
guard fd.read(&count, 4) == 4 else {
|
||||||
return nil
|
return nil
|
||||||
@ -74,7 +75,7 @@ private final class MediaBoxFileMap {
|
|||||||
memcpy(&intervalLength, bytes.advanced(by: offset + 8), 8)
|
memcpy(&intervalLength, bytes.advanced(by: offset + 8), 8)
|
||||||
offset += 8 * 2
|
offset += 8 * 2
|
||||||
|
|
||||||
ranges.insert(integersIn: Int(intervalOffset) ..< Int(intervalOffset + intervalLength))
|
ranges.insert(contentsOf: intervalOffset ..< (intervalOffset + intervalLength))
|
||||||
|
|
||||||
sum += intervalLength
|
sum += intervalLength
|
||||||
}
|
}
|
||||||
@ -95,7 +96,7 @@ private final class MediaBoxFileMap {
|
|||||||
let crc: UInt32 = firstUInt32
|
let crc: UInt32 = firstUInt32
|
||||||
var count: Int32 = 0
|
var count: Int32 = 0
|
||||||
var sum: Int32 = 0
|
var sum: Int32 = 0
|
||||||
var ranges: IndexSet = IndexSet()
|
var ranges = RangeSet<Int64>()
|
||||||
|
|
||||||
guard fd.read(&count, 4) == 4 else {
|
guard fd.read(&count, 4) == 4 else {
|
||||||
return nil
|
return nil
|
||||||
@ -135,7 +136,7 @@ private final class MediaBoxFileMap {
|
|||||||
memcpy(&intervalLength, bytes.advanced(by: offset + 4), 4)
|
memcpy(&intervalLength, bytes.advanced(by: offset + 4), 4)
|
||||||
offset += 8
|
offset += 8
|
||||||
|
|
||||||
ranges.insert(integersIn: Int(intervalOffset) ..< Int(intervalOffset + intervalLength))
|
ranges.insert(contentsOf: Int64(intervalOffset) ..< Int64(intervalOffset + intervalLength))
|
||||||
|
|
||||||
sum += intervalLength
|
sum += intervalLength
|
||||||
}
|
}
|
||||||
@ -164,7 +165,7 @@ private final class MediaBoxFileMap {
|
|||||||
var zero: Int32 = 0
|
var zero: Int32 = 0
|
||||||
buffer.write(&zero, offset: 0, length: 4)
|
buffer.write(&zero, offset: 0, length: 4)
|
||||||
|
|
||||||
let rangeView = self.ranges.rangeView
|
let rangeView = self.ranges.ranges
|
||||||
var count: Int32 = Int32(rangeView.count)
|
var count: Int32 = Int32(rangeView.count)
|
||||||
buffer.write(&count, offset: 0, length: 4)
|
buffer.write(&count, offset: 0, length: 4)
|
||||||
|
|
||||||
@ -172,8 +173,8 @@ private final class MediaBoxFileMap {
|
|||||||
buffer.write(&truncationSizeValue, offset: 0, length: 8)
|
buffer.write(&truncationSizeValue, offset: 0, length: 8)
|
||||||
|
|
||||||
for range in rangeView {
|
for range in rangeView {
|
||||||
var intervalOffset = Int32(range.lowerBound)
|
var intervalOffset = range.lowerBound
|
||||||
var intervalLength = Int32(range.count)
|
var intervalLength = range.upperBound - range.lowerBound
|
||||||
buffer.write(&intervalOffset, offset: 0, length: 8)
|
buffer.write(&intervalOffset, offset: 0, length: 8)
|
||||||
buffer.write(&intervalLength, offset: 0, length: 8)
|
buffer.write(&intervalLength, offset: 0, length: 8)
|
||||||
}
|
}
|
||||||
@ -184,10 +185,13 @@ private final class MediaBoxFileMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func fill(_ range: Range<Int64>) {
|
fileprivate func fill(_ range: Range<Int64>) {
|
||||||
let intRange: Range<Int> = Int(range.lowerBound) ..< Int(range.upperBound)
|
var previousCount: Int64 = 0
|
||||||
let previousCount = self.ranges.count(in: intRange)
|
for intersectionRange in self.ranges.intersection(RangeSet<Int64>(range)).ranges {
|
||||||
self.ranges.insert(integersIn: intRange)
|
previousCount += intersectionRange.upperBound - intersectionRange.lowerBound
|
||||||
self.sum += Int64(range.count - previousCount)
|
}
|
||||||
|
|
||||||
|
self.ranges.insert(contentsOf: range)
|
||||||
|
self.sum += (range.upperBound - range.lowerBound) - previousCount
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func truncate(_ size: Int64) {
|
fileprivate func truncate(_ size: Int64) {
|
||||||
@ -199,21 +203,23 @@ private final class MediaBoxFileMap {
|
|||||||
|
|
||||||
fileprivate func reset() {
|
fileprivate func reset() {
|
||||||
self.truncationSize = nil
|
self.truncationSize = nil
|
||||||
self.ranges.removeAll()
|
self.ranges = RangeSet<Int64>()
|
||||||
self.sum = 0
|
self.sum = 0
|
||||||
self.progress = nil
|
self.progress = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func contains(_ range: Range<Int64>) -> Range<Int64>? {
|
fileprivate func contains(_ range: Range<Int64>) -> Range<Int64>? {
|
||||||
let maxValue: Int
|
let maxValue: Int64
|
||||||
if let truncationSize = self.truncationSize {
|
if let truncationSize = self.truncationSize {
|
||||||
maxValue = Int(truncationSize)
|
maxValue = truncationSize
|
||||||
} else {
|
} else {
|
||||||
maxValue = Int.max
|
maxValue = Int64.max
|
||||||
}
|
}
|
||||||
let intRange: Range<Int> = Int(range.lowerBound) ..< min(maxValue, Int(range.upperBound))
|
let clippedRange: Range<Int64> = range.lowerBound ..< min(maxValue, range.upperBound)
|
||||||
if self.ranges.contains(integersIn: intRange) {
|
let clippedRangeSet = RangeSet<Int64>(clippedRange)
|
||||||
return Int64(intRange.lowerBound) ..< Int64(intRange.upperBound)
|
|
||||||
|
if self.ranges.isSuperset(of: clippedRangeSet) {
|
||||||
|
return clippedRange
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -243,7 +249,7 @@ final class MediaBoxPartialFile {
|
|||||||
fileprivate let fileMap: MediaBoxFileMap
|
fileprivate let fileMap: MediaBoxFileMap
|
||||||
private var dataRequests = Bag<MediaBoxPartialFileDataRequest>()
|
private var dataRequests = Bag<MediaBoxPartialFileDataRequest>()
|
||||||
private let missingRanges: MediaBoxFileMissingRanges
|
private let missingRanges: MediaBoxFileMissingRanges
|
||||||
private let rangeStatusRequests = Bag<((IndexSet) -> Void, () -> Void)>()
|
private let rangeStatusRequests = Bag<((RangeSet<Int64>) -> Void, () -> Void)>()
|
||||||
private let statusRequests = Bag<((MediaResourceStatus) -> Void, Int64?)>()
|
private let statusRequests = Bag<((MediaResourceStatus) -> Void, Int64?)>()
|
||||||
|
|
||||||
private let fullRangeRequests = Bag<Disposable>()
|
private let fullRangeRequests = Bag<Disposable>()
|
||||||
@ -263,7 +269,7 @@ final class MediaBoxPartialFile {
|
|||||||
self.fd = fd
|
self.fd = fd
|
||||||
if let fileMap = MediaBoxFileMap(fd: self.metadataFd) {
|
if let fileMap = MediaBoxFileMap(fd: self.metadataFd) {
|
||||||
if !fileMap.ranges.isEmpty {
|
if !fileMap.ranges.isEmpty {
|
||||||
let upperBound = fileMap.ranges[fileMap.ranges.endIndex]
|
let upperBound = fileMap.ranges.ranges.last!.upperBound
|
||||||
if let actualSize = fileSize(path, useTotalFileAllocatedSize: false) {
|
if let actualSize = fileSize(path, useTotalFileAllocatedSize: false) {
|
||||||
if upperBound > actualSize {
|
if upperBound > actualSize {
|
||||||
self.fileMap = MediaBoxFileMap()
|
self.fileMap = MediaBoxFileMap()
|
||||||
@ -460,8 +466,8 @@ final class MediaBoxPartialFile {
|
|||||||
assertionFailure()
|
assertionFailure()
|
||||||
removeIndices.append((index, request))
|
removeIndices.append((index, request))
|
||||||
} else {
|
} else {
|
||||||
let intRange: Range<Int> = Int(request.range.lowerBound) ..< Int(min(maxValue, request.range.upperBound))
|
let intRange: Range<Int64> = request.range.lowerBound ..< min(maxValue, request.range.upperBound)
|
||||||
if self.fileMap.ranges.contains(integersIn: intRange) {
|
if self.fileMap.ranges.isSuperset(of: RangeSet<Int64>(intRange)) {
|
||||||
removeIndices.append((index, request))
|
removeIndices.append((index, request))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -650,7 +656,7 @@ final class MediaBoxPartialFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rangeStatus(next: @escaping (IndexSet) -> Void, completed: @escaping () -> Void) -> Disposable {
|
func rangeStatus(next: @escaping (RangeSet<Int64>) -> Void, completed: @escaping () -> Void) -> Disposable {
|
||||||
assert(self.queue.isCurrent())
|
assert(self.queue.isCurrent())
|
||||||
|
|
||||||
next(self.fileMap.ranges)
|
next(self.fileMap.ranges)
|
||||||
@ -758,7 +764,7 @@ final class MediaBoxPartialFile {
|
|||||||
strongSelf.write(offset: resourceOffset, data: data, dataRange: range)
|
strongSelf.write(offset: resourceOffset, data: data, dataRange: range)
|
||||||
}
|
}
|
||||||
if complete {
|
if complete {
|
||||||
if let maxOffset = strongSelf.fileMap.ranges.rangeView.reversed().first?.upperBound {
|
if let maxOffset = strongSelf.fileMap.ranges.ranges.reversed().first?.upperBound {
|
||||||
let maxValue = max(resourceOffset + Int64(range.count), Int64(maxOffset))
|
let maxValue = max(resourceOffset + Int64(range.count), Int64(maxOffset))
|
||||||
strongSelf.truncate(maxValue)
|
strongSelf.truncate(maxValue)
|
||||||
}
|
}
|
||||||
@ -807,15 +813,14 @@ final class MediaBoxPartialFile {
|
|||||||
private final class MediaBoxFileMissingRange {
|
private final class MediaBoxFileMissingRange {
|
||||||
var range: Range<Int64>
|
var range: Range<Int64>
|
||||||
let priority: MediaBoxFetchPriority
|
let priority: MediaBoxFetchPriority
|
||||||
var remainingRanges: IndexSet
|
var remainingRanges: RangeSet<Int64>
|
||||||
let error: (MediaResourceDataFetchError) -> Void
|
let error: (MediaResourceDataFetchError) -> Void
|
||||||
let completion: () -> Void
|
let completion: () -> Void
|
||||||
|
|
||||||
init(range: Range<Int64>, priority: MediaBoxFetchPriority, error: @escaping (MediaResourceDataFetchError) -> Void, completion: @escaping () -> Void) {
|
init(range: Range<Int64>, priority: MediaBoxFetchPriority, error: @escaping (MediaResourceDataFetchError) -> Void, completion: @escaping () -> Void) {
|
||||||
self.range = range
|
self.range = range
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
let intRange: Range<Int> = Int(range.lowerBound) ..< Int(range.upperBound)
|
self.remainingRanges = RangeSet<Int64>(range)
|
||||||
self.remainingRanges = IndexSet(integersIn: intRange)
|
|
||||||
self.error = error
|
self.error = error
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
}
|
}
|
||||||
@ -824,8 +829,8 @@ private final class MediaBoxFileMissingRange {
|
|||||||
private final class MediaBoxFileMissingRanges {
|
private final class MediaBoxFileMissingRanges {
|
||||||
private var requestedRanges = Bag<MediaBoxFileMissingRange>()
|
private var requestedRanges = Bag<MediaBoxFileMissingRange>()
|
||||||
|
|
||||||
private var missingRangesFlattened = IndexSet()
|
private var missingRangesFlattened = RangeSet<Int64>()
|
||||||
private var missingRangesByPriority: [MediaBoxFetchPriority: IndexSet] = [:]
|
private var missingRangesByPriority: [MediaBoxFetchPriority: RangeSet<Int64>] = [:]
|
||||||
|
|
||||||
func clear() -> [((MediaResourceDataFetchError) -> Void, () -> Void)] {
|
func clear() -> [((MediaResourceDataFetchError) -> Void, () -> Void)] {
|
||||||
let errorsAndCompletions = self.requestedRanges.copyItems().map({ ($0.error, $0.completion) })
|
let errorsAndCompletions = self.requestedRanges.copyItems().map({ ($0.error, $0.completion) })
|
||||||
@ -838,14 +843,14 @@ private final class MediaBoxFileMissingRanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func missingRequestedIntervals() -> [(Range<Int64>, MediaBoxFetchPriority)] {
|
private func missingRequestedIntervals() -> [(Range<Int64>, MediaBoxFetchPriority)] {
|
||||||
var intervalsByPriority: [MediaBoxFetchPriority: IndexSet] = [:]
|
var intervalsByPriority: [MediaBoxFetchPriority: RangeSet<Int64>] = [:]
|
||||||
var remainingIntervals = IndexSet()
|
var remainingIntervals = RangeSet<Int64>()
|
||||||
for item in self.requestedRanges.copyItems() {
|
for item in self.requestedRanges.copyItems() {
|
||||||
var requestedInterval = IndexSet(integersIn: Int(item.range.lowerBound) ..< Int(item.range.upperBound))
|
var requestedInterval = RangeSet<Int64>(item.range)
|
||||||
requestedInterval.formIntersection(self.missingRangesFlattened)
|
requestedInterval.formIntersection(self.missingRangesFlattened)
|
||||||
if !requestedInterval.isEmpty {
|
if !requestedInterval.isEmpty {
|
||||||
if intervalsByPriority[item.priority] == nil {
|
if intervalsByPriority[item.priority] == nil {
|
||||||
intervalsByPriority[item.priority] = IndexSet()
|
intervalsByPriority[item.priority] = RangeSet<Int64>()
|
||||||
}
|
}
|
||||||
intervalsByPriority[item.priority]?.formUnion(requestedInterval)
|
intervalsByPriority[item.priority]?.formUnion(requestedInterval)
|
||||||
remainingIntervals.formUnion(requestedInterval)
|
remainingIntervals.formUnion(requestedInterval)
|
||||||
@ -857,9 +862,9 @@ private final class MediaBoxFileMissingRanges {
|
|||||||
for priority in intervalsByPriority.keys.sorted(by: { $0.rawValue > $1.rawValue }) {
|
for priority in intervalsByPriority.keys.sorted(by: { $0.rawValue > $1.rawValue }) {
|
||||||
let currentIntervals = intervalsByPriority[priority]!.intersection(remainingIntervals)
|
let currentIntervals = intervalsByPriority[priority]!.intersection(remainingIntervals)
|
||||||
remainingIntervals.subtract(currentIntervals)
|
remainingIntervals.subtract(currentIntervals)
|
||||||
for range in currentIntervals.rangeView {
|
for range in currentIntervals.ranges {
|
||||||
if !range.isEmpty {
|
if !range.isEmpty {
|
||||||
result.append((Int64(range.lowerBound) ..< Int64(range.upperBound), priority))
|
result.append((range, priority))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -868,17 +873,16 @@ private final class MediaBoxFileMissingRanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fill(_ range: Range<Int64>) -> ([(Range<Int64>, MediaBoxFetchPriority)], [() -> Void])? {
|
func fill(_ range: Range<Int64>) -> ([(Range<Int64>, MediaBoxFetchPriority)], [() -> Void])? {
|
||||||
let intRange: Range<Int> = Int(range.lowerBound) ..< Int(range.upperBound)
|
if self.missingRangesFlattened.intersects(range) {
|
||||||
if self.missingRangesFlattened.intersects(integersIn: intRange) {
|
self.missingRangesFlattened.remove(contentsOf: range)
|
||||||
self.missingRangesFlattened.remove(integersIn: intRange)
|
|
||||||
for priority in self.missingRangesByPriority.keys {
|
for priority in self.missingRangesByPriority.keys {
|
||||||
self.missingRangesByPriority[priority]!.remove(integersIn: intRange)
|
self.missingRangesByPriority[priority]!.remove(contentsOf: range)
|
||||||
}
|
}
|
||||||
|
|
||||||
var completions: [() -> Void] = []
|
var completions: [() -> Void] = []
|
||||||
for (index, item) in self.requestedRanges.copyItemsWithIndices() {
|
for (index, item) in self.requestedRanges.copyItemsWithIndices() {
|
||||||
if item.range.overlaps(range) {
|
if item.range.overlaps(range) {
|
||||||
item.remainingRanges.remove(integersIn: intRange)
|
item.remainingRanges.remove(contentsOf: range)
|
||||||
if item.remainingRanges.isEmpty {
|
if item.remainingRanges.isEmpty {
|
||||||
self.requestedRanges.remove(index)
|
self.requestedRanges.remove(index)
|
||||||
completions.append(item.completion)
|
completions.append(item.completion)
|
||||||
@ -904,15 +908,15 @@ private final class MediaBoxFileMissingRanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func update(fileMap: MediaBoxFileMap) -> [(Range<Int64>, MediaBoxFetchPriority)]? {
|
private func update(fileMap: MediaBoxFileMap) -> [(Range<Int64>, MediaBoxFetchPriority)]? {
|
||||||
var byPriority: [MediaBoxFetchPriority: IndexSet] = [:]
|
var byPriority: [MediaBoxFetchPriority: RangeSet<Int64>] = [:]
|
||||||
var flattened = IndexSet()
|
var flattened = RangeSet<Int64>()
|
||||||
for item in self.requestedRanges.copyItems() {
|
for item in self.requestedRanges.copyItems() {
|
||||||
let intRange: Range<Int> = Int(item.range.lowerBound) ..< Int(item.range.upperBound)
|
let intRange: Range<Int64> = item.range
|
||||||
if byPriority[item.priority] == nil {
|
if byPriority[item.priority] == nil {
|
||||||
byPriority[item.priority] = IndexSet()
|
byPriority[item.priority] = RangeSet<Int64>()
|
||||||
}
|
}
|
||||||
byPriority[item.priority]!.insert(integersIn: intRange)
|
byPriority[item.priority]!.insert(contentsOf: intRange)
|
||||||
flattened.insert(integersIn: intRange)
|
flattened.insert(contentsOf: intRange)
|
||||||
}
|
}
|
||||||
for priority in byPriority.keys {
|
for priority in byPriority.keys {
|
||||||
byPriority[priority]!.subtract(fileMap.ranges)
|
byPriority[priority]!.subtract(fileMap.ranges)
|
||||||
@ -1041,10 +1045,10 @@ final class MediaBoxFileContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rangeStatus(next: @escaping (IndexSet) -> Void, completed: @escaping () -> Void) -> Disposable {
|
func rangeStatus(next: @escaping (RangeSet<Int64>) -> Void, completed: @escaping () -> Void) -> Disposable {
|
||||||
switch self.content {
|
switch self.content {
|
||||||
case let .complete(_, size):
|
case let .complete(_, size):
|
||||||
next(IndexSet(integersIn: 0 ..< Int(size)))
|
next(RangeSet<Int64>(0 ..< size))
|
||||||
completed()
|
completed()
|
||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
case let .partial(file):
|
case let .partial(file):
|
||||||
|
|||||||
@ -326,6 +326,17 @@ private enum MultipartFetchSource {
|
|||||||
case .revalidate:
|
case .revalidate:
|
||||||
return .fail(.revalidateMediaReference)
|
return .fail(.revalidateMediaReference)
|
||||||
case let .location(parsedLocation):
|
case let .location(parsedLocation):
|
||||||
|
#if DEBUG
|
||||||
|
switch parsedLocation {
|
||||||
|
case let .inputDocumentFileLocation(id, _, _, _):
|
||||||
|
if id == 5467475273110788326 {
|
||||||
|
return Signal<Data, MultipartFetchDownloadError>.single(Data(count: Int(limit))) |> delay(0.01, queue: .concurrentDefaultQueue())
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return download.request(Api.functions.upload.getFile(flags: 0, location: parsedLocation, offset: offset, limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground)
|
return download.request(Api.functions.upload.getFile(flags: 0, location: parsedLocation, offset: offset, limit: Int32(limit)), tag: tag, continueInBackground: continueInBackground)
|
||||||
|> mapError { error -> MultipartFetchDownloadError in
|
|> mapError { error -> MultipartFetchDownloadError in
|
||||||
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
if error.errorDescription.hasPrefix("FILEREF_INVALID") || error.errorDescription.hasPrefix("FILE_REFERENCE_") {
|
||||||
|
|||||||
@ -269,6 +269,7 @@ swift_library(
|
|||||||
"//submodules/BrowserUI:BrowserUI",
|
"//submodules/BrowserUI:BrowserUI",
|
||||||
"//submodules/PremiumUI:PremiumUI",
|
"//submodules/PremiumUI:PremiumUI",
|
||||||
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
|
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
|
||||||
|
"//submodules/Utils/RangeSet:RangeSet",
|
||||||
] + select({
|
] + select({
|
||||||
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
"@build_bazel_rules_apple//apple:ios_armv7": [],
|
||||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||||
|
|||||||
@ -155,10 +155,19 @@ private final class InlineStickerItemLayer: SimpleLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var didRequestFrame = false
|
||||||
|
|
||||||
private func loadNextFrame() {
|
private func loadNextFrame() {
|
||||||
guard let frameSource = self.frameSource else {
|
guard let frameSource = self.frameSource else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if self.contents != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if self.didRequestFrame {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.didRequestFrame = true
|
||||||
frameSource.with { [weak self] impl in
|
frameSource.with { [weak self] impl in
|
||||||
if let animationFrame = impl.takeFrame(draw: true) {
|
if let animationFrame = impl.takeFrame(draw: true) {
|
||||||
var image: UIImage?
|
var image: UIImage?
|
||||||
@ -191,6 +200,9 @@ private final class InlineStickerItemLayer: SimpleLayer {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if strongSelf.contents != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
strongSelf.contents = image.cgImage
|
strongSelf.contents = image.cgImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,9 +476,10 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
/*if item.context.sharedContext.immediateExperimentalUISettings.inlineStickers*/ do {
|
/*if item.context.sharedContext.immediateExperimentalUISettings.inlineStickers*/ do {
|
||||||
var currentCount = 0
|
var currentCount = 0
|
||||||
let updatedString = NSMutableAttributedString(attributedString: attributedText)
|
let updatedString = NSMutableAttributedString(attributedString: attributedText)
|
||||||
|
var startIndex = updatedString.string.startIndex
|
||||||
while true {
|
while true {
|
||||||
var hadUpdates = false
|
var hadUpdates = false
|
||||||
updatedString.string.enumerateSubstrings(in: updatedString.string.startIndex ..< updatedString.string.endIndex, options: [.byComposedCharacterSequences]) { substring, substringRange, _, stop in
|
updatedString.string.enumerateSubstrings(in: startIndex ..< updatedString.string.endIndex, options: [.byComposedCharacterSequences]) { substring, substringRange, _, stop in
|
||||||
if let substring = substring {
|
if let substring = substring {
|
||||||
let emoji = substring.basicEmoji.0
|
let emoji = substring.basicEmoji.0
|
||||||
|
|
||||||
@ -477,14 +490,22 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let emojiFile = emojiFile {
|
if let emojiFile = emojiFile {
|
||||||
updatedString.replaceCharacters(in: NSRange(substringRange, in: updatedString.string), with: NSAttributedString(string: "[\u{00a0}\u{00a0}\u{00a0}\u{00a0}\u{00a0}]", attributes: [NSAttributedString.Key("Attribute__EmbeddedItem"): InlineStickerItem(file: emojiFile), NSAttributedString.Key.foregroundColor: UIColor.clear.cgColor]))
|
let currentDict = updatedString.attributes(at: NSRange(substringRange, in: updatedString.string).lowerBound, effectiveRange: nil)
|
||||||
|
var updatedAttributes: [NSAttributedString.Key: Any] = currentDict
|
||||||
|
updatedAttributes[NSAttributedString.Key.foregroundColor] = UIColor.clear.cgColor
|
||||||
|
updatedAttributes[NSAttributedString.Key("Attribute__EmbeddedItem")] = InlineStickerItem(file: emojiFile)
|
||||||
|
|
||||||
|
let insertString = NSAttributedString(string: "[\u{00a0}\u{00a0}\u{00a0}]", attributes: updatedAttributes)
|
||||||
|
//updatedString.insert(insertString, at: NSRange(substringRange, in: updatedString.string).upperBound)
|
||||||
|
updatedString.replaceCharacters(in: NSRange(substringRange, in: updatedString.string), with: insertString)
|
||||||
|
startIndex = substringRange.lowerBound
|
||||||
currentCount += 1
|
currentCount += 1
|
||||||
hadUpdates = true
|
hadUpdates = true
|
||||||
stop = true
|
stop = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !hadUpdates || currentCount >= 10 {
|
if !hadUpdates || currentCount >= 1000 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -701,7 +722,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
|||||||
itemLayer.isVisibleForAnimations = self.isVisibleForAnimations
|
itemLayer.isVisibleForAnimations = self.isVisibleForAnimations
|
||||||
}
|
}
|
||||||
|
|
||||||
itemLayer.frame = CGRect(origin: item.rect.offsetBy(dx: textLayout.insets.left, dy: textLayout.insets.top + 1.0).center, size: CGSize()).insetBy(dx: -11.0, dy: -11.0)
|
itemLayer.frame = CGRect(origin: item.rect.offsetBy(dx: textLayout.insets.left, dy: textLayout.insets.top + 0.0).center, size: CGSize()).insetBy(dx: -12.0, dy: -12.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import AccountContext
|
|||||||
import PhotoResources
|
import PhotoResources
|
||||||
import AppBundle
|
import AppBundle
|
||||||
import ManagedAnimationNode
|
import ManagedAnimationNode
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
private func generateBackground(theme: PresentationTheme) -> UIImage? {
|
private func generateBackground(theme: PresentationTheme) -> UIImage? {
|
||||||
return generateImage(CGSize(width: 20.0, height: 10.0 + 8.0), rotatedContext: { size, context in
|
return generateImage(CGSize(width: 20.0, height: 10.0 + 8.0), rotatedContext: { size, context in
|
||||||
@ -401,7 +402,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
strongSelf.currentFileReference = fileReference
|
strongSelf.currentFileReference = fileReference
|
||||||
if let size = fileReference.media.size {
|
if let size = fileReference.media.size {
|
||||||
strongSelf.scrubberNode.bufferingStatus = strongSelf.postbox.mediaBox.resourceRangesStatus(fileReference.media.resource)
|
strongSelf.scrubberNode.bufferingStatus = strongSelf.postbox.mediaBox.resourceRangesStatus(fileReference.media.resource)
|
||||||
|> map { ranges -> (IndexSet, Int64) in
|
|> map { ranges -> (RangeSet<Int64>, Int64) in
|
||||||
return (ranges, size)
|
return (ranges, size)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -22,6 +22,7 @@ swift_library(
|
|||||||
"//submodules/LegacyComponents:LegacyComponents",
|
"//submodules/LegacyComponents:LegacyComponents",
|
||||||
"//submodules/RadialStatusNode:RadialStatusNode",
|
"//submodules/RadialStatusNode:RadialStatusNode",
|
||||||
"//submodules/AppBundle:AppBundle",
|
"//submodules/AppBundle:AppBundle",
|
||||||
|
"//submodules/Utils/RangeSet:RangeSet",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import UniversalMediaPlayer
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import PhotoResources
|
import PhotoResources
|
||||||
import UIKitRuntimeUtils
|
import UIKitRuntimeUtils
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
public enum NativeVideoContentId: Hashable {
|
public enum NativeVideoContentId: Hashable {
|
||||||
case message(UInt32, MediaId)
|
case message(UInt32, MediaId)
|
||||||
@ -130,8 +131,8 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
|
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
|
||||||
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
|
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
|
||||||
return self._bufferingStatus.get()
|
return self._bufferingStatus.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import UniversalMediaPlayer
|
|||||||
import TelegramAudio
|
import TelegramAudio
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import PhotoResources
|
import PhotoResources
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
public enum PlatformVideoContentId: Hashable {
|
public enum PlatformVideoContentId: Hashable {
|
||||||
case message(MessageId, UInt32, MediaId)
|
case message(MessageId, UInt32, MediaId)
|
||||||
@ -132,8 +133,8 @@ private final class PlatformVideoContentNode: ASDisplayNode, UniversalVideoConte
|
|||||||
return self._status.get()
|
return self._status.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
|
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
|
||||||
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
|
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
|
||||||
return self._bufferingStatus.get()
|
return self._bufferingStatus.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import LegacyComponents
|
|||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import PhotoResources
|
import PhotoResources
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
public final class SystemVideoContent: UniversalVideoContent {
|
public final class SystemVideoContent: UniversalVideoContent {
|
||||||
public let id: AnyHashable
|
public let id: AnyHashable
|
||||||
@ -50,8 +51,8 @@ private final class SystemVideoContentNode: ASDisplayNode, UniversalVideoContent
|
|||||||
return self._status.get()
|
return self._status.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
|
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
|
||||||
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
|
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
|
||||||
return self._bufferingStatus.get()
|
return self._bufferingStatus.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import AsyncDisplayKit
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
private final class UniversalVideoContentSubscriber {
|
private final class UniversalVideoContentSubscriber {
|
||||||
let id: Int32
|
let id: Int32
|
||||||
@ -28,11 +29,11 @@ private final class UniversalVideoContentHolder {
|
|||||||
var statusValue: MediaPlayerStatus?
|
var statusValue: MediaPlayerStatus?
|
||||||
|
|
||||||
var bufferingStatusDisposable: Disposable?
|
var bufferingStatusDisposable: Disposable?
|
||||||
var bufferingStatusValue: (IndexSet, Int64)?
|
var bufferingStatusValue: (RangeSet<Int64>, Int64)?
|
||||||
|
|
||||||
var playbackCompletedIndex: Int?
|
var playbackCompletedIndex: Int?
|
||||||
|
|
||||||
init(content: UniversalVideoContent, contentNode: UniversalVideoContentNode & ASDisplayNode, statusUpdated: @escaping (MediaPlayerStatus?) -> Void, bufferingStatusUpdated: @escaping ((IndexSet, Int64)?) -> Void, playbackCompleted: @escaping () -> Void) {
|
init(content: UniversalVideoContent, contentNode: UniversalVideoContentNode & ASDisplayNode, statusUpdated: @escaping (MediaPlayerStatus?) -> Void, bufferingStatusUpdated: @escaping ((RangeSet<Int64>, Int64)?) -> Void, playbackCompleted: @escaping () -> Void) {
|
||||||
self.content = content
|
self.content = content
|
||||||
self.contentNode = contentNode
|
self.contentNode = contentNode
|
||||||
|
|
||||||
@ -131,7 +132,7 @@ private final class UniversalVideoContentHolder {
|
|||||||
private final class UniversalVideoContentHolderCallbacks {
|
private final class UniversalVideoContentHolderCallbacks {
|
||||||
let playbackCompleted = Bag<() -> Void>()
|
let playbackCompleted = Bag<() -> Void>()
|
||||||
let status = Bag<(MediaPlayerStatus?) -> Void>()
|
let status = Bag<(MediaPlayerStatus?) -> Void>()
|
||||||
let bufferingStatus = Bag<((IndexSet, Int64)?) -> Void>()
|
let bufferingStatus = Bag<((RangeSet<Int64>, Int64)?) -> Void>()
|
||||||
|
|
||||||
var isEmpty: Bool {
|
var isEmpty: Bool {
|
||||||
return self.playbackCompleted.isEmpty && self.status.isEmpty && self.bufferingStatus.isEmpty
|
return self.playbackCompleted.isEmpty && self.status.isEmpty && self.bufferingStatus.isEmpty
|
||||||
@ -278,7 +279,7 @@ public final class UniversalVideoManagerImpl: UniversalVideoManager {
|
|||||||
} |> runOn(Queue.mainQueue())
|
} |> runOn(Queue.mainQueue())
|
||||||
}
|
}
|
||||||
|
|
||||||
public func bufferingStatusSignal(content: UniversalVideoContent) -> Signal<(IndexSet, Int64)?, NoError> {
|
public func bufferingStatusSignal(content: UniversalVideoContent) -> Signal<(RangeSet<Int64>, Int64)?, NoError> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
var callbacks: UniversalVideoContentHolderCallbacks
|
var callbacks: UniversalVideoContentHolderCallbacks
|
||||||
if let current = self.holderCallbacks[content.id] {
|
if let current = self.holderCallbacks[content.id] {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import UniversalMediaPlayer
|
|||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import PhotoResources
|
import PhotoResources
|
||||||
|
import RangeSet
|
||||||
|
|
||||||
public final class WebEmbedVideoContent: UniversalVideoContent {
|
public final class WebEmbedVideoContent: UniversalVideoContent {
|
||||||
public let id: AnyHashable
|
public let id: AnyHashable
|
||||||
@ -50,8 +51,8 @@ final class WebEmbedVideoContentNode: ASDisplayNode, UniversalVideoContentNode {
|
|||||||
return self._status.get()
|
return self._status.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private let _bufferingStatus = Promise<(IndexSet, Int64)?>()
|
private let _bufferingStatus = Promise<(RangeSet<Int64>, Int64)?>()
|
||||||
var bufferingStatus: Signal<(IndexSet, Int64)?, NoError> {
|
var bufferingStatus: Signal<(RangeSet<Int64>, Int64)?, NoError> {
|
||||||
return self._bufferingStatus.get()
|
return self._bufferingStatus.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user