<commit_before>//
//  Regift.swift
//  Regift
//
//  Created by <NAME> on 27/12/2014.
//  Copyright (c) 2014 <NAME>. All rights reserved.
//

#if os(iOS)
import UIKit
import MobileCoreServices
#elseif os(OSX)
import AppKit
#endif

import ImageIO
import AVFoundation

public typealias TimePoint = CMTime

/// Errors thrown by Regift
public enum RegiftError: String, ErrorType {
    case DestinationNotFound = "The temp file destination could not be created or found"
    case SourceFormatInvalid = "The source file does not appear to be a valid format"
    case AddFrameToDestination = "An error occurred when adding a frame to the destination"
    case DestinationFinalize = "An error occurred when finalizing the destination"
}

// Convenience struct for managing dispatch groups.
private struct Group {
    let group = dispatch_group_create()
    func enter() { dispatch_group_enter(group) }
    func leave() { dispatch_group_leave(group) }
    func wait() { dispatch_group_wait(group, DISPATCH_TIME_FOREVER) }
}

/// Easily convert a video to a GIF.
///
/// Usage:
///
///      let regift = Regift(sourceFileURL: movieFileURL, frameCount: 24, delayTime: 0.5, loopCount: 7)
///      print(regift.createGif())
///
public struct Regift {
    private struct Constants {
        static let FileName = "regift.gif"
        static let TimeInterval: Int32 = 600
        static let Tolerance = 0.01
    }

    /// A reference to the asset we are converting.
    private var asset: AVAsset

    /// The url for the source file.
    private let sourceFileURL: NSURL

    /// The total length of the movie, in seconds.
    private var movieLength: Float

    /// The number of frames we are going to use to create the gif.
    private let frameCount: Int

    /// The amount of time each frame will remain on screen in the gif.
    private let delayTime: Float

    /// The number of times the gif will loop (0 is infinite).
    private let loopCount: Int

    /// The destination path for the generated file.
    private var destinationFileURL: NSURL?
    
    /// Create a GIF from a movie stored at the given URL.
    ///
    /// :param: frameCount The number of frames to include in the gif; each frame has the same duration and is spaced evenly over the video.
    /// :param: delayTime The amount of time each frame exists for in the GIF.
    /// :param: loopCount The number of times the GIF will repeat. This defaults to 0, which means that the GIF will repeat infinitely.
    public init(sourceFileURL: NSURL, destinationFileURL: NSURL?, frameCount: Int, delayTime: Float, loopCount: Int = 0) {
        self.sourceFileURL = sourceFileURL
        self.asset = AVURLAsset(URL: sourceFileURL, options: nil)
        self.movieLength = Float(asset.duration.value) / Float(asset.duration.timescale)
        self.delayTime = delayTime
        self.loopCount = loopCount
        self.destinationFileURL = destinationFileURL
        self.frameCount = frameCount
    }
    
    public init(sourceFileURL: NSURL, frameCount: Int, delayTime: Float, loopCount: Int = 0) {
        self.init(sourceFileURL:sourceFileURL, destinationFileURL:nil, frameCount:frameCount, delayTime:delayTime, loopCount:loopCount)
    }
    
    /// Get the URL of the GIF created with the attributes provided in the initializer.
    public func createGif() -> NSURL? {

        let fileProperties = [kCGImagePropertyGIFDictionary as String:[
            kCGImagePropertyGIFLoopCount as String: NSNumber(int: Int32(loopCount))],
            kCGImagePropertyGIFHasGlobalColorMap as String: NSValue(nonretainedObject: true)
        ]
        
        let frameProperties = [
            kCGImagePropertyGIFDictionary as String:[
                kCGImagePropertyGIFDelayTime as String:delayTime
            ]
        ]

        // How far along the video track we want to move, in seconds.
        let increment = Float(movieLength) / Float(frameCount)
        
        // Add each of the frames to the buffer
        var timePoints: [TimePoint] = []
        
        for frameNumber in 0 ..< frameCount {
            let seconds: Float64 = Float64(increment) * Float64(frameNumber)
            let time = CMTimeMakeWithSeconds(seconds, Constants.TimeInterval)
            
            timePoints.append(time)
        }
        
        do {
            return try createGIFForTimePoints(timePoints, fileProperties: fileProperties, frameProperties: frameProperties, frameCount: frameCount)
            
        } catch {
            return nil
        }
    }
    
