Skip to content
Merged
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
21 changes: 21 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true

[*.{kt,kts}]
max_line_length = 140

[*.md]
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2

[*.{json,toml}]
indent_size = 2
11 changes: 11 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ jobs:
name: Test
needs: [ build ]
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
steps:

# Free GitHub Actions Environment Disk Space
Expand Down Expand Up @@ -139,6 +142,14 @@ jobs:
name: tests-result
path: ${{ github.workspace }}/build/reports/tests

# Upload Detekt SARIF for GitHub Code Scanning
- name: Upload Detekt SARIF
if: ${{ always() }}
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: build/reports/detekt/detekt.sarif
category: detekt

# Upload the Kover report to CodeCov
- name: Upload Code Coverage Report
uses: codecov/codecov-action@v5
Expand Down
18 changes: 17 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ plugins {
id("java")
alias(libs.plugins.kotlin)
alias(libs.plugins.intelliJPlatform)
alias(libs.plugins.detekt)
alias(libs.plugins.qodana)
alias(libs.plugins.kover)
}
Expand Down Expand Up @@ -47,6 +48,8 @@ dependencies {
implementation("org.bytedeco:javacpp:1.5.13:macosx-arm64")
implementation("org.bytedeco:javacpp:1.5.13:windows-x86_64")

detektPlugins(libs.detekt.formatting)

testImplementation(libs.junit)
testImplementation(libs.opentest4j)

Expand Down Expand Up @@ -111,6 +114,19 @@ kover {
}
}

detekt {
buildUponDefaultConfig = true
config.setFrom(files("$projectDir/detekt.yml"))
basePath.set(projectDir)
}

tasks.withType<dev.detekt.gradle.Detekt>().configureEach {
reports {
html.required.set(true)
sarif.required.set(true)
}
}

val buildPlayerUi by tasks.registering(Exec::class) {
workingDir = file("ui")
commandLine("bash", "-lc", "npm run build")
Expand Down Expand Up @@ -150,4 +166,4 @@ intellijPlatformTesting {
}
}
}
}
}
52 changes: 52 additions & 0 deletions detekt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
complexity:
LongMethod:
allowedLines: 80
LongParameterList:
allowedFunctionParameters: 8
allowedConstructorParameters: 10
TooManyFunctions:
allowedFunctionsPerClass: 15
allowedFunctionsPerFile: 15
CyclomaticComplexMethod:
allowedComplexity: 20
NestedBlockDepth:
allowedDepth: 5

exceptions:
TooGenericExceptionCaught:
active: false

style:
ReturnCount:
max: 3
MagicNumber:
ignorePropertyDeclaration: true
ignoreCompanionObjectPropertyDeclaration: true
ignoreAnnotation: true
ignoreEnums: true
ignoreNamedArgument: true
ignoreLocalVariableDeclaration: true
ignoreNumbers:
- '-1'
- '0'
- '1'
- '2'
MaxLineLength:
maxLineLength: 140
WildcardImport:
active: true

ktlint:
MaximumLineLength:
maxLineLength: 140
ArgumentListWrapping:
maxLineLength: 140
TrailingCommaOnCallSite:
active: true
useTrailingCommaOnCallSite: true
TrailingCommaOnDeclarationSite:
active: true
useTrailingCommaOnDeclarationSite: true
ImportOrdering:
active: true
layout: '*,java.**,javax.**,kotlin.**,^'
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@ junit = "4.13.2"
opentest4j = "1.3.0"

# plugins
detekt = "2.0.0-alpha.2"
intelliJPlatform = "2.13.1"
kotlin = "2.3.20"
kover = "0.9.7"
qodana = "2025.3.2"

[libraries]
detekt-formatting = { module = "dev.detekt:detekt-rules-ktlint-wrapper", version.ref = "detekt" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
opentest4j = { group = "org.opentest4j", name = "opentest4j", version.ref = "opentest4j" }

[plugins]
detekt = { id = "dev.detekt", version.ref = "detekt" }
intelliJPlatform = { id = "org.jetbrains.intellij.platform", version.ref = "intelliJPlatform" }
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/dev/twango/jetplay/JetPlayConstants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ object JetPlayConstants {
const val PLUGIN_NAME = "JetPlay"
const val NOTIFICATION_GROUP_ID = PLUGIN_NAME
const val ISSUES_URL = "https://github.com/twangodev/jetplay/issues"
}
const val BYTES_PER_KB = 1024
}
27 changes: 11 additions & 16 deletions src/main/kotlin/dev/twango/jetplay/browser/PlayerBridge.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,13 @@ class PlayerBridge(private val browser: JBCefBrowser) {
}
}

fun updateProgress(percent: Double) =
executeJs("window.jetplayUpdateProgress?.($percent)")
fun updateProgress(percent: Double) = executeJs("window.jetplayUpdateProgress?.($percent)")

