Video Creator – #Parte 10: Upload do video para o Youtube

Bom dia, boa tarde, boa noite!

Essa é a décima e última parte da série sobre o projeto Video Creator, onde mostrarei como fiz o upload automático do video no o Youtube.

Se você entrou direto nesse post, te aconselho a começar por aqui, para entender exatamente do que se trata essa série.

Sobre essa parte

Para fazer o upload do video no Youtube, usei apenas algumas configurações básicas, mas vale muito a pena dar uma olhada na documentação da API, porque tem muita opção para configurar. Para esse projeto, segui o mais básico.

Requisitos:

Para essa parte, precisaremos habilitar a API do Youtube na sua conta Google Cloud, baixar o arquivo com a secret key que será gerada, fazer alguns imports e está quase tudo pronto. Isso porque 90% do código já está pronto no próprio site da API, e é ele que vamos usar como base:

https://developers.google.com/youtube/v3/guides/uploading_a_video#Sample_Code

Habilitar API do Youtube:

Visite o link abaixo e entre na sua conta, ou crie uma, é gratuíto é tão simples quando criar qualquer outra conta de serviço web.

https://cloud.google.com/

Após logado, você precisará criar um novo projeto, ao lado de Google Cloud Platform, clique no menu dropdown e a modal abaixo aparecerá, clique em Novo Projeto, preencha com o nome desejado e clique em criar. Se o projeto não ficar selecionado automaticamente, clique novamente no menu dropdown e selecione o projeto recém criado.

layout fev/2022

Agora clique nas barrinhas horizontais no canto superior esquerdo da tela, depois vá em APIs e serviços, e por fim selecione Biblioteca.

layout fev/2022

Na tela que abrir, selecione Youtube Data Api v(x):

Na tela que abrir, clique no botão ativar, e sua tela ficará assim:

layout fev/2022

Para visitar a documentação da API, clique em TESTAR ESTA API.

Clique em Gerenciar, e na tela que abrir, selecione a aba Credenciais e depois em Criar credenciais.

layout fev/2022

Selecione ID do cliente OAuth.

Em tipo de aplicativo, selecione Aplicativo da WEB:

layout fev/2022

Digite um nome, e depois em criar, aparecerá um modal com seu ID de cliente e Chave secreta de cliente. Você pode salvar esses dados em um arquivo de config, ou fazer como eu fiz, que é na listagem de credenciais, clicar no botão de download, e baixar o arquivo com esses dados.

Hora de codificar:

Com a API ativada e o arquivo com a secret key já salvo na pasta credentials, é hora de pegar o sample code e fazer algumas personalizações.

O robô inicia com a função start(), que carrega o conteúdo do arquivo content.json para dentro do objeto video_content, cria o objeto youtube, e nesse momento, o navegador abrirá perguntando para qual conta do Youtube você deseja fazer o upload. Após isso, ele chama a função create_thumbnail(), que cria as thumbnails e inicia o processo de Upload.

A função start() ficou assim:


def start():
    logging.info('--- Starting Youtube robot ---')
    video_content = rcontent.load()
    youtube = get_authenticated_service()
    try:
        create_thumbnail(video_content)
        initialize_upload(youtube, video_content)
        result = "Upload concluído!"
    except HttpError as e:
            result = "An HTTP error %d occurred:\n%s" % (e.resp.status, e.content)
    except Exception as ex:
        result = ex
    print(result)

A função create_thumbnail() ficou assim:


