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

挑戦の初級・中級・上級を実装 #18

Open
wants to merge 17 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
9 changes: 7 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
lazy val commonSettings = Seq(
version := "1.0.0-SNAPSHOT",
name := "nightcoreplayer",
version := "1.0.0",
organization := "jp.ed.nnn",
scalaVersion := "2.12.7",
test in assembly := {}
Expand Down Expand Up @@ -31,4 +32,8 @@ libraryDependencies += "org.openjfx" % "javafx-media" % "11-ea+25" classifier os
assemblyMergeStrategy in assembly := {
case PathList("module-info.class") => MergeStrategy.first
case x => (assemblyMergeStrategy in assembly).value(x)
}
<<<<<<< HEAD
}
=======
}
>>>>>>> webapp-curriculum-1017
2 changes: 1 addition & 1 deletion project/assembly.sbt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.8")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.8")
23 changes: 23 additions & 0 deletions src/main/scala/jp/ed/nnn/nightcoreplayer/ButtonCreator.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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 ButtonCreator {
def create(imagePath: String, event: 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(event)
button.addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler[MouseEvent] {
override def handle(t: MouseEvent): Unit =
button.setStyle("-fx-body-color: Black")
})
button.addEventHandler(MouseEvent.MOUSE_EXITED, (t: MouseEvent) =>
button.setStyle("-fx-background-color: Black"))
button
}
}
11 changes: 8 additions & 3 deletions src/main/scala/jp/ed/nnn/nightcoreplayer/DeleteCell.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import javafx.scene.control.{Button, TableCell, TableView}
import javafx.scene.layout.HBox
import javafx.scene.media.MediaView

class DeleteCell(movies: ObservableList[Movie], mediaView: MediaView, tableView: TableView[Movie]) extends TableCell[Movie, Long]() {
class DeleteCell(movies: ObservableList[Movie], mediaView: MediaView,
tableView: TableView[Movie]) extends TableCell[Movie, Long]() {
val hBox = new HBox()
hBox.setAlignment(Pos.CENTER)
val button = new Button("X")
Expand All @@ -20,10 +21,15 @@ class DeleteCell(movies: ObservableList[Movie], mediaView: MediaView, tableView:
} else {
button.setOnAction(new EventHandler[ActionEvent] {
override def handle(event: ActionEvent): Unit = {
//println("movies=" + movies.size())
val deleted = movies.toArray(Array[Movie]()).toSeq.find(m => m.id == id)
/*deleted.foreach(m => {
println("deleted=" + m.id + " " + m.fileName)
})*/
if (tableView.getSelectionModel.getSelectedItem != null
&& tableView.getSelectionModel.getSelectedItem.id == id) {
&& tableView.getSelectionModel.getSelectedItem.id == id) {
mediaView.getMediaPlayer.stop()
//println("Player stop for delete")
}
deleted.map(m => movies.removeAll(m))
}
Expand All @@ -32,5 +38,4 @@ class DeleteCell(movies: ObservableList[Movie], mediaView: MediaView, tableView:
}
setText(null)
}

}
154 changes: 36 additions & 118 deletions src/main/scala/jp/ed/nnn/nightcoreplayer/Main.scala
Original file line number Diff line number Diff line change
@@ -1,63 +1,46 @@
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.control.{Label, TableColumn, TableRow, TableView}
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 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.setText("00:00:00.000/00:00:00.000")
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]()
tableView.setItems(movies)
tableView.setRowFactory(new Callback[TableView[Movie], TableRow[Movie]]() {
override def call(param: TableView[Movie]): TableRow[Movie] = {
val row = new TableRow[Movie]()
row.setOnMouseClicked(new EventHandler[MouseEvent] {
override def handle(event: MouseEvent): Unit = {
if (event.getClickCount >= 1 && !row.isEmpty) {
playMovie(row.getItem, tableView, mediaView, timeLabel)
}
}
})
row
}
tableView.setRowFactory((param: TableView[Movie]) => {
val row = new TableRow[Movie]()
row.setOnMouseClicked((event: MouseEvent) => {
if (event.getClickCount >= 1 && !row.isEmpty) {
MoviePlayer.play(row.getItem, tableView, mediaView, timeLabel)
}
})
row
})


val fileNameColumn = new TableColumn[Movie, String]("ファイル名")
fileNameColumn.setCellValueFactory(new PropertyValueFactory("fileName"))
fileNameColumn.setPrefWidth(160)
Expand All @@ -67,110 +50,45 @@ class Main extends Application {
val deleteActionColumn = new TableColumn[Movie, Long]("削除")
deleteActionColumn.setCellValueFactory(new PropertyValueFactory("id"))
deleteActionColumn.setPrefWidth(60)
deleteActionColumn.setCellFactory(new Callback[TableColumn[Movie, Long], TableCell[Movie, Long]]() {
override def call(param: TableColumn[Movie, Long]): TableCell[Movie, Long] = {
new DeleteCell(movies, mediaView, tableView)
}
deleteActionColumn.setCellFactory((param: TableColumn[Movie, Long]) => {
new DeleteCell(movies, mediaView, tableView)
})

tableView.getColumns.setAll(fileNameColumn, timeColumn, deleteActionColumn)

val toolBar = ToolbarCreator.create(mediaView, tableView, timeLabel, primaryStage)

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

val scene = new Scene(baseBorderPane, mediaViewFitWidth + tableMinWidth, mediaViewFitHeight + toolBarMinHeight)
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: _*)
scene.setOnDragOver(new MovieFileDragOverEventHandler(scene))
scene.setOnDragDropped(new MovieFileDragDroppedEventHandler(movies))

primaryStage.fullScreenProperty().addListener(new ChangeListener[java.lang.Boolean] {
override def changed(observableValue: ObservableValue[_ <: java.lang.Boolean],
t: java.lang.Boolean, t1: java.lang.Boolean): Unit = {
if (t1) {
mediaView.fitWidthProperty().bind(scene.widthProperty())
mediaView.fitHeightProperty().bind(scene.heightProperty())
} else {
mediaView.fitWidthProperty().bind(scene.widthProperty().subtract(tableMinWidth))
mediaView.fitHeightProperty().bind(scene.heightProperty().subtract(toolBarMinHeight))
}
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()
}
})

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

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)}"
}
5 changes: 1 addition & 4 deletions src/main/scala/jp/ed/nnn/nightcoreplayer/Movie.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package jp.ed.nnn.nightcoreplayer

import javafx.scene.media.Media

import scala.beans.BeanProperty

class Movie {
Expand All @@ -25,8 +24,7 @@ class Movie {

override def equals(other: Any): Boolean = other match {
case that: Movie =>
(that canEqual this) &&
id == that.id
(that canEqual this) && id == that.id
case _ => false
}

Expand All @@ -47,5 +45,4 @@ object Movie {
movie.setMedia(media)
movie
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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(t: DragEvent): Unit = {
val db = t.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(() => {
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()
})
}
}
t.consume()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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(t: DragEvent): Unit = {
if (t.getGestureSource != scene
&& t.getDragboard.hasFiles) {
t.acceptTransferModes(TransferMode.COPY_OR_MOVE: _*)
}
t.consume()
}
}
Loading