fun updateDownloadProgress(percent: Double) =
executeJs("window.jetplayUpdateDownloadProgress?.($percent)")
fun updateDownloadProgress(percent: Double) = executeJs("window.jetplayUpdateDownloadProgress?.($percent)")

fun mediaReady(url: String) =
executeJs("window.jetplayReady?.('${escapeJs(url)}')")
fun mediaReady(url: String) = executeJs("window.jetplayReady?.('${escapeJs(url)}')")

fun showError(message: String) =
executeJs("window.jetplayError?.('${escapeJs(message)}')")
fun showError(message: String) = executeJs("window.jetplayError?.('${escapeJs(message)}')")

fun loadHtml(html: String) = browser.loadHTML(html)

Expand All @@ -50,13 +46,12 @@ class PlayerBridge(private val browser: JBCefBrowser) {
}

companion object {
fun escapeJs(s: String): String =
s.replace("\\", "\\\\")
.replace("'", "\\'")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "")
.replace("<", "\\x3c")
.replace(">", "\\x3e")
fun escapeJs(s: String): String = s.replace("\\", "\\\\")
.replace("'", "\\'")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\r", "")
.replace("<", "\\x3c")
.replace(">", "\\x3e")
}
}
4 changes: 2 additions & 2 deletions src/main/kotlin/dev/twango/jetplay/browser/PlayerConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ data class PlayerConfig(
val errorMessage: String = "",
val transcodingReason: String = "",
val downloadingReason: String = "",
val ui: UiStrings = UiStrings()
val ui: UiStrings = UiStrings(),
)

data class UiStrings(
val downloadingLabel: String = "",
val transcodingLabel: String = "",
val transcodingTip: String = "",
val errorTitle: String = ""
val errorTitle: String = "",
)
41 changes: 20 additions & 21 deletions src/main/kotlin/dev/twango/jetplay/browser/PlayerHtmlLoader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,24 @@ class PlayerHtmlLoader(private val bridge: PlayerBridge) {
bridge.loadHtml(playerHtml.replace("</head>", "$configScript</head>"))
}

private fun buildConfigScript(config: PlayerConfig, openLinkJs: String): String =
buildString {
append("<script>")
append("window.jetplayOpenLink = function(url) { $openLinkJs };")
append("window.jetplay = {")
append("state: '${config.state}',")
append("isVideo: ${config.isVideo},")
append("fileName: '${PlayerBridge.escapeJs(config.fileName)}',")
append("fileExtension: '${PlayerBridge.escapeJs(config.fileExtension)}',")
config.mediaUrl?.let { append("mediaUrl: '${PlayerBridge.escapeJs(it)}',") }
if (config.errorMessage.isNotEmpty()) append("errorMessage: '${PlayerBridge.escapeJs(config.errorMessage)}',")
if (config.transcodingReason.isNotEmpty()) append("transcodingReason: '${PlayerBridge.escapeJs(config.transcodingReason)}',")
if (config.downloadingReason.isNotEmpty()) append("downloadingReason: '${PlayerBridge.escapeJs(config.downloadingReason)}',")
append("ui: {")
append("downloadingLabel: '${PlayerBridge.escapeJs(config.ui.downloadingLabel)}',")
append("transcodingLabel: '${PlayerBridge.escapeJs(config.ui.transcodingLabel)}',")
append("transcodingTip: '${PlayerBridge.escapeJs(config.ui.transcodingTip)}',")
append("errorTitle: '${PlayerBridge.escapeJs(config.ui.errorTitle)}',")
append("},")
append("};</script>")
}
private fun buildConfigScript(config: PlayerConfig, openLinkJs: String): String = buildString {
append("<script>")
append("window.jetplayOpenLink = function(url) { $openLinkJs };")
append("window.jetplay = {")
append("state: '${config.state}',")
append("isVideo: ${config.isVideo},")
append("fileName: '${PlayerBridge.escapeJs(config.fileName)}',")
append("fileExtension: '${PlayerBridge.escapeJs(config.fileExtension)}',")
config.mediaUrl?.let { append("mediaUrl: '${PlayerBridge.escapeJs(it)}',") }
if (config.errorMessage.isNotEmpty()) append("errorMessage: '${PlayerBridge.escapeJs(config.errorMessage)}',")
if (config.transcodingReason.isNotEmpty()) append("transcodingReason: '${PlayerBridge.escapeJs(config.transcodingReason)}',")
if (config.downloadingReason.isNotEmpty()) append("downloadingReason: '${PlayerBridge.escapeJs(config.downloadingReason)}',")
append("ui: {")
append("downloadingLabel: '${PlayerBridge.escapeJs(config.ui.downloadingLabel)}',")
append("transcodingLabel: '${PlayerBridge.escapeJs(config.ui.transcodingLabel)}',")
append("transcodingTip: '${PlayerBridge.escapeJs(config.ui.transcodingTip)}',")
append("errorTitle: '${PlayerBridge.escapeJs(config.ui.errorTitle)}',")
append("},")
append("};</script>")
}
}
38 changes: 19 additions & 19 deletions src/main/kotlin/dev/twango/jetplay/editor/MediaFileEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@ import java.beans.PropertyChangeListener
import javax.swing.JComponent
import javax.swing.JPanel