def  create_thumbnail(video_content):
    logging.info("Creating Thumbnail...")

    # apagando as thumbs existentes
    os.system("rm -rf {}/thumb*".format(CONTENT_IMAGES_PATH))

    # criando os comandos para execução.
    # Importante: Refatorar para que a montagem do comando esteja dentro de um for

    command_thumb_default = "convert -size {}x{} -font helvetica -pointsize 24 -background 'black' -fill white -gravity center caption:'{}' ./content/images/thumb_default.png"\
        .format(YOUTUBE_VIDEO_THUMBNAILS['default']['width'],YOUTUBE_VIDEO_THUMBNAILS['default']['height'], video_content['youtube_details']['title'])
        
    command_thumb_medium = "convert -size {}x{} -font helvetica -pointsize 26 -background 'black' -fill white -gravity center caption:'{}' ./content/images/thumb_medium.png"\
        .format(YOUTUBE_VIDEO_THUMBNAILS['medium']['width'],YOUTUBE_VIDEO_THUMBNAILS['medium']['height'], video_content['youtube_details']['title'])

    command_thumb_high = "convert -size {}x{} -font helvetica -pointsize 30 -background 'black' -fill white -gravity center caption:'{}' ./content/images/thumb_high.png"\
        .format(YOUTUBE_VIDEO_THUMBNAILS['high']['width'],YOUTUBE_VIDEO_THUMBNAILS['high']['height'], video_content['youtube_details']['title'])
    
    command_thumb_standard = "convert -size {}x{} -font helvetica -pointsize 50 -background 'black' -fill white -gravity center caption:'{}' ./content/images/thumb_standard.png"\
        .format(YOUTUBE_VIDEO_THUMBNAILS['standard']['width'],YOUTUBE_VIDEO_THUMBNAILS['high']['height'], video_content['youtube_details']['title'])

    command_thumb_maxres = "convert -size {}x{} -font helvetica -pointsize 70 -background 'black' -fill white -gravity center caption:'{}' ./content/images/thumb_maxres.png"\
        .format(YOUTUBE_VIDEO_THUMBNAILS['maxres']['width'],YOUTUBE_VIDEO_THUMBNAILS['high']['height'], video_content['youtube_details']['title'])

    os.system(command_thumb_default)
    os.system(command_thumb_medium)
    os.system(command_thumb_high)
    os.system(command_thumb_standard)
    os.system(command_thumb_maxres)

Uma informação importante, é que nessa função eu faço todas as possíveis thumbnails que o Youtube permite, porém não consegui fazer funcionar o upload das thumbnails junto com o video. Precisei utilizar a função set do objeto thumbnails após o upload de video.

Abaixo, as 2 funções que fazem o processo de upload do video:


def initialize_upload(youtube, video_content):
    list_of_used_images = '\n'.join('✅ ' + img for img in video_content['images_used'])
    list_of_sentences = '\n'.join([ s['text'] for s in video_content['sentences']])
    YOUTUBE_VIDEO_DESCRIPTION = """
Conheça um pouco mais sobre {}:

{}

👉 Referências:

💥 Wikipedia
💥 Custom Search API - Bing.

👉 Imagens utilizadas no vídeo:

{}
""".format(video_content['search_term'], list_of_sentences, list_of_used_images)

    YOUTUBE_VIDEO_FILE = CONTENT_PATH + '/{}'.format(video_content['video_filename'])
        
    body= {
        "snippet": {
            "title": video_content['youtube_details']['title'],
            "description": YOUTUBE_VIDEO_DESCRIPTION,
            "tags": get_tags(video_content),
            "categoryId": video_content['youtube_details']['category_id']
        },
        "status": {
            "privacyStatus": YOUTUBE_VIDEO_PRIVACY_STATUS
        }
    }
    # Call the API's videos.insert method to create and upload the video.
    insert_request = youtube.videos().insert(
    part=",".join(body.keys()),
    body=body,
    # The chunksize parameter specifies the size of each chunk of data, in
    # bytes, that will be uploaded at a time. Set a higher value for
    # reliable connections as fewer chunks lead to faster uploads. Set a lower
    # value for better recovery on less reliable connections.
    #
    # Setting "chunksize" equal to -1 in the code below means that the entire
    # file will be uploaded in a single HTTP request. (If the upload fails,
    # it will still be retried where it left off.) This is usually a best
    # practice, but if you're using Python older than 2.6 or if you're
    # running on App Engine, you should set the chunksize to something like
    # 1024 * 1024 (1 megabyte).
    media_body=MediaFileUpload(YOUTUBE_VIDEO_FILE, chunksize=-1, resumable=True)
    )
    insert_response = resumable_upload(insert_request)
    # set thumbnail
    set_thumbnail(youtube, insert_response['id'])

    
# This method implements an exponential backoff strategy to resume a
# failed upload.
def resumable_upload(insert_request):
    response = None
    error = None
    retry = 0
    while response is None:
        try:
            print ("Uploading file...")
            status, response = insert_request.next_chunk()
            if response is not None:
                if 'id' in response:
                    print ("Video id '%s' was successfully uploaded." % response['id'])
                else:
                    exit("The upload failed with an unexpected response: %s" % response)
        except HttpError as e:
            if e.resp.status in RETRIABLE_STATUS_CODES:
                error = "A retriable HTTP error %d occurred:\n%s" % (e.resp.status,e.content)
            else:
                raise
        except RETRIABLE_EXCEPTIONS as e:
            error = "A retriable error occurred: %s" % e

        if error is not None:
            print (error)
            retry += 1
            if retry > MAX_RETRIES:
                exit("No longer attempting to retry.")
            max_sleep = 2 ** retry
            sleep_seconds = random.random() * max_sleep
            print ("Sleeping %f seconds and then retrying..." % sleep_seconds)
            time.sleep(sleep_seconds)

    return response

