Skip to content

Commit

Permalink
YouTube download link obtained
Browse files Browse the repository at this point in the history
  • Loading branch information
BShakhovsky committed Mar 1, 2020
1 parent 4e8050a commit a6c8431
Show file tree
Hide file tree
Showing 19 changed files with 411 additions and 12 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.google.android.gms:play-services-ads:18.3.0'
implementation 'com.google.code.gson:gson:2.8.6'

implementation 'com.github.kittinunf.fuel:fuel-android:2.2.1'
implementation 'com.github.pdrogfer:MidiDroid:v1.1'
}
11 changes: 11 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>

<tools:validation testUrl="http:https://bshakhovsky.github.io" />
<tools:validation testUrl="https://bshakhovsky.github.io" />
<!-- Android - Lint - Correctness - App Links Auto Verification Failure
Expand All @@ -55,5 +61,10 @@
android:label="@string/midi" android:theme="@style/AppTheme" />
<activity android:name=".GuideActivity" android:parentActivityName=".MainActivity"
android:label="@string/guide" android:theme="@style/AppTheme" />

<activity android:name=".Web.WebActivity" android:parentActivityName=".MainActivity"
android:label="@string/surf" android:theme="@style/AppTheme" />

</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import kotlinx.android.synthetic.main.dialog_add.midiFile
import kotlinx.android.synthetic.main.dialog_add.record
import kotlinx.android.synthetic.main.dialog_add.surf

import ru.BShakhovsky.Piano_Transcription.Web.WebActivity

import java.io.FileDescriptor
import java.io.IOException

Expand Down Expand Up @@ -65,7 +67,10 @@ class AddDialog : DialogFragment(), View.OnClickListener {
DebugMode.assertArgument(view != null)
DebugMode.assertState((dialog != null) and (activity != null))
when (view?.id) {
R.id.surf -> dialog?.dismiss()
R.id.surf -> {
startActivity(Intent(activity, WebActivity::class.java))
dialog?.dismiss()
}
R.id.mediaFile -> {
with(Intent(Intent.ACTION_GET_CONTENT)) {
type = "*/*"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ object DebugMode {
debug = true // BuildConfig.DEBUG
}

fun assertArgument(b: Boolean, msg: String = ""): Unit =
if (debug) require(b) { msg } else Unit

fun assertState(b: Boolean, msg: String = ""): Unit =
if (debug) check(b) { msg } else Unit
fun assertArgument(b: Boolean, msg: String = ""): Unit = if (debug) require(b) { msg } else Unit
fun assertState(b: Boolean, msg: String = ""): Unit = if (debug) check(b) { msg } else Unit
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import ru.BShakhovsky.Piano_Transcription.MainUI.Touch
import ru.BShakhovsky.Piano_Transcription.Midi.Midi
import ru.BShakhovsky.Piano_Transcription.Midi.MidiActivity
import ru.BShakhovsky.Piano_Transcription.OpenGL.Render
import ru.BShakhovsky.Piano_Transcription.Web.WebActivity

import java.io.FileNotFoundException
import java.util.concurrent.Executors
Expand Down Expand Up @@ -175,6 +176,13 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte

MobileAds.initialize(this)
AdBanner(adMain)

// TODO: onCreate not called after onStop, and intent.extras == null anyway
if (intent.hasExtra(Intent.EXTRA_TEXT))
with(Intent(this@MainActivity, WebActivity::class.java)) {
putExtras(intent)
startActivity(this)
}
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
Expand Down Expand Up @@ -321,7 +329,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
try {
contentResolver.openInputStream(uri)
} catch (e: FileNotFoundException) {
with(e) { showMsg(R.string.notFound, "${localizedMessage ?: this}\n\n$uri") }
with(e) { showMsg(R.string.noFile, "${localizedMessage ?: this}\n\n$uri") }
return
}.also { inputStream ->
DebugMode.assertState(inputStream != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ class Primitive(cords: FloatArray, order: IntArray, texArray: FloatArray? = null
y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2
).also { (crossX, crossY, crossZ) ->
sqrt(
crossX.pow(2) + crossY.pow(2)
+ crossZ.pow(2)
crossX.pow(2) + crossY.pow(2) + crossZ.pow(2)
).also { distance ->
for (k in 0..2) {
normArray[i * 9 + k * 3] = crossX / distance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ class RecordMsg(private val recDlg: AlertDialog, record: MediaRecorder, context:

fun start(): Unit = schedule.execute(this)
fun stop(): Unit = schedule.shutdown()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
@file:Suppress("PackageName")

package ru.BShakhovsky.Piano_Transcription.Web

import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.github.kittinunf.fuel.core.Response
import com.github.kittinunf.fuel.httpGet
import com.github.kittinunf.result.Result
import com.google.android.material.snackbar.Snackbar
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException

import kotlinx.android.synthetic.main.activity_web.download
import kotlinx.android.synthetic.main.activity_web.goBack
import kotlinx.android.synthetic.main.activity_web.goForward
import kotlinx.android.synthetic.main.activity_web.goHome

import kotlinx.android.synthetic.main.content_web.web
import kotlinx.android.synthetic.main.content_web.webText

import ru.BShakhovsky.Piano_Transcription.DebugMode
import ru.BShakhovsky.Piano_Transcription.R
import java.net.URLDecoder

class WebActivity : AppCompatActivity(), View.OnClickListener {

data class AdaptiveFormat(
val url: String, val mimeType: String, @Suppress("SpellCheckingInspection") val itag: Int,
val averageBitrate: Int, val audioSampleRate: String, val contentsLength: String
)

data class Thumbnail(val url: String, val width: String, val height: String)
data class Thumbnails(val thumbnails: List<Thumbnail>)

data class StreamingData(val adaptiveFormats: List<AdaptiveFormat>)
data class PlayabilityStatus(val status: String)
data class VideoDetails(val title: String, val author: String, val thumbnail: Thumbnails)

data class PlayerResponse(
val streamingData: StreamingData?,
val playabilityStatus: PlayabilityStatus, val videoDetails: VideoDetails
)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_web)

with(web) {
@SuppressLint("SetJavaScriptEnabled")
settings.javaScriptEnabled = true

savedInstanceState?.let {
web.restoreState(it)
return@with
}

with(intent.extras) {
(if (this == null) "https://youtu.be"
else get(Intent.EXTRA_TEXT).toString().also {
if (!intent.hasExtra(Intent.EXTRA_SUBJECT)) Snackbar.make(
web, getString(R.string.notUrl, it), Snackbar.LENGTH_LONG
).show()
}).also {
webViewClient = WebClient(it, webText)
loadUrl(it)
}
}
}

arrayOf(goHome, goBack, goForward, download).forEach { it.setOnClickListener(this) }
}

override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
web.saveState(outState)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
web.restoreState(savedInstanceState)
}

override fun onClick(view: View?) {
DebugMode.assertArgument(view != null)
with(web) {
when (view?.id) {
R.id.goHome -> super.onBackPressed()
R.id.goBack -> if (canGoBack()) goBack()
R.id.goForward -> if (canGoForward()) goForward()
R.id.download -> tryDownload()
else -> DebugMode.assertState(false)
}
}
}

override fun onBackPressed() {
with(web) { if (canGoBack()) goBack() else super.onBackPressed() }
}

private fun tryDownload() {
DebugMode.assertState(web.url != null)
web.url?.let { url ->
@Suppress("RegExpAnonymousGroup") when {
Regex("""^https?:https://(www\.)?(m\.)?youtube\.com""").containsMatchIn(url) ->
Regex("""v=[\w\-]{11}""").find(url)?.value?.substring(2)
@Suppress("SpellCheckingInspection")
Regex("""^https?:https://(www\.)?(m\.)?youtu.be/""").containsMatchIn(url) ->
@Suppress("SpellCheckingInspection")
Regex("""youtu.be/[\w\-]{11}""").find(url)?.value?.substring(9)
else -> null
}.also { id ->
if (id == null)
Snackbar.make(web, R.string.noLink, Snackbar.LENGTH_LONG).show()
else "https://www.youtube.com/get_video_info?video_id=$id"
.httpGet().response { _, response, result ->
when (result) {
is Result.Failure -> {
Snackbar.make(web, R.string.linkFailed, Snackbar.LENGTH_LONG).show()
return@response
}
is Result.Success -> startDownload(response)
else -> DebugMode.assertState(false)
}
}
}
}
}

private fun startDownload(response: Response) {
try {
var playerResponse = ""
for (p in response.toString().split('&'))
p.split('=').also { if (it[0] == "player_response") playerResponse = it[1] }
val parsedJson = Gson().fromJson(
URLDecoder.decode(playerResponse, "UTF-8"), PlayerResponse::class.java
)
if (parsedJson == null) {
Snackbar.make(web, R.string.copyrightProtected, Snackbar.LENGTH_LONG).show()
return
}
// if (parsedJson.playabilityStatus.status == "UNPLAYABLE") {
if (parsedJson.streamingData == null) {
Snackbar.make(web, R.string.copyrightProtected, Snackbar.LENGTH_LONG).show()
return
}

var maxITag = 0
var bestQualityAudioFormat: AdaptiveFormat? = null
for (adaptiveFormat in parsedJson.streamingData.adaptiveFormats)
if ((adaptiveFormat.mimeType.startsWith("audio"))
and (adaptiveFormat.itag >= maxITag)
) {
maxITag = adaptiveFormat.itag
bestQualityAudioFormat = adaptiveFormat
}

Snackbar.make(
// Uri.parse(bestQualityAudioFormat.url).toString()
web, bestQualityAudioFormat?.url
?: getString(R.string.noAudioStream), Snackbar.LENGTH_LONG
).show()
} catch (e: JsonSyntaxException) {
Snackbar.make(web, R.string.notJson, Snackbar.LENGTH_LONG).show()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@file:Suppress("PackageName")

package ru.BShakhovsky.Piano_Transcription.Web

import android.view.View

import android.webkit.WebView
import android.webkit.WebViewClient
import android.webkit.WebResourceRequest

import android.widget.TextView

import ru.BShakhovsky.Piano_Transcription.DebugMode
import ru.BShakhovsky.Piano_Transcription.R

class WebClient(private var requestedUrl: String, private var webText: TextView) : WebViewClient() {

override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
DebugMode.assertArgument((view != null) and (request != null))
request?.url.toString().also {
requestedUrl = it
view?.loadUrl(it)
}
return super.shouldOverrideUrlLoading(view, request)
}

override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
DebugMode.assertArgument((view != null) and (url != null))
if (url == "about:blank") with(webText) {
visibility = View.VISIBLE
text = context.getString(R.string.aboutBlank, requestedUrl)
}
}
}
Binary file modified app/src/main/res/drawable-nodpi/google_play_icon.webp
Binary file not shown.
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/back.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector xmlns:android="http:https://schemas.android.com/apk/res/android"
android:width="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportHeight="24">
<!--suppress LongLine -->
<path android:fillColor="#FF000000" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/close.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector xmlns:android="http:https://schemas.android.com/apk/res/android"
android:width="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportHeight="24">
<!--suppress LongLine -->
<path android:fillColor="#FF000000" android:pathData="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/download.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector xmlns:android="http:https://schemas.android.com/apk/res/android"
android:width="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportHeight="24">
<!--suppress LongLine -->
<path android:fillColor="#FF000000" android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/>
</vector>
6 changes: 6 additions & 0 deletions app/src/main/res/drawable/forward.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector xmlns:android="http:https://schemas.android.com/apk/res/android"
android:width="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportHeight="24">
<!--suppress LongLine -->
<path android:fillColor="#FF000000" android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
</vector>
1 change: 1 addition & 0 deletions app/src/main/res/drawable/round_corners.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
android:color="@color/colorAccent" />

<corners android:radius="24dp" />

</shape>
Loading

0 comments on commit a6c8431

Please sign in to comment.