rubymotion/BubbleWrap

View on GitHub
motion/media/player.rb

Summary

Maintainability
B
4 hrs
Test Coverage
module BubbleWrap
  module Media
    module Error
      class InvalidPlayerType < StandardError; end
      class NilPlayerCallback < StandardError; end
    end

    class Player
      attr_reader :media_player
      ############
      # Playing Media

      # Plays media in the system-default modal controller
      # Takes same parameters as #play
      # NOTE: If you don't supply a :controller option,
      # the rootViewController will be used.
      def play_modal(content_url, options = {})
        play(content_url, options.merge(modal: true))
      end

      # @param [String, NSURL] content_url is either a local or remote string
      #   for the location of the media you're playing.
      #   NOTE: if you're playing a remote file, your server needs to support
      #   range requests for that URL.
      #
      # @param [Hash] options to open the MPMoviePlayerController with
      # the form {
      #   ### These are properties of MPMoviePlayerController
      #   allows_air_play: true/false; default false,
      #   control_style: [MPMovieControlStyle]; default MPMovieControlStyleDefault,
      #   end_playback_time: [Integer] end time (in seconds) for media; default is -1,
      #   initial_playback_time: [Integer] start time (in seconds) for media; default is -1,
      #   movie_source_type: [MPMovieSourceType] a "hint" so the player knows how to load the data type;
      #     either MPMovieSourceTypeFile or MPMovieSourceTypeStreaming; default is MPMovieSourceTypeUnknown
      #     which may delay playback,
      #   repeat_mode: [MPMovieRepeatMode] how the player repeats at the end of playback; defautl is
      #     MPMovieRepeatModeNone
      #   scaling_mode: [MPMovieScalingMode] scaling mode for movies; default is MPMovieScalingModeAspectFit
      #   should_autoplay: true/false; default true,
      #   use_application_audio_session: true/false; default true.
      #
      #   ### These are properties of just the ::Player
      #   delay_play: true/false, default false. If false then you have to manually call
      #     @media_player.play in your code
      #   modal: true/false; default false,
      #   controller: [UIViewController] used to present the player modally;
      #     default uses root view controller of window
      # }
      #
      # @block for when setup is done; use this block to present
      #   @media_player.view if options[:modal] == false
      #
      # EX
      #  From a local URL:
      #    file = File.join(App.resources_path, 'test.mp3')
      #    BW::Media::Player.play(NSURL.fileURLWithPath(file)) do |media_player|
      #      media_player.view.frame = some_view.bounds
      #      self.view.addSubview media_player.view
      #    end
      #
      #  From a remote URL:
      #    BW::Media::Player.play("http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3") do |media_player|
      #      media_player.view.frame = some_view.bounds
      #      self.view.addSubview media_player.view
      #    end
      def play(content_url, options = {}, &block)
        options = {
          delay_play: false
        }.merge(options)

        display_modal = !!options[:modal]

        klass = display_modal ? MPMoviePlayerViewController : MPMoviePlayerController

        content_url = content_url.is_a?(NSURL) ? content_url : NSURL.URLWithString(content_url)
        @media_player = klass.alloc.init

        self.media_player.prepareToPlay if not display_modal

        set_player_options(options)
        self.media_player.setContentURL(content_url)

        NSNotificationCenter.defaultCenter.observe MPMoviePlayerPlaybackDidFinishNotification do |notification|
          h = notification.userInfo
          error = h["error"]
          if error
            p "BW::Media::Player error: #{error.userInfo.inspect}"
            p "Code: #{error.code}, Domain: #{error.domain}"
          end
        end

        if display_modal
          @presenting_controller = options[:controller]
          @presenting_controller ||= App.window.rootViewController
          if Device.ios_version < "7.0"
            @presenting_controller.presentMoviePlayerViewControllerAnimated(@media_player)
          else
            @presenting_controller.presentViewController(@media_player, animated:true, completion:nil)
          end
        else
          if block.nil?
            raise Error::NilPlayerCallback, "no block callback given in #play; you need\
                                              to supply one if options[:modal] == false"
          end
          block.call(@media_player)
        end

        if not display_modal and not options[:delay_play]
          @media_player.play
        end
      end

      # Stops playback for a Media::Player
      def stop
        if @media_player.is_a? MPMoviePlayerViewController
          if Device.ios_version < "7.0"
            @presenting_controller.dismissMoviePlayerViewControllerAnimated
          else
            @presenting_controller.dismissViewControllerAnimated(true, completion:nil)
          end
          @presenting_controller = nil
        else
          @media_player.stop
        end
        @media_player = nil
      end

      def media_player
        if @media_player.is_a? MPMoviePlayerViewController
          return @media_player.moviePlayer
        end
        @media_player
      end

      private
      # RubyMotion has a problem calling some objective-c methods at runtime, so we can't
      # do any cool `camelize` tricks.
      def set_player_options(options)
        self.media_player.allowsAirPlay = options[:allows_air_play] if options.has_key? :allows_air_play
        self.media_player.controlStyle = options[:control_style] if options.has_key? :control_style
        self.media_player.endPlaybackTime = options[:end_playback_time] if options.has_key? :end_playback_time
        self.media_player.initialPlaybackTime = options[:initial_playback_time] if options.has_key? :initial_playback_time
        self.media_player.movieSourceType = options[:movie_source_type] if options.has_key? :movie_source_type
        self.media_player.repeatMode = options[:repeat_mode] if options.has_key? :repeat_mode
        self.media_player.scalingMode = options[:scaling_mode] if options.has_key? :scaling_mode
        self.media_player.shouldAutoplay = options[:should_autoplay] if options.has_key? :should_autoplay
        self.media_player.useApplicationAudioSession = options[:use_application_audio_session] if options.has_key? :use_application_audio_session
      end
    end
  end
end