Build your own video downloader app - NullClass

Special Sale the courses from 497 rs for limited time.

Build your own video downloader app

Video downloader

  • Initial Setup

Add the following dependencies to the build.gradle(Module) file ->

 

 

Next, go to https://developers.google.com/youtube/android/player/downloads/, and download the zip file and extract it. Look for youtube api jar file and copy it.

Now go to project level tab in the top left corner.

 

 

Go to app->libs and paste the jar file here. Right click and select “Add as library”.

Now go to https://console.cloud.google.com/projectcreate?previousPage=%2Fapis%2Fcredentials%3Fproject%3Diintroidxprojects&organizationId=0,

Create a new project and get an API key.

 

  • Layouts

Create two new activities. Facebook Activity and Youtube Activity.

Add the following lines to the manifest file ->

<activity
android:name=”.FacebookActivity”
android:configChanges=”keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode”
android:exported=”true” />
<activity
android:name=”.YoutubeActivity”
android:configChanges=”keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode”
android:exported=”true” />

 

This registers our two new activities.

Add the following code to MainActivity.xml ->

<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”

android:background=”#171f2b”
tools:context=”.MainActivity”>

<TextView
android:id=”@+id/tvHeading”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginStart=”20dp”
android:layout_marginTop=”80dp”
android:layout_marginEnd=”20dp”
android:text=”Choose video Source”
android:textAlignment=”center”
android:textColor=”@color/mypink”
android:textSize=”25sp”
android:textStyle=”bold” />

<androidx.cardview.widget.CardView
android:id=”@+id/yt_layout”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@id/tvHeading”
android:layout_marginStart=”20dp”
android:layout_marginTop=”50dp”
android:layout_marginEnd=”20dp”
android:backgroundTint=”@color/myblack”
app:cardCornerRadius=”15dp”>

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginStart=”20dp”
android:layout_marginTop=”10dp”
android:layout_marginEnd=”20dp”
android:layout_marginBottom=”10dp”
android:orientation=”horizontal”>

<ImageView
android:layout_width=”50dp”
android:layout_height=”50dp”
android:src=”@drawable/youtube” />

<TextView
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:layout_marginStart=”20dp”
android:text=”Youtube”
android:textColor=”@color/mypink”
android:textSize=”20sp” />
</LinearLayout>
</androidx.cardview.widget.CardView>

<androidx.cardview.widget.CardView
android:id=”@+id/fb_layout”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@id/yt_layout”
android:layout_marginStart=”20dp”
android:layout_marginTop=”20dp”
android:layout_marginEnd=”20dp”
android:backgroundTint=”@color/myblack”
app:cardCornerRadius=”15dp”>

<LinearLayout
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginStart=”20dp”
android:layout_marginTop=”10dp”
android:layout_marginEnd=”20dp”
android:layout_marginBottom=”10dp”
android:orientation=”horizontal”>

<ImageView
android:layout_width=”50dp”
android:layout_height=”50dp”
android:src=”@drawable/fb_icon” />

<TextView
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:layout_marginStart=”20dp”
android:text=”Facebook”
android:textColor=”@color/mypink”
android:textSize=”20sp” />
</LinearLayout>
</androidx.cardview.widget.CardView>
</RelativeLayout>

 

 

Here, we have two cardview which act as buttons for taking the user to the Facebook and Youtube Activity.

Add the following code in Youtube activity layout->

<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”

android:background=”#171f2b”

android:layout_height=”match_parent”
tools:context=”.FacebookActivity”>

<androidx.cardview.widget.CardView
android:id=”@+id/url_layout”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginStart=”20dp”
android:layout_marginTop=”30dp”
android:layout_marginEnd=”20dp”
android:background=”@color/white”
app:cardCornerRadius=”20dp”>

<EditText
android:id=”@+id/url_edit_text”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginStart=”20dp”
android:layout_marginTop=”10dp”
android:layout_marginEnd=”20dp”
android:layout_marginBottom=”10dp”
android:hint=”Enter Youtube Url here” />
</androidx.cardview.widget.CardView>

<Button
android:id=”@+id/get_video_button”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@id/url_layout”
android:layout_marginStart=”60dp”
android:layout_marginTop=”20dp”
android:layout_marginEnd=”60dp”
android:background=”@drawable/rounded_button”
android:text=”Get Video” />

<com.google.android.youtube.player.YouTubePlayerView
android:id=”@+id/youtube_player_view”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@id/get_video_button”
android:layout_marginTop=”40dp” />
</RelativeLayout>

 

Next, add the following code to the Facebook Activity ->

<?xml version=”1.0″ encoding=”utf-8″?>
<RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/android”
xmlns:app=”http://schemas.android.com/apk/res-auto”
xmlns:tools=”http://schemas.android.com/tools”
android:layout_width=”match_parent”
android:layout_height=”match_parent”

