package com.github.segator.proxylive.service; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.segator.proxylive.ProxyLiveUtils; import com.github.segator.proxylive.config.GitSource; import com.github.segator.proxylive.config.ProxyLiveConfiguration; import com.github.segator.proxylive.entity.Channel; import com.github.segator.proxylive.entity.ChannelSource; import com.github.segator.proxylive.tasks.StreamProcessorsSession; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.eclipse.jgit.api.*; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.TrackingRefUpdate; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.*; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; public class ChannelURLService implements ChannelService { private final Logger logger = LoggerFactory.getLogger(ChannelURLService.class); @Autowired private ProxyLiveConfiguration config; private List<Channel> channels; //private File tmpGitPath; private Git git; private File tempLogoFilePath; private long lastUpdate=0; @Override public List<Channel> getChannelList() { return channels; } @Override public Channel getChannelByID(String channelID) { Optional<Channel> channelOptional = channels.stream().filter(ch -> ch.getId().equals(channelID)).findFirst(); if(channelOptional.isPresent()){ return channelOptional.get(); }else{ return null; } } private void setGitCredentials( TransportCommand gitCommand){ GitSource gitSource = config.getSource().getChannels().getGit(); if(gitSource.getUser()!=null) { gitCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider(gitSource.getUser(), gitSource.getPass())); } } @Scheduled(fixedDelay = 60 * 1000) //Every Minute @PostConstruct public void getChannelInfo() throws Exception { if(new Date().getTime()-lastUpdate>+(config.getSource().getEpg().getRefresh()*1000)) { ObjectMapper objectMapper = new ObjectMapper(); List<Channel> channels=null; //URL if(config.getSource().getChannels().getUrl()!=null) { URL channelURL = new URL(config.getSource().getChannels().getUrl()); BufferedReader in = new BufferedReader(new InputStreamReader(channelURL.openStream())); channels = objectMapper.readValue(in, new TypeReference<List<Channel>>() { }); in.close(); //Git }else if(config.getSource().getChannels().getGit()!=null){ //not initialized yet if(git==null){ logger.info("Loading Channels from: "+config.getSource().getChannels().getGit().getRepository()); File tmpGitPath = Files.createTempDirectory("proxyliveGit").toFile(); GitSource gitSource = config.getSource().getChannels().getGit(); CloneCommand cloneCommand = Git.cloneRepository(); cloneCommand.setURI(gitSource.getRepository()); cloneCommand.setDirectory(tmpGitPath); cloneCommand.setCloneAllBranches(true); cloneCommand.setBranch(gitSource.getBranch()); setGitCredentials(cloneCommand); git = cloneCommand.call(); PullCommand pull = git.pull().setRemote("origin"); setGitCredentials(pull); pull.call(); }else { PullCommand pullCommand = git.pull().setRemote("origin"); setGitCredentials(pullCommand); PullResult pulLResult = pullCommand.call(); if(!pulLResult.isSuccessful()){ throw new Exception("error when pulling"); } Collection<TrackingRefUpdate> commitsUpdated = pulLResult.getFetchResult().getTrackingRefUpdates(); if (commitsUpdated.size() > 0) { logger.info("Channels modified, updating.."); RevWalk walk = new RevWalk(git.getRepository()); for (TrackingRefUpdate commitRef : commitsUpdated) { RevCommit commit = walk.parseCommit(commitRef.getNewObjectId()); logger.info("(" + commit.getAuthorIdent().getName() + "<" + commit.getAuthorIdent().getEmailAddress() + ">) " + commit.getFullMessage()); } }else{ //no changes, don't need to update return; } } File channelsFile = new File(git.getRepository().getDirectory().getParentFile(), "channels.json"); if(!channelsFile.exists()){ throw new Exception("channels.json doesn't exist on git repository"); } channels = objectMapper.readValue(channelsFile, new TypeReference<List<Channel>>() { }); } readPicons(channels); //fix TVH Urls for (Channel channel:channels) { for(ChannelSource channelSource: channel.getSources()){ String sourceURL= channelSource.getUrl(); if(sourceURL.startsWith("tvh://") || sourceURL.startsWith("tvhs://")){ sourceURL = ProxyLiveUtils.replaceSchemes(sourceURL); URL tvhURL = new URL(sourceURL); tvhURL = new URL(tvhURL.getProtocol()+"://"+ tvhURL.getUserInfo() + "@"+tvhURL.getHost()+":"+tvhURL.getPort()+"/"); String[] tvhURLSplit = sourceURL.split("/"); String tvhUUID = tvhURLSplit[tvhURLSplit.length-1]; String findType = tvhURLSplit[tvhURLSplit.length-2]; switch(findType){ case "channel": JSONObject jsonResponse = getJSONResponse(new URL(tvhURL,"/api/channel/list")); JSONArray tvhChannelList = (JSONArray) jsonResponse.get("entries"); boolean found=false; for (Object obj: tvhChannelList) { JSONObject tvhChannelRefObj = (JSONObject) obj; if(((String)tvhChannelRefObj.get("val")).toLowerCase().trim().equals(tvhUUID.toLowerCase().trim())){ found=true; channelSource.setUrl(new URL(tvhURL,"/stream/channel/"+tvhChannelRefObj.get("key")).toString()); break; } } if(!found){ //System.out.println("Channel " + tvhUUID + " not found.. canceling update"); throw new Exception("Channel " + tvhUUID + " not found.. canceling update"); } break; } } } } this.channels = channels; logger.info("Channels loaded"); lastUpdate = new Date().getTime(); } } private JSONObject getJSONResponse(URL url) throws IOException, ParseException { JSONParser jsonParser = new JSONParser(); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setReadTimeout(10000); if (url.getUserInfo() != null) { String basicAuth = "Basic " + new String(Base64.getEncoder().encode(url.getUserInfo().getBytes())); connection.setRequestProperty("Authorization", basicAuth); } connection.setRequestMethod("GET"); connection.connect(); if (connection.getResponseCode() != 200) { throw new IOException("Error on open stream:" + url); } JSONObject returnObject = (JSONObject) jsonParser.parse(new InputStreamReader(connection.getInputStream(), "UTF-8")); connection.disconnect(); return returnObject; } private void readPicons(List<Channel> channels) throws IOException, URISyntaxException { Path piconsPath = Files.createTempDirectory("proxylivePicons"); for (Channel channel: channels) { if(channel.getLogoURL()!=null){ URL logoURL = new URL(channel.getLogoURL()); InputStream is = logoURL.openStream(); File logoFile = Paths.get(piconsPath.toString(),channel.getId()+".png").toFile(); channel.setLogoFile(logoFile); FileOutputStream fos = new FileOutputStream(logoFile); IOUtils.copy(is, fos); fos.flush(); is.close(); fos.close(); //logoFile }else{ if(channel.getLogoFile()!=null){ //if sources got from git then load picon from git if(git!=null){ channel.setLogoFile(new File(git.getRepository().getDirectory().getParentFile(),channel.getLogoFile().toString())); }else if(config.getSource().getChannels().getUrl()!=null){ String basePath = new File(new URL(config.getSource().getChannels().getUrl()).toURI()).getParent(); channel.setLogoFile(Paths.get(basePath,channel.getLogoFile().toString()).toFile()); } } } } tempLogoFilePath = piconsPath.toFile(); } @PreDestroy private void cleanup() throws IOException { logger.debug("cleaning picons directory"); if (tempLogoFilePath != null && tempLogoFilePath.exists()) { FileUtils.deleteDirectory(tempLogoFilePath); } if(git!=null && git.getRepository().getDirectory().getParentFile().exists()){ FileUtils.deleteDirectory(git.getRepository().getDirectory().getParentFile()); } } }