Dentro da primeiro função initialize_upload(), comecei montando os dados que eu precisaria para fazer o upload do video, utilizei o assunto pesquisado, as sentenças e a lista de imagens usadas para compor a descrição do video e dar os devidos créditos para os sites detentores das imagens usadas.

Na hora de montar as tags, utilizei as keywords das sentenças para. Para isso, criei uma função para buscar as keywords e devolvê-las numa lista.

Ficou assim:


def get_tags(video_content):
    keywords = []

    for sentence in video_content['sentences']:
        for k in sentence['keywords']:
            if k not in keywords:
                keywords.append(k)
    
    return keywords

E após o upload, faço o set da thumbnail:


def set_thumbnail(youtube, video_id):
    print('Setting the thumbnail: {} for video: {}'.format(CONTENT_IMAGES_PATH + "/thumb_maxres.png", video_id))
    youtube.thumbnails().set(
        videoId=video_id,
        media_body=CONTENT_IMAGES_PATH + "/thumb_maxres.png"
    ).execute()

É isso!

Conclusão!

E assim encerro essa série de posts sobre o projeto Video-Creator. Muito feliz com o resultado final.

Sei que tem muitas coisas pra refatorar, pra evoluir, muitas oportunidades de novas implementações, mas o objetivo principal foi atingido: Receber um assunto e gerar um video para o Youtube automaticamente.

Espero que tenham gostado da série, qualquer dúvida, sugestão ou crítica, por favor entrem em contato. Se tiver algum assunto relacionado a essa série de posts que vocês queiram que eu me aprofunde um pouco mais na explicação, mandem um comentário pedindo, prometo trazer assim que possível.

Um grande abraço!

Video Creator – #Parte 9: Renderização de video com Python

Bom dia, boa tarde, boa noite!

Essa é a nona parte da série sobre o projeto Video Creator, onde mostrarei como fiz a renderização do video.

Se você entrou direto nesse post, te aconselho a começar por aqui, para entender exatamente do que se trata essa série.

Sobre essa parte

Para renderização do video, utilizei uma lib fantástica chamada MoviePy.

A lib possui diversas funcionalidades legais, e pra quem quiser saber mais, recomendo dar uma olhada no link abaixo:

https://pypi.org/project/moviepy/

Requisitos:

Para a renderização do video, precisaremos importar as libs do MoviePy.

from moviepy.editor import *
from moviepy.video.tools.segmenting import findObjects

MoviePy:

O MoviePy utiliza algumas features do ImageMagick, e precisamos fazer alguns ajustes nas configurações.

No linux, o MoviePy detecta automaticamente o Image Magick. No windows é preciso criar uma variável de ambiente chamada IMAGEMAGICK_BINARY contendo o caminho do binário. Como utilizei Ubuntu, não tive nenhum problema quanto a isso.

Para que ele funcione corretamente, é preciso configurar uma política de segurança, do contrário, dará erro na hora de compilar o video.

Como isso é somente uma POC, apenas comentei no arquivo /etc/ImageMagick-x/policy.xml algumas linhas:

<!--<policy domain="path" rights="none" pattern="@*"/>-->
<!--<policy domain="resource" name="width" value="16KP"/>-->
<!--<policy domain="resource" name="height" value="16KP"/>-->

Setei algumas variáveis no arquivo rconfig.py:


# FOR VIDEO
VIDEO_INTRODUCTION_TEXT = "Variavel Constante\napresenta..."
VIDEO_SCREENSIZE = (1920,1080)
VIDEO_TEXT_COLOR_DEFAULT = 'white'
VIDEO_TEXT_FONT_DEFAULT = 'helvetica'
VIDEO_FONT_SIZE_DEFAULT = 50
VIDEO_TEXT_POSITION_DEFAULT = 'center'
VIDEO_FRAME_DURATION_DEFAULT = 4
VIDEO_TEXT_KERNING_DEFAULT = 5
VIDEO_CODEC_DEFAULT = 'mpeg4'
VIDEO_FPS_DEFAULT = 30

A parte de compilação do video, encapsulei dentro de uma função, que chamei de compile_video(). Ela ficou assim:


