mirror of
https://github.com/jlengrand/OpenGraphKt.git
synced 2026-03-10 15:51:39 +00:00
Compare commits
19 Commits
feat/multi
...
renovate/m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dad5e92160 | ||
|
|
89dbda55b8 | ||
|
|
d982bc94cb | ||
|
|
f829d56a43 | ||
|
|
0364ab5d0c | ||
|
|
389f16fe1c | ||
|
|
f2cbbe048d | ||
|
|
4333d077c3 | ||
|
|
20dfc326c7 | ||
|
|
3ec5410f94 | ||
|
|
12de34aa60 | ||
|
|
e38c968151 | ||
|
|
e2817cb15c | ||
|
|
5f390de944 | ||
|
|
58abce8cb1 | ||
|
|
91da68172f | ||
|
|
d7cef1714e | ||
|
|
9d94d22a5e | ||
|
|
3c0eed60a7 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "Java",
|
"name": "Java",
|
||||||
"image": "mcr.microsoft.com/devcontainers/java:1-21",
|
"image": "mcr.microsoft.com/devcontainers/java:3-21",
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/java:1": {
|
"ghcr.io/devcontainers/features/java:1": {
|
||||||
"version": "none",
|
"version": "none",
|
||||||
|
|||||||
6
.github/workflows/gradle.yml
vendored
6
.github/workflows/gradle.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
|||||||
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
|
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
|
||||||
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
|
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
|
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||||
|
|
||||||
- name: Build with Gradle Wrapper
|
- name: Build with Gradle Wrapper
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
|
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||||
|
|
||||||
- name: Build with Gradle Wrapper
|
- name: Build with Gradle Wrapper
|
||||||
run: ./gradlew koverXmlReport
|
run: ./gradlew koverXmlReport
|
||||||
@@ -88,5 +88,5 @@ jobs:
|
|||||||
# Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies.
|
# Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies.
|
||||||
# See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md
|
# See: https://github.com/gradle/actions/blob/main/dependency-submission/README.md
|
||||||
- name: Generate and submit dependency graph
|
- name: Generate and submit dependency graph
|
||||||
uses: gradle/actions/dependency-submission@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
|
uses: gradle/actions/dependency-submission@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||||
|
|
||||||
|
|||||||
6
.idea/AndroidProjectSystem.xml
generated
Normal file
6
.idea/AndroidProjectSystem.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="2.1.21" />
|
<option name="version" value="2.2.20" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -4,7 +4,7 @@
|
|||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
74
CLAUDE.md
Normal file
74
CLAUDE.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
OpenGraphKt is a minimalist Kotlin multiplatform library for parsing Open Graph protocol tags from HTML. It wraps Ksoup (a Kotlin port of JSoup) to extract and structure Open Graph metadata.
|
||||||
|
|
||||||
|
**Current Status**: Pre-alpha - Protocol implementation is complete for `og:` tags, but type system needs refinement.
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
This is a multi-module Gradle project:
|
||||||
|
|
||||||
|
- `opengraphkt/` - Core library module (published to Maven Central as `fr.lengrand:opengraphkt`)
|
||||||
|
- `demo/` - Local file parsing examples
|
||||||
|
- `demo-remote/` - Remote URL parsing examples (see Main.kt for usage)
|
||||||
|
- `scrape-test/` - Testing/scraping utilities
|
||||||
|
|
||||||
|
## Common Commands
|
||||||
|
|
||||||
|
### Build and Test
|
||||||
|
```bash
|
||||||
|
./gradlew build # Build all modules
|
||||||
|
./gradlew test # Run all tests
|
||||||
|
./gradlew :opengraphkt:test # Run tests for core library only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Coverage
|
||||||
|
```bash
|
||||||
|
./gradlew koverXmlReport # Generate XML coverage report
|
||||||
|
./gradlew koverVerify # Verify coverage meets 70% minimum threshold
|
||||||
|
```
|
||||||
|
|
||||||
|
### Publishing
|
||||||
|
```bash
|
||||||
|
./gradlew publishToMavenLocal # Publish to local Maven repo for testing
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
**Parser (`Parser.kt`)**: Main entry point that accepts multiple input types:
|
||||||
|
- `parse(url: URL)` - Fetches and parses remote HTML
|
||||||
|
- `parse(html: String)` - Parses raw HTML string
|
||||||
|
- `parse(file: File)` - Parses local HTML file
|
||||||
|
- `parse(document: Document)` - Parses Ksoup Document
|
||||||
|
|
||||||
|
The parser extracts `meta[property^=og:]` tags and builds structured data models.
|
||||||
|
|
||||||
|
**Data Models (`Models.kt`)**: Type-safe representations of Open Graph data:
|
||||||
|
- `Data` - Main container with `isValid()` method checking required fields (title, type, image, url)
|
||||||
|
- Base types: `Image`, `Video`, `Audio`
|
||||||
|
- Content-specific types: `Article`, `Book`, `Profile`
|
||||||
|
- Music types: `MusicSong`, `MusicAlbum`, `MusicPlaylist`, `MusicRadioStation`
|
||||||
|
- Video types: `VideoMovie`, `VideoEpisode`
|
||||||
|
|
||||||
|
### Key Implementation Details
|
||||||
|
|
||||||
|
**Tag Grouping**: Tags are grouped by namespace (prefix before first colon) to handle structured properties like `og:image:width`, `og:image:height` that belong to the preceding `og:image` tag.
|
||||||
|
|
||||||
|
**Date Handling**: ISO 8601 datetime parsing with fallback for date-only formats (appends `T00:00:00Z`).
|
||||||
|
|
||||||
|
**Structured Property Association**: Images/Videos/Audio with their metadata (width, height, type, etc.) are associated by parsing sequential tags - each base tag (`og:image`) is paired with following attribute tags (`og:image:width`) until the next base tag.
|
||||||
|
|
||||||
|
## Development Notes
|
||||||
|
|
||||||
|
- **JVM Toolchain**: Java 17 (see `jvmToolchain(17)` in build files)
|
||||||
|
- **Minimum Java Version**: Java 17 (required by Ksoup dependency)
|
||||||
|
- **Testing**: CI matrix tests on Java 17 and 23 via GitHub Actions
|
||||||
|
- **Dependencies**: Core library uses Ksoup (v0.2.5) for HTML parsing and network requests
|
||||||
|
- **Maven Coordinates**: Group `fr.lengrand`, artifact `opengraphkt`, currently at `0.1.2-SNAPSHOT`
|
||||||
|
- **Code Coverage**: Kover plugin enforces 70% minimum coverage threshold
|
||||||
158
CONTRIBUTING.md
Normal file
158
CONTRIBUTING.md
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# Contributing to OpenGraphKt
|
||||||
|
|
||||||
|
Thank you for your interest in contributing to OpenGraphKt! This document provides guidelines and instructions for contributing to the project.
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Java 17 or later (JVM toolchain configured for Java 17)
|
||||||
|
- Gradle 8.14.3+ (wrapper included)
|
||||||
|
- Git
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
|
||||||
|
1. Clone the repository:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/jlengrand/OpenGraphKt.git
|
||||||
|
cd OpenGraphKt
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Build the project:
|
||||||
|
```bash
|
||||||
|
./gradlew build
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Run tests:
|
||||||
|
```bash
|
||||||
|
./gradlew test
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Check code coverage:
|
||||||
|
```bash
|
||||||
|
./gradlew koverXmlReport
|
||||||
|
./gradlew koverVerify # Enforces 70% minimum coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
- `opengraphkt/` - Core library module (published to Maven Central)
|
||||||
|
- `demo/` - Local file parsing examples
|
||||||
|
- `demo-remote/` - Remote URL parsing examples using published artifact
|
||||||
|
- `scrape-test/` - Testing/scraping utilities
|
||||||
|
|
||||||
|
## Making Changes
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
|
||||||
|
- Follow Kotlin coding conventions
|
||||||
|
- Use meaningful variable and function names
|
||||||
|
- Add KDoc comments for public APIs
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
- Write tests for all new functionality
|
||||||
|
- Maintain minimum 70% code coverage (enforced by Kover)
|
||||||
|
- Run tests locally before submitting PR: `./gradlew test`
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- Write clear, concise commit messages
|
||||||
|
- Reference issue numbers when applicable
|
||||||
|
- Keep commits focused and atomic
|
||||||
|
|
||||||
|
## Submitting Changes
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
2. Create a feature branch: `git checkout -b feature/your-feature-name`
|
||||||
|
3. Make your changes
|
||||||
|
4. Run tests and ensure coverage: `./gradlew test koverVerify`
|
||||||
|
5. Commit your changes
|
||||||
|
6. Push to your fork
|
||||||
|
7. Submit a Pull Request
|
||||||
|
|
||||||
|
## Publishing New Versions
|
||||||
|
|
||||||
|
This section is for maintainers only.
|
||||||
|
|
||||||
|
### Version Numbering
|
||||||
|
|
||||||
|
OpenGraphKt follows [Semantic Versioning](https://semver.org/):
|
||||||
|
- **MAJOR** version for incompatible API changes
|
||||||
|
- **MINOR** version for backwards-compatible functionality additions
|
||||||
|
- **PATCH** version for backwards-compatible bug fixes
|
||||||
|
|
||||||
|
### Pre-Release Checklist
|
||||||
|
|
||||||
|
1. **Update version number** in `opengraphkt/build.gradle.kts`:
|
||||||
|
```kotlin
|
||||||
|
version = "0.1.3-SNAPSHOT" // Change to "0.1.3" for release
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Update demo-remote dependency** in `demo-remote/build.gradle.kts`:
|
||||||
|
```kotlin
|
||||||
|
dependencies {
|
||||||
|
implementation("fr.lengrand:opengraphkt:0.1.3") // Match release version
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Run full test suite**:
|
||||||
|
```bash
|
||||||
|
./gradlew clean build test koverVerify
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Test demo applications**:
|
||||||
|
```bash
|
||||||
|
./gradlew :demo:run
|
||||||
|
./gradlew :demo-remote:run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Publishing to Maven Central
|
||||||
|
|
||||||
|
The project uses the `com.vanniktech.maven.publish` plugin for publishing. Publishing is automated through GitHub Actions.
|
||||||
|
|
||||||
|
1. **Bump version ** in `opengraphkt/build.gradle.kts`:
|
||||||
|
```kotlin
|
||||||
|
version = "0.1.4"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Publish to local Maven for testing** (optional):
|
||||||
|
```bash
|
||||||
|
./gradlew publishToMavenLocal
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create GitHub release** (this triggers the publishing workflow):
|
||||||
|
- Go to GitHub repository → Releases → "Draft a new release"
|
||||||
|
- Click "Choose a tag" and create a new tag (e.g., `v0.1.3`)
|
||||||
|
- Set the release title (e.g., `v0.1.3`)
|
||||||
|
- Add release notes describing changes
|
||||||
|
- Click "Publish release"
|
||||||
|
|
||||||
|
4. **GitHub Actions will automatically**:
|
||||||
|
- Build the project
|
||||||
|
- Run tests
|
||||||
|
- Publish to Maven Central
|
||||||
|
- The workflow is triggered automatically when you create a new release
|
||||||
|
|
||||||
|
5. After a few minutes, you will see the new version in [Maven Central](https://mvnrepository.com/artifact/fr.lengrand/opengraphkt). You can also directly check the real-time status on [Central Sonartype](https://central.sonatype.com/publishing/deployments).
|
||||||
|
|
||||||
|
### Post-Release Steps
|
||||||
|
|
||||||
|
1. **Bump version to next SNAPSHOT** in `opengraphkt/build.gradle.kts`:
|
||||||
|
```kotlin
|
||||||
|
version = "0.1.5-SNAPSHOT"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Commit version bump**:
|
||||||
|
```bash
|
||||||
|
git commit -am "Bump version to 0.1.5-SNAPSHOT"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
## Questions?
|
||||||
|
|
||||||
|
If you have questions or need help, please:
|
||||||
|
- Open an issue on GitHub
|
||||||
|
- Check existing issues and discussions
|
||||||
|
|
||||||
|
Thank you for contributing to OpenGraphKt!
|
||||||
10
README.md
10
README.md
@@ -9,13 +9,12 @@
|
|||||||

|

|
||||||
|
|
||||||
|
|
||||||
[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 multiplatform 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.
|
||||||
|
|
||||||
## Current status
|
## Current status
|
||||||
|
|
||||||
* Library can extract OpenGraph tags from HTML via a `URL`, `String` or `File` input.
|
* Library can extract OpenGraph tags from HTML via a `URL`, `String` or `File` input.
|
||||||
* Current implementation is JVM only, due to the `JSoup` dependency.
|
|
||||||
* Protocol implementation is complete for `og:` tags, but types aren't fully correct (most types currently are `String`).
|
* Protocol implementation is complete for `og:` tags, but types aren't fully correct (most types currently are `String`).
|
||||||
* Library should be considered in pre-alpha, use this in production at your own risks :).
|
* Library should be considered in pre-alpha, use this in production at your own risks :).
|
||||||
|
|
||||||
@@ -28,7 +27,7 @@ In short :
|
|||||||
* Add dependency to your Maven / Gradle file. For example :
|
* Add dependency to your Maven / Gradle file. For example :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
implementation("fr.lengrand:opengraphkt:0.0.2")
|
implementation("fr.lengrand:opengraphkt:0.1.0")
|
||||||
```
|
```
|
||||||
|
|
||||||
* Enjoy:
|
* Enjoy:
|
||||||
@@ -44,11 +43,6 @@ println("Is valid: ${openGraphDataDoc.isValid()}")
|
|||||||
// Is valid: true
|
// Is valid: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
- [JSoup](https://jsoup.org/)
|
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
* [Julien Lengrand-Lambert](https://github.com/jlengrand)
|
* [Julien Lengrand-Lambert](https://github.com/jlengrand)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "2.1.21" apply false
|
kotlin("jvm") version "2.2.20" apply false
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("fr.lengrand:opengraphkt:0.0.2")
|
implementation("fr.lengrand:opengraphkt:0.1.3")
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,9 +19,9 @@ tasks.test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(23)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass = "fr.lengrand.opengraphkt.MainKt"
|
mainClass = "fr.lengrand.opengraphktremote.MainKt"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.fleeksoft.ksoup:ksoup:0.2.4")
|
implementation("com.fleeksoft.ksoup:ksoup:0.2.5")
|
||||||
implementation("com.fleeksoft.ksoup:ksoup-kotlinx:0.2.4")
|
implementation("com.fleeksoft.ksoup:ksoup-kotlinx:0.2.5")
|
||||||
implementation("com.fleeksoft.ksoup:ksoup-network:0.2.4")
|
implementation("com.fleeksoft.ksoup:ksoup-network:0.2.5")
|
||||||
implementation(project(":opengraphkt"))
|
implementation(project(":opengraphkt"))
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ tasks.test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(23)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
import com.vanniktech.maven.publish.SonatypeHost
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm")
|
kotlin("jvm")
|
||||||
id("com.vanniktech.maven.publish") version "0.32.0"
|
id("com.vanniktech.maven.publish") version "0.34.0"
|
||||||
id("org.jetbrains.kotlinx.kover") version "0.9.1"
|
id("org.jetbrains.kotlinx.kover") version "0.9.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "fr.lengrand"
|
group = "fr.lengrand"
|
||||||
version = "0.0.3-SNAPSHOT"
|
version = "0.1.4-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.fleeksoft.ksoup:ksoup:0.2.4")
|
implementation("com.fleeksoft.ksoup:ksoup:0.2.5")
|
||||||
implementation("com.fleeksoft.ksoup:ksoup-kotlinx:0.2.4")
|
implementation("com.fleeksoft.ksoup:ksoup-kotlinx:0.2.5")
|
||||||
implementation("com.fleeksoft.ksoup:ksoup-network:0.2.4")
|
implementation("com.fleeksoft.ksoup:ksoup-network:0.2.5")
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,12 +33,20 @@ tasks.jar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(23)
|
jvmToolchain(17)
|
||||||
|
compilerOptions {
|
||||||
|
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mavenPublishing {
|
mavenPublishing {
|
||||||
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
|
publishToMavenCentral()
|
||||||
|
|
||||||
signAllPublications()
|
signAllPublications()
|
||||||
|
|
||||||
|
|||||||
337
opengraphkt/src/main/kotlin/fr/lengrand/opengraphkt/Generator.kt
Normal file
337
opengraphkt/src/main/kotlin/fr/lengrand/opengraphkt/Generator.kt
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
package fr.lengrand.opengraphkt
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generator for Open Graph protocol HTML meta tags.
|
||||||
|
*
|
||||||
|
* This class converts an OpenGraph Data object into HTML meta tags according to the Open Graph protocol specification.
|
||||||
|
* It can be used to generate the appropriate meta tags for embedding in HTML documents.
|
||||||
|
*
|
||||||
|
* @see <a href="https://ogp.me/">Open Graph Protocol</a>
|
||||||
|
*/
|
||||||
|
class Generator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates HTML meta tags from an OpenGraph Data object.
|
||||||
|
*
|
||||||
|
* @param data The OpenGraph Data object to convert to HTML meta tags
|
||||||
|
* @return A string containing the HTML meta tags
|
||||||
|
*/
|
||||||
|
fun generate(data: Data): String {
|
||||||
|
val tags = mutableListOf<String>()
|
||||||
|
|
||||||
|
// Add basic metadata tags
|
||||||
|
addBasicMetaTags(data, tags)
|
||||||
|
|
||||||
|
// Add image tags
|
||||||
|
addImageTags(data.images, tags)
|
||||||
|
|
||||||
|
// Add video tags
|
||||||
|
addVideoTags(data.videos, tags)
|
||||||
|
|
||||||
|
// Add audio tags
|
||||||
|
addAudioTags(data.audios, tags)
|
||||||
|
|
||||||
|
// Add type-specific tags
|
||||||
|
when (data.getType()) {
|
||||||
|
Type.ARTICLE -> addArticleTags(data.article, tags)
|
||||||
|
Type.PROFILE -> addProfileTags(data.profile, tags)
|
||||||
|
Type.BOOK -> addBookTags(data.book, tags)
|
||||||
|
Type.MUSIC_SONG -> addMusicSongTags(data.musicSong, tags)
|
||||||
|
Type.MUSIC_ALBUM -> addMusicAlbumTags(data.musicAlbum, tags)
|
||||||
|
Type.MUSIC_PLAYLIST -> addMusicPlaylistTags(data.musicPlaylist, tags)
|
||||||
|
Type.MUSIC_RADIO_STATION -> addMusicRadioStationTags(data.musicRadioStation, tags)
|
||||||
|
Type.VIDEO_MOVIE, Type.VIDEO_TV_SHOW, Type.VIDEO_OTHER -> addVideoMovieTags(data.videoMovie, tags)
|
||||||
|
Type.VIDEO_EPISODE -> addVideoEpisodeTags(data.videoEpisode, tags)
|
||||||
|
else -> { /* No additional tags for other types */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags.joinToString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds basic Open Graph meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param data The OpenGraph Data object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addBasicMetaTags(data: Data, tags: MutableList<String>) {
|
||||||
|
// Required properties
|
||||||
|
data.title?.let { tags.add(createMetaTag("og:title", it)) }
|
||||||
|
data.type?.let { tags.add(createMetaTag("og:type", it)) }
|
||||||
|
data.url?.let { tags.add(createMetaTag("og:url", it.toString())) }
|
||||||
|
|
||||||
|
// Optional properties
|
||||||
|
data.description?.let { tags.add(createMetaTag("og:description", it)) }
|
||||||
|
data.siteName?.let { tags.add(createMetaTag("og:site_name", it)) }
|
||||||
|
data.determiner?.let { tags.add(createMetaTag("og:determiner", it)) }
|
||||||
|
data.locale?.let { tags.add(createMetaTag("og:locale", it)) }
|
||||||
|
|
||||||
|
// Locale alternates
|
||||||
|
data.localeAlternate.forEach { locale ->
|
||||||
|
tags.add(createMetaTag("og:locale:alternate", locale))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds image meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param images The list of Image objects
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addImageTags(images: List<Image>, tags: MutableList<String>) {
|
||||||
|
images.forEach { image ->
|
||||||
|
image.url?.let { tags.add(createMetaTag("og:image", it)) }
|
||||||
|
image.secureUrl?.let { tags.add(createMetaTag("og:image:secure_url", it)) }
|
||||||
|
image.type?.let { tags.add(createMetaTag("og:image:type", it)) }
|
||||||
|
image.width?.let { tags.add(createMetaTag("og:image:width", it.toString())) }
|
||||||
|
image.height?.let { tags.add(createMetaTag("og:image:height", it.toString())) }
|
||||||
|
image.alt?.let { tags.add(createMetaTag("og:image:alt", it)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds video meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param videos The list of Video objects
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addVideoTags(videos: List<Video>, tags: MutableList<String>) {
|
||||||
|
videos.forEach { video ->
|
||||||
|
video.url?.let { tags.add(createMetaTag("og:video", it)) }
|
||||||
|
video.secureUrl?.let { tags.add(createMetaTag("og:video:secure_url", it)) }
|
||||||
|
video.type?.let { tags.add(createMetaTag("og:video:type", it)) }
|
||||||
|
video.width?.let { tags.add(createMetaTag("og:video:width", it.toString())) }
|
||||||
|
video.height?.let { tags.add(createMetaTag("og:video:height", it.toString())) }
|
||||||
|
video.duration?.let { tags.add(createMetaTag("og:video:duration", it.toString())) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds audio meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param audios The list of Audio objects
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addAudioTags(audios: List<Audio>, tags: MutableList<String>) {
|
||||||
|
audios.forEach { audio ->
|
||||||
|
audio.url?.let { tags.add(createMetaTag("og:audio", it)) }
|
||||||
|
audio.secureUrl?.let { tags.add(createMetaTag("og:audio:secure_url", it)) }
|
||||||
|
audio.type?.let { tags.add(createMetaTag("og:audio:type", it)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds article-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param article The Article object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addArticleTags(article: Article?, tags: MutableList<String>) {
|
||||||
|
if (article == null) return
|
||||||
|
|
||||||
|
article.publishedTime?.let { tags.add(createMetaTag("og:article:published_time", formatDateTime(it))) }
|
||||||
|
article.modifiedTime?.let { tags.add(createMetaTag("og:article:modified_time", formatDateTime(it))) }
|
||||||
|
article.expirationTime?.let { tags.add(createMetaTag("og:article:expiration_time", formatDateTime(it))) }
|
||||||
|
article.section?.let { tags.add(createMetaTag("og:article:section", it)) }
|
||||||
|
|
||||||
|
article.authors.forEach { author ->
|
||||||
|
tags.add(createMetaTag("og:article:author", author))
|
||||||
|
}
|
||||||
|
|
||||||
|
article.tags.forEach { tag ->
|
||||||
|
tags.add(createMetaTag("og:article:tag", tag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds profile-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param profile The Profile object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addProfileTags(profile: Profile?, tags: MutableList<String>) {
|
||||||
|
if (profile == null) return
|
||||||
|
|
||||||
|
profile.firstName?.let { tags.add(createMetaTag("og:profile:first_name", it)) }
|
||||||
|
profile.lastName?.let { tags.add(createMetaTag("og:profile:last_name", it)) }
|
||||||
|
profile.username?.let { tags.add(createMetaTag("og:profile:username", it)) }
|
||||||
|
profile.gender?.let { tags.add(createMetaTag("og:profile:gender", it.toString())) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds book-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param book The Book object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addBookTags(book: Book?, tags: MutableList<String>) {
|
||||||
|
if (book == null) return
|
||||||
|
|
||||||
|
book.authors.forEach { author ->
|
||||||
|
tags.add(createMetaTag("og:book:author", author))
|
||||||
|
}
|
||||||
|
|
||||||
|
book.isbn?.let { tags.add(createMetaTag("og:book:isbn", it)) }
|
||||||
|
book.releaseDate?.let { tags.add(createMetaTag("og:book:release_date", formatDateTime(it))) }
|
||||||
|
|
||||||
|
book.tags.forEach { tag ->
|
||||||
|
tags.add(createMetaTag("og:book:tag", tag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds music.song-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param musicSong The MusicSong object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addMusicSongTags(musicSong: MusicSong?, tags: MutableList<String>) {
|
||||||
|
if (musicSong == null) return
|
||||||
|
|
||||||
|
musicSong.duration?.let { tags.add(createMetaTag("og:music:duration", it.toString())) }
|
||||||
|
musicSong.album?.let { tags.add(createMetaTag("og:music:album", it)) }
|
||||||
|
musicSong.albumDisc?.let { tags.add(createMetaTag("og:music:album:disc", it.toString())) }
|
||||||
|
musicSong.albumTrack?.let { tags.add(createMetaTag("og:music:album:track", it.toString())) }
|
||||||
|
|
||||||
|
musicSong.musician.forEach { musician ->
|
||||||
|
tags.add(createMetaTag("og:music:musician", musician))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds music.album-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param musicAlbum The MusicAlbum object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addMusicAlbumTags(musicAlbum: MusicAlbum?, tags: MutableList<String>) {
|
||||||
|
if (musicAlbum == null) return
|
||||||
|
|
||||||
|
musicAlbum.songs.forEach { song ->
|
||||||
|
tags.add(createMetaTag("og:music:song", song))
|
||||||
|
}
|
||||||
|
|
||||||
|
musicAlbum.songDisc?.let { tags.add(createMetaTag("og:music:song:disc", it.toString())) }
|
||||||
|
musicAlbum.songTrack?.let { tags.add(createMetaTag("og:music:song:track", it.toString())) }
|
||||||
|
|
||||||
|
musicAlbum.musician.forEach { musician ->
|
||||||
|
tags.add(createMetaTag("og:music:musician", musician))
|
||||||
|
}
|
||||||
|
|
||||||
|
musicAlbum.releaseDate?.let { tags.add(createMetaTag("og:music:release_date", formatDateTime(it))) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds music.playlist-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param musicPlaylist The MusicPlaylist object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addMusicPlaylistTags(musicPlaylist: MusicPlaylist?, tags: MutableList<String>) {
|
||||||
|
if (musicPlaylist == null) return
|
||||||
|
|
||||||
|
musicPlaylist.songs.forEach { song ->
|
||||||
|
tags.add(createMetaTag("og:music:song", song))
|
||||||
|
}
|
||||||
|
|
||||||
|
musicPlaylist.songDisc?.let { tags.add(createMetaTag("og:music:song:disc", it.toString())) }
|
||||||
|
musicPlaylist.songTrack?.let { tags.add(createMetaTag("og:music:song:track", it.toString())) }
|
||||||
|
musicPlaylist.creator?.let { tags.add(createMetaTag("og:music:creator", it)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds music.radio_station-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param musicRadioStation The MusicRadioStation object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addMusicRadioStationTags(musicRadioStation: MusicRadioStation?, tags: MutableList<String>) {
|
||||||
|
if (musicRadioStation == null) return
|
||||||
|
|
||||||
|
musicRadioStation.creator?.let { tags.add(createMetaTag("og:music:creator", it)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds video.movie-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param videoMovie The VideoMovie object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addVideoMovieTags(videoMovie: VideoMovie?, tags: MutableList<String>) {
|
||||||
|
if (videoMovie == null) return
|
||||||
|
|
||||||
|
videoMovie.actors.forEach { actor ->
|
||||||
|
tags.add(createMetaTag("og:video:actor", actor))
|
||||||
|
}
|
||||||
|
|
||||||
|
videoMovie.director.forEach { director ->
|
||||||
|
tags.add(createMetaTag("og:video:director", director))
|
||||||
|
}
|
||||||
|
|
||||||
|
videoMovie.writer.forEach { writer ->
|
||||||
|
tags.add(createMetaTag("og:video:writer", writer))
|
||||||
|
}
|
||||||
|
|
||||||
|
videoMovie.duration?.let { tags.add(createMetaTag("og:video:duration", it.toString())) }
|
||||||
|
videoMovie.releaseDate?.let { tags.add(createMetaTag("og:video:release_date", formatDateTime(it))) }
|
||||||
|
|
||||||
|
videoMovie.tags.forEach { tag ->
|
||||||
|
tags.add(createMetaTag("og:video:tag", tag))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds video.episode-specific meta tags to the list.
|
||||||
|
*
|
||||||
|
* @param videoEpisode The VideoEpisode object
|
||||||
|
* @param tags The list to add the tags to
|
||||||
|
*/
|
||||||
|
private fun addVideoEpisodeTags(videoEpisode: VideoEpisode?, tags: MutableList<String>) {
|
||||||
|
if (videoEpisode == null) return
|
||||||
|
|
||||||
|
videoEpisode.actors.forEach { actor ->
|
||||||
|
tags.add(createMetaTag("og:video:actor", actor))
|
||||||
|
}
|
||||||
|
|
||||||
|
videoEpisode.director.forEach { director ->
|
||||||
|
tags.add(createMetaTag("og:video:director", director))
|
||||||
|
}
|
||||||
|
|
||||||
|
videoEpisode.writer.forEach { writer ->
|
||||||
|
tags.add(createMetaTag("og:video:writer", writer))
|
||||||
|
}
|
||||||
|
|
||||||
|
videoEpisode.duration?.let { tags.add(createMetaTag("og:video:duration", it.toString())) }
|
||||||
|
videoEpisode.releaseDate?.let { tags.add(createMetaTag("og:video:release_date", formatDateTime(it))) }
|
||||||
|
|
||||||
|
videoEpisode.tags.forEach { tag ->
|
||||||
|
tags.add(createMetaTag("og:video:tag", tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
videoEpisode.series?.let { tags.add(createMetaTag("og:video:series", it)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an HTML meta tag with the given property and content.
|
||||||
|
*
|
||||||
|
* @param property The property attribute value
|
||||||
|
* @param content The content attribute value
|
||||||
|
* @return The HTML meta tag string
|
||||||
|
*/
|
||||||
|
private fun createMetaTag(property: String, content: String): String {
|
||||||
|
val escapedContent = content.replace("\"", """)
|
||||||
|
return "<meta property=\"$property\" content=\"$escapedContent\" />"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats an OffsetDateTime to a string suitable for OpenGraph tags.
|
||||||
|
*
|
||||||
|
* @param dateTime The OffsetDateTime to format
|
||||||
|
* @return The formatted date string in ISO-8601 format with 'Z' timezone indicator
|
||||||
|
*/
|
||||||
|
private fun formatDateTime(dateTime: OffsetDateTime): String {
|
||||||
|
return dateTime.toInstant().toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
1420
opengraphkt/src/test/kotlin/fr/lengrand/opengraphkt/GeneratorTest.kt
Normal file
1420
opengraphkt/src/test/kotlin/fr/lengrand/opengraphkt/GeneratorTest.kt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -11,18 +11,18 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
testImplementation(platform("org.junit:junit-bom:5.14.0"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
|
|
||||||
implementation(project(":opengraphkt"))
|
implementation(project(":opengraphkt"))
|
||||||
implementation("io.ktor:ktor-client-core:3.1.3")
|
implementation("io.ktor:ktor-client-core:3.3.1")
|
||||||
implementation("io.ktor:ktor-client-cio:3.1.3")
|
implementation("io.ktor:ktor-client-cio:3.3.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
kotlin {
|
kotlin {
|
||||||
jvmToolchain(23)
|
jvmToolchain(17)
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "2.1.21"
|
kotlin("jvm") version "2.2.20"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
plugins {
|
plugins {
|
||||||
|
|||||||
Reference in New Issue
Block a user