Skip to content

Commit

Permalink
Merge pull request #16 from thanek/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
thanek authored Oct 24, 2023
2 parents 4f07610 + 54a0e14 commit 2a33a89
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 43 deletions.
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ devices in your network.
It supports the group folders as well.

## Running in Docker

You can use the docker image with nextcloud-dlna e.g.:

```bash
Expand All @@ -20,7 +21,7 @@ docker run -d \
thanek/nextcloud-dlna
```

or, if used together with the official Nextcloud docker image using the docker-composer. See the [examples](./examples)
or, if used together with the official Nextcloud docker image using the docker-composer. See the [examples](./examples)
directory. for more details about running nextcloud-dlna server in the docker container.

You can pass to the container other env variables that are listed below.
Expand Down Expand Up @@ -48,21 +49,20 @@ or, if you've already built the project and created the jar file:

Available env variables with their default values that you can overwrite:

| env variable | default value | description |
|------------------------------|----------------|---------------------------------------------------------|
| NEXTCLOUD_DLNA_SERVER_PORT | 8080 | port on which the contentController will listen |
| NEXTCLOUD_DLNA_INTERFACE | eth0 | interface the server will be listening on |
| NEXTCLOUD_DLNA_FRIENDLY_NAME | Nextcloud-DLNA | friendly name of the DLNA service |
| NEXTCLOUD_DATA_DIR | | nextcloud installation directory (that ends with /data) |
| NEXTCLOUD_DB_TYPE | mariadb | nextcloud database type (mysql, mariadb, postgresql) |
| NEXTCLOUD_DB_HOST | localhost | nextcloud database host |
| NEXTCLOUD_DB_PORT | 3306 | nextcloud database port |
| NEXTCLOUD_DB_NAME | nextcloud | nextcloud database name |
| NEXTCLOUD_DB_USER | nextcloud | nextcloud database username |
| NEXTCLOUD_DB_PASS | nextcloud | nextcloud database password |


### Code used
| env variable | default value | description |
|------------------------------|----------------|---------------------------------------------------------------------------------------------------------------|
| NEXTCLOUD_DLNA_SERVER_PORT | 8080 | port on which the contentController will listen |
| NEXTCLOUD_DLNA_INTERFACE | | (optional) interface the server will be listening on<br/>if not given, the default local address will be used |
| NEXTCLOUD_DLNA_FRIENDLY_NAME | Nextcloud-DLNA | friendly name of the DLNA service |
| NEXTCLOUD_DATA_DIR | | nextcloud installation directory (that ends with /data) |
| NEXTCLOUD_DB_TYPE | mariadb | nextcloud database type (mysql, mariadb, postgresql) |
| NEXTCLOUD_DB_HOST | localhost | nextcloud database host |
| NEXTCLOUD_DB_PORT | 3306 | nextcloud database port |
| NEXTCLOUD_DB_NAME | nextcloud | nextcloud database name |
| NEXTCLOUD_DB_USER | nextcloud | nextcloud database username |
| NEXTCLOUD_DB_PASS | nextcloud | nextcloud database password |

### Code used

Some java code was taken from https://github.com/haku/dlnatoad
and https://github.com/UniversalMediaServer/UniversalMediaServer converted to Kotlin with upgrade to jupnp instead of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,8 @@ enum class MediaFormat(
}