def compile_video():
    logging.info("Compiling video...")

    
    #os.chdir('./')
    path = CONTENT_IMAGES_PATH
    
    # create directory
    video_content = rcontent.load()

    video_filename = util.remove_accents(video_content['search_term']).replace(" ", "_") + ".mp4"
    video_content['video_filename'] = video_filename
    video_content['youtube_details']['title'] = "Um pouco sobre {}".format(video_content['search_term'])

    rcontent.save(video_content)
    # carregando musica
    music = AudioFileClip('{}/gypsy-jaxx.mp3'.format(CONTENT_SOUND_PATH))
    new_audioclip = CompositeAudioClip([music])
    
    screensize = VIDEO_SCREENSIZE
    txt_size = (.8*screensize[0],0)
    color = VIDEO_TEXT_COLOR_DEFAULT
    font = VIDEO_TEXT_FONT_DEFAULT
    font_size = VIDEO_FONT_SIZE_DEFAULT
    list_video_clips = []
    # Criando introdução
    txt_clip1 = TextClip(VIDEO_INTRODUCTION_TEXT,color=color, font=font, kerning = VIDEO_TEXT_KERNING_DEFAULT, fontsize=100, method='caption', size=txt_size).set_pos(VIDEO_TEXT_POSITION_DEFAULT).set_duration(VIDEO_FRAME_DURATION_DEFAULT)
    txt_clip2 = TextClip('um pouco sobre...',color=color, font=font, kerning = VIDEO_TEXT_KERNING_DEFAULT, fontsize=100, method='caption', size=txt_size).set_pos(VIDEO_TEXT_POSITION_DEFAULT).set_duration(VIDEO_FRAME_DURATION_DEFAULT)
    txt_clip3 = TextClip(video_content['search_term'],color=color, font=font, kerning = VIDEO_TEXT_KERNING_DEFAULT, fontsize=100, method='caption', size=txt_size).set_pos(VIDEO_TEXT_POSITION_DEFAULT).set_duration(VIDEO_FRAME_DURATION_DEFAULT)
    img_clip = ImageClip("{}../../default/bg_default_new.jpeg".format(path)).set_duration(VIDEO_FRAME_DURATION_DEFAULT)
    cvc1 = CompositeVideoClip([img_clip,txt_clip1], size=screensize)
    cvc2 = CompositeVideoClip([img_clip,txt_clip2], size=screensize)
    cvc3 = CompositeVideoClip([img_clip,txt_clip3], size=screensize)
    list_video_clips.append(cvc1)
    list_video_clips.append(cvc2)
    list_video_clips.append(cvc3)

    #for root, folders, files in os.walk('./content/images'):
    for idx, sentence in enumerate(video_content['sentences']):

        f  = "{}/{}_composite.jpg".format(path, idx)
        filename = f if os.path.exists(f) else CONTENT_DEFAULT_PATH+"/missing_image.jpg"
        
        # workaround to resolve the problems with grayscale image
        '''
        img = Image.open(filename)
        rgbimg = Image.new(formatter.get(img.format, 'RGB'), img.size)
        rgbimg.paste(img)
        rgbimg.save(filename, format=img.format)
        '''

        duration = int(len(sentence['text'])/15)

        txt_clip = TextClip('{}'.format(sentence['text']),color=color, font=font, kerning = 5, fontsize=font_size, method='caption', size=txt_size).set_pos('center').set_duration(duration)
        im_width, im_height = txt_clip.size
        color_clip = ColorClip(size=(1920, int(im_height*3)), color=(0, 0, 0)).set_pos('center')
        color_clip = color_clip.set_opacity(.6)
        
        clip_to_overlay = CompositeVideoClip([color_clip, txt_clip], size=screensize).set_position('center')
        #cvc = CompositeVideoClip([txt_clip,img_clip], size=screensize)
        if os.path.isfile(filename):
            img_clip = ImageClip(filename).set_duration(duration)
            cvc = CompositeVideoClip([img_clip,clip_to_overlay], size=screensize).set_duration(duration)
        else:
            cvc = CompositeVideoClip([clip_to_overlay], size=screensize).set_duration(duration)

        list_video_clips.append(cvc)
    
    # Criando o fechamento do video
    txt_fechamento = TextClip('Obrigado por assistir!!!\nd(~.~)b',color=color, font=font, kerning = 5, fontsize=100, method='caption', size=screensize).set_pos('center').set_duration(4)
    list_video_clips.append(txt_fechamento)

    #final_clip = concatenate_videoclips(clips)
    #final_clip.write_videofile('./content/coolTextEffects.avi',fps=25,codec='mpeg4')
    final_clip = concatenate_videoclips(list_video_clips)

    audio = afx.audio_loop(music, duration=final_clip.duration)
    final = final_clip.set_audio(audio).audio_fadeout(5)
    final.write_videofile('{}/{}'.format(CONTENT_PATH, video_filename),fps=VIDEO_FPS_DEFAULT,codec=VIDEO_CODEC_DEFAULT)

