initial commit
This commit is contained in:
commit
abcd9ff04d
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
servers
|
||||
target
|
||||
bin
|
||||
.settings
|
||||
.metadata
|
||||
.classpath
|
||||
.project
|
||||
|
||||
4
Dockerfile
Normal file
4
Dockerfile
Normal file
@ -0,0 +1,4 @@
|
||||
FROM openjdk:11
|
||||
COPY target/*.jar /tmp
|
||||
WORKDIR /tmp
|
||||
CMD ["java", "-jar", "cz-basketball-scraper-1.0-SNAPSHOT.jar"]
|
||||
37
dependency-reduced-pom.xml
Normal file
37
dependency-reduced-pom.xml
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cz.kamma.czb</groupId>
|
||||
<artifactId>cz-basketball-scraper</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer>
|
||||
<mainClass>cz.kamma.czb.BasketballServer</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<properties>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
</properties>
|
||||
</project>
|
||||
52
pom.xml
Normal file
52
pom.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>cz.kamma.czb</groupId>
|
||||
<artifactId>cz-basketball-scraper</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Jackson pro JSON parsing -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.17.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Maven compiler plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<!-- Maven shade plugin pro spustitelný JAR -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals><goal>shade</goal></goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>cz.kamma.czb.BasketballServer</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
110
src/main/java/cz/kamma/czb/BasketballServer.java
Normal file
110
src/main/java/cz/kamma/czb/BasketballServer.java
Normal file
@ -0,0 +1,110 @@
|
||||
package cz.kamma.czb;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BasketballServer {
|
||||
|
||||
private static JsonNode cachedData;
|
||||
private static final Object lock = new Object();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
fetchDataForDate("Sat Nov 01 2025 01:00:00 GMT+0100 (Central European Standard Time)"); // inicialní fetch
|
||||
|
||||
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
scheduler.scheduleAtFixedRate(() -> fetchDataForDate("Sat Nov 01 2025 01:00:00 GMT+0100 (Central European Standard Time)"),
|
||||
5, 5, TimeUnit.MINUTES);
|
||||
|
||||
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
|
||||
server.createContext("/api/matches", new ApiHandler());
|
||||
server.createContext("/", new WebHandler("src/main/resources/index.html"));
|
||||
server.setExecutor(Executors.newFixedThreadPool(4));
|
||||
server.start();
|
||||
|
||||
System.out.println("Server running at http://localhost:8000/");
|
||||
}
|
||||
|
||||
// --- fetch dat pro určité datum ---
|
||||
private static JsonNode fetchDataForDate(String dateParam) {
|
||||
try {
|
||||
String urlString = "https://cz.basketball/?do=customHomepage-getLeagues&date="
|
||||
+ URLEncoder.encode(dateParam, StandardCharsets.UTF_8) + "&categories=";
|
||||
URL url = new URL(urlString);
|
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection();
|
||||
con.setRequestMethod("GET");
|
||||
con.setRequestProperty("Cookie", "cc_cookie={\"level\":[\"necessary\",\"analytics\",\"targeting\",\"social\"],\"revision\":0,\"data\":null,\"rfc_cookie\":false}; _ga_YVJ6WB27SJ=GS2.2.s1746868725$o1$g1$t1746868780$j0$l0$h0; _ga_QHKEFEZRL5=GS2.1.s1761514243$o7$g1$t1761514385$j60$l0$h0; _nss=1; PHPSESSID=0dmq2ps6c0dv5bhdg5ukjjl6e7; _gid=GA1.2.1121240707.1762001487; _gat_UA-12082319-2=1; _ga=GA1.2.1277704385.1746363814; _ga_JYB7G0MLMT=GS2.1.s1762001486$o30$g1$t1762001571$j60$l0$h0"); // nahraď platnou cookie
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8));
|
||||
StringBuilder content = new StringBuilder();
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) content.append(line);
|
||||
in.close();
|
||||
con.disconnect();
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode data = mapper.readTree(content.toString());
|
||||
synchronized (lock) { cachedData = data; }
|
||||
System.out.println("Data fetched successfully for date: " + dateParam);
|
||||
return data;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// --- REST API handler ---
|
||||
static class ApiHandler implements HttpHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
String query = exchange.getRequestURI().getQuery();
|
||||
String dateParam = "Sat Nov 01 2025 01:00:00 GMT+0100 (Central European Standard Time)";
|
||||
|
||||
if (query != null && query.contains("date=")) {
|
||||
dateParam = java.net.URLDecoder.decode(query.split("date=")[1], StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
JsonNode data = fetchDataForDate(dateParam);
|
||||
String response = data != null ? data.toString() : "{}";
|
||||
|
||||
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=UTF-8");
|
||||
exchange.sendResponseHeaders(200, response.getBytes(StandardCharsets.UTF_8).length);
|
||||
OutputStream os = exchange.getResponseBody();
|
||||
os.write(response.getBytes(StandardCharsets.UTF_8));
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Web UI handler ---
|
||||
static class WebHandler implements HttpHandler {
|
||||
private final String htmlFile;
|
||||
|
||||
public WebHandler(String htmlFile) { this.htmlFile = htmlFile; }
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
File file = new File(htmlFile);
|
||||
if (!file.exists()) { exchange.sendResponseHeaders(404, -1); return; }
|
||||
|
||||
byte[] bytes = java.nio.file.Files.readAllBytes(file.toPath());
|
||||
exchange.getResponseHeaders().set("Content-Type", "text/html; charset=UTF-8");
|
||||
exchange.sendResponseHeaders(200, bytes.length);
|
||||
OutputStream os = exchange.getResponseBody();
|
||||
os.write(bytes);
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
194
src/main/resources/index.html
Normal file
194
src/main/resources/index.html
Normal file
@ -0,0 +1,194 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CZ.Basketball - zápasy</title>
|
||||
<link rel="icon" type="image/png" href="https://cz.basketball/img/logo-icon.png">
|
||||
<!-- Google Fonts -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Roboto', Arial, sans-serif;
|
||||
background: #f4f7f9;
|
||||
color: #333;
|
||||
margin: 20px;
|
||||
}
|
||||
h1 { color: #2c3e50; }
|
||||
label { margin-right: 8px; font-weight: 500; }
|
||||
input, select, button {
|
||||
padding: 6px 10px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
button {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
button:hover { background-color: #2980b9; }
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
th, td {
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
tr.highlight {
|
||||
background-color: #ffeb3b !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
tr.nymburk {
|
||||
background-color: #90caf9;
|
||||
}
|
||||
tr:nth-child(even) { background-color: #f9f9f9; }
|
||||
a { color: #2980b9; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>CZ.Basketball - zápasy</h1>
|
||||
|
||||
<label for="date">Vyber datum:</label>
|
||||
<input type="date" id="date" onchange="loadMatches()"/>
|
||||
<button onclick="loadMatches()">Načíst zápasy</button>
|
||||
|
||||
<label for="league">Vyber ligu:</label>
|
||||
<select id="league" onchange="filterMatches()">
|
||||
<option value="">Všechny ligy</option>
|
||||
</select>
|
||||
|
||||
<table id="matches">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Čas</th>
|
||||
<th>Liga</th>
|
||||
<th>Domácí</th>
|
||||
<th>Hosté</th>
|
||||
<th>Preview</th>
|
||||
<th>Live</th>
|
||||
<th>TV</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
// --- Nastavit kalendář na aktuální den při načtení stránky ---
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const today = new Date();
|
||||
const yyyy = today.getFullYear();
|
||||
const mm = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(today.getDate()).padStart(2, '0');
|
||||
|
||||
document.getElementById('date').value = `${yyyy}-${mm}-${dd}`;
|
||||
|
||||
loadMatches(); // načíst zápasy pro dnešní datum
|
||||
});
|
||||
|
||||
// --- Načíst zápasy z API ---
|
||||
async function loadMatches() {
|
||||
const dateInput = document.getElementById('date').value;
|
||||
let url = '/api/matches';
|
||||
if (dateInput) url += '?date=' + encodeURIComponent(dateInput);
|
||||
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
|
||||
const select = document.getElementById('league');
|
||||
const tbody = document.querySelector('#matches tbody');
|
||||
tbody.innerHTML = '';
|
||||
select.innerHTML = '<option value="">Všechny ligy</option>';
|
||||
|
||||
if (!data || !data.leagues) return;
|
||||
|
||||
let closestTimeDiff = Infinity;
|
||||
let closestRow = null;
|
||||
const now = new Date();
|
||||
|
||||
data.leagues.forEach(league => {
|
||||
// --- kontrola, zda liga má alespoň jeden zápas s TV odkazem ---
|
||||
const hasTV = league.matches.some(m => m.links.tvcom && m.links.tvcom.url);
|
||||
if (!hasTV) return; // přeskočit ligu bez TV zápasů
|
||||
|
||||
// přidat do select boxu
|
||||
const option = document.createElement('option');
|
||||
option.value = league.name;
|
||||
option.textContent = league.name;
|
||||
select.appendChild(option);
|
||||
|
||||
league.matches.forEach(match => {
|
||||
if (!match.links.live && !match.links.tvcom) return; // přeskočit zápasy bez Live a TV
|
||||
|
||||
const tr = document.createElement('tr');
|
||||
tr.dataset.league = league.name;
|
||||
|
||||
// doplnit https://cz.basketball pokud preview.url nezačíná http/https
|
||||
let previewUrl = '';
|
||||
if (match.links.preview && match.links.preview.url) {
|
||||
previewUrl = match.links.preview.url;
|
||||
if (!previewUrl.startsWith('http://') && !previewUrl.startsWith('https://')) {
|
||||
previewUrl = 'https://cz.basketball' + previewUrl;
|
||||
}
|
||||
}
|
||||
|
||||
tr.innerHTML = `
|
||||
<td>${match.status}</td>
|
||||
<td>${league.name}</td>
|
||||
<td>${match.home.name}</td>
|
||||
<td>${match.away.name}</td>
|
||||
<td>${previewUrl ? '<a href="'+previewUrl+'">Preview</a>' : ''}</td>
|
||||
<td>${match.links.live && match.links.live.url ? '<a href="'+match.links.live.url+'" target="_blank">Live</a>' : ''}</td>
|
||||
<td>${match.links.tvcom && match.links.tvcom.url ? '<a href="'+match.links.tvcom.url+'" target="_blank">TV</a>' : ''}</td>
|
||||
`;
|
||||
|
||||
// zvýraznit zápasy s Nymburkem
|
||||
if ((match.home.name && match.home.name.includes('Nymburk')) ||
|
||||
(match.away.name && match.away.name.includes('Nymburk'))) {
|
||||
tr.classList.add('nymburk');
|
||||
}
|
||||
|
||||
// zvýraznit nejbližší zápas (Live zápasy)
|
||||
if (match.links.live && match.links.live.url) {
|
||||
const [hours, minutes] = match.status.split(':').map(Number);
|
||||
if (!isNaN(hours) && !isNaN(minutes)) {
|
||||
const matchDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hours, minutes);
|
||||
const diff = matchDate - now;
|
||||
if (diff >= 0 && diff < closestTimeDiff) {
|
||||
closestTimeDiff = diff;
|
||||
closestRow = tr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
});
|
||||
|
||||
if (closestRow) closestRow.classList.add('highlight');
|
||||
}
|
||||
|
||||
// --- Filtrovat podle ligy ---
|
||||
function filterMatches() {
|
||||
const league = document.getElementById('league').value;
|
||||
document.querySelectorAll('#matches tbody tr').forEach(row => {
|
||||
row.style.display = (!league || row.dataset.league === league) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user