require 'dotenv/load' require 'google/apis/youtube_v3' require 'json' require 'open-uri' require 'twitter' Dotenv.load YOUTUBE_API_KEY = ENV["YOUTUBE_API_KEY"] TWITTER_CONSUMER_KEY = ENV["TWITTER_CONSUMER_KEY"] TWITTER_CONSUMER_SECRET = ENV["TWITTER_CONSUMER_SECRET"] TWITTER_ACCESS_TOKEN = ENV["TWITTER_ACCESS_TOKEN"] TWITTER_ACCESS_TOKEN_SECRET = ENV["TWITTER_ACCESS_TOKEN_SECRET"] YOUTUBE_DL_PATH = `which youtube-dl`.chomp FFMPEG_PATH = `which ffmpeg`.chomp FFPROBE_PATH = `which ffprobe`.chomp throw "YOUTUBE_API_KEY is required" unless YOUTUBE_API_KEY throw "TWITTER_CONSUMER_KEY is required" unless TWITTER_CONSUMER_KEY throw "TWITTER_CONSUMER_SECRET is required" unless TWITTER_CONSUMER_SECRET throw "TWITTER_ACCESS_TOKEN is required" unless TWITTER_ACCESS_TOKEN throw "TWITTER_ACCESS_TOKEN_SECRET is required" unless TWITTER_ACCESS_TOKEN_SECRET throw "Can't find youtube-dl" if YOUTUBE_DL_PATH.empty? throw "Can't find ffprobe" if FFPROBE_PATH.empty? throw "Can't find ffmpeg" if FFMPEG_PATH.empty? class YouTubeService @@RESULTS_PER_PAGE = 50.0 @@PLAYLIST_OPTIONS = { fields: 'items/snippet/resourceId/videoId,nextPageToken,page_info', max_results: @@RESULTS_PER_PAGE, } def initialize @youtube = Google::Apis::YoutubeV3::YouTubeService.new @youtube.key = YOUTUBE_API_KEY end def fetch_random_frame_from_random_video video_id = fetch_random_video_from_channel('ComputerChroniclesYT') video_path = download_video(video_id) frame_path = extract_frame(video_path) delete_file(video_path) frame_path end def fetch_random_video_from_channel(channel_name) uploads_playlist_id = fetch_channel_uploads_playlist(channel_name) response = fetch_playlist_videos(uploads_playlist_id) extract_random_video(uploads_playlist_id, response) end def fetch_channel_uploads_playlist(channel_name) channel = @youtube.list_channels('contentDetails', for_username: channel_name) throw "No channel found" unless channel.items.first channel.items.first.content_details.related_playlists.uploads end def fetch_playlist_videos(playlist_id) @youtube.list_playlist_items('snippet', @@PLAYLIST_OPTIONS.merge(playlist_id: playlist_id)) end def extract_random_video(playlist_id, response) number_of_videos = response.page_info.total_results number_of_pages = (number_of_videos / @@RESULTS_PER_PAGE).ceil random_page_number = rand(1..number_of_pages) (1..random_page_number).each do response = @youtube.list_playlist_items( 'snippet', @@PLAYLIST_OPTIONS.merge( page_token: response.next_page_token, playlist_id: playlist_id ) ) end response.items.sample.snippet.resource_id.video_id end def download_video(video_id) output_path = "/tmp/video.mp4" `#{YOUTUBE_DL_PATH} -f mp4 -o /tmp/video.mp4 #{video_id}` output_path end def extract_frame(video_path) output_path = "/tmp/frame.png" length = get_video_length(video_path) random_second = rand(0..length) `#{FFMPEG_PATH} -v error -ss #{random_second} -i #{video_path} -frames 1 -y #{output_path}` output_path end def get_video_length(video_path) `#{FFPROBE_PATH} -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 #{video_path}`.chomp.to_i end def delete_file(path) `rm #{path}` end end class TwitterService def initialize @twitter = Twitter::REST::Client.new do |config| config.consumer_key = TWITTER_CONSUMER_KEY config.consumer_secret = TWITTER_CONSUMER_SECRET config.access_token = TWITTER_ACCESS_TOKEN config.access_token_secret = TWITTER_ACCESS_TOKEN_SECRET end end def tweet_image(filepath) @twitter.update_with_media(String.new, File.new(filepath)) end end youtube = YouTubeService.new filepath = youtube.fetch_random_frame_from_random_video TwitterService.new.tweet_image(filepath) youtube.delete_file(filepath)