No início da função já criei alguns dados que serão úteis mais pra frente, e salvei no arquivo content.json.

Depois criei um objeto contendo a música que utilizei de fundo. Para esse projeto, deixei uma música fixa de fundo, a mesma para todos os videos.

Depois setei algumas variáveis contendo as configurações do video, como resolução, tamanho, cor e fonte do texto, tamanho da tarja preto onde posicionei o texto.

Todas as configurações relacionadas ao texto foram para chegar nesse resultado:

Após o ajuste da configuração do video, criei os 3 primeiros slides do video com textos padrões de apresentação e uma imagem fixa.

Após, comecei a iterar nas sentenças para incluir o texto, e buscando a imagem correspondente aquela sentença para incluir de background.

Para conseguir chegar numa duração adequada para cada slide, cronometrei quanto tempo eu levava para ler tranquilamente um texto com 150 caracteres, +- 10 segundos. Então para setar a duração do slide contei o número de caracteres de cada sentença e dividi por 15:

duration = int(len(sentence['text'])/15)

Com isso, cada slide ficou com uma duração adequada de acordo com o tamanho da sentença que será exibida.

Por fim, inclui um texto padrão de encerramento do video, inclui a música padrão, e mandei compilar o video. O trecho que compila o video é esse abaixo, e encontra-se no finalzinho da function:

final.write_videofile('{}/{}'.format(CONTENT_PATH, video_filename),fps=VIDEO_FPS_DEFAULT,codec=VIDEO_CODEC_DEFAULT)

Eis o resultado do video sobre Bitcoin:

https://variavelconstante.com.br/wp-content/uploads/2022/02/Um-pouco-sobre-Bitcoin.mp4

Muito legal né 😀

Agora é só fazer o upload para o Youtube. No próximo post eu mostro como eu fiz!

Abraço!

Video Creator – #Parte 8: Tratamento das imagens

Bom dia, boa tarde, boa noite!

Essa é a oitava parte da série sobre o projeto Video Creator, onde mostrarei como fiz o tratamento das imagens que serão utilizadas nos videos.

Se você entrou direto nesse post, te aconselho a começar por aqui, para entender exatamente do que se trata essa série.

Sobre essa parte

Aqui começamos a desenvolver o penúltimo robô, o rvideo.py, que será responsável por tratar as imagens e compilar o video.

Nesse post mostrarei o tratamento das imagens com uma ferramenta fantástica chamada ImageMagick.

Existe uma outra ferramenta fantástica que cheguei a testar, mas na hora acabei mantendo o ImageMagick por já estar um pouco mais familiarizado. Mas para quem quiser conhecer, segue o link da lib:

https://pypi.org/project/Pillow/

Requisitos:

Para realizar o tratamento das imagens, precisei do ImageMagick instalado, e de algumas outras libs:

import sys
sys.path.insert(0, './')
from PIL import Image
import os
import rcontent

A instrução sys.path.insert(0, './') server para que eu consiga importar para o código, arquivos de outras pastas.

ImageMagick

Para começar, precisamos instalar o ImageMagick na máquina.

No site do programa você encontra toda a documentação necessária para você fazer o que quiser, além de exemplos e os links para download do programa.

No ubuntu precisei apenas digitar:

sudo apt-get install imagemagick

Tratamento das imagens:

Para o tratamento das imagens, encapsulei todo o código dentro da função prepare_images_downloaded(). Ficou assim:


