Fullscreen/yt-core

View on GitHub
lib/yt/video.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Yt
  # Provides methods to interact with YouTube videos.
  # @see https://developers.google.com/youtube/v3/docs/videos
  class Video < Resource
    # @!attribute [r] title
    # @return [String] the video’s title. Has a maximum length of 100 characters
    #   and may contain all valid UTF-8 characters except < and >.
    has_attribute :title, in: :snippet

    # @!attribute [r] description
    # @return [String] the video’s description. Has a maximum length of 5000
    #   bytes and may contain all valid UTF-8 characters except < and >.
    has_attribute :description, in: :snippet

    # @!attribute [r] published_at
    # @return [Time] the date and time that the video was published. Note that
    #   this time might be different than the time that the video was uploaded.
    #   For example, if a video is uploaded as a private video and then made
    #   public at a later time, this property will specify the time that the
    #   video was made public.
    has_attribute :published_at, in: :snippet, type: Time

    # @!attribute [r] thumbnails
    # @return [Hash<String, Hash>] the thumbnails associated with the video.
    has_attribute :thumbnails, in: :snippet

    # @!attribute [r] channel_id
    # @return [String] the ID of the channel that the video was uploaded to.
    has_attribute :channel_id, in: :snippet

    # @!attribute [r] channel_title
    # @return [String] the title of the channel that the video was uploaded to.
    has_attribute :channel_title, in: :snippet

    # @!attribute [r] tags
    # @return [Array<String>] the list of tags associated with the video.
    has_attribute :tags, in: :snippet, default: []

    # @!attribute [r] category_id
    # @return [Integer] the ID of the associated YouTube video category.
    # @see https://developers.google.com/youtube/v3/docs/videoCategories/list
    has_attribute :category_id, in: :snippet, type: Integer

    # @!attribute [r] live_broadcast_content
    # @return [String] whether the video is an upcoming/active live broadcast.
    #   Valid values are: +"live"+, +"none"+, +"upcoming"+.
    has_attribute :live_broadcast_content, in: :snippet

    # has_attribute :default_language, in: :snippet not sure how to set to test
    # has_attribute :localized, in: :snippet not yet implemented
    # has_attribute :default_audio_language, in: :snippet not sure how to test

    # @!attribute [r] upload_status
    # @return [String] the status of the uploaded video. Valid values are:
    #   +"deleted"+, +"failed"+, +"processed"+, +"rejected"+, and +"unlisted"+.
    has_attribute :upload_status, in: :status

    # @!attribute [r] privacy_status
    # @return [String] the video’s privacy status. Valid values are:
    #   +"private"+, +"public"+, and +"unlisted"+.
    has_attribute :privacy_status, in: :status

    # @!attribute [r] license
    # @return [String] the video’s license. Valid values are:
    #   +"creative_common"+ and +"youtube"+.
    has_attribute :license, in: :status

    # @!attribute [r] embeddable
    # @return [Boolean] whether the video can be embedded on another website.
    has_attribute :embeddable, in: :status

    # @!attribute [r] public_stats_viewable
    # @return [Boolean] whether the extended video statistics on the video’s
    #   watch page are publicly viewable.
    has_attribute :public_stats_viewable, in: :status

    # has_attribute :failure_reason, in: :status not yet implemented
    # has_attribute :rejection_reason, in: :status not yet implemented
    # has_attribute :publish_at, in: :status not yet implemented

    # @!attribute [r] view_count
    # @return [<Integer>] the number of times the video has been viewed.
    has_attribute :view_count, in: :statistics, type: Integer

    # @!attribute [r] like_count
    # @return [<Integer>] the number of users who have liked the video.
    has_attribute :like_count, in: :statistics, type: Integer

    # @!attribute [r] dislike_count
    # @return [<Integer>] the number of users who have disliked the video.
    has_attribute :dislike_count, in: :statistics, type: Integer

    # @!attribute [r] comment_count
    # @return [<Integer>] the number of comments for the video.
    has_attribute :comment_count, in: :statistics, type: Integer

    # @!attribute [r] duration
    # @return [<String>] the length of the video as an ISO 8601 duration.
    has_attribute :duration, in: :content_details

    # @!attribute [r] dimension
    # @return [String] whether the video is available in 3D or in 2D.
    #   Valid values are: +"2d"+ and +"3d".
    has_attribute :dimension, in: :content_details

    # @!attribute [r] definition
    # @return [String] whether the video is available in high definition or only
    #   in standard definition. Valid values are: +"sd"+ and +"hd".
    has_attribute :definition, in: :content_details

    # @!attribute [r] caption
    # @return [Boolean] whether captions are available for the video.
    has_attribute :caption, in: :content_details do |captioned|
      captioned == 'true'
    end

    # @!attribute [r] licensed_content
    # @return [Boolean] whether the video represents licensed content, which
    #   means that the content was uploaded to a channel linked to a YouTube
    #   content partner and then claimed by that partner.
    has_attribute :licensed_content, in: :content_details

    # @!attribute [r] projection
    # @return [String] the projection format of the video. Valid values are:
    #   +"360"+ and +"rectangular".
    has_attribute :projection, in: :content_details

    # has_attribute :has_custom_thumbnail, in: :content_details to do
    # has_attribute :content_rating, in: :content_details to do

    # Returns the URL of the video’s thumbnail.
    # @param [Symbol, String] size The size of the video’s thumbnail.
    # @return [String] if +size+ is +:default+, the URL of a 120x90px image.
    # @return [String] if +size+ is +:medium+, the URL of a 320x180px image.
    # @return [String] if +size+ is +:high+, the URL of a 480x360px image.
    # @return [String] if +size+ is +:standard+, the URL of a 640x480px image.
    # @return [String] if +size+ is +:maxres+, the URL of a 1280x720px image.
    # @return [nil] if the +size+ is none of the above.
    def thumbnail_url(size = :default)
      thumbnails.fetch(size.to_s, {})['url']
    end

    # @return [String] the canonical form of the video’s URL.
    def canonical_url
      "https://www.youtube.com/watch?v=#{id}"
    end

    # @return [Hash<Integer, String>] the list of YouTube video categories.
    CATEGORIES = {
       1 => 'Film & Animation', 2 => 'Autos & Vehicles', 10 => 'Music',
      15 => 'Pets & Animals', 17 => 'Sports', 18 => 'Short Movies',
      19 => 'Travel & Events', 20 => 'Gaming', 21 => 'Videoblogging',
      22 => 'People & Blogs', 23 => 'Comedy', 24 => 'Entertainment',
      25 => 'News & Politics', 26 => 'Howto & Style', 27 => 'Education',
      28 => 'Science & Technology', 29 => 'Nonprofits & Activism',
      30 => 'Movies', 31 => 'Anime/Animation', 32 => 'Action/Adventure',
      33 => 'Classics', 34 => 'Comedy', 35 => 'Documentary', 36 => 'Drama',
      37 => 'Family', 38 => 'Foreign', 39 => 'Horror', 40 => 'Sci-Fi/Fantasy',
      41 => 'Thriller', 42 => 'Shorts', 43 => 'Shows', 44 => 'Trailers',
    }

    # @return [String] the title of the associated YouTube video category.
    def category_title
      CATEGORIES[category_id]
    end

    # @return [<Integer>] the length of the video in seconds.
    def seconds
      to_seconds duration
    end

    # @return [<String>] the length of the video as an ISO 8601 time, HH:MM:SS.
    def length
      hh, mm, ss = seconds / 3600, seconds / 60 % 60, seconds % 60
      [hh, mm, ss].map{|t| t.to_s.rjust(2,'0')}.join(':')
    end

    # @return [Yt::Channel] the channel the video belongs to.
    def channel
      @channel ||= Channel.new id: channel_id
    end

    # @return [Yt::Relation<Yt::CommentThread>] the threads of the video.
    def threads
      @threads ||= Relation.new(CommentThread, video_id: id) do |options|
        get '/youtube/v3/commentThreads', video_threads_params(options)
      end
    end

  private

    # @return [Integer] the duration of the resource as reported by YouTube.
    # @see https://developers.google.com/youtube/v3/docs/videos
    #
    # According to YouTube documentation, the value is an ISO 8601 duration
    # in the format PT#M#S, in which the letters PT indicate that the value
    # specifies a period of time, and the letters M and S refer to length in
    # minutes and seconds, respectively. The # characters preceding the M and
    # S letters are both integers that specify the number of minutes (or
    # seconds) of the video. For example, a value of PT15M51S indicates that
    # the video is 15 minutes and 51 seconds long.
    #
    # The ISO 8601 duration standard, though, is not +always+ respected by
    # the results returned by YouTube API. For instance: video 2XwmldWC_Ls
    # reports a duration of 'P1W2DT6H21M32S', which is to be interpreted as
    # 1 week, 2 days, 6 hours, 21 minutes, 32 seconds. Mixing weeks with
    # other time units is not strictly part of ISO 8601; in this context,
    # weeks will be interpreted as "the duration of 7 days". Similarly, a
    # day will be interpreted as "the duration of 24 hours".
    # Video tPEE9ZwTmy0 reports a duration of 'PT2S'. Skipping time units
    # such as minutes is not part of the standard either; in this context,
    # it will be interpreted as "0 minutes and 2 seconds".
    def to_seconds(iso8601_duration)
      match = iso8601_duration.match %r{^P(?:|(?<weeks>\d*?)W)(?:|(?<days>\d*?)D)(?:|T(?:|(?<hours>\d*?)H)(?:|(?<min>\d*?)M)(?:|(?<sec>\d*?)S))$}
      weeks = (match[:weeks] || '0').to_i
      days = (match[:days] || '0').to_i
      hours = (match[:hours] || '0').to_i
      minutes = (match[:min] || '0').to_i
      seconds = (match[:sec]).to_i
      (((((weeks * 7) + days) * 24 + hours) * 60) + minutes) * 60 + seconds
    end
  end
end