    /// Create a GIF using the given time points in a movie file stored at the URL provided.
    ///
    /// :param: timePoints An array of `TimePoint`s (which are typealiased `CMTime`s) to use as the frames in the GIF.
    /// :param: URL The URL of the video file to convert
    /// :param: fileProperties The desired attributes of the resulting GIF.
    /// :param: frameProperties The desired attributes of each frame in the resulting GIF.
    public func createGIFForTimePoints(timePoints: [TimePoint], fileProperties: [String: AnyObject], frameProperties: [String: AnyObject], frameCount: Int) throws -> NSURL {
        // Ensure the source media is a valid file.
        guard asset.tracksWithMediaCharacteristic(AVMediaCharacteristicVisual).count > 0 else {
            throw RegiftError.SourceFormatInvalid
        }

        var fileURL:NSURL?
        if self.destinationFileURL != nil {
            fileURL = self.destinationFileURL
        } else {<commit_msg>Modify createGif method to support a start time and duration<commit_after>//
//  Regift.swift
//  Regift
//
//  Created by <NAME> on 27/12/2014.
//  Copyright (c) 2014 <NAME>. All rights reserved.
//

#if os(iOS)
import UIKit
import MobileCoreServices
#elseif os(OSX)
import AppKit
#endif

import ImageIO
import AVFoundation

public typealias TimePoint = CMTime

/// Errors thrown by Regift
public enum RegiftError: String, ErrorType {
    case DestinationNotFound = "The temp file destination could not be created or found"
    case SourceFormatInvalid = "The source file does not appear to be a valid format"
    case AddFrameToDestination = "An error occurred when adding a frame to the destination"
    case DestinationFinalize = "An error occurred when finalizing the destination"
}

// Convenience struct for managing dispatch groups.
private struct Group {
    let group = dispatch_group_create()
    func enter() { dispatch_group_enter(group) }
    func leave() { dispatch_group_leave(group) }
    func wait() { dispatch_group_wait(group, DISPATCH_TIME_FOREVER) }
}

/// Easily convert a video to a GIF.
///
/// Usage:
///
///      let regift = Regift(sourceFileURL: movieFileURL, frameCount: 24, delayTime: 0.5, loopCount: 7)
///      print(regift.createGif())
///
public struct Regift {
    private struct Constants {
        static let FileName = "regift.gif"
        static let TimeInterval: Int32 = 600
        static let Tolerance = 0.01
    }

    /// A reference to the asset we are converting.
    private var asset: AVAsset

    /// The url for the source file.
    private let sourceFileURL: NSURL

    /// The point in time in the source which we will start from.
    private var startTime: Float = 0

    /// The desired duration of the gif.
    private var duration: Float

    /// The total length of the movie, in seconds.
    private var movieLength: Float

    /// The number of frames we are going to use to create the gif.
    private let frameCount: Int

    /// The amount of time each frame will remain on screen in the gif.
    private let delayTime: Float

    /// The number of times the gif will loop (0 is infinite).
    private let loopCount: Int

    /// The destination path for the generated file.
    private var destinationFileURL: NSURL?
    
    /// Create a GIF from a movie stored at the given URL.
    ///
    /// :param: frameCount The number of frames to include in the gif; each frame has the same duration and is spaced evenly over the video.
    /// :param: delayTime The amount of time each frame exists for in the GIF.
    /// :param: loopCount The number of times the GIF will repeat. This defaults to 0, which means that the GIF will repeat infinitely.
    public init(sourceFileURL: NSURL, destinationFileURL: NSURL?, frameCount: Int, delayTime: Float, loopCount: Int = 0) {
        self.sourceFileURL = sourceFileURL
        self.asset = AVURLAsset(URL: sourceFileURL, options: nil)
        self.movieLength = Float(asset.duration.value) / Float(asset.duration.timescale)
        self.duration = movieLength
        self.delayTime = delayTime
        self.loopCount = loopCount
        self.destinationFileURL = destinationFileURL
        self.frameCount = frameCount
    }
    
    public init(sourceFileURL: NSURL, frameCount: Int, delayTime: Float, loopCount: Int = 0) {
        self.init(sourceFileURL:sourceFileURL, destinationFileURL:nil, frameCount:frameCount, delayTime:delayTime, loopCount:loopCount)
    }
    
    /// Get the URL of the GIF created with the attributes provided in the initializer.
    public func createGif() -> NSURL? {

        let fileProperties = [kCGImagePropertyGIFDictionary as String:[
            kCGImagePropertyGIFLoopCount as String: NSNumber(int: Int32(loopCount))],
            kCGImagePropertyGIFHasGlobalColorMap as String: NSValue(nonretainedObject: true)
        ]
        
        let frameProperties = [
            kCGImagePropertyGIFDictionary as String:[
                kCGImagePropertyGIFDelayTime as String:delayTime
            ]
        ]

        // How far along the video track we want to move, in seconds.
        let increment = Float(duration) / Float(frameCount)
        
        // Add each of the frames to the buffer
        var timePoints: [TimePoint] = []
        
        for frameNumber in 0 ..< frameCount {
            let seconds: Float64 = Float64(startTime) + (Float64(increment) * Float64(frameNumber))
            let time = CMTimeMakeWithSeconds(seconds, Constants.TimeInterval)
            
            timePoints.append(time)
        }
        
        do {
            return try createGIFForTimePoints(timePoints, fileProperties: fileProperties, frameProperties: frameProperties, frameCount: frameCount)
            
        } catch {
            return nil
        }
    }
    
    /// Create a GIF using the given time points in a movie file stored at the URL provided.
    ///
    /// :param: timePoints An array of `TimePoint`s (which are typealiased `CMTime`s) to use as the frames in the GIF.
    /// :param: URL The URL of the video file to convert
    /// :param: fileProperties The desired attributes of the resulting GIF.
    /// :param: frameProperties The desired attributes of each frame in the resulting GIF.
    public func createGIFForTimePoints(timePoints: [TimePoint], fileProperties: [String: AnyObject], frameProperties: [String: AnyObject], frameCount: Int) throws -> NSURL {
        // Ensure the source media is a valid file.
        guard asset.tracksWithMediaCharacteristic(AVMediaCharacteristicVisual).count > 0 else {
            throw RegiftError.SourceFormatInvalid
        }

        var fileURL:NSURL?
        if self.destinationFileURL != nil {
            fileURL = self.destinationFileURL
        } else {
{'fraction_non_alphanumeric': 0.07768023410481512, 'fraction_numerical': 0.007182761372705506, 'mean_word_length': 2.6784083496412263, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1158417', 'n_tokens_mistral': 3103, 'n_tokens_neox': 2999, 'n_words': 1151}