android:background=”#171f2b”

tools:context=”.FacebookActivity”>

<androidx.cardview.widget.CardView
android:id=”@+id/url_layout”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginStart=”20dp”
android:layout_marginTop=”30dp”
android:layout_marginEnd=”20dp”
android:background=”@color/white”
app:cardCornerRadius=”20dp”>

<EditText
android:id=”@+id/url_edit_text”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_marginTop=”10dp”
android:layout_marginEnd=”20dp”
android:hint=”Enter Facebook post Url here”
android:layout_marginStart=”20dp”
android:layout_marginBottom=”10dp”/>
</androidx.cardview.widget.CardView>

<Button
android:id=”@+id/get_video_button”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@id/url_layout”
android:layout_marginStart=”60dp”
android:background=”@drawable/rounded_button”
android:layout_marginTop=”20dp”
android:layout_marginEnd=”60dp”
android:text=”Get Video” />

<Button
android:id=”@+id/download_button”
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:layout_below=”@id/get_video_button”
android:layout_marginStart=”100dp”
android:layout_marginTop=”20dp”
android:layout_marginEnd=”100dp”
android:background=”@drawable/rounded_button”
android:text=”Download”
/>

<com.google.android.exoplayer2.ui.PlayerView
android:id=”@+id/exoplayerView”
android:layout_width=”match_parent”
android:layout_height=”200dp”
android:layout_below=”@id/download_button”
android:layout_marginTop=”20dp” />
</RelativeLayout>

 

 

  • Coding the Facebook Activity ->

Add the following code to the facebook Activity ->

class FacebookActivity : AppCompatActivity() {
val ROOT_DIR = “My Story Saver/facebook/”
private lateinit var binding: ActivityFacebookBinding
val videoUrl = MutableLiveData<String>(null)
private val dataSourceFactory: DataSource.Factory by lazy {
DefaultDataSourceFactory(this, “exoplayer-sample”)
}
private lateinit var exoplayer: SimpleExoPlayer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFacebookBinding.inflate(layoutInflater)
setContentView(binding.root)
exoplayer = SimpleExoPlayer.Builder(this).build()
videoUrl.observe(this) {
if (it != null) {
binding.downloadButton.isVisible = true
val mediaSource = buildMediaSource(Uri.parse(it))
exoplayer.prepare(mediaSource)
exoplayer.playWhenReady = true
binding.exoplayerView.player = exoplayer
} else {
binding.downloadButton.isVisible = false
}
}
binding.getVideoButton.setOnClickListener {
val url = binding.urlEditText.text.toString()
Toast.makeText(this@FacebookActivity, “Extracting video”, Toast.LENGTH_SHORT).show()
this.lifecycleScope.launchWhenStarted {
withContext(Dispatchers.IO) {
try {
val fbDoc = Jsoup.connect(url).get()
runOnUiThread {
videoUrl.value = fbDoc.select(“meta[property=\”og:video\”]”)
.last().attr(“content”)
}
} catch (e: Exception) {
runOnUiThread {
Toast.makeText(
this@FacebookActivity,
e.localizedMessage,
Toast.LENGTH_SHORT
).show()
}
}
}
}
}
binding.downloadButton.setOnClickListener {
download(
videoUrl.value!!,
ROOT_DIR,
this@FacebookActivity,
“fb_” + System.currentTimeMillis() + “.mp4”
)
}
}

private fun buildMediaSource(uri: Uri): MediaSource =
ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(uri)

private fun download(
downloadPath: String,
destPath: String,
context: Context,
fileName: String
) {
try {
val uri = Uri.parse(downloadPath)
val request = DownloadManager.Request(uri)
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE or DownloadManager.Request.NETWORK_WIFI)
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setTitle(fileName)
request.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,
destPath + fileName
)
val downloadManager = context.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
} catch (e: Exception) {
runOnUiThread {
Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
}
}
}

override fun onDestroy() {
super.onDestroy()
exoplayer.release()
}
}

 

Lets go through the code step by step.

First we intialise viewbinding and create a string to store the directory name for the downloads and create a datasource factory for the exoplayer ->

 

We also have a videoUrl mutable livedata to store the current url.

 

We add an observer to the mutable livedata value. When it is not null, we make the download button visible and start preparing the exoplayer and create a media source for exoplayer to play from. We also set the playWhenReady property of the exoplayer as true. We create the media source using the buildMediaSource function ->

 

 

We use the progressive media source factory which automatically alters its speed and buffering depending on the available internet bandwidth.

We then set on onClickListener on the getVideo button ->

 

When the button is clicked, we get the url from the edittext and launch a new coroutine which is scoped to the activity. We launch it on Dispatchers.IO thread and then instead the new thread, we use the JSoup library to get the video url.

