Merge pull request #9 from jlengrand/feat/invisible-jsoup

Feat/invisible jsoup

Fixes #8
This commit is contained in:
julien Lengrand-Lambert
2025-05-16 23:58:00 +02:00
committed by GitHub
13 changed files with 193 additions and 95 deletions

View File

@@ -17,7 +17,7 @@ jobs:
distribution: 'zulu' distribution: 'zulu'
java-version: 21 java-version: 21
- name: Publish to MavenCentral - name: Publish to MavenCentral
run: ./gradlew publishToMavenCentral --no-configuration-cache run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache
env: env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}

1
.idea/gradle.xml generated
View File

@@ -10,6 +10,7 @@
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/demo" /> <option value="$PROJECT_DIR$/demo" />
<option value="$PROJECT_DIR$/demo-remote" />
<option value="$PROJECT_DIR$/opengraphkt" /> <option value="$PROJECT_DIR$/opengraphkt" />
</set> </set>
</option> </option>

View File

@@ -1,5 +1,8 @@
# OpenGraphKt # OpenGraphKt
![Maven Central Version](https://img.shields.io/maven-central/v/fr.lengrand/opengraphkt)
[OpenGraphKt](https://github.com/jlengrand/OpenGraphKt) is a minimalist Kotlin library to work with the [Open Graph tags](https://ogp.me/) protocol. [OpenGraphKt](https://github.com/jlengrand/OpenGraphKt) is a minimalist Kotlin library to work with the [Open Graph tags](https://ogp.me/) protocol.
OpenGraphKt is a tiny wrapper on top of JSoup. OpenGraphKt is a tiny wrapper on top of JSoup.

View File

@@ -0,0 +1,28 @@
plugins {
kotlin("jvm")
application
}
group = "fr.lengrand"
repositories {
mavenCentral()
}
dependencies {
implementation("org.jsoup:jsoup:1.20.1")
implementation("fr.lengrand:opengraphkt:0.0.1")
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(23)
}
application {
mainClass = "fr.lengrand.opengraphkt.MainKt"
}

View File

@@ -0,0 +1,38 @@
package fr.lengrand.opengraphkt
import org.jsoup.Jsoup
/**
* This module is only here to verify that the latest Maven Central release can be imported and used as intended.
*/
fun main() {
val parser = OpenGraphParser()
val html = """
<!DOCTYPE html>
<html>
<head>
<title>Open Graph Example</title>
<meta property="og:title" content="The Rock" />
<meta property="og:type" content="video.movie" />
<meta property="og:url" content="https://example.com/the-rock" />
<meta property="og:image" content="https://example.com/rock.jpg" />
<meta property="og:image:width" content="300" />
<meta property="og:image:height" content="200" />
<meta property="og:description" content="An action movie about a rock" />
<meta property="og:site_name" content="Example Movies" />
</head>
<body>
<h1>Example Page</h1>
</body>
</html>
""".trimIndent()
println("Parsing from JSoup Document")
val doc = Jsoup.parse(html)
val openGraphDataDoc = parser.parse(doc)
println("Title: ${openGraphDataDoc.title}")
println("Is valid: ${openGraphDataDoc.isValid()}")
}

View File

@@ -1,12 +1,11 @@
import org.gradle.kotlin.dsl.implementation import org.gradle.kotlin.dsl.implementation
plugins { plugins {
kotlin("jvm") version "2.1.21" kotlin("jvm")
application application
} }
group = "nl.lengrand" group = "fr.lengrand"
version = "0.1-SNAPSHOT"
repositories { repositories {
mavenCentral() mavenCentral()
@@ -18,22 +17,10 @@ dependencies {
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
} }
java {
withSourcesJar()
}
tasks.test { tasks.test {
useJUnitPlatform() useJUnitPlatform()
} }
tasks.jar {
manifest {
attributes(mapOf("Implementation-Title" to project.name,
"Implementation-Version" to project.version))
}
}
kotlin { kotlin {
jvmToolchain(23) jvmToolchain(23)
} }

View File

@@ -1,19 +1,19 @@
package fr.lengrand.opengraphkt package fr.lengrand.opengraphkt
import org.jsoup.Jsoup
import java.io.File import java.io.File
import java.net.URI
/** /**
* Example demonstrating how to use the OpenGraphParser to extract Open Graph data from HTML. * Example demonstrating how to use the OpenGraphParser to extract Open Graph data from HTML.
*/ */
fun main() { fun main() {
val parser = OpenGraphParser() val parser = OpenGraphParser()
val fetcher = DocumentFetcher()
// Example 1: Parse Open Graph data from a URL // Example 1: Parse Open Graph data from a URL
println("Example 1: Parsing from URL") println("Example 1: Parsing from URL")
try { try {
val document = fetcher.fromUrl("https://www.imdb.com/title/tt0068646/") val openGraphData = parser.parse(URI("https://www.imdb.com/title/tt0068646/").toURL())
val openGraphData = parser.parse(document)
println("Title: ${openGraphData.title}") println("Title: ${openGraphData.title}")
println("Is valid: ${openGraphData.isValid()}") println("Is valid: ${openGraphData.isValid()}")
@@ -28,8 +28,7 @@ fun main() {
val resourceFile = File(resourceUrl.toURI()) val resourceFile = File(resourceUrl.toURI())
// Parse the file // Parse the file
val document = fetcher.fromFile(resourceFile) val openGraphData = parser.parse(resourceFile)
val openGraphData = parser.parse(document)
println("Title: ${openGraphData.title}") println("Title: ${openGraphData.title}")
println("Is valid: ${openGraphData.isValid()}") println("Is valid: ${openGraphData.isValid()}")
@@ -38,7 +37,7 @@ fun main() {
} }
// Example 3: Parse Open Graph data from an HTML string // Example 3: Parse Open Graph data from an HTML string
println("\nExample 2: Parsing from HTML string") println("\nExample 3: Parsing from HTML string")
val html = """ val html = """
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@@ -59,9 +58,17 @@ fun main() {
</html> </html>
""".trimIndent() """.trimIndent()
val document = fetcher.fromString(html) val openGraphData = parser.parse(html)
val openGraphData = parser.parse(document)
println("Title: ${openGraphData.title}") println("Title: ${openGraphData.title}")
println("Is valid: ${openGraphData.isValid()}") println("Is valid: ${openGraphData.isValid()}")
// Example 4: Parse Open Graph data from a Jsoup Document
println("\nExample 4: Parsing from JSoup Document")
val doc = Jsoup.parse(html)
val openGraphDataDoc = parser.parse(doc)
println("Title: ${openGraphDataDoc.title}")
println("Is valid: ${openGraphDataDoc.isValid()}")
} }

View File

@@ -1,7 +1,7 @@
import com.vanniktech.maven.publish.SonatypeHost import com.vanniktech.maven.publish.SonatypeHost
plugins { plugins {
kotlin("jvm") version "2.1.21" kotlin("jvm")
id("com.vanniktech.maven.publish") version "0.30.0" id("com.vanniktech.maven.publish") version "0.30.0"
} }

View File

@@ -1,29 +0,0 @@
package fr.lengrand.opengraphkt
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import java.io.File
/**
* DocumentFetcher's job is to take any type of input and transform it into a JSoup document for the Parser to then do its job
*/
class DocumentFetcher {
fun fromUrl(url: String): Document {
return Jsoup.connect(url).get()
}
fun fromString(html: String): Document {
return Jsoup.parse(html)
}
/**
* Parses HTML from a file and returns a JSoup Document
* @param file The file to parse
* @param charsetName The charset to use for parsing (default is UTF-8)
* @return A JSoup Document representing the parsed HTML
*/
fun fromFile(file: File, charsetName: String = "UTF-8") : Document {
return Jsoup.parse(file, charsetName)
}
}

View File

@@ -1,7 +1,10 @@
package fr.lengrand.opengraphkt package fr.lengrand.opengraphkt
import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.select.Elements import org.jsoup.select.Elements
import java.io.File
import java.net.URL
data class OpenGraphTag( data class OpenGraphTag(
val property: String, val property: String,
@@ -149,6 +152,40 @@ class OpenGraphParser {
return buildOpenGraphData(openGraphTags) return buildOpenGraphData(openGraphTags)
} }
/**
* Extracts all Open Graph tags from a URL and returns a structured OpenGraphData object.
*
* @param url The URL to be parsed for Open Graph information.
* @return An OpenGraphData object containing all extracted Open Graph data.
*/
fun parse(url: URL) : OpenGraphData {
val doc = Jsoup.connect(url.toString()).get()
return parse(doc)
}
/**
* Extracts all Open Graph tags from a raw HTML String and returns a structured OpenGraphData object.
*
* @param html The raw HTML String to be parsed for Open Graph information.
* @return An OpenGraphData object containing all extracted Open Graph data.
*/
fun parse(html: String) : OpenGraphData {
val doc = Jsoup.parse(html)
return parse(doc)
}
/**
* Extracts all Open Graph tags from a raw HTML String and returns a structured OpenGraphData object.
*
* @param file The file to parse
* @param charset The charset to use for parsing (default is UTF-8)
* @return An OpenGraphData object containing all extracted Open Graph data.
*/
fun parse(file: File, charset: String = "UTF-8") : OpenGraphData {
val doc = Jsoup.parse(file, charset)
return parse(doc)
}
/** /**
* Extracts Open Graph tags from JSoup Elements and converts them to OpenGraphTag objects. * Extracts Open Graph tags from JSoup Elements and converts them to OpenGraphTag objects.
* *

View File

@@ -1,6 +1,8 @@
package fr.lengrand.opengraphkt package fr.lengrand.opengraphkt
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import java.io.File
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
@@ -8,7 +10,6 @@ import kotlin.test.assertTrue
class OpenGraphParserTest { class OpenGraphParserTest {
private val parser = OpenGraphParser() private val parser = OpenGraphParser()
private val fetcher = DocumentFetcher()
// Sample HTML with all required OpenGraph tags and some structured properties // Sample HTML with all required OpenGraph tags and some structured properties
private val completeHtml = """ private val completeHtml = """
@@ -139,8 +140,7 @@ class OpenGraphParserTest {
@Test @Test
fun `test parse with complete OpenGraph tags`() { fun `test parse with complete OpenGraph tags`() {
val document = fetcher.fromString(completeHtml) val openGraphData = parser.parse(completeHtml)
val openGraphData = parser.parse(document)
// Verify that all required properties are extracted correctly // Verify that all required properties are extracted correctly
assertEquals("The Rock", openGraphData.title) assertEquals("The Rock", openGraphData.title)
@@ -184,8 +184,7 @@ class OpenGraphParserTest {
@Test @Test
fun `test parse with article-specific tags`() { fun `test parse with article-specific tags`() {
val document = fetcher.fromString(articleHtml) val openGraphData = parser.parse(articleHtml)
val openGraphData = parser.parse(document)
// Verify basic properties // Verify basic properties
assertEquals("Breaking News", openGraphData.title) assertEquals("Breaking News", openGraphData.title)
@@ -195,21 +194,20 @@ class OpenGraphParserTest {
// Verify article-specific properties // Verify article-specific properties
assertNotNull(openGraphData.article) assertNotNull(openGraphData.article)
assertEquals("2023-01-01T00:00:00Z", openGraphData.article?.publishedTime) assertEquals("2023-01-01T00:00:00Z", openGraphData.article.publishedTime)
assertEquals("2023-01-02T12:00:00Z", openGraphData.article?.modifiedTime) assertEquals("2023-01-02T12:00:00Z", openGraphData.article.modifiedTime)
assertEquals("News", openGraphData.article?.section) assertEquals("News", openGraphData.article.section)
assertEquals(2, openGraphData.article?.authors?.size) assertEquals(2, openGraphData.article.authors.size)
assertTrue(openGraphData.article?.authors?.contains("John Doe") ?: false) assertTrue(openGraphData.article.authors.contains("John Doe"))
assertTrue(openGraphData.article?.authors?.contains("Jane Smith") ?: false) assertTrue(openGraphData.article.authors.contains("Jane Smith"))
assertEquals(2, openGraphData.article?.tags?.size) assertEquals(2, openGraphData.article.tags.size)
assertTrue(openGraphData.article?.tags?.contains("breaking") ?: false) assertTrue(openGraphData.article.tags.contains("breaking"))
assertTrue(openGraphData.article?.tags?.contains("news") ?: false) assertTrue(openGraphData.article.tags.contains("news"))
} }
@Test @Test
fun `test parse with profile-specific tags`() { fun `test parse with profile-specific tags`() {
val document = fetcher.fromString(profileHtml) val openGraphData = parser.parse(profileHtml)
val openGraphData = parser.parse(document)
// Verify basic properties // Verify basic properties
assertEquals("John Doe", openGraphData.title) assertEquals("John Doe", openGraphData.title)
@@ -219,16 +217,15 @@ class OpenGraphParserTest {
// Verify profile-specific properties // Verify profile-specific properties
assertNotNull(openGraphData.profile) assertNotNull(openGraphData.profile)
assertEquals("John", openGraphData.profile?.firstName) assertEquals("John", openGraphData.profile.firstName)
assertEquals("Doe", openGraphData.profile?.lastName) assertEquals("Doe", openGraphData.profile.lastName)
assertEquals("johndoe", openGraphData.profile?.username) assertEquals("johndoe", openGraphData.profile.username)
assertEquals("male", openGraphData.profile?.gender) assertEquals("male", openGraphData.profile.gender)
} }
@Test @Test
fun `test parse with book-specific tags`() { fun `test parse with book-specific tags`() {
val document = fetcher.fromString(bookHtml) val openGraphData = parser.parse(bookHtml)
val openGraphData = parser.parse(document)
// Verify basic properties // Verify basic properties
assertEquals("The Great Novel", openGraphData.title) assertEquals("The Great Novel", openGraphData.title)
@@ -238,19 +235,18 @@ class OpenGraphParserTest {
// Verify book-specific properties // Verify book-specific properties
assertNotNull(openGraphData.book) assertNotNull(openGraphData.book)
assertEquals(1, openGraphData.book?.authors?.size) assertEquals(1, openGraphData.book.authors.size)
assertEquals("Famous Author", openGraphData.book?.authors?.get(0)) assertEquals("Famous Author", openGraphData.book.authors.get(0))
assertEquals("1234567890123", openGraphData.book?.isbn) assertEquals("1234567890123", openGraphData.book.isbn)
assertEquals("2023-01-01", openGraphData.book?.releaseDate) assertEquals("2023-01-01", openGraphData.book.releaseDate)
assertEquals(2, openGraphData.book?.tags?.size) assertEquals(2, openGraphData.book.tags.size)
assertTrue(openGraphData.book?.tags?.contains("fiction") ?: false) assertTrue(openGraphData.book.tags.contains("fiction"))
assertTrue(openGraphData.book?.tags?.contains("novel") ?: false) assertTrue(openGraphData.book.tags.contains("novel"))
} }
@Test @Test
fun `test parse with multiple images`() { fun `test parse with multiple images`() {
val document = fetcher.fromString(multipleImagesHtml) val openGraphData = parser.parse(multipleImagesHtml)
val openGraphData = parser.parse(document)
// Verify basic properties // Verify basic properties
assertEquals("Photo Gallery", openGraphData.title) assertEquals("Photo Gallery", openGraphData.title)
@@ -276,4 +272,28 @@ class OpenGraphParserTest {
assertEquals(1200, openGraphData.images[2].width) assertEquals(1200, openGraphData.images[2].width)
assertEquals(900, openGraphData.images[2].height) assertEquals(900, openGraphData.images[2].height)
} }
@Test
fun `test parse with File`(@TempDir tempDir: File) {
// Create a temporary HTML file
val htmlFile = File(tempDir, "test.html")
htmlFile.writeText(articleHtml)
val openGraphData = parser.parse(htmlFile)
// Verify basic properties
assertEquals("Breaking News", openGraphData.title)
assertEquals("article", openGraphData.type)
assertEquals("https://example.com/news/breaking", openGraphData.url)
assertEquals("Latest breaking news", openGraphData.description)
// Verify article-specific properties
assertNotNull(openGraphData.article)
assertEquals("2023-01-01T00:00:00Z", openGraphData.article.publishedTime)
assertEquals("2023-01-02T12:00:00Z", openGraphData.article.modifiedTime)
assertEquals("News", openGraphData.article.section)
assertEquals(2, openGraphData.article.authors.size)
assertTrue(openGraphData.article.authors.contains("John Doe"))
assertTrue(openGraphData.article.authors.contains("Jane Smith"))
}
} }

View File

@@ -1,6 +1,12 @@
pluginManagement {
plugins {
kotlin("jvm") version "2.1.21"
}
}
plugins { plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
} }
rootProject.name = "OpenGraphKt" rootProject.name = "OpenGraphKt"
include("opengraphkt") include("opengraphkt")
include("demo") include("demo")
include("demo-remote")