<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: FlyNestor</title>
    <description>The latest articles on DEV Community by FlyNestor (@flynestor).</description>
    <link>https://dev.to/flynestor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F737949%2F59f76508-dafb-498b-bc01-bd8623cc1c2a.jpg</url>
      <title>DEV Community: FlyNestor</title>
      <link>https://dev.to/flynestor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/flynestor"/>
    <language>en</language>
    <item>
      <title>Create a video with auto-generated voice-overs with an API call</title>
      <dc:creator>FlyNestor</dc:creator>
      <pubDate>Thu, 28 Apr 2022 13:52:06 +0000</pubDate>
      <link>https://dev.to/rollideo/create-a-video-with-auto-generated-voice-overs-with-an-api-call-4pi9</link>
      <guid>https://dev.to/rollideo/create-a-video-with-auto-generated-voice-overs-with-an-api-call-4pi9</guid>
      <description>&lt;p&gt;If you want to generate videos with voice-overs and to skip the repetitive tasks of video creation, follow this tutorial.&lt;/p&gt;

&lt;p&gt;In this tutorial we are going to call an API with Python.&lt;/p&gt;

&lt;p&gt;On the website &lt;strong&gt;&lt;a href="https://rollideo.com/"&gt;Rollideo&lt;/a&gt;&lt;/strong&gt;, you can launch the video generation directly from the home page, or use the APIs for more automation.&lt;/p&gt;

&lt;p&gt;To use the Rollideo APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1:&lt;/strong&gt; Create an account to get a free API key&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2:&lt;/strong&gt; put your API key in the following code.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests, json, time

APIKEY = 'YOUR SECRET KEY HERE'
voice1 = "Joanna"

text1 = "A super easy tool to automatically create videos with auto-generated voice-overs and the exact subtitles. Example of a video sequence with " + voice1 +"'s voice."

picture1 = 'https://i.postimg.cc/3r4RL5YX/ocean1.jpg'

data = {
    'text_segments': [
   {
    'api_source_type': 'picture',
    'picture': picture1,
    'textforvideo': text1,  
    'voice_id': voice1  
   }
  ]
}

headers = {'x-api-key': f"{APIKEY}"}
response = requests.post('https://rollideo.com/create/video', headers=headers, json=data)
data2 = response.json()
print(data2)
launched = data2.get("launched", "ko")

if launched == "ok":
    # Use the Polling API endpoint to find out the status the above request /create/video.
    for i in range(1000):
        time.sleep(10)
        print(i)
        response2 = requests.post('https://rollideo.com/task/video', headers=headers, json=data2)
        success = response2.json().get("success", "not yet")
        if success == "ok" or success == "ko":
            print(response2.json())
            break
