mirror of
https://github.com/jlengrand/OpenGraphKt.git
synced 2026-03-10 15:51:39 +00:00
Compare commits
29 Commits
feat/invis
...
feat/kover
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
909979974a | ||
|
|
f1544bf599 | ||
|
|
9799f3eb14 | ||
|
|
ee84878ef1 | ||
|
|
ca31ffa8f7 | ||
|
|
45bb727e13 | ||
|
|
bb5919d892 | ||
|
|
d819c2346c | ||
|
|
e4410a7b79 | ||
|
|
42c8accd3d | ||
|
|
4c20ea5dac | ||
|
|
60cc4118d9 | ||
|
|
aa34916640 | ||
|
|
8e2a256eb8 | ||
|
|
b361b87070 | ||
|
|
8943e7217c | ||
|
|
5f5bccbc7c | ||
|
|
aa056eabcf | ||
|
|
668b91cbcf | ||
|
|
dcf69d495b | ||
|
|
34400572d4 | ||
|
|
019076ff04 | ||
|
|
28781409ec | ||
|
|
25cfba57d6 | ||
|
|
9daa2a7a4a | ||
|
|
90b9d7ad35 | ||
|
|
b61b43c220 | ||
|
|
9e8e979514 | ||
|
|
5f9b8de728 |
48
.github/workflows/gradle.yml
vendored
48
.github/workflows/gradle.yml
vendored
@@ -34,24 +34,45 @@ jobs:
|
||||
# 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
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
|
||||
uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
|
||||
|
||||
- name: Build with Gradle Wrapper
|
||||
run: ./gradlew build
|
||||
|
||||
# NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html).
|
||||
# If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version.
|
||||
#
|
||||
# - name: Setup Gradle
|
||||
# uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
|
||||
# with:
|
||||
# gradle-version: '8.9'
|
||||
#
|
||||
# - name: Build with Gradle 8.9
|
||||
# run: gradle build
|
||||
code-coverage:
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: ['23']
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK ${{ matrix.java-version }}
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
|
||||
|
||||
- name: Build with Gradle Wrapper
|
||||
run: ./gradlew koverXmlReport
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: all
|
||||
path: .
|
||||
- name: Upload coverage reports
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: opengraphkt/build/reports/kover/report.xml
|
||||
|
||||
dependency-submission:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -67,4 +88,5 @@ jobs:
|
||||
# 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
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
|
||||
uses: gradle/actions/dependency-submission@8379f6a1328ee0e06e2bb424dadb7b159856a326 # v4.4.0
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# OpenGraphKt
|
||||
|
||||
[](https://github.com/simplex-chat/jlengrand/OpenGraphKt/workflows/gradle.yml)
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
[OpenGraphKt](https://github.com/jlengrand/OpenGraphKt) is a minimalist Kotlin library to work with the [Open Graph tags](https://ogp.me/) protocol.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.lengrand.opengraphkt
|
||||
package fr.lengrand.opengraphktremote
|
||||
|
||||
import fr.lengrand.opengraphkt.OpenGraphParser
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
/**
|
||||
@@ -8,7 +8,7 @@ import java.net.URI
|
||||
* Example demonstrating how to use the OpenGraphParser to extract Open Graph data from HTML.
|
||||
*/
|
||||
fun main() {
|
||||
val parser = OpenGraphParser()
|
||||
val parser = Parser()
|
||||
|
||||
// Example 1: Parse Open Graph data from a URL
|
||||
println("Example 1: Parsing from URL")
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
||||
#Wed Apr 30 23:06:11 CEST 2025
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
47
gradlew
vendored
47
gradlew
vendored
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +82,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -114,7 +114,7 @@ case "$( uname )" in #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
CLASSPATH="\\\"\\\""
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
@@ -133,22 +133,29 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,18 +200,28 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
183
gradlew.bat
vendored
183
gradlew.bat
vendored
@@ -1,89 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
|
||||
@@ -2,11 +2,12 @@ import com.vanniktech.maven.publish.SonatypeHost
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("com.vanniktech.maven.publish") version "0.30.0"
|
||||
id("com.vanniktech.maven.publish") version "0.32.0"
|
||||
id("org.jetbrains.kotlinx.kover") version "0.9.1"
|
||||
}
|
||||
|
||||
group = "fr.lengrand"
|
||||
version = "0.0.1"
|
||||
version = "0.0.3-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -68,4 +69,14 @@ mavenPublishing {
|
||||
developerConnection = "scm:git:ssh://git@github.com/jlengrand/OpenGraphKt.git"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kover {
|
||||
reports {
|
||||
verify {
|
||||
rule {
|
||||
minBound(70)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
207
opengraphkt/src/main/kotlin/fr/lengrand/opengraphkt/Models.kt
Normal file
207
opengraphkt/src/main/kotlin/fr/lengrand/opengraphkt/Models.kt
Normal file
@@ -0,0 +1,207 @@
|
||||
package fr.lengrand.opengraphkt
|
||||
|
||||
/**
|
||||
* Enum representing the different types of Open Graph objects.
|
||||
*/
|
||||
enum class Type {
|
||||
ARTICLE,
|
||||
PROFILE,
|
||||
BOOK,
|
||||
MUSIC_SONG,
|
||||
MUSIC_ALBUM,
|
||||
MUSIC_PLAYLIST,
|
||||
MUSIC_RADIO_STATION,
|
||||
VIDEO_MOVIE,
|
||||
VIDEO_TV_SHOW,
|
||||
VIDEO_OTHER,
|
||||
VIDEO_EPISODE,
|
||||
WEBSITE,
|
||||
UNKNOWN;
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Converts a string type to the corresponding enum value.
|
||||
*
|
||||
* @param type The string representation of the type
|
||||
* @return The corresponding Type enum value, or UNKNOWN if not recognized
|
||||
*/
|
||||
fun fromString(type: String?): Type {
|
||||
return when (type) {
|
||||
"article" -> ARTICLE
|
||||
"profile" -> PROFILE
|
||||
"book" -> BOOK
|
||||
"music.song" -> MUSIC_SONG
|
||||
"music.album" -> MUSIC_ALBUM
|
||||
"music.playlist" -> MUSIC_PLAYLIST
|
||||
"music.radio_station" -> MUSIC_RADIO_STATION
|
||||
"video.movie" -> VIDEO_MOVIE
|
||||
"video.tv_show" -> VIDEO_TV_SHOW
|
||||
"video.other" -> VIDEO_OTHER
|
||||
"video.episode" -> VIDEO_EPISODE
|
||||
"website" -> WEBSITE
|
||||
null -> WEBSITE // No type tag defaults to Website
|
||||
else -> UNKNOWN // Another type we are not aware of
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Tag(
|
||||
val property: String,
|
||||
val content: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents structured Open Graph data extracted from HTML.
|
||||
*/
|
||||
data class Data(
|
||||
val tags: List<Tag>,
|
||||
|
||||
// Basic metadata
|
||||
val title: String?,
|
||||
val type: String?,
|
||||
val url: String?,
|
||||
val description: String?,
|
||||
|
||||
val siteName: String?,
|
||||
val determiner: String?,
|
||||
val locale: String?,
|
||||
val localeAlternate: List<String>,
|
||||
|
||||
// Structured properties
|
||||
val images: List<Image>,
|
||||
val videos: List<Video>,
|
||||
val audios: List<Audio>,
|
||||
|
||||
// Optional type-specific metadata
|
||||
val article: Article?,
|
||||
val profile: Profile?,
|
||||
val book: Book?,
|
||||
|
||||
// Music types
|
||||
val musicSong: MusicSong?,
|
||||
val musicAlbum: MusicAlbum?,
|
||||
val musicPlaylist: MusicPlaylist?,
|
||||
val musicRadioStation: MusicRadioStation?,
|
||||
|
||||
// Video types
|
||||
val videoMovie: VideoMovie?,
|
||||
val videoEpisode: VideoEpisode?
|
||||
) {
|
||||
/**
|
||||
* Checks if this Open Graph data contains the minimum required properties.
|
||||
*
|
||||
* According to the Open Graph protocol, the minimum required properties are:
|
||||
* - og:title
|
||||
* - og:type
|
||||
* - og:image
|
||||
* - og:url
|
||||
*
|
||||
* @return true if all required properties are present, false otherwise
|
||||
*/
|
||||
fun isValid(): Boolean {
|
||||
return title != null && type != null && images.isNotEmpty() && url != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this Open Graph data as an enum value.
|
||||
*
|
||||
* @return The Type enum value corresponding to the type string, or UNKNOWN if the type is not recognized
|
||||
*/
|
||||
fun getType(): Type {
|
||||
return Type.fromString(type)
|
||||
}
|
||||
}
|
||||
|
||||
data class Image(
|
||||
val url: String?,
|
||||
val secureUrl: String?,
|
||||
val type: String?,
|
||||
val width: Int?,
|
||||
val height: Int?,
|
||||
val alt: String?
|
||||
)
|
||||
|
||||
data class Video(
|
||||
val url: String?,
|
||||
val secureUrl: String?,
|
||||
val type: String?,
|
||||
val width: Int?,
|
||||
val height: Int?,
|
||||
val duration: Int?
|
||||
)
|
||||
|
||||
data class Audio(
|
||||
val url: String?,
|
||||
val secureUrl: String?,
|
||||
val type: String?
|
||||
)
|
||||
|
||||
/**
|
||||
* * video.tv_show - same as video.movie
|
||||
* * video.other - same as video.movie
|
||||
*/
|
||||
data class Article(
|
||||
val publishedTime: String?,
|
||||
val modifiedTime: String?,
|
||||
val expirationTime: String?,
|
||||
val section: String?,
|
||||
val authors: List<String>,
|
||||
val tags: List<String>
|
||||
)
|
||||
|
||||
data class Profile(
|
||||
val firstName: String?,
|
||||
val lastName: String?,
|
||||
val username: String?,
|
||||
val gender: String?
|
||||
)
|
||||
|
||||
data class Book(
|
||||
val authors: List<String>,
|
||||
val isbn: String?,
|
||||
val releaseDate: String?,
|
||||
val tags: List<String>
|
||||
)
|
||||
|
||||
data class MusicSong(
|
||||
val duration: Int?,
|
||||
val album: String?,
|
||||
val albumDisc: Int?,
|
||||
val albumTrack: Int?,
|
||||
val musician: List<String>
|
||||
)
|
||||
|
||||
data class MusicAlbum(
|
||||
val songs: List<String>,
|
||||
val musician: List<String>,
|
||||
val releaseDate: String?
|
||||
)
|
||||
|
||||
data class MusicPlaylist(
|
||||
val songs: List<String>,
|
||||
val creator: String?
|
||||
)
|
||||
|
||||
data class MusicRadioStation(
|
||||
val creator: String?
|
||||
)
|
||||
|
||||
data class VideoMovie(
|
||||
val actors: List<String>,
|
||||
val director: List<String>,
|
||||
val writer: List<String>,
|
||||
val duration: Int?,
|
||||
val releaseDate: String?,
|
||||
val tags: List<String>
|
||||
)
|
||||
|
||||
data class VideoEpisode(
|
||||
val actors: List<String>,
|
||||
val director: List<String>,
|
||||
val writer: List<String>,
|
||||
val duration: Int?,
|
||||
val releaseDate: String?,
|
||||
val tags: List<String>,
|
||||
val series: String?
|
||||
)
|
||||
@@ -6,128 +6,6 @@ import org.jsoup.select.Elements
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
data class OpenGraphTag(
|
||||
val property: String,
|
||||
val content: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents structured Open Graph data extracted from HTML.
|
||||
*/
|
||||
data class OpenGraphData(
|
||||
val tags: List<OpenGraphTag>,
|
||||
|
||||
// Basic metadata
|
||||
val title: String?,
|
||||
val type: String?,
|
||||
val url: String?,
|
||||
val description: String?,
|
||||
|
||||
val siteName: String?,
|
||||
val determiner: String?,
|
||||
val locale: String?,
|
||||
val localeAlternate: List<String>,
|
||||
|
||||
// Structured properties
|
||||
val images: List<OpenGraphImage>,
|
||||
val videos: List<OpenGraphVideo>,
|
||||
val audios: List<OpenGraphAudio>,
|
||||
|
||||
// Optional type-specific metadata
|
||||
val article: OpenGraphArticle?,
|
||||
val profile: OpenGraphProfile?,
|
||||
val book: OpenGraphBook?
|
||||
) {
|
||||
/**
|
||||
* Checks if this Open Graph data contains the minimum required properties.
|
||||
*
|
||||
* According to the Open Graph protocol, the minimum required properties are:
|
||||
* - og:title
|
||||
* - og:type
|
||||
* - og:image
|
||||
* - og:url
|
||||
*
|
||||
* @return true if all required properties are present, false otherwise
|
||||
*/
|
||||
fun isValid(): Boolean {
|
||||
return title != null && type != null && images.isNotEmpty() && url != null
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first image URL, or null if no images are present.
|
||||
*
|
||||
* @return The URL of the first image, or null
|
||||
*/
|
||||
fun getFirstImageUrl(): String? {
|
||||
return images.firstOrNull()?.url
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an Open Graph image.
|
||||
*/
|
||||
data class OpenGraphImage(
|
||||
val url: String?,
|
||||
val secureUrl: String?,
|
||||
val type: String?,
|
||||
val width: Int?,
|
||||
val height: Int?,
|
||||
val alt: String?
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents an Open Graph video.
|
||||
*/
|
||||
data class OpenGraphVideo(
|
||||
val url: String?,
|
||||
val secureUrl: String?,
|
||||
val type: String?,
|
||||
val width: Int?,
|
||||
val height: Int?,
|
||||
val duration: Int?
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents an Open Graph audio.
|
||||
*/
|
||||
data class OpenGraphAudio(
|
||||
val url: String?,
|
||||
val secureUrl: String?,
|
||||
val type: String?
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents Open Graph article metadata.
|
||||
*/
|
||||
data class OpenGraphArticle(
|
||||
val publishedTime: String?,
|
||||
val modifiedTime: String?,
|
||||
val expirationTime: String?,
|
||||
val section: String?,
|
||||
val authors: List<String>,
|
||||
val tags: List<String>
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents Open Graph profile metadata.
|
||||
*/
|
||||
data class OpenGraphProfile(
|
||||
val firstName: String?,
|
||||
val lastName: String?,
|
||||
val username: String?,
|
||||
val gender: String?
|
||||
)
|
||||
|
||||
/**
|
||||
* Represents Open Graph book metadata.
|
||||
*/
|
||||
data class OpenGraphBook(
|
||||
val authors: List<String>,
|
||||
val isbn: String?,
|
||||
val releaseDate: String?,
|
||||
val tags: List<String>
|
||||
)
|
||||
|
||||
/**
|
||||
* A comprehensive parser for Open Graph protocol tags.
|
||||
*
|
||||
@@ -137,15 +15,15 @@ data class OpenGraphBook(
|
||||
*
|
||||
* @see <a href="https://ogp.me/">Open Graph Protocol</a>
|
||||
*/
|
||||
class OpenGraphParser {
|
||||
class Parser {
|
||||
|
||||
/**
|
||||
* Extracts all Open Graph tags from a JSoup Document and returns a structured OpenGraphData object.
|
||||
* Extracts all Open Graph tags from a JSoup Document and returns a structured Data object.
|
||||
*
|
||||
* @param document The JSoup Document to parse
|
||||
* @return An OpenGraphData object containing all extracted Open Graph data
|
||||
* @return An Data object containing all extracted Open Graph data
|
||||
*/
|
||||
fun parse(document: Document): OpenGraphData {
|
||||
fun parse(document: Document): Data {
|
||||
val tags = document.select("meta[property^=og:]")
|
||||
val openGraphTags = extractOpenGraphTags(tags)
|
||||
|
||||
@@ -153,62 +31,62 @@ class OpenGraphParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all Open Graph tags from a URL and returns a structured OpenGraphData object.
|
||||
* Extracts all Open Graph tags from a URL and returns a structured Data object.
|
||||
*
|
||||
* @param url The URL to be parsed for Open Graph information.
|
||||
* @return An OpenGraphData object containing all extracted Open Graph data.
|
||||
* @return An Data object containing all extracted Open Graph data.
|
||||
*/
|
||||
fun parse(url: URL) : OpenGraphData {
|
||||
fun parse(url: URL) : Data {
|
||||
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.
|
||||
* Extracts all Open Graph tags from a raw HTML String and returns a structured Data object.
|
||||
*
|
||||
* @param html The raw HTML String to be parsed for Open Graph information.
|
||||
* @return An OpenGraphData object containing all extracted Open Graph data.
|
||||
* @return An Data object containing all extracted Open Graph data.
|
||||
*/
|
||||
fun parse(html: String) : OpenGraphData {
|
||||
fun parse(html: String) : Data {
|
||||
val doc = Jsoup.parse(html)
|
||||
return parse(doc)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts all Open Graph tags from a raw HTML String and returns a structured OpenGraphData object.
|
||||
* Extracts all Open Graph tags from a raw HTML String and returns a structured Data 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.
|
||||
* @return An Data object containing all extracted Open Graph data.
|
||||
*/
|
||||
fun parse(file: File, charset: String = "UTF-8") : OpenGraphData {
|
||||
fun parse(file: File, charset: String = "UTF-8") : Data {
|
||||
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 Tag objects.
|
||||
*
|
||||
* @param elements The JSoup Elements containing Open Graph meta tags
|
||||
* @return A list of OpenGraphTag objects
|
||||
* @return A list of Tag objects
|
||||
*/
|
||||
private fun extractOpenGraphTags(elements: Elements): List<OpenGraphTag> {
|
||||
private fun extractOpenGraphTags(elements: Elements): List<Tag> {
|
||||
return elements.map { element ->
|
||||
val fullProperty = element.attr("property")
|
||||
val property = fullProperty.substring(3) // Remove "og:" prefix
|
||||
val content = element.attr("content")
|
||||
|
||||
OpenGraphTag(property, content)
|
||||
Tag(property, content)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an OpenGraphData object from a list of OpenGraphTag objects.
|
||||
* Builds an Data object from a list of Tag objects.
|
||||
*
|
||||
* @param tags The list of OpenGraphTag objects
|
||||
* @return An OpenGraphData object containing structured Open Graph data
|
||||
* @param tags The list of Tag objects
|
||||
* @return An Data object containing structured Open Graph data
|
||||
*/
|
||||
private fun buildOpenGraphData(tags: List<OpenGraphTag>): OpenGraphData {
|
||||
private fun buildOpenGraphData(tags: List<Tag>): Data {
|
||||
// Group tags by their namespace (before the first colon)
|
||||
val groupedTags = tags.groupBy { tag ->
|
||||
if (tag.property.contains(":")) {
|
||||
@@ -243,7 +121,17 @@ class OpenGraphParser {
|
||||
// Build book-specific properties if the type is "book"
|
||||
val book = if (type == "book") buildBook(groupedTags) else null
|
||||
|
||||
return OpenGraphData(
|
||||
// Build music-specific properties based on type
|
||||
val musicSong = if (type == "music.song") buildMusicSong(groupedTags) else null
|
||||
val musicAlbum = if (type == "music.album") buildMusicAlbum(groupedTags) else null
|
||||
val musicPlaylist = if (type == "music.playlist") buildMusicPlaylist(groupedTags) else null
|
||||
val musicRadioStation = if (type == "music.radio_station") buildMusicRadioStation(groupedTags) else null
|
||||
|
||||
// Build video-specific properties based on type
|
||||
val videoMovie = if (type == "video.movie" || type == "video.tv_show" || type == "video.other") buildVideoMovie(groupedTags) else null
|
||||
val videoEpisode = if (type == "video.episode") buildVideoEpisode(groupedTags) else null
|
||||
|
||||
return Data(
|
||||
tags = tags,
|
||||
title = title,
|
||||
type = type,
|
||||
@@ -258,39 +146,45 @@ class OpenGraphParser {
|
||||
audios = audios,
|
||||
article = article,
|
||||
profile = profile,
|
||||
book = book
|
||||
book = book,
|
||||
musicSong = musicSong,
|
||||
musicAlbum = musicAlbum,
|
||||
musicPlaylist = musicPlaylist,
|
||||
musicRadioStation = musicRadioStation,
|
||||
videoMovie = videoMovie,
|
||||
videoEpisode = videoEpisode
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of the first tag with the specified property.
|
||||
*
|
||||
* @param tags The list of OpenGraphTag objects
|
||||
* @param tags The list of Tag objects
|
||||
* @param property The property to look for
|
||||
* @return The content of the first tag with the specified property, or null if not found
|
||||
*/
|
||||
private fun getFirstTagContent(tags: List<OpenGraphTag>, property: String): String? {
|
||||
private fun getFirstTagContent(tags: List<Tag>, property: String): String? {
|
||||
return tags.firstOrNull { it.property == property }?.content
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of all tags with the specified property.
|
||||
*
|
||||
* @param tags The list of OpenGraphTag objects
|
||||
* @param tags The list of Tag objects
|
||||
* @param property The property to look for
|
||||
* @return A list of content values from all tags with the specified property
|
||||
*/
|
||||
private fun getTagsContent(tags: List<OpenGraphTag>, property: String): List<String> {
|
||||
private fun getTagsContent(tags: List<Tag>, property: String): List<String> {
|
||||
return tags.filter { it.property == property }.map { it.content }
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of OpenGraphImage objects from image tags.
|
||||
* Builds a list of Image objects from image tags.
|
||||
*
|
||||
* @param imageTags The list of image-related OpenGraphTag objects
|
||||
* @return A list of OpenGraphImage objects
|
||||
* @param imageTags The list of image-related Tag objects
|
||||
* @return A list of Image objects
|
||||
*/
|
||||
private fun buildImages(imageTags: List<OpenGraphTag>): List<OpenGraphImage> {
|
||||
private fun buildImages(imageTags: List<Tag>): List<Image> {
|
||||
val baseImageTags = imageTags.filter {
|
||||
it.property == "image" || it.property == "image:url"
|
||||
}
|
||||
@@ -300,7 +194,7 @@ class OpenGraphParser {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val images = mutableListOf<OpenGraphImage>()
|
||||
val images = mutableListOf<Image>()
|
||||
|
||||
// For each base image tag, create an image object and find its attributes
|
||||
baseImageTags.forEach { baseTag ->
|
||||
@@ -319,7 +213,7 @@ class OpenGraphParser {
|
||||
val height = attributeTags.firstOrNull { it.property == "image:height" }?.content?.toIntOrNull()
|
||||
val alt = attributeTags.firstOrNull { it.property == "image:alt" }?.content
|
||||
|
||||
images.add(OpenGraphImage(
|
||||
images.add(Image(
|
||||
url = baseTag.content,
|
||||
secureUrl = secureUrl,
|
||||
type = type,
|
||||
@@ -333,12 +227,12 @@ class OpenGraphParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of OpenGraphVideo objects from video tags.
|
||||
* Builds a list of Video objects from video tags.
|
||||
*
|
||||
* @param videoTags The list of video-related OpenGraphTag objects
|
||||
* @return A list of OpenGraphVideo objects
|
||||
* @param videoTags The list of video-related Tag objects
|
||||
* @return A list of Video objects
|
||||
*/
|
||||
private fun buildVideos(videoTags: List<OpenGraphTag>): List<OpenGraphVideo> {
|
||||
private fun buildVideos(videoTags: List<Tag>): List<Video> {
|
||||
val baseVideoTags = videoTags.filter {
|
||||
it.property == "video" || it.property == "video:url"
|
||||
}
|
||||
@@ -348,7 +242,7 @@ class OpenGraphParser {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val videos = mutableListOf<OpenGraphVideo>()
|
||||
val videos = mutableListOf<Video>()
|
||||
|
||||
// For each base video tag, create a video object and find its attributes
|
||||
baseVideoTags.forEach { baseTag ->
|
||||
@@ -367,7 +261,7 @@ class OpenGraphParser {
|
||||
val height = attributeTags.firstOrNull { it.property == "video:height" }?.content?.toIntOrNull()
|
||||
val duration = attributeTags.firstOrNull { it.property == "video:duration" }?.content?.toIntOrNull()
|
||||
|
||||
videos.add(OpenGraphVideo(
|
||||
videos.add(Video(
|
||||
url = baseTag.content,
|
||||
secureUrl = secureUrl,
|
||||
type = type,
|
||||
@@ -381,12 +275,12 @@ class OpenGraphParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of OpenGraphAudio objects from audio tags.
|
||||
* Builds a list of Audio objects from audio tags.
|
||||
*
|
||||
* @param audioTags The list of audio-related OpenGraphTag objects
|
||||
* @return A list of OpenGraphAudio objects
|
||||
* @param audioTags The list of audio-related Tag objects
|
||||
* @return A list of Audio objects
|
||||
*/
|
||||
private fun buildAudios(audioTags: List<OpenGraphTag>): List<OpenGraphAudio> {
|
||||
private fun buildAudios(audioTags: List<Tag>): List<Audio> {
|
||||
val baseAudioTags = audioTags.filter {
|
||||
it.property == "audio" || it.property == "audio:url"
|
||||
}
|
||||
@@ -396,7 +290,7 @@ class OpenGraphParser {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val audios = mutableListOf<OpenGraphAudio>()
|
||||
val audios = mutableListOf<Audio>()
|
||||
|
||||
// For each base audio tag, create an audio object and find its attributes
|
||||
baseAudioTags.forEach { baseTag ->
|
||||
@@ -412,7 +306,7 @@ class OpenGraphParser {
|
||||
val secureUrl = attributeTags.firstOrNull { it.property == "audio:secure_url" }?.content
|
||||
val type = attributeTags.firstOrNull { it.property == "audio:type" }?.content
|
||||
|
||||
audios.add(OpenGraphAudio(
|
||||
audios.add(Audio(
|
||||
url = baseTag.content,
|
||||
secureUrl = secureUrl,
|
||||
type = type
|
||||
@@ -423,12 +317,12 @@ class OpenGraphParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an OpenGraphArticle object from article-related tags.
|
||||
* Builds an Article object from article-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped OpenGraphTag objects
|
||||
* @return An OpenGraphArticle object, or null if no article tags are found
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An Article object, or null if no article tags are found
|
||||
*/
|
||||
private fun buildArticle(groupedTags: Map<String, List<OpenGraphTag>>): OpenGraphArticle? {
|
||||
private fun buildArticle(groupedTags: Map<String, List<Tag>>): Article? {
|
||||
val articleTags = groupedTags.getOrDefault("article", emptyList())
|
||||
|
||||
if (articleTags.isEmpty()) {
|
||||
@@ -442,7 +336,7 @@ class OpenGraphParser {
|
||||
val authors = articleTags.filter { it.property == "article:author" }.map { it.content }
|
||||
val tags = articleTags.filter { it.property == "article:tag" }.map { it.content }
|
||||
|
||||
return OpenGraphArticle(
|
||||
return Article(
|
||||
publishedTime = publishedTime,
|
||||
modifiedTime = modifiedTime,
|
||||
expirationTime = expirationTime,
|
||||
@@ -453,12 +347,12 @@ class OpenGraphParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an OpenGraphProfile object from profile-related tags.
|
||||
* Builds an Profile object from profile-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped OpenGraphTag objects
|
||||
* @return An OpenGraphProfile object, or null if no profile tags are found
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An Profile object, or null if no profile tags are found
|
||||
*/
|
||||
private fun buildProfile(groupedTags: Map<String, List<OpenGraphTag>>): OpenGraphProfile? {
|
||||
private fun buildProfile(groupedTags: Map<String, List<Tag>>): Profile? {
|
||||
val profileTags = groupedTags.getOrDefault("profile", emptyList())
|
||||
|
||||
if (profileTags.isEmpty()) {
|
||||
@@ -470,7 +364,7 @@ class OpenGraphParser {
|
||||
val username = profileTags.firstOrNull { it.property == "profile:username" }?.content
|
||||
val gender = profileTags.firstOrNull { it.property == "profile:gender" }?.content
|
||||
|
||||
return OpenGraphProfile(
|
||||
return Profile(
|
||||
firstName = firstName,
|
||||
lastName = lastName,
|
||||
username = username,
|
||||
@@ -479,12 +373,12 @@ class OpenGraphParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an OpenGraphBook object from book-related tags.
|
||||
* Builds an Book object from book-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped OpenGraphTag objects
|
||||
* @return An OpenGraphBook object, or null if no book tags are found
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An Book object, or null if no book tags are found
|
||||
*/
|
||||
private fun buildBook(groupedTags: Map<String, List<OpenGraphTag>>): OpenGraphBook? {
|
||||
private fun buildBook(groupedTags: Map<String, List<Tag>>): Book? {
|
||||
val bookTags = groupedTags.getOrDefault("book", emptyList())
|
||||
|
||||
if (bookTags.isEmpty()) {
|
||||
@@ -496,11 +390,167 @@ class OpenGraphParser {
|
||||
val releaseDate = bookTags.firstOrNull { it.property == "book:release_date" }?.content
|
||||
val tags = bookTags.filter { it.property == "book:tag" }.map { it.content }
|
||||
|
||||
return OpenGraphBook(
|
||||
return Book(
|
||||
authors = authors,
|
||||
isbn = isbn,
|
||||
releaseDate = releaseDate,
|
||||
tags = tags
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an MusicSong object from music.song-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An MusicSong object, or null if no music.song tags are found
|
||||
*/
|
||||
private fun buildMusicSong(groupedTags: Map<String, List<Tag>>): MusicSong? {
|
||||
val musicTags = groupedTags.getOrDefault("music", emptyList())
|
||||
|
||||
if (musicTags.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val duration = musicTags.firstOrNull { it.property == "music:duration" }?.content?.toIntOrNull()
|
||||
val album = musicTags.firstOrNull { it.property == "music:album" }?.content
|
||||
val albumDisc = musicTags.firstOrNull { it.property == "music:album:disc" }?.content?.toIntOrNull()
|
||||
val albumTrack = musicTags.firstOrNull { it.property == "music:album:track" }?.content?.toIntOrNull()
|
||||
val musicians = musicTags.filter { it.property == "music:musician" }.map { it.content }
|
||||
|
||||
return MusicSong(
|
||||
duration = duration,
|
||||
album = album,
|
||||
albumDisc = albumDisc,
|
||||
albumTrack = albumTrack,
|
||||
musician = musicians
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an MusicAlbum object from music.album-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An MusicAlbum object, or null if no music.album tags are found
|
||||
*/
|
||||
private fun buildMusicAlbum(groupedTags: Map<String, List<Tag>>): MusicAlbum? {
|
||||
val musicTags = groupedTags.getOrDefault("music", emptyList())
|
||||
|
||||
if (musicTags.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val songs = musicTags.filter { it.property == "music:song" }.map { it.content }
|
||||
val musicians = musicTags.filter { it.property == "music:musician" }.map { it.content }
|
||||
val releaseDate = musicTags.firstOrNull { it.property == "music:release_date" }?.content
|
||||
|
||||
return MusicAlbum(
|
||||
songs = songs,
|
||||
musician = musicians,
|
||||
releaseDate = releaseDate
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an MusicPlaylist object from music.playlist-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An MusicPlaylist object, or null if no music.playlist tags are found
|
||||
*/
|
||||
private fun buildMusicPlaylist(groupedTags: Map<String, List<Tag>>): MusicPlaylist? {
|
||||
val musicTags = groupedTags.getOrDefault("music", emptyList())
|
||||
|
||||
if (musicTags.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val songs = musicTags.filter { it.property == "music:song" }.map { it.content }
|
||||
val creator = musicTags.firstOrNull { it.property == "music:creator" }?.content
|
||||
|
||||
return MusicPlaylist(
|
||||
songs = songs,
|
||||
creator = creator
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an MusicRadioStation object from music.radio_station-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An MusicRadioStation object, or null if no music.radio_station tags are found
|
||||
*/
|
||||
private fun buildMusicRadioStation(groupedTags: Map<String, List<Tag>>): MusicRadioStation? {
|
||||
val musicTags = groupedTags.getOrDefault("music", emptyList())
|
||||
|
||||
if (musicTags.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val creator = musicTags.firstOrNull { it.property == "music:creator" }?.content
|
||||
|
||||
return MusicRadioStation(
|
||||
creator = creator
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an VideoMovie object from video.movie-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An VideoMovie object, or null if no video.movie tags are found
|
||||
*/
|
||||
private fun buildVideoMovie(groupedTags: Map<String, List<Tag>>): VideoMovie? {
|
||||
val videoTags = groupedTags.getOrDefault("video", emptyList())
|
||||
|
||||
if (videoTags.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val actors = videoTags.filter { it.property == "video:actor" }.map { it.content }
|
||||
val directors = videoTags.filter { it.property == "video:director" }.map { it.content }
|
||||
val writers = videoTags.filter { it.property == "video:writer" }.map { it.content }
|
||||
val duration = videoTags.firstOrNull { it.property == "video:duration" }?.content?.toIntOrNull()
|
||||
val releaseDate = videoTags.firstOrNull { it.property == "video:release_date" }?.content
|
||||
val tags = videoTags.filter { it.property == "video:tag" }.map { it.content }
|
||||
|
||||
return VideoMovie(
|
||||
actors = actors,
|
||||
director = directors,
|
||||
writer = writers,
|
||||
duration = duration,
|
||||
releaseDate = releaseDate,
|
||||
tags = tags
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an VideoEpisode object from video.episode-related tags.
|
||||
*
|
||||
* @param groupedTags The map of grouped Tag objects
|
||||
* @return An VideoEpisode object, or null if no video.episode tags are found
|
||||
*/
|
||||
private fun buildVideoEpisode(groupedTags: Map<String, List<Tag>>): VideoEpisode? {
|
||||
val videoTags = groupedTags.getOrDefault("video", emptyList())
|
||||
|
||||
if (videoTags.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val actors = videoTags.filter { it.property == "video:actor" }.map { it.content }
|
||||
val directors = videoTags.filter { it.property == "video:director" }.map { it.content }
|
||||
val writers = videoTags.filter { it.property == "video:writer" }.map { it.content }
|
||||
val duration = videoTags.firstOrNull { it.property == "video:duration" }?.content?.toIntOrNull()
|
||||
val releaseDate = videoTags.firstOrNull { it.property == "video:release_date" }?.content
|
||||
val tags = videoTags.filter { it.property == "video:tag" }.map { it.content }
|
||||
val series = videoTags.firstOrNull { it.property == "video:series" }?.content
|
||||
|
||||
return VideoEpisode(
|
||||
actors = actors,
|
||||
director = directors,
|
||||
writer = writers,
|
||||
duration = duration,
|
||||
releaseDate = releaseDate,
|
||||
tags = tags,
|
||||
series = series
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,9 @@ import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class OpenGraphParserTest {
|
||||
class ParserTest {
|
||||
|
||||
private val parser = OpenGraphParser()
|
||||
private val parser = Parser()
|
||||
|
||||
// Sample HTML with all required OpenGraph tags and some structured properties
|
||||
private val completeHtml = """
|
||||
@@ -180,6 +180,15 @@ class OpenGraphParserTest {
|
||||
assertEquals(2, openGraphData.localeAlternate.size)
|
||||
assertTrue(openGraphData.localeAlternate.contains("fr_FR"))
|
||||
assertTrue(openGraphData.localeAlternate.contains("es_ES"))
|
||||
|
||||
// Verify video.movie properties
|
||||
assertNotNull(openGraphData.videoMovie)
|
||||
assertEquals(0, openGraphData.videoMovie.actors.size)
|
||||
assertEquals(0, openGraphData.videoMovie.director.size)
|
||||
assertEquals(0, openGraphData.videoMovie.writer.size)
|
||||
assertEquals(null, openGraphData.videoMovie.duration)
|
||||
assertEquals(null, openGraphData.videoMovie.releaseDate)
|
||||
assertEquals(0, openGraphData.videoMovie.tags.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -296,4 +305,118 @@ class OpenGraphParserTest {
|
||||
assertTrue(openGraphData.article.authors.contains("John Doe"))
|
||||
assertTrue(openGraphData.article.authors.contains("Jane Smith"))
|
||||
}
|
||||
|
||||
// Sample HTML with video.movie-specific tags
|
||||
private val videoMovieHtml = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Video Movie Example</title>
|
||||
<meta property="og:title" content="The Matrix" />
|
||||
<meta property="og:type" content="video.movie" />
|
||||
<meta property="og:url" content="https://example.com/movies/the-matrix" />
|
||||
<meta property="og:image" content="https://example.com/matrix-poster.jpg" />
|
||||
<meta property="og:description" content="A sci-fi action movie" />
|
||||
<meta property="og:video:actor" content="Keanu Reeves" />
|
||||
<meta property="og:video:actor" content="Laurence Fishburne" />
|
||||
<meta property="og:video:director" content="Lana Wachowski" />
|
||||
<meta property="og:video:director" content="Lilly Wachowski" />
|
||||
<meta property="og:video:writer" content="Lana Wachowski" />
|
||||
<meta property="og:video:writer" content="Lilly Wachowski" />
|
||||
<meta property="og:video:duration" content="136" />
|
||||
<meta property="og:video:release_date" content="1999-03-31" />
|
||||
<meta property="og:video:tag" content="sci-fi" />
|
||||
<meta property="og:video:tag" content="action" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>The Matrix</h1>
|
||||
</body>
|
||||
</html>
|
||||
""".trimIndent()
|
||||
|
||||
private val noTypeHtml = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Video Movie Example</title>
|
||||
<meta property="og:title" content="The Matrix" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>The Matrix</h1>
|
||||
</body>
|
||||
</html>
|
||||
""".trimIndent()
|
||||
|
||||
private val unknownTypeHtml = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Video Movie Example</title>
|
||||
<meta property="og:title" content="The Matrix" />
|
||||
<meta property="og:type" content="test" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>The Matrix</h1>
|
||||
</body>
|
||||
</html>
|
||||
""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `test parse with video movie-specific tags`() {
|
||||
val openGraphData = parser.parse(videoMovieHtml)
|
||||
|
||||
// Verify basic properties
|
||||
assertEquals("The Matrix", openGraphData.title)
|
||||
assertEquals("video.movie", openGraphData.type)
|
||||
assertEquals("https://example.com/movies/the-matrix", openGraphData.url)
|
||||
assertEquals("A sci-fi action movie", openGraphData.description)
|
||||
|
||||
// Verify video.movie-specific properties
|
||||
assertNotNull(openGraphData.videoMovie)
|
||||
assertEquals(2, openGraphData.videoMovie.actors.size)
|
||||
assertTrue(openGraphData.videoMovie.actors.contains("Keanu Reeves"))
|
||||
assertTrue(openGraphData.videoMovie.actors.contains("Laurence Fishburne"))
|
||||
assertEquals(2, openGraphData.videoMovie.director.size)
|
||||
assertTrue(openGraphData.videoMovie.director.contains("Lana Wachowski"))
|
||||
assertTrue(openGraphData.videoMovie.director.contains("Lilly Wachowski"))
|
||||
assertEquals(2, openGraphData.videoMovie.writer.size)
|
||||
assertTrue(openGraphData.videoMovie.writer.contains("Lana Wachowski"))
|
||||
assertTrue(openGraphData.videoMovie.writer.contains("Lilly Wachowski"))
|
||||
assertEquals(136, openGraphData.videoMovie.duration)
|
||||
assertEquals("1999-03-31", openGraphData.videoMovie.releaseDate)
|
||||
assertEquals(2, openGraphData.videoMovie.tags.size)
|
||||
assertTrue(openGraphData.videoMovie.tags.contains("sci-fi"))
|
||||
assertTrue(openGraphData.videoMovie.tags.contains("action"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test getType method returns correct enum values`() {
|
||||
// Test video.movie type
|
||||
val videoMovieData = parser.parse(videoMovieHtml)
|
||||
assertEquals(Type.VIDEO_MOVIE, videoMovieData.getType())
|
||||
|
||||
// Test article type
|
||||
val articleData = parser.parse(articleHtml)
|
||||
assertEquals(Type.ARTICLE, articleData.getType())
|
||||
|
||||
// Test profile type
|
||||
val profileData = parser.parse(profileHtml)
|
||||
assertEquals(Type.PROFILE, profileData.getType())
|
||||
|
||||
// Test book type
|
||||
val bookData = parser.parse(bookHtml)
|
||||
assertEquals(Type.BOOK, bookData.getType())
|
||||
|
||||
// Test website type (should return UNKNOWN as it's not in our enum)
|
||||
val websiteData = parser.parse(multipleImagesHtml)
|
||||
assertEquals(Type.WEBSITE, websiteData.getType())
|
||||
|
||||
// Test no type defaults to Website
|
||||
val noTypeData = parser.parse(noTypeHtml)
|
||||
assertEquals(Type.WEBSITE, noTypeData.getType())
|
||||
|
||||
// Test unrecognized type is Unknown
|
||||
val unkwownData = parser.parse(unknownTypeHtml)
|
||||
assertEquals(Type.UNKNOWN, unkwownData.getType())
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ pluginManagement {
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0"
|
||||
}
|
||||
rootProject.name = "OpenGraphKt"
|
||||
include("opengraphkt")
|
||||
|
||||
Reference in New Issue
Block a user