make first working M3ULoader, however there is huge bug related to M3U parsing that needs to be fixed asap, as well as unit tests are incomplete

This commit is contained in:
mykola2312 2024-04-21 22:50:38 +03:00
parent 4dd212dff8
commit 9cc7f254e4
7 changed files with 124 additions and 29 deletions

4
.gitignore vendored
View file

@ -42,4 +42,6 @@ build/
# mptv # mptv
config.json config.json
*.db *.db
log/ log/
*.m3u
*.m3u8

View file

@ -18,12 +18,12 @@
"type": "m3u", "type": "m3u",
"url": "https://example.com/list.m3u", "url": "https://example.com/list.m3u",
"cookies": null, "cookies": null,
"singleCategory": null "rootCategory": null
}, },
{ {
"type": "m3u-local", "type": "m3u-local",
"path": "test.m3u8", "path": "test.m3u8",
"singleCategory": "test" "rootCategory": "test"
} }
] ]
} }

View file

@ -1,18 +1,28 @@
package com.mykola2312.mptv; package com.mykola2312.mptv;
import com.mykola2312.mptv.config.Config; import com.mykola2312.mptv.config.Config;
import com.mykola2312.mptv.config.SourceItem;
import com.mykola2312.mptv.db.DB; import com.mykola2312.mptv.db.DB;
import com.mykola2312.mptv.db.M3ULoader;
import com.mykola2312.mptv.parser.M3U;
import com.mykola2312.mptv.parser.M3UException;
import com.mykola2312.mptv.parser.M3UParser;
import com.mykola2312.mptv.ui.MainFrame; import com.mykola2312.mptv.ui.MainFrame;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.flywaydb.core.Flyway; import org.flywaydb.core.Flyway;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
public class Main { public class Main {
private static final Logger logger = Logger.getLogger(Main.class); private static final Logger logger = Logger.getLogger(Main.class);
public static void main(String[] args) { public static void main(String[] args) {
// parse command line
final Options options = new Options(); final Options options = new Options();
options.addOption(Option options.addOption(Option
.builder("c") .builder("c")
@ -29,6 +39,7 @@ public class Main {
return; return;
} }
// load config
final String configPath = cmd.hasOption('c') ? cmd.getOptionValue('c') : "config.json"; final String configPath = cmd.hasOption('c') ? cmd.getOptionValue('c') : "config.json";
Config config; Config config;
try { try {
@ -45,6 +56,7 @@ public class Main {
return; return;
} }
// setup db
try { try {
DB.setupFromConfig(config.db); DB.setupFromConfig(config.db);
} catch (RuntimeException e) { } catch (RuntimeException e) {
@ -54,14 +66,49 @@ public class Main {
return; return;
} }
// migrate db
Flyway flyway = new Flyway( Flyway flyway = new Flyway(
Flyway.configure() Flyway.configure()
.dataSource(DB.URL, DB.USER, DB.PASSWORD) .dataSource(DB.URL, DB.USER, DB.PASSWORD)
.baselineOnMigrate(true)
.load() .load()
.getConfiguration() .getConfiguration()
); );
flyway.migrate(); flyway.migrate();
// load sources, start crawlers
for (SourceItem source : config.sources) {
switch (source.type) {
case M3U_LOCAL -> {
try {
if (source.path == null) {
logger.error("m3u local has to have \"path\" variable");
continue;
} else if (source.rootCategory == null) {
logger.error("source has to have \"rootCategory\"");
continue;
}
String m3uData = Files.readString(Paths.get(source.path), StandardCharsets.UTF_8);
ArrayList<M3U> m3u = M3UParser.parse(m3uData);
M3ULoader.loadAll(m3u, source.rootCategory);
} catch (IOException e) {
logger.error(e);
logger.error(String.format("failed to read local m3u file: %s", e.getMessage()));
} catch (M3UException e) {
logger.error(e);
logger.error(String.format("failed to parse m3u: %s", e.getMessage()));
}
}
default -> {
logger.error(String.format("source type %s is not implemented yet :(", source.type.name()));
}
}
}
// initialize ui
MainFrame frame = new MainFrame(); MainFrame frame = new MainFrame();
frame.create(config.frame); frame.create(config.frame);

View file

@ -26,5 +26,5 @@ public class SourceItem {
public String cookies; public String cookies;
@Nullable @Nullable
public String singleCategory; public String rootCategory;
} }

View file

@ -3,43 +3,89 @@ package com.mykola2312.mptv.db;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import org.jooq.*; import org.jooq.exception.NoDataFoundException;
import org.jooq.impl.*; import org.jooq.impl.*;
import static com.mykola2312.mptv.tables.Category.*; import static com.mykola2312.mptv.tables.Category.*;
import static com.mykola2312.mptv.tables.Channel.*;
import com.mykola2312.mptv.db.pojo.Category;
import com.mykola2312.mptv.parser.M3U; import com.mykola2312.mptv.parser.M3U;
public class M3ULoader { public class M3ULoader {
public static void loadAll(ArrayList<M3U> items) { private static Integer ensureRootCategory(String rootName) {
try {
return DSL.using(DB.CONFIG)
.select(CATEGORY.ID)
.from(CATEGORY)
.where(CATEGORY.TITLE.eq(rootName))
.limit(1)
.fetchSingleInto(Integer.class);
} catch (NoDataFoundException e) {
return DSL.using(DB.CONFIG)
.insertInto(CATEGORY, CATEGORY.TITLE)
.values(rootName)
.returningResult(CATEGORY.ID)
.fetchSingleInto(Integer.class);
}
}
public static void loadAll(ArrayList<M3U> items, String rootName) {
Integer rootCategoryId = ensureRootCategory(rootName);
// cache categories' ids // cache categories' ids
HashMap<String, Integer> categories = new HashMap<>(); HashMap<String, Integer> categories = new HashMap<>();
for (M3U item : items) { for (M3U item : items) {
// category // category
Integer categoryId;
if (item.groupTitle != null) { if (item.groupTitle != null) {
Integer id; categoryId = categories.get(item.groupTitle);
Category category = DSL.using(DB.CONFIG) if (categoryId == null) {
.select() Integer id;
.from(CATEGORY) try {
.where(CATEGORY.TITLE.eq(item.groupTitle)) id = DSL.using(DB.CONFIG)
.limit(1) .select(CATEGORY.ID)
.fetchOne() .from(CATEGORY)
.into(Category.class); .where(CATEGORY.TITLE.eq(item.groupTitle))
if (category == null) { .limit(1)
id = DSL.using(DB.CONFIG) .fetchSingleInto(Integer.class);
.insertInto(CATEGORY, CATEGORY.TITLE) } catch (NoDataFoundException e) {
.values(item.groupTitle) id = DSL.using(DB.CONFIG)
.returningResult(CATEGORY.ID) .insertInto(CATEGORY, CATEGORY.TITLE)
.fetchOne() .values(item.groupTitle)
.into(Integer.class); .returningResult(CATEGORY.ID)
} else { .fetchOne()
id = category.id; .into(Integer.class);
} }
categories.put(item.groupTitle, id); categories.put(item.groupTitle, id);
categoryId = id;
}
} else {
categoryId = rootCategoryId;
} }
// channel // channel
try {
Integer channelId = DSL.using(DB.CONFIG)
.select(CHANNEL.ID)
.from(CHANNEL)
.where(CHANNEL.CATEGORY.eq(categoryId)
.and(CHANNEL.TITLE.eq(item.title)))
.limit(1)
.fetchSingleInto(Integer.class);
DSL.using(DB.CONFIG)
.update(CHANNEL)
.set(CHANNEL.URL, item.url)
.set(CHANNEL.LOGO, item.tvgLogo)
.where(CHANNEL.ID.eq(channelId))
.execute();
} catch (NoDataFoundException e) {
DSL.using(DB.CONFIG)
.insertInto(CHANNEL,
CHANNEL.CATEGORY, CHANNEL.TITLE,
CHANNEL.URL, CHANNEL.LOGO)
.values(categoryId, item.title, item.url, item.tvgLogo)
.execute();
}
} }
} }
} }

View file

@ -15,4 +15,4 @@ CREATE TABLE channel (
FOREIGN KEY (category) REFERENCES category(id) FOREIGN KEY (category) REFERENCES category(id)
); );
CREATE INDEX idx_channel_title ON channel(title); CREATE INDEX idx_channel_category_title ON channel(category,title);

View file

@ -83,12 +83,12 @@ public class TestConfig {
assertEquals("https://example.com/list.m3u", m3u.url); assertEquals("https://example.com/list.m3u", m3u.url);
assertNull(m3u.path); assertNull(m3u.path);
assertNull(m3u.cookies); assertNull(m3u.cookies);
assertNull(m3u.singleCategory); assertNull(m3u.rootCategory);
SourceItem m3uLocal = config.sources.get(1); SourceItem m3uLocal = config.sources.get(1);
assertEquals(SourceItem.SourceType.M3U_LOCAL, m3uLocal.type); assertEquals(SourceItem.SourceType.M3U_LOCAL, m3uLocal.type);
assertEquals("test.m3u8", m3uLocal.path); assertEquals("test.m3u8", m3uLocal.path);
assertEquals("test", m3uLocal.singleCategory); assertEquals("test", m3uLocal.rootCategory);
assertNull(m3uLocal.url); assertNull(m3uLocal.url);
assertNull(m3uLocal.cookies); assertNull(m3uLocal.cookies);
} }