Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webapp curriculum 1018 #14

Open
wants to merge 3 commits into
base: webapp-curriculum-1018
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
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
lazy val commonSettings = Seq(
version := "1.0.0-SNAPSHOT",
version := "1.0.0",
organization := "jp.ed.nnn",
scalaVersion := "2.12.7",
test in assembly := {}
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version = 1.2.6
sbt.version = 1.2.7
112 changes: 11 additions & 101 deletions src/main/scala/jp/ed/nnn/nightcoreplayer/Main.scala
Original file line number Diff line number Diff line change
@@ -1,44 +1,31 @@
package jp.ed.nnn.nightcoreplayer

import java.io.File
import javafx.application.Application
import javafx.beans.value.{ChangeListener, ObservableValue}
import javafx.collections.FXCollections
import javafx.event.EventHandler
import javafx.geometry.Pos
import javafx.scene.Scene
import javafx.scene.control.cell.PropertyValueFactory
import javafx.scene.control._
import javafx.scene.input.{DragEvent, MouseEvent, TransferMode}
import javafx.scene.layout.{BorderPane, HBox}
import javafx.scene.media.{Media, MediaPlayer, MediaView}
import javafx.scene.input.MouseEvent
import javafx.scene.layout.BorderPane
import javafx.scene.media.MediaView
import javafx.scene.paint.Color
import javafx.stage.Stage
import javafx.util.{Callback, Duration}
import javafx.util.Callback
import jp.ed.nnn.nightcoreplayer.SizeConstants._

object Main extends App {
Application.launch(classOf[Main], args: _*)
}

class Main extends Application {

private[this] val mediaViewFitWidth = 800
private[this] val mediaViewFitHeight = 450
private[this] val toolBarMinHeight = 50
private[this] val tableMinWidth = 300

override def start(primaryStage: Stage): Unit = {
val mediaView = new MediaView()

val timeLabel = new Label()
timeLabel.setText("00:00:00/00:00:00")
timeLabel.setTextFill(Color.WHITE)

val toolBar = new HBox(timeLabel)
toolBar.setMinHeight(toolBarMinHeight)
toolBar.setAlignment(Pos.CENTER)
toolBar.setStyle("-fx-background-color: Black")

val tableView = new TableView[Movie]()
tableView.setMinWidth(tableMinWidth)
val movies = FXCollections.observableArrayList[Movie]()
Expand All @@ -49,15 +36,14 @@ class Main extends Application {
row.setOnMouseClicked(new EventHandler[MouseEvent] {
override def handle(event: MouseEvent): Unit = {
if (event.getClickCount >= 1 && !row.isEmpty) {
playMovie(row.getItem, tableView, mediaView, timeLabel)
MoviePlayer.play(row.getItem, tableView, mediaView, timeLabel)
}
}
})
row
}
})


val fileNameColumn = new TableColumn[Movie, String]("ファイル名")
fileNameColumn.setCellValueFactory(new PropertyValueFactory("fileName"))
fileNameColumn.setPrefWidth(160)
Expand All @@ -76,101 +62,25 @@ class Main extends Application {
tableView.getColumns.setAll(fileNameColumn, timeColumn, deleteActionColumn)

val baseBorderPane = new BorderPane()
val scene = new Scene(baseBorderPane, mediaViewFitWidth + tableMinWidth, mediaViewFitHeight + toolBarMinHeight)
val toolBar = ToolbarCreator.create(mediaView, tableView, timeLabel, scene, primaryStage)

baseBorderPane.setStyle("-fx-background-color: Black")
baseBorderPane.setCenter(mediaView)
baseBorderPane.setBottom(toolBar)
baseBorderPane.setRight(tableView)

val scene = new Scene(baseBorderPane, mediaViewFitWidth + tableMinWidth, mediaViewFitHeight + toolBarMinHeight)
scene.setFill(Color.BLACK)
mediaView.fitWidthProperty().bind(scene.widthProperty().subtract(tableMinWidth))
mediaView.fitHeightProperty().bind(scene.heightProperty().subtract(toolBarMinHeight))

scene.setOnDragOver(new EventHandler[DragEvent] {
override def handle(event: DragEvent): Unit = {
if (event.getGestureSource != scene &&
event.getDragboard.hasFiles) {
event.acceptTransferModes(TransferMode.COPY_OR_MOVE: _*)
}
event.consume()
}
})

scene.setOnDragDropped(new EventHandler[DragEvent] {
override def handle(event: DragEvent): Unit = {
val db = event.getDragboard
if (db.hasFiles) {
db.getFiles.toArray(Array[File]()).toSeq.foreach { f =>
val filePath = f.getAbsolutePath
val fileName = f.getName
val media = new Media(f.toURI.toString)
val player = new MediaPlayer(media)
player.setOnReady(new Runnable {
override def run(): Unit = {
val time = formatTime(media.getDuration)
val movie = Movie(System.currentTimeMillis(), fileName, time, filePath, media)
while (movies.contains(movie)) {
movie.setId(movie.getId + 1L)
}
movies.add(movie)
player.dispose()
}
})
}
}
event.consume()
}
})
scene.setOnDragOver(new MovieFileDragOverEventHandler(scene))
scene.setOnDragDropped(new MovieFileDragDroppedEventHandler(movies))

primaryStage.setTitle("mp4ファイルをドラッグ&ドロップしてください")

primaryStage.setScene(scene)
primaryStage.show()
}

private[this] def playMovie(movie: Movie, tableView: TableView[Movie], mediaView: MediaView, timeLabel: Label): Unit = {
if (mediaView.getMediaPlayer != null) {
val oldPlayer = mediaView.getMediaPlayer
oldPlayer.stop()
oldPlayer.dispose()
}

val mediaPlayer = new MediaPlayer(movie.media)
mediaPlayer.currentTimeProperty().addListener(new ChangeListener[Duration] {
override def changed(observable: ObservableValue[_ <: Duration], oldValue: Duration, newValue: Duration): Unit =
timeLabel.setText(formatTime(mediaPlayer.getCurrentTime, mediaPlayer.getTotalDuration))
})
mediaPlayer.setOnReady(new Runnable {
override def run(): Unit =
timeLabel.setText(formatTime(mediaPlayer.getCurrentTime, mediaPlayer.getTotalDuration))
})
mediaPlayer.setOnEndOfMedia(new Runnable {
override def run(): Unit = playNext(tableView, mediaView, timeLabel)
})

mediaView.setMediaPlayer(mediaPlayer)
mediaPlayer.setRate(1.25)
mediaPlayer.play()
}

private[this] def playNext(tableView: TableView[Movie], mediaView: MediaView, timeLabel: Label): Unit = {
val selectionModel = tableView.getSelectionModel
if (selectionModel.isEmpty) return
val index = selectionModel.getSelectedIndex
val nextIndex = (index + 1) % tableView.getItems.size()
selectionModel.select(nextIndex)
val movie = selectionModel.getSelectedItem
playMovie(movie, tableView, mediaView, timeLabel)
}

private[this] def formatTime(elapsed: Duration): String = {
"%02d:%02d:%02d".format(
elapsed.toHours.toInt,
elapsed.toMinutes.toInt % 60,
elapsed.toSeconds.toInt % 60
)
}

private[this] def formatTime(elapsed: Duration, duration: Duration): String =
s"${formatTime(elapsed)}/${formatTime(duration)}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package jp.ed.nnn.nightcoreplayer

import java.io.File
import javafx.collections.ObservableList
import javafx.event.EventHandler
import javafx.scene.input.DragEvent
import javafx.scene.media.{Media, MediaPlayer}

class MovieFileDragDroppedEventHandler(movies: ObservableList[Movie]) extends EventHandler[DragEvent] {
override def handle(event: DragEvent): Unit = {
val db = event.getDragboard
if (db.hasFiles) {
db.getFiles.toArray(Array[File]()).toSeq.foreach { f =>
val filePath = f.getAbsolutePath
val fileName = f.getName
val media = new Media(f.toURI.toString)
val player = new MediaPlayer(media)
player.setOnReady(new Runnable {
override def run(): Unit = {
val time = formatTime(media.getDuration)
val movie = Movie(System.currentTimeMillis(), fileName, time, filePath, media)
while (movies.contains(movie)) {
movie.setId(movie.getId + 1L)
}
movies.add(movie)
player.dispose()
}
})
}
}
event.consume()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package jp.ed.nnn.nightcoreplayer

import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.scene.input.{DragEvent, TransferMode}


class MovieFileDragOverEventHandler(scene: Scene) extends EventHandler[DragEvent] {
override def handle(event: DragEvent): Unit = {
if (event.getGestureSource != scene &&
event.getDragboard.hasFiles) {
event.acceptTransferModes(TransferMode.COPY_OR_MOVE: _*)
}
event.consume()
}
}
57 changes: 57 additions & 0 deletions src/main/scala/jp/ed/nnn/nightcoreplayer/MoviePlayer.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package jp.ed.nnn.nightcoreplayer

import javafx.beans.value.{ChangeListener, ObservableValue}
import javafx.scene.control.{Label, TableView}
import javafx.scene.media.{MediaPlayer, MediaView}
import javafx.util.Duration

object MoviePlayer {
def play(movie: Movie, tableView: TableView[Movie], mediaView: MediaView, timeLabel: Label): Unit = {
if (mediaView.getMediaPlayer != null) {
val oldPlayer = mediaView.getMediaPlayer
oldPlayer.stop()
oldPlayer.dispose()
}
val mediaPlayer = new MediaPlayer(movie.media)
mediaPlayer.currentTimeProperty().addListener(new ChangeListener[Duration] {
override def changed(observable: ObservableValue[_ <: Duration], oldValue: Duration, newValue: Duration): Unit =
timeLabel.setText(formatTime(mediaPlayer.getCurrentTime, mediaPlayer.getTotalDuration))
})
mediaPlayer.setOnReady(new Runnable {
override def run(): Unit =
timeLabel.setText(formatTime(mediaPlayer.getCurrentTime, mediaPlayer.getTotalDuration))
})
mediaPlayer.setOnEndOfMedia(new Runnable {
override def run(): Unit = playNext(tableView, mediaView, timeLabel)
})
mediaView.setMediaPlayer(mediaPlayer)
mediaPlayer.setRate(1.25)
mediaPlayer.play()
}

sealed trait Track

object Pre extends Track

object Next extends Track

private[this] def playAt(track: Track, tableView: TableView[Movie], mediaView: MediaView, timeLabel: Label): Unit = {
val selectionModel = tableView.getSelectionModel
if (selectionModel.isEmpty) return
val index = selectionModel.getSelectedIndex
val changedIndex = track match {
case Pre => (tableView.getItems.size() + index - 1) % tableView.getItems.size()
case Next => (index + 1) % tableView.getItems.size()
}
selectionModel.select(changedIndex)
val movie = selectionModel.getSelectedItem
play(movie, tableView, mediaView, timeLabel)
}

def playPre(tableView: TableView[Movie], mediaView: MediaView, timeLabel: Label): Unit =
playAt(Pre, tableView, mediaView, timeLabel)

def playNext(tableView: TableView[Movie], mediaView: MediaView, timeLabel: Label): Unit =
playAt(Next, tableView, mediaView, timeLabel)

}
Binary file added src/main/scala/jp/ed/nnn/nightcoreplayer/STOP.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/main/scala/jp/ed/nnn/nightcoreplayer/SizeConstants.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package jp.ed.nnn.nightcoreplayer

object SizeConstants {
val mediaViewFitWidth = 800
val mediaViewFitHeight = 450
val toolBarMinHeight = 50
val tableMinWidth = 300
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package jp.ed.nnn.nightcoreplayer

import javafx.event.{ActionEvent, EventHandler}
import javafx.scene.control.Button
import javafx.scene.image.{Image, ImageView}
import javafx.scene.input.MouseEvent

object ToolbarButtonCreator {
def createButton(imagePath: String, eventHandler: EventHandler[ActionEvent]): Button = {
val buttonImage = new Image(getClass.getResourceAsStream(imagePath))
val button = new Button()
button.setGraphic(new ImageView(buttonImage))
button.setStyle("-fx-background-color: Black")
button.setOnAction(eventHandler)
button.addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler[MouseEvent]() {
override def handle(event: MouseEvent): Unit = {
button.setStyle("-fx-body-color: Black")
}
})
button.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler[MouseEvent]() {
override def handle(event: MouseEvent): Unit = {
button.setStyle("-fx-background-color: Black")
}
})
button
}
}
Loading