else:
    print("The launch of the task has failed")
    print(data2.get("message"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a free account, you can create a video with one sequence.&lt;/p&gt;

&lt;p&gt;You can upgrade your account to create longer videos with many sequences.&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Crop a picture automatically with a fixed ratio - Python example</title>
      <dc:creator>FlyNestor</dc:creator>
      <pubDate>Sun, 27 Feb 2022 11:21:55 +0000</pubDate>
      <link>https://dev.to/flynestor/crop-a-picture-automatically-with-a-fixed-ratio-python-example-1j1a</link>
      <guid>https://dev.to/flynestor/crop-a-picture-automatically-with-a-fixed-ratio-python-example-1j1a</guid>
      <description>&lt;p&gt;There is a large number of occasions where you have to crop a picture selected by the user of a website.&lt;/p&gt;

&lt;p&gt;I have already made an article on &lt;a href="https://dev.to/flynestor/crop-a-picture-and-upload-it-to-the-server-with-vuejs-1p8l"&gt;how to manually crop a picture with JavaScript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article I will show how to automatically crop a picture with Python in the back-end. This code allows automatic video creation from a text and a picture on my website &lt;strong&gt;&lt;a href="https://rollideo.com/"&gt;rollideo.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this example the cropped picture will have a fixed ratio of 16:9. The user can choose a picture of any ratio as an input. The output will always be 16:9.&lt;/p&gt;

&lt;p&gt;I have decided to offer a centered crop as a first option because usually the most interesting part of a picture is in the center. A more sophisticated code can detect faces or other parts in a picture. You can also add the option to crop on one specific corner or other crop possibilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#This code will automatically crop the picture to a ratio of 16:9.
from PIL import Image

with Image.open(path_file_temp) as image:
    width, height = image.size
    if width/height == 16/9:
        if width != 1280:
            image_crop = image.resize((1280, 720))
        else:
            image_crop = image
    elif width/height &amp;gt; 16/9:
        offset  = int(abs(width-16/9*height)/2)
        image_crop1 = image.crop([offset,0,width-offset,height])
        image_crop = image_crop1.resize((1280, 720))
    else:
        offset  = int(abs(9/16*width-height)/2)
        image_crop1 = image.crop([0,offset,width,height-offset])
        image_crop = image_crop1.resize((1280, 720))

    file_source = random_id + '.png'
    path_file_crop = os.path.join(UPLOAD_FOLDER, file_source)              
    image_crop.save(path_file_crop, format="png")


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A warning about Pillow&lt;/strong&gt;&lt;br&gt;
Pillow is using External Libraries to process the different format of images (.jpg .png .webp etc).&lt;/p&gt;

&lt;p&gt;To test the image processing code, you have to make a test with all the format of image that you want to allow. If &lt;strong&gt;Image.open&lt;/strong&gt; failed it's probably because there is some missing external libraries.&lt;/p&gt;

&lt;p&gt;For more details, see &lt;a href="https://pillow.readthedocs.io/en/stable/installation.html"&gt;the Pillow installation guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once the tests are done with the different formats allowed, you should list the allowed image format in the user guide, and also add warning messages "The image format X is not supported".&lt;/p&gt;

</description>
      <category>python</category>
    </item>
    <item>
      <title>Display a sticky feedback box with Vue.js</title>
      <dc:creator>FlyNestor</dc:creator>
      <pubDate>Tue, 02 Nov 2021 08:55:51 +0000</pubDate>
      <link>https://dev.to/flynestor/display-a-sticky-feedback-box-with-vuejs-n9m</link>
      <guid>https://dev.to/flynestor/display-a-sticky-feedback-box-with-vuejs-n9m</guid>
      <description>&lt;p&gt;One of the first thing to implement with a &lt;strong&gt;MVP&lt;/strong&gt; (minimum viable product) is a &lt;strong&gt;feedback form&lt;/strong&gt; to know which feature the users are interested in.&lt;/p&gt;

&lt;p&gt;The feedback form is also useful if a user has found a bug.&lt;/p&gt;

&lt;p&gt;At first I have just made the contact form. The link to the contact form is in the footer of the Home page. I have got some feedbacks with that form but I want to get more. Not all the users are scrolling all the way to the footer section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are sticky elements?&lt;/strong&gt;&lt;br&gt;
Sticky elements are the features that appear to follow you down the page as you scroll. You can see an example on the bottom right corner of my website &lt;a href="https://rollideo.com/"&gt;Rollideo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can put a text like “Feedback” or “Need help?” on the sticky element.&lt;/p&gt;

&lt;p&gt;To display a sticky element with Vue.js, you need to install &lt;a href="https://www.npmjs.com/package/vue-sticky-directive"&gt;vue-sticky-directive&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the template, you can include the container element &lt;strong&gt;sticky-container&lt;/strong&gt; at the beginning.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;template&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;div sticky-container&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you want the sticky element to be displayed at the bottom of the webpage you can put the sticky element after the footer section.&lt;/p&gt;

&lt;p&gt;In the sticky element I have included a link to the contact page.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div v-sticky sticky-side="bottom"&amp;gt;
&amp;lt;div align="right" class="row-full"&amp;gt;
&amp;lt;span&amp;gt; 
&amp;lt;a v-bind:href="apiUrl + '/contact'"&amp;gt;Feedback&amp;lt;/a&amp;gt;  
&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In my particular case I want the sticky element to be displayed in the bottom right corner of the webpage, so I have added a CSS class &lt;strong&gt;row-full&lt;/strong&gt; with a width of 100vw (100% width of the viewport).&lt;/p&gt;

&lt;p&gt;To make the element looks like a box, we can add a span section with a background color and a border. In the CSS it’s defined as &lt;strong&gt;.row-full span:first-child&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.row-full{
font-size: 0.75rem;
padding-bottom: 1rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
width: 100vw;
position: relative;
margin-left: -50vw;
height: 25px;
margin-top: 100px;
left: 50%;
}

.row-full span:first-child {
padding: 0.5rem;
background-color: rgb(250, 255, 151);
border-radius:5px;
border:2px solid #c6c2be;
border-bottom-left-radius:0;
border-bottom-right-radius:0;
border-bottom:0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Crop a picture and upload it to the server with Vue.js</title>
      <dc:creator>FlyNestor</dc:creator>
      <pubDate>Sun, 31 Oct 2021 07:18:36 +0000</pubDate>
      <link>https://dev.to/flynestor/crop-a-picture-and-upload-it-to-the-server-with-vuejs-1p8l</link>
      <guid>https://dev.to/flynestor/crop-a-picture-and-upload-it-to-the-server-with-vuejs-1p8l</guid>
      <description>&lt;p&gt;There is a large number of occasions when you have to implement a “select a picture and crop it” feature: for example when a user is uploading a picture of him to customize his profile on a website.&lt;/p&gt;

&lt;p&gt;First you need to install &lt;a href="https://www.npmjs.com/package/vue-cropperjs"&gt;vue-cropperjs &lt;/a&gt;, a wrapper component for &lt;strong&gt;cropperjs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will use the method &lt;strong&gt;getCroppedCanvas&lt;/strong&gt; to upload the cropped image to the server. The full documentation about the cropperjs methods is available &lt;a href="https://github.com/fengyuanchen/cropperjs#methods"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the template you can indicate which type of cropper you need. In the example below the cropper has a fixed ratio of 16/9.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;VueCropper 
v-show="selectedFile" ref="cropper" :src="selectedFile" 
:aspectRatio="16/9" :initialAspectRatio="16/9" :autoCropArea="1"
:zoomable="false"
&amp;gt;
&amp;lt;/VueCropper&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;replace&lt;/strong&gt; method is used to rebuild the cropper when you select a file and crop it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;this.$refs.cropper.replace(vm.file);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;getCroppedCanvas&lt;/strong&gt; method gets a canvas drawn from the cropped image. After you can use HTMLCanvasElement.toBlob to get a blob and upload it to the server with FormData.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation of the terms above:&lt;/strong&gt;&lt;br&gt;
Blob:&lt;br&gt;
A binary large object (BLOB) is a collection of binary data stored as a single entity. Blobs are typically images, audio or other multimedia objects.&lt;/p&gt;

&lt;p&gt;FormData:&lt;br&gt;
To send multipart form data with Axios, you need to use the FormData class. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Upload cropped image to server if the browser supports `HTMLCanvasElement.toBlob`
this.$refs.cropper.getCroppedCanvas({width: 1280, height: 720}).toBlob((blob) =&amp;gt; {

              var data = new FormData();
              data.append('cropped_picture', blob, 'cropped.png')
              ...
              axios.post(load_url,data, {
                      headers: {
                     'Content-Type': 'multipart/form-data'
                     }}
                  ).then(response =&amp;gt; {
                  …
                 }).catch(error =&amp;gt; {
                 console.log(error)
                 })
      /* second parameter mime_type toBlob set image/png as default */
      } /*, 'image/png' */)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can see an example on my website &lt;a href="https://rollideo.com/"&gt;Rollideo: video generation from a text&lt;/a&gt;. This feature allows the user to choose his own picture to generate a video.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Display an animated loading icon with Vue.js</title>
      <dc:creator>FlyNestor</dc:creator>
      <pubDate>Sat, 30 Oct 2021 07:36:52 +0000</pubDate>
      <link>https://dev.to/flynestor/display-an-animated-loading-icon-with-vuejs-15j2</link>
      <guid>https://dev.to/flynestor/display-an-animated-loading-icon-with-vuejs-15j2</guid>
      <description>&lt;p&gt;A &lt;strong&gt;throbber&lt;/strong&gt;, also known as a &lt;strong&gt;loading icon&lt;/strong&gt;, is an animated icon used to show that a software is performing an action in the background (such as downloading content or conducting intensive calculations).&lt;/p&gt;

&lt;p&gt;To indicate to the user that the software is working in the background, you can easily put a throbber on a webpage. &lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Font Awesome&lt;/strong&gt; icons you can use &lt;a href="https://fontawesome.com/v5.15/how-to-use/on-the-web/styling/animating-icons"&gt;the fa-spin class&lt;/a&gt; to make an icon rotates as a throbber.&lt;/p&gt;

&lt;p&gt;Example of usage from my website &lt;a href="https://rollideo.com/"&gt;Rollideo&lt;/a&gt;: On the home page the user can click on a button to generate a video from a text, then a throbber is displayed to indicate that a process is running in the back-end.&lt;/p&gt;

&lt;p&gt;In the code bellow the throbber will be displayed if wait_video_generation equals true. On a side note, if you can evaluate the total duration of the the process it’s a good practice to display it to the user. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="box" v-if="wait_video_generation"&amp;gt;      
&amp;lt;font-awesome-icon icon="spinner" class="fa-spin" size="3x"  /&amp;gt;
&amp;lt;/div&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;wait_video_generation is set to false when the front-end gets the response from the back-end. Then the throbber is not displayed anymore.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;axios.post(load_url,data, {
headers: {
'Content-Type': 'multipart/form-data'
}}
).then(response =&amp;gt; {
...
this.wait_video_generation = false
}).catch(error =&amp;gt; {
console.log(error)
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Sometimes you need to disable an element when a process is running in the background, for example to avoid that the user clicks 10 times on the video generation button.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;button :disabled="wait_video_generation" class="button is-info" @click="createvideo"&amp;gt;
Launch the video generation
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Lazy loading of a YouTube video with Vue.js</title>
      <dc:creator>FlyNestor</dc:creator>
      <pubDate>Wed, 27 Oct 2021 14:05:39 +0000</pubDate>
      <link>https://dev.to/flynestor/lazy-loading-of-a-youtube-video-with-vuejs-5ig</link>
      <guid>https://dev.to/flynestor/lazy-loading-of-a-youtube-video-with-vuejs-5ig</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8jAYKFMf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nggyj9csordenp14cfas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8jAYKFMf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nggyj9csordenp14cfas.png" alt="Vue js" width="290" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you embed a YouTube video on a webpage, you have to think that many users will not click to play the video, some of them want to do something else like reading the contents on the page or whatever features the website is providing.&lt;/p&gt;

&lt;p&gt;That’s why it’s a good idea to implement &lt;strong&gt;lazy loading&lt;/strong&gt; on the video. Lazy loading is the practice of delaying the load of resources until they’re actually needed.&lt;/p&gt;

&lt;p&gt;By reducing the initial load time, you can offer a better experience to the users and you are also doing &lt;strong&gt;SEO&lt;/strong&gt; since Google takes into account the initial load time.&lt;/p&gt;

&lt;p&gt;How to implement this lazy loading on Vue.js?&lt;/p&gt;

&lt;p&gt;Example of usage:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;LazyYoutube
max-width="414px"
aspect-ratio="16:9"
thumbnail-quality="standard"
src="https://www.youtube.com/watch?v=CCBpeKLaX3o"
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The 'LazyYoutubeVideo' component renders only thumbnail images on pager renders. On clicking the play button the YouTube video gets downloaded. &lt;/p&gt;

&lt;p&gt;You can see the behavior on my website where a YouTube video is embedded on the home page: &lt;a href="https://rollideo.com/"&gt;https://rollideo.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more details about the installation, check the github documentation:&lt;br&gt;
&lt;a href="https://github.com/andrewvasilchuk/vue-lazy-youtube-video"&gt;https://github.com/andrewvasilchuk/vue-lazy-youtube-video&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