def prepare_images_downloaded():
    logging.info("Preparing images downloaded to compile the video")
    
    #os.chdir('./')
    #path = "./content/images"
    path = CONTENT_IMAGES_PATH
    # removing existing composite images
    os.system("rm -rf {}/*_composite*".format(path))
    # create directory
    video_content = rcontent.load()
        
    for root, folders, files in os.walk('./content/images'):

        for f in files:
            f_split = f.split("_")
            idx_sentence = int(f_split[0])
            filename_original = os.path.join(root, f)
            filename_composite = "{}/{}_composite.jpg".format(path, f_split[0])
            filename_resized = "{}/{}_resized.jpg".format(path, f_split[0])
            sentence = video_content['sentences'][idx_sentence]['text']
            # creating comands
            create_image_resized = "convert {} -resize '1280x720' {}".format(filename_original, filename_resized)
            create_image_composite = "convert {} -background 'white' -blur '0x9' -resize '1920x1080^' -extent 1920x1080 \( {} -bordercolor white -border 10x10 \) -compose over -gravity center -composite {}".format(filename_original, filename_resized, filename_composite)
            
            # creating images
            os.system(create_image_resized)
            os.system(create_image_composite)

            # removing resized images
            os.system("rm -rf {}/*_resized*".format(path))

            # workaround to resolve the problems with grayscale image
            img = Image.open(filename_composite)
            rgbimg = Image.new(IMAGE_FORMATTER.get(img.format, 'RGB'), img.size)
            rgbimg.paste(img)
            rgbimg.save(filename_composite, format=img.format)

A função é bem simples, mas ela faz muita coisa legal.

Primeira ela elimina da pasta de imagens todas as imagens que contenham _composite no nome.

Depois carrega o conteúdo do arquivo content.json para dentro do objeto video_content.

Depois itera nas imagens da pasta com as imagens baixadas, e converte todas elas para o tamanho de 1280 x 720px.

Após, cria uma montagem esticando a imagem principal para cobrir todo o frame e cria o efeito embaçado. Depois coloca no centro da imagem a foto redimensionada anteriormente.

O resultado é bem legal 😀

Imagem original
Imagem editada

Notem que no final da function, existe um trecho onde precisei utilizar a lib Pillow para tratar um problema de imagens em grayscale na hora de compilar o video.

Bom, agora que temos as imagens tratadas, hora de compilar o video!

Mostro como eu fiz no próximo post!

Abraço.

Gerando videos para o Youtube com linguagem Python

Navegando pelo Youtube a mais ou menos 2 anos atrás, me deparei com uma playlist do canal Filipe Deschamps onde ele mostrava como havia programado 4 robôs para automatizar a criação de videos para o Youtube fornecendo apenas o assunto para o robô.

Para quem quiser conferir, esse é o link do primeiro video da série:

Programei 4 robôs que criam vídeos para mim no YouTube

Bom, na época havia começado minha jornada de aprendizado em Python, e resolvi que faria um projeto similar, utilizando Python, e ferramentas alternativas as utilizadas no projeto original.

Pelos meses seguinte, a idéia não saiu do papel, por diversas razões que não precisam ser citadas aqui.

Até que no final de 2021, resolvi que tiraria esse projeto do papel, apenas por diversão e para praticar um pouco de Python… foi bem divertido :D.

O projeto ficou pronto e pode ser conferido no repositório que postarei mais abaixo. Porém não foi fácil concluí-lo. Diversas coisas no projeto segui o exatamente o que ele fez, outras nem tanto. Também utilizei muito do conhecimento passado nos videos do Filipe Deschamps, porém ele usou Node.js e como resolvi fazer em Python, muitas coisas precisei pesquisar como poderia fazer, quais libs poderia usar, etc.

Então para registrar o conhecimento que adquiri com esse pequeno projeto, resolvi escrever uma série de posts, cada qual abordando um pedaço do projeto, assim como o Filipe fez em video.

O objetivo principal é para reforçar, por meio do repasse, o aprendizado adquirido. Espero que estes posts possam ajudar vocês de alguma forma, seja com o conteúdo técnico ou simplesmente motivando-os a iniciarem seus projetos pessoais. É a melhor forma de aprender, e como o Filipe falou nos videos, são nesses projetos, que podemos usar toda e qualquer tecnologia que temos vontade, sem nos preocupar se é viável ou não.

Repositório com o projeto concluído:
https://github.com/thmlima87/videocreator

O que foi usado no projeto?

Apesar de parecer simples, o projeto envolveu bastante coisa legal:

Bora começar!

Abaixo os links de todos os posts para facilitar a navegação.

Parte 1: Arquitetura da aplicação
Parte 2: Idéias de conteúdo!
Parte 3: Buscando o assunto escolhido na Wikipedia
Parte 4: Limpeza e interpretação do texto
Parte 5: Persistência do conteúdo
Parte 6: Bing Custom Search
Parte 7: Busca e download de imagens
Parte 8: Tratamento das imagens
Parte 9: Renderização do video
Parte 10: Upload para o Youtube

Exit mobile version