Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Comment thread
twangodev marked this conversation as resolved.
Comment thread
twangodev marked this conversation as resolved.

# Upload the Kover report to CodeCov
- name: Upload Code Coverage Report
uses: codecov/codecov-action@v5
Expand Down
17 changes: 17 additions & 0 deletions 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,20 @@ kover {
}
}

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

tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
reports {
html.required.set(true)
xml.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
54 changes: 54 additions & 0 deletions detekt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
complexity:
LongMethod:
threshold: 80
LongParameterList:
functionThreshold: 8
constructorThreshold: 10
TooManyFunctions:
thresholdInClasses: 15
thresholdInFiles: 15
CyclomaticComplexMethod:
threshold: 20
NestedBlockDepth:
threshold: 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
UnnecessaryAbstractClass:
active: false
WildcardImport:
active: true

formatting:
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 = "1.23.7"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
intelliJPlatform = "2.13.1"
kotlin = "2.3.20"
kover = "0.9.7"
qodana = "2025.3.2"

[libraries]
detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
opentest4j = { group = "org.opentest4j", name = "opentest4j", version.ref = "opentest4j" }

[plugins]
detekt = { id = "io.gitlab.arturbosch.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
2 changes: 1 addition & 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,4 @@ object JetPlayConstants {
const val PLUGIN_NAME = "JetPlay"
const val NOTIFICATION_GROUP_ID = PLUGIN_NAME
const val ISSUES_URL = "https://github.com/twangodev/jetplay/issues"
}
}
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 = "",
)
32 changes: 17 additions & 15 deletions src/main/kotlin/dev/twango/jetplay/editor/MediaFileEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import javax.swing.JPanel
class MediaFileEditor(
private val project: Project,
private val file: VirtualFile,
private val source: MediaSource
private val source: MediaSource,
) : UserDataHolderBase(), FileEditor {

private val browser = JBCefBrowser()
Expand All @@ -41,7 +41,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 +66,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 +93,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 +107,8 @@ class MediaFileEditor(
fileName = source.fileName,
fileExtension = source.extension,
mediaUrl = source.resolvePlayableUrl(),
ui = uiStrings
)
ui = uiStrings,
),
)
}

Expand All @@ -119,22 +119,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 @@ -28,4 +28,4 @@ class MediaFileEditorProvider : FileEditorProvider, DumbAware {
override fun getEditorTypeId(): String = "media-player"

override fun getPolicy(): FileEditorPolicy = FileEditorPolicy.HIDE_DEFAULT_EDITOR
}
}
10 changes: 9 additions & 1 deletion src/main/kotlin/dev/twango/jetplay/media/MediaClassification.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ package dev.twango.jetplay.media
object MediaClassification {

private val VIDEO_EXTENSIONS = setOf(
"mp4", "m4v", "mkv", "avi", "mov", "wmv", "flv", "webm", "ogv"
"mp4",
"m4v",
"mkv",
"avi",
"mov",
"wmv",
"flv",
"webm",
"ogv",
)
Comment thread
twangodev marked this conversation as resolved.

fun isVideo(extension: String): Boolean =
Expand Down
55 changes: 42 additions & 13 deletions src/main/kotlin/dev/twango/jetplay/transcode/MediaTranscoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,28 @@ object MediaTranscoder {

private val log = Logger.getInstance(MediaTranscoder::class.java)

private const val DEFAULT_VIDEO_BITRATE = 2_000_000
private const val DEFAULT_FRAME_RATE = 30.0
private const val DEFAULT_GOP_SIZE = 120
private const val OPUS_BITRATE = 128_000
private const val OPUS_SAMPLE_RATE = 48_000
private const val PROGRESS_COMPLETE = 100.0
private const val PROGRESS_MAX = 99.9
private const val PROGRESS_PRECISION = 10
private const val BYTES_PER_KB = 1024

// Formats that JCEF (Chromium) can play natively without transcoding
private val JCEF_NATIVE_EXTENSIONS = setOf(
"webm", "ogv", // video
"ogg", "oga", "opus", "wav", "flac", "mp3" // audio
// video
"webm",
"ogv",
// audio
"ogg",
"oga",
"opus",
"wav",
"flac",
"mp3",
)
Comment thread
twangodev marked this conversation as resolved.

fun needsTranscoding(extension: String?): Boolean {
Expand All @@ -31,31 +49,42 @@ object MediaTranscoder {
val hasAudio = grabber.audioChannels > 0
val totalMicroseconds = grabber.lengthInTime

val recorder = FFmpegFrameRecorder(outputFile, grabber.imageWidth, grabber.imageHeight, grabber.audioChannels)
val recorder = FFmpegFrameRecorder(
outputFile,
grabber.imageWidth,
grabber.imageHeight,
grabber.audioChannels,
)
recorder.format = "webm"
if (hasVideo) {
recorder.videoCodec = avcodec.AV_CODEC_ID_VP9
recorder.videoBitrate = grabber.videoBitrate.takeIf { it > 0 } ?: 2_000_000
recorder.frameRate = grabber.frameRate.takeIf { it > 0 } ?: 30.0
recorder.gopSize = 120
recorder.videoBitrate = grabber.videoBitrate.takeIf { it > 0 } ?: DEFAULT_VIDEO_BITRATE
recorder.frameRate = grabber.frameRate.takeIf { it > 0 } ?: DEFAULT_FRAME_RATE
recorder.gopSize = DEFAULT_GOP_SIZE
}
if (hasAudio) {
recorder.audioCodec = avcodec.AV_CODEC_ID_OPUS
recorder.audioBitrate = 128_000
recorder.sampleRate = 48000
recorder.audioBitrate = OPUS_BITRATE
recorder.sampleRate = OPUS_SAMPLE_RATE
recorder.audioChannels = grabber.audioChannels
}
recorder.start()

var lastReportedTenth = -1L
try {
while (true) {
val frame = grabber.grabFrame(hasAudio, true, true, false, false) ?: break
val frame = grabber.grabFrame(
hasAudio,
true,
true,
false,
false,
) ?: break
recorder.record(frame)

if (totalMicroseconds > 0) {
val pct = (grabber.timestamp.toDouble() * 100.0 / totalMicroseconds).coerceIn(0.0, 99.9)
val tenth = (pct * 10).toLong()
val pct = (grabber.timestamp.toDouble() * PROGRESS_COMPLETE / totalMicroseconds).coerceIn(0.0, PROGRESS_MAX)
val tenth = (pct * PROGRESS_PRECISION).toLong()
if (tenth != lastReportedTenth) {
lastReportedTenth = tenth
Comment thread
twangodev marked this conversation as resolved.
onProgress(pct)
Expand All @@ -72,8 +101,8 @@ object MediaTranscoder {
grabber.release()
}

onProgress(100.0)
log.info("Transcoded ${inputFile.name} -> ${outputFile.name} (${outputFile.length() / 1024}KB)")
onProgress(PROGRESS_COMPLETE)
log.info("Transcoded ${inputFile.name} -> ${outputFile.name} (${outputFile.length() / BYTES_PER_KB}KB)")
Comment thread
twangodev marked this conversation as resolved.
Outdated
return outputFile
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlin.concurrent.thread

class TranscodeSession(
private val inputFile: File,
private val bridge: PlayerBridge
private val bridge: PlayerBridge,
) {

companion object {
Expand Down
Loading
Loading