companion object {
val EXT_TO_FORMAT: Map<String, MediaFormat> = values().associateBy { it.ext }

fun fromMimeType(mimetype: String): MediaFormat {
return stream(values()).filter { i -> i.mime.equals(mimetype) }
return stream(values()).filter { i -> i.mime == mimetype }
.findFirst()
.orElseThrow { RuntimeException("Unknown mime type $mimetype") }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,14 @@ class NextcloudDB(
children.filter { f -> f.mimetype == folderMimeType }
.forEach { folder -> n.addNode(asNode(folder)) }

try {
children.filter { f -> f.mimetype != folderMimeType }
.forEach { file -> n.addItem(asItem(file)) }
} catch (e: Exception) {
logger.warn(e.message)
}
children.filter { f -> f.mimetype != folderMimeType }
.forEach { file ->
runCatching {
n.addItem(asItem(file))
}.recover {
logger.warn(it.message)
}
}
}

fun maxMtime(): Long = filecacheRepository.findFirstByOrderByStorageMtimeDesc().storageMtime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,46 @@ class ServerInfoProviderImpl(
@param:Value("\${server.port}") override val port: Int,
@param:Value("\${server.interface}") private val networkInterface: String
) : ServerInfoProvider {
override val host: String get() = address!!.hostAddress
var address: InetAddress? = null
override val host: String get() = address.hostAddress
private val address: InetAddress = getInetAddress()

@PostConstruct
fun init() {
address = guessInetAddress()
logger.info("Using server address: {} and port {}", address!!.hostAddress, port)
init {
logger.info("Using server address: ${address.hostAddress} and port $port")
}

private fun guessInetAddress(): InetAddress {
return try {
val iface = NetworkInterface.getByName(networkInterface)
?: throw RuntimeException("Could not find network interface $networkInterface")
val addresses = iface.inetAddresses
while (addresses.hasMoreElements()) {
val x = addresses.nextElement()
if (x is Inet4Address) {
return x
}
private fun getInetAddress(): InetAddress {
try {
return if (networkInterface.isNotEmpty()) {
logger.debug { "Using network interface $networkInterface" }
val iface = NetworkInterface.getByName(networkInterface)
?: throw RuntimeException("Could not find network interface $networkInterface")

iface.inetAddresses.toList().filterIsInstance<Inet4Address>().first()
} else {
logger.info { "No network interface name given, trying to use default local address" }
guessLocalAddress()
}.also {
logger.debug { "Found local address ${it.hostAddress}" }
}
InetAddress.getLocalHost()
} catch (e: UnknownHostException) {
throw RuntimeException(e)
} catch (e: SocketException) {
throw RuntimeException(e)
}
}

// perform fake request to 1.1.1.1:80 just to get the localAddress
// with use of the default routing.
// if it fails, we use the localAddress() which can be wrong
private fun guessLocalAddress() = try {
DatagramSocket().use { s ->
s.connect(InetAddress.getByAddress(byteArrayOf(1, 1, 1, 1)), 80)
s.localAddress
}
} catch (e: Exception) {
logger.warn { e.message }
InetAddress.getLocalHost()
}

companion object : KLogging()
}
2 changes: 1 addition & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
server:
port: ${NEXTCLOUD_DLNA_SERVER_PORT:8080}
interface: ${NEXTCLOUD_DLNA_INTERFACE:eth0}
interface: ${NEXTCLOUD_DLNA_INTERFACE:}
friendlyName: ${NEXTCLOUD_DLNA_FRIENDLY_NAME:Nextcloud-DLNA}

nextcloud:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package net.schowek.nextclouddlna.nextcloud.db

import net.schowek.nextclouddlna.nextcloud.config.NextcloudConfigDiscovery
import net.schowek.nextclouddlna.nextcloud.content.ContentNode
import spock.lang.Specification

import static net.schowek.nextclouddlna.nextcloud.content.MediaFormat.*

class NextcloudDBTest extends Specification {
static final def thumbStorageId = 2
static final def TEXT_MD = 1
static final def VIDEO_MP4 = 2
static final def IMAGE_JPG = 3
static final def DIRECTORY = 4
static final def AUDIO_MP3 = 5
static final def APP_PDF = 6
def configDiscovery = Mock(NextcloudConfigDiscovery)
def mimeTypeRepository = Mock(MimetypeRepository)
def filecacheRepository = Mock(FilecacheRepository)
def groupFolderRepository = Mock(GroupFolderRepository)
def tmpDir = File.createTempDir()

def setup() {
configDiscovery.getAppDataDir() >> "/tmp/app_0987654321"
configDiscovery.getNextcloudDir() >> tmpDir
filecacheRepository.findFirstByPath(configDiscovery.appDataDir)
>> new Filecache(999, thumbStorageId, "thumbs", 0, "thumbs", DIRECTORY, 123L, 0L, 0L)
mimeTypeRepository.findAll() >> [
new Mimetype(DIRECTORY, 'httpd/unix-directory'),
new Mimetype(TEXT_MD, 'text/markdown'),
new Mimetype(AUDIO_MP3, 'audio/mpeg'),
new Mimetype(VIDEO_MP4, 'video/mp4'),
new Mimetype(IMAGE_JPG, 'image/jpeg'),
new Mimetype(APP_PDF, 'application/pdf')
]
}

def cleanup() {
tmpDir.delete()
}

def "should append children to the parent node"() {
given:
def parentId = 123
def parentNode = new ContentNode(parentId, 0, "stuff")
filecacheRepository.findByParent(parentId) >> [
aFilecache(1, "/stuff/foo.jpg", parentId, IMAGE_JPG),
aFilecache(2, "/stuff/readme.md", parentId, TEXT_MD),
aFilecache(4, "/stuff/baz.mp3", parentId, AUDIO_MP3),
aFilecache(3, "/stuff/bar.jpeg", parentId, IMAGE_JPG),
aFilecache(5, "/stuff/blah.mp4", parentId, VIDEO_MP4),
aFilecache(6, "/stuff/documents", parentId, DIRECTORY),
aFilecache(7, "/stuff/documents/resume.pdf", 6, APP_PDF)
]

def sut = new NextcloudDB(configDiscovery, mimeTypeRepository, filecacheRepository, groupFolderRepository)

when:
sut.appendChildren(parentNode)

then: "appends only items with known media types"
parentNode.items.any()
parentNode.items.find { it.name == 'foo.jpg' }.format.mime == JPEG.mime
parentNode.items.find { it.name == 'bar.jpeg' }.format.mime == JPEG.mime
parentNode.items.find { it.name == 'baz.mp3' }.format.mime == MP3.mime
parentNode.items.find { it.name == 'blah.mp4' }.format.mime == MP4.mime
parentNode.items.find { it.name == 'readme.md' } == null

then: "appends all subnodes without their children"
parentNode.nodes.size() == 1
with(parentNode.nodes[0]) {
assert it.name == 'documents'
assert it.items == []
assert it.nodes == []
}
}

private static def aFilecache(int id, String path, int parent, int mimeType) {
def name = new File(path).getName()
return new Filecache(id, thumbStorageId, path, parent, name, mimeType, 0L, 0L, 0L)
}
}

0 comments on commit 2a33a89

Please sign in to comment.