Dispatchers.IO thread is a thread that has been optimized for long running operations (for example network requests) and run them on a background thread. This ensures that the UI of the app does not freeze.

Doing this inside a co-routine bound to the “viewLifecycle” of the activity ensures that all the long running operations occurring on the background get cancelled when the activity gets destroyed.

In the end, we set the value of the video url to the videoUrl mutable live data on the ui thread.

We do all this this inside a try catch block. Inside the catch block, we show a toast message to the user stating the error cause on the ui thread.

Next we set an onClickListener on the download button. Inside the onClickListener, we call the download function and send the videoUrl, the root directory path , the context and the file name. System.currentTimeMillis() function just returns the current time in milliseconds. We use it for generating a unique name for all the videos the user downloads.

 

The download function is as follows ->

 

We first take the string and parse it using the Uri class and convert it to a uri object.

We use the download Manager class to download the video on a background thread and display a notification when the download ends.

We configure the allowed network types for downloads to work -> Network mobile and network wifi.

We then store the video in the directory path we passed to this function and also the set its title and set the request to the downloadManager using the “enqueue” function. This automatically gets executed on a background thread without freezing the UI.

 

  • Youtube Activity ->

We create the binding variable and a string to store the API KEY we got from the google play console earlier.

 

             We set an onClickListener on the getVideoButton and inside, we take the url from the   edittext and extract the video Id from it.

 

To play the video, we need the videoId of the video to be played. The youtube url is generally of the form -> https://www.youtube.com/watch?v=videoId (in case of Youtube web app) or https://youtu.be/videoId (incase of Youtube mobile app).

We first get the index of “=”  character in the url using the indexOf function. If the pos variable is not -1, it means the character is present in the string and we then get the string after the “=” sign (which is our videoId) using the substring function. Otherwise, the “=” sign is not present ,which means the url entered by the user is from youtube mobile app. We look for the last occurrence of the “/” character using the lastIndexOf function and extract the string after it using the substring function. This way, we get the videoId.

Now, we intialise the youtube player view and pass the video id to it to play.

 

We have two functions -> onIntializationSuccess, in which we are passed the YoutubePlayer (p1).

We check if p1 variable is not null by using the let operator (let operator will not execute if the player p1 is null) and pass the extracted videoId to the YoutubePlayer in the loadVideo function.

Then we play the video using the play() function.

The second function, onIntializationFailure is called in case of any errors. Inside this function, we display a toast message, telling the user that some error occurred.

Final code for youtube Activity ->

class YoutubeActivity : YouTubeBaseActivity() {
private lateinit var binding: ActivityYoutubeBinding
val KEY = “AIzaSyBHIcdJTfMxZ5erZrlaq7TjOR2LKbjemZ4”
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityYoutubeBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.getVideoButton.setOnClickListener {
val url = binding.urlEditText.text.toString()
var pos = url.indexOf(‘=’)
val videoId = if (pos != -1) {
url.substring(pos + 1)
} else {
pos = url.lastIndexOf(‘/’)
url.substring(pos + 1)
}
binding.youtubePlayerView.initialize(
KEY,
object : YouTubePlayer.OnInitializedListener {
override fun onInitializationSuccess(
p0: YouTubePlayer.Provider?,
p1: YouTubePlayer?,
p2: Boolean
) {
p1?.let {
it
.loadVideo(videoId)
it.play()
}
}

override fun onInitializationFailure(
p0: YouTubePlayer.Provider?,
p1: YouTubeInitializationResult?
) {
Toast.makeText(getApplicationContext(),
“Video player Failed”, Toast.LENGTH_SHORT).show();
}
})
}
}
}

 

  • Main Activity ->

We add permissions check for accessing internal storage. We check if the build version of the mobile on which the app is running is greater than 23. If yes, we ask for the permission to write to external storage. (This permission is already granted in android versions less than 23) . Now, we set onClickListeners to the two buttons to take us to the two different activities. We create an intent inside each of the onClickListeners which takes us to the Youtube Activity and Facebook Activity respectively.

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED
) {
val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
ActivityCompat.requestPermissions(this, permissions, 1)
}
}
binding.ytLayout.setOnClickListener {
startActivity(Intent(this, YoutubeActivity::class.java))
}

binding.fbLayout.setOnClickListener {
startActivity(Intent(this, FacebookActivity::class.java))
}
}
}

 

Now, add the following permissions to the android manifest file ->

 

<uses-permission android:name=”android.permission.INTERNET” />
<uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />
<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />
<uses-permission android:name=”android.permission.READ_EXTERNAL_STORAGE” />
<uses-permission android:name=”android.permission.FOREGROUND_SERVICE” />

 

 

Final App ->

 

 

January 16, 2022

0 responses on "Build your own video downloader app"

Leave a Message