class MediaFileEditor(
private val project: Project,
private val file: VirtualFile,
private val source: MediaSource
) : UserDataHolderBase(), FileEditor {
class MediaFileEditor(private val project: Project, private val file: VirtualFile, private val source: MediaSource) :
UserDataHolderBase(),
FileEditor {

private val browser = JBCefBrowser()
private val bridge = PlayerBridge(browser)
Expand All @@ -41,7 +39,7 @@ class MediaFileEditor(
downloadingLabel = JetPlayBundle.message("ui.downloading.label"),
transcodingLabel = JetPlayBundle.message("ui.transcoding.label"),
transcodingTip = JetPlayBundle.message("ui.transcoding.tip"),
errorTitle = JetPlayBundle.message("ui.error.title")
errorTitle = JetPlayBundle.message("ui.error.title"),
)

private val component: JComponent = JPanel(BorderLayout()).apply {
Expand All @@ -66,8 +64,8 @@ class MediaFileEditor(
fileName = source.fileName,
fileExtension = source.extension,
downloadingReason = JetPlayBundle.message("downloading.reason"),
ui = uiStrings
)
ui = uiStrings,
),
)
downloadSession = DownloadSession(source as RemoteFileMediaSource, bridge) {
if (source.needsTranscoding) {
Expand All @@ -93,8 +91,8 @@ class MediaFileEditor(
fileName = source.fileName,
fileExtension = source.extension,
transcodingReason = JetPlayBundle.message("transcoding.reason", source.extension.uppercase()),
ui = uiStrings
)
ui = uiStrings,
),
)
}
transcodeSession = TranscodeSession(source.toLocalFile(), bridge).also { it.start() }
Expand All @@ -107,8 +105,8 @@ class MediaFileEditor(
fileName = source.fileName,
fileExtension = source.extension,
mediaUrl = source.resolvePlayableUrl(),
ui = uiStrings
)
ui = uiStrings,
),
)
}

Expand All @@ -119,22 +117,24 @@ class MediaFileEditor(
.createNotification(
JetPlayBundle.message("error.transcoding.notification.title"),
JetPlayBundle.message("error.transcoding.notification.content", source.extension.uppercase()),
NotificationType.WARNING
NotificationType.WARNING,
)
.addAction(
NotificationAction.createSimpleExpiring(JetPlayBundle.message("action.report.issue")) {
BrowserUtil.browse(JetPlayConstants.ISSUES_URL)
},
)
.addAction(NotificationAction.createSimpleExpiring(JetPlayBundle.message("action.report.issue")) {
BrowserUtil.browse(JetPlayConstants.ISSUES_URL)
})
.notify(project)
}

override fun getComponent(): JComponent = component
override fun getPreferredFocusedComponent(): JComponent = component
override fun getName(): String = JetPlayBundle.message("editor.name")
override fun setState(state: FileEditorState) {}
override fun setState(state: FileEditorState) = Unit
override fun isModified(): Boolean = false
override fun isValid(): Boolean = file.isValid
override fun addPropertyChangeListener(listener: PropertyChangeListener) {}
override fun removePropertyChangeListener(listener: PropertyChangeListener) {}
override fun addPropertyChangeListener(listener: PropertyChangeListener) = Unit
override fun removePropertyChangeListener(listener: PropertyChangeListener) = Unit
override fun getFile(): VirtualFile = file

override fun dispose() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import com.intellij.openapi.vfs.VirtualFile
import dev.twango.jetplay.media.LocalFileMediaSource
import dev.twango.jetplay.media.RemoteFileMediaSource

class MediaFileEditorProvider : FileEditorProvider, DumbAware {
class MediaFileEditorProvider :
FileEditorProvider,
DumbAware {

override fun accept(project: Project, file: VirtualFile): Boolean {
return file.fileType == MediaFileType.INSTANCE
}
override fun accept(project: Project, file: VirtualFile): Boolean = file.fileType == MediaFileType.INSTANCE

override fun createEditor(project: Project, file: VirtualFile): FileEditor {
val source = if (file.fileSystem is LocalFileSystem) {
Expand All @@ -28,4 +28,4 @@ class MediaFileEditorProvider : FileEditorProvider, DumbAware {
override fun getEditorTypeId(): String = "media-player"

override fun getPolicy(): FileEditorPolicy = FileEditorPolicy.HIDE_DEFAULT_EDITOR
}
}
Loading
Loading