Last active
November 2, 2018 15:02
-
-
Save obliviusm/b9c76980619b9fd182838190b90d54b5 to your computer and use it in GitHub Desktop.
Rails API sample code to get playlist and watch videos
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'proxy_list_api_client' | |
class ExtractYoutubeVideo | |
def initialize(video_id) | |
@video_id = video_id | |
end | |
def perform | |
extract_video_stream_uri.presence || extract_video_stream_uri(with_proxy: true) | |
end | |
private | |
def extract_video_stream_uri(with_proxy: false) | |
proxy_flag = with_proxy ? "--proxy #{fetch_proxy}" : '' | |
`./bin/youtube-dl -f 22/18/17 #{proxy_flag} -g -- #{@video_id}`.chomp | |
end | |
def fetch_proxy | |
ProxyListApiClient.new.fetch_ua_proxy | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
json.array! @videos do |video| | |
json.id video.id | |
json.name video.name | |
json.previewImage video.preview_image_url | |
json.duration video.duration | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'youtube_api_client' | |
class Playlist | |
DEFAULT_ID = ENV.fetch('YOUTUBE_DEFAULT_PLAYLIST_ID') { 'PLAPIZDiVa7x7XgtRMSCcon-EEJsPCtjAw' } | |
include ActiveModel::Model | |
attr_accessor :id | |
def find_video(video_id) | |
result = videos.find { |video| video.id == video_id } | |
raise ActiveRecord::RecordNotFound, 'video has not been found' unless result | |
result | |
end | |
def videos | |
raise AssociationNotFoundError, 'Playlist id is missed' unless id | |
@_videos ||= begin | |
videos_attrs = api_client.fetch_playlist(id) | |
videos_attrs.map do |video_attrs| | |
build_video(video_attrs) | |
end | |
end | |
end | |
def build_video(video_attrs) | |
Video.new({ playlist: self }.merge!(video_attrs)) | |
end | |
def self.default | |
new(id: DEFAULT_ID) | |
end | |
private | |
def api_client | |
YoutubeApiClient.new | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ProxyListApiClient | |
include HTTParty | |
base_uri 'https://api.getproxylist.com' | |
def fetch_ua_proxy | |
proxy_details = self.class.get( | |
'/proxy', | |
query: { | |
country: ['UA'], | |
protocol: ['http'], | |
minUptime: 75, | |
maxSecondsToFirstByte: 1, | |
maxConnectTime: 1 | |
} | |
) | |
"#{proxy_details['ip']}:#{proxy_details['port']}" | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
json.id @video.id | |
json.name @video.name | |
json.uri @video.uri | |
json.nextVideoId @video.next_video_id |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
class Video | |
include ActiveModel::Model | |
attr_accessor :id, :name, :preview_image_url, :playlist, :uri, :next_video_id | |
# Lazy loading of uri. | |
# Uri of the stream is extracted on real page. | |
# Uri stream is dynamic and often changes that's why we need to use tools to extract it. | |
def uri | |
Rails.cache.fetch("#{id}/uri") do | |
@uri.is_a?(Proc) ? @uri.call : @uri | |
end | |
end | |
# Faked | |
def duration | |
'3:05' | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Api::V1 | |
class VideosController < BaseController | |
def index | |
@videos = Playlist.default.videos | |
end | |
def show | |
@video = Playlist.default.find_video(params[:id]) | |
render json: { error: 'Not Found' }, status: :not_found unless @video | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'extract_youtube_video' | |
class YoutubeApiClient | |
include HTTParty | |
base_uri 'https://www.googleapis.com/youtube/v3' | |
def initialize | |
@default_options = { | |
key: Rails.application.credentials.youtube_api_key, | |
maxResults: 50 | |
} | |
end | |
# @return [ | |
# { | |
# id: | |
# next_video_id: | |
# name: | |
# preview_image_url: | |
# uri: lazy uri extraction | |
# }, | |
# ... | |
# ] | |
def fetch_playlist(playlist_id) | |
raw_playlist_details = self.class.get( | |
'/playlistItems', | |
query: { part: 'snippet', playlistId: playlist_id }.merge(@default_options) | |
) | |
extract_playlist_details(raw_playlist_details) | |
end | |
# TODO: Implement load full details for video | |
# @return { | |
# video_id: | |
# name: | |
# preview_image_url: | |
# uri: -> {} lazy loading or video stream uri | |
# } | |
# def fetch_full_video_details | |
# @return String URI to Video Stream on YouTube | |
def extract_video_uri(video_id) | |
ExtractYoutubeVideo.new(video_id).perform | |
end | |
private | |
def extract_playlist_details(raw_playlist_details) | |
result = raw_playlist_details['items'].map do |raw_video_item| | |
extract_video_details(raw_video_item) | |
end | |
fill_next_video_id(result) | |
result | |
end | |
def extract_video_details(item) | |
snippet = item['snippet'] | |
video_id = snippet['resourceId']['videoId'] | |
{ | |
id: video_id, | |
name: snippet['title'], | |
preview_image_url: snippet['thumbnails']['medium']['url'], | |
uri: -> { extract_video_uri(video_id) } | |
} | |
end | |
def fill_next_video_id(video_attrs) | |
video_attrs[0...-1].each_with_index { |video, i| video[:next_video_id] = video_attrs[i + 1][:id] } | |
video_attrs.last[:next_video_id] = video_attrs.first[:id] | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment