Skip to content
This repository has been archived by the owner on Dec 12, 2020. It is now read-only.

fix: connect.sid renamed to cid #130

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ Make sure to read the initial in-app guide and watch out for tooltips.

### Authentication

For downloading and listening to early access, insert a valid `connect.sid` at the bottom of the Downloader.
For downloading and listening to early access, insert a valid `cid` at the bottom of the Downloader.
Obtain it though the process below or simply supply your credentials to the login form in the application.

1) [Sign in on monstercat.com][Monstercat-signin] and ensure that you have a valid [Monstercat Gold] subscription
2) Find a cookie for `connect.monstercat.com` in your browser
Chromium-based browsers: [chrome://settings/cookies/detail?site=connect.monstercat.com](chrome://settings/cookies/detail?site=connect.monstercat.com)
Firefox: Go to https://connect.monstercat.com/ (ignore any errors), open dev tools (Ctrl+Shift+I), go to `Storage > Cookies > https://connect.monstercat.com`
3) Find the content of `connect.sid` - a string starting with `s%3A` and around 90 characters in length
4) Copy that string into the `connect.sid` Textfield at the bottom of the Downloader
3) Find the content of `cid`
4) Copy that string into the `cid` Textfield at the bottom of the Downloader

The `connect.sid` can be used to obtain full access to your account, so treat it carefully. The application never sends it anywhere but [Monstercat] servers.
The `cid` can be used to obtain full access to your account, so treat it carefully. The application never sends it anywhere but [Monstercat] servers.
Please note though that it is persisted on your computer using the [Java Preferences API](https://stackoverflow.com/a/1320798) for your convenience.

### Caching & Offline usage
Expand Down
50 changes: 29 additions & 21 deletions src/main/xerus/monstercat/api/APIConnection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import xerus.ktutil.collections.isEmpty
import xerus.ktutil.helpers.HTTPQuery
import xerus.ktutil.javafx.properties.SimpleObservable
import xerus.ktutil.javafx.properties.listen
import xerus.ktutil.nullIfEmpty
import xerus.monstercat.Settings
import xerus.monstercat.Sheets
import xerus.monstercat.api.response.*
Expand Down Expand Up @@ -58,27 +59,28 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
fun <T> parseJSON(destination: Class<T>): T? {
val inputStream = try {
getContent()
} catch(e: IOException) {
} catch (e: IOException) {
return null
}
return try {
Sheets.JSON_FACTORY.fromInputStream(inputStream, destination)
} catch(e: Exception) {
} catch (e: Exception) {
logger.warn("Error parsing response of $uri: $e", e)
null
}
}

/** @return null when the connection fails, else the parsed result */
fun getReleases() =
parseJSON(ReleaseListResponse::class.java)?.results?.map { it.init() }
parseJSON(ReleaseListResponse::class.java)?.results?.map { it.init() }

fun getRelease() = parseJSON(ReleaseResponse::class.java)

/** @return null when the connection fails, else the parsed result */
fun getTracks() = getRelease()?.tracks

private var httpRequest: HttpUriRequest? = null

/** Aborts this connection and thus terminates the InputStream if active */
fun abort() {
httpRequest?.abort()
Expand All @@ -93,7 +95,7 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {

private var response: HttpResponse? = null
fun getResponse(): HttpResponse {
if(response == null)
if (response == null)
execute(HttpGet(uri))
return response!!
}
Expand All @@ -102,7 +104,7 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
* @throws IOException when the connection fails */
fun getContent(): InputStream {
val resp = getResponse()
if(!resp.entity.isRepeatable)
if (!resp.entity.isRepeatable)
response = null
return resp.entity.content
}
Expand Down Expand Up @@ -134,7 +136,7 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
* @param track to get the stream URL from
* @return the redirected (real) stream URL for use with [javafx.scene.media.Media] which doesn't support redirections
*/
fun getRedirectedStreamURL(track: Track): String?{
fun getRedirectedStreamURL(track: Track): String? {
val connection = APIConnection("v2", "release", track.release.id, "track-stream", track.id)
val context = HttpClientContext()
connection.execute(HttpGet(connection.uri), context)
Expand All @@ -146,7 +148,7 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
val oldClient = httpClient
val manager = connectionManager
GlobalScope.launch {
while(manager.totalStats.leased > 0)
while (manager.totalStats.leased > 0)
delay(200)
oldClient.close()
}
Expand All @@ -156,15 +158,21 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {


private fun createHttpClient(connectsid: String): CloseableHttpClient {
val cookieStore = BasicCookieStore()
cookieStore.addCookie(
connectsid.nullIfEmpty()?.let {
BasicClientCookie("cid", it).apply {
domain = "connect.monstercat.com"
path = "/"
}
}
)

return HttpClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build())
.setDefaultCookieStore(BasicClientCookie("connect.sid", connectsid).run {
domain = "connect.monstercat.com"
path = "/"
BasicCookieStore().also { it.addCookie(this) }
})
.setConnectionManager(createConnectionManager())
.build()
.setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build())
.setDefaultCookieStore(cookieStore)
.setConnectionManager(createConnectionManager())
.build()
}

private fun createConnectionManager(): PoolingHttpClientConnectionManager {
Expand All @@ -180,14 +188,14 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
}
}
// trace ConnectionManager stats
if(logger.isTraceEnabled)
if (logger.isTraceEnabled)
GlobalScope.launch {
val name = connectionManager.javaClass.simpleName + "@" + connectionManager.hashCode()
var stats = connectionManager.totalStats
val managerWeak = WeakReference(connectionManager)
while(!managerWeak.isEmpty()) {
while (!managerWeak.isEmpty()) {
val newStats = managerWeak.get()?.totalStats ?: break
if(stats.leased != newStats.leased || stats.pending != newStats.pending || stats.available != newStats.available) {
if (stats.leased != newStats.leased || stats.pending != newStats.pending || stats.available != newStats.available) {
logger.trace("$name: $newStats")
stats = newStats
}
Expand All @@ -198,13 +206,13 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
}

fun checkConnectsid(connectsid: String) {
if(connectsid.isBlank()) {
if (connectsid.isBlank()) {
connectValidity.value = ConnectValidity.NOUSER
return
}
GlobalScope.launch {
val result = getConnectValidity(connectsid)
if(QUALITY().isEmpty())
if (QUALITY().isEmpty())
result.session?.settings?.run {
QUALITY.set(preferredDownloadFormat)
}
Expand Down Expand Up @@ -238,7 +246,7 @@ class APIConnection(vararg path: String): HTTPQuery<APIConnection>() {
val code = connection.response?.statusLine?.statusCode
logger.trace("Login POST returned response code $code")
if (code !in 200..206) return false
CONNECTSID.value = (context.cookieStore.cookies.find { it.name == "connect.sid" }?.value ?: return false)
CONNECTSID.value = (context.cookieStore.cookies.find { it.name == "cid" }?.value ?: return false)
return true
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/xerus/monstercat/api/response/Session.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import com.google.api.client.util.Key
/** Session obtained by /self/session */
data class Session(@Key var user: User? = null, @Key var settings: Settings? = null)

/** User infos, included in a session if the connect.sid is valid */
/** User infos, included in a session if the cid is valid */
data class User(@Key var hasGold: Boolean = false)

/** User settings, included in a session if the connect.sid is valid */
/** User settings, included in a session if the cid is valid */
data class Settings(@Key var preferredDownloadFormat: String = "")
2 changes: 1 addition & 1 deletion src/main/xerus/monstercat/downloader/DownloaderSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ val COVERARTSIZE = DownloaderSettings.create("coverArtSize", 1024)
val COVERPATTERN = DownloaderSettings.create("coverPattern", "{%artistsTitle% - }%title%")

val QUALITY = DownloaderSettings.create("quality")
val CONNECTSID = DownloaderSettings.create("connect.sid")
val CONNECTSID = DownloaderSettings.create("cid")

val DOWNLOADTHREADS = DownloaderSettings.create("threads", Runtime.getRuntime().availableProcessors().minus(1).coerceIn(2, 4))

Expand Down
20 changes: 10 additions & 10 deletions src/main/xerus/monstercat/downloader/TabDownloader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -328,18 +328,18 @@ class TabDownloader: VTab() {
}.tooltip("Selects all Albums+EPs and then all other Songs that are not included in these"))

addRow(TextField(CONNECTSID()).apply {
promptText = "connect.sid"
tooltip = Tooltip("Log in on monstercat.com from your browser, find the cookie \"connect.sid\" from \"connect2.monstercat.com\" and copy the content into here")
promptText = "cid Cookie"
tooltip = Tooltip("Log in on monstercat.com from your browser, find the cookie \"cid\" from \"connect2.monstercat.com\" and copy the content into here")
val textListener = textProperty().debounce(400) { text ->
CONNECTSID.set(text)
}
CONNECTSID.listen { textProperty().setWithoutListener(it, textListener) }
maxWidth = Double.MAX_VALUE
}.grow(), createButton("?") {
Alert(Alert.AlertType.NONE, null, ButtonType.OK).apply {
title = "How to get your connect.sid"
title = "How to get your cid Cookie"
dialogPane.content = VBox(
Label("""Log in on monstercat.com from your browser, go to your browser's cookies (usually somewhere in settings), find the cookie "connect.sid" from "connect.monstercat.com" and copy the content into the Textfield. It should start with with "s%3A"."""),
Label("""Log in on monstercat.com from your browser, go to your browser's cookies (usually somewhere in settings), find the cookie "cid" from "connect.monstercat.com" and copy the content into the Textfield.."""),
HBox(Label("If you use Chrome, you can simply paste this into the address bar after logging in:"), TextField().apply {
isEditable = false
maxWidth = Double.MAX_VALUE
Expand All @@ -351,7 +351,7 @@ class TabDownloader: VTab() {
}
}
}),
Label("Note that your connect.sid might expire at some point, then simply go through these steps again."))
Label("Note that your cid Cookie might expire at some point, then simply go through these steps again."))
dialogPane.minHeight = Region.USE_PREF_SIZE
initWindowOwner(App.stage)
show()
Expand All @@ -361,9 +361,9 @@ class TabDownloader: VTab() {
CONNECTSID.listen {
checkFx {
isDisable = true
text = "Verifying connect.sid..."
text = "Verifying cid Cookie..."
}
logger.trace("Verifying connect.sid...")
logger.trace("Verifying cid Cookie...")
}
arrayOf<Observable>(patternValid, noItemsSelected, APIConnection.connectValidity, QUALITY, songView.ready).addListener {
onFx { refreshDownloadButton(this) }
Expand All @@ -387,7 +387,7 @@ class TabDownloader: VTab() {
if(CONNECTSID.value.isEmpty())
"Click to login to Monstercat..."
else
"Invalid connect.sid! Click to login to Monstercat..."
"Invalid cid Cookie! Click to login to Monstercat..."
}
ConnectValidity.NOGOLD -> "No Gold subscription"
ConnectValidity.GOLD -> {
Expand Down Expand Up @@ -472,8 +472,8 @@ class TabDownloader: VTab() {
noConnection -> button.setOnAction { openLoginDialog() }
else -> button.setOnAction {
button.isDisable = true
button.text = "Verifying connect.sid..."
logger.trace("Verifying connect.sid...")
button.text = "Verifying cid Cookie..."
logger.trace("Verifying cid Cookie...")
APIConnection.checkConnectsid(CONNECTSID())
}
}
Expand Down