From a7d6d998deee49af772154e72634f1b1ed7e380d Mon Sep 17 00:00:00 2001 From: Alessandro Scuderoni Date: Sun, 29 Sep 2019 16:42:44 +0100 Subject: [PATCH 1/3] fixed TvTime URL and added movie title --- .../tvshowtime/ApplicationLauncher.java | 21 +++-------- .../tvshowtime/business/plex/Video.java | 37 ++++++++++++------- .../org/nuvola/tvshowtime/util/Constants.java | 6 +-- src/main/resources/banner.txt | 2 +- 4 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/nuvola/tvshowtime/ApplicationLauncher.java b/src/main/java/org/nuvola/tvshowtime/ApplicationLauncher.java index fbded82..e5125ae 100644 --- a/src/main/java/org/nuvola/tvshowtime/ApplicationLauncher.java +++ b/src/main/java/org/nuvola/tvshowtime/ApplicationLauncher.java @@ -24,13 +24,10 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.time.LocalDateTime; -import java.util.List; import java.util.Timer; import java.util.TimerTask; -import java.util.stream.Collectors; import org.nuvola.tvshowtime.business.plex.MediaContainer; -import org.nuvola.tvshowtime.business.plex.User; import org.nuvola.tvshowtime.business.plex.Video; import org.nuvola.tvshowtime.business.tvshowtime.AccessToken; import org.nuvola.tvshowtime.business.tvshowtime.AuthorizationCode; @@ -220,14 +217,7 @@ private void processWatchedEpisodes() { for (Video video : mediaContainer.getVideo()) { LocalDateTime date = DateUtils.getDateTimeFromTimestamp(video.getViewedAt()); - // Mark as watched only episodes for configured user - if (pmsConfig.getUsername() != null && video.getUser() != null) { - List users = video.getUser().stream().filter(user -> user.getName().equals(pmsConfig.getUsername())).collect(Collectors.toList()); - - if (users.stream().count() == 0) { - continue; - } - } + LOG.trace(video.toString()); // Mark as watched only today and yesterday episodes if (DateUtils.isTodayOrYesterday(date) || pmsConfig.getMarkall() == true) { @@ -239,14 +229,13 @@ private void processWatchedEpisodes() { .toString(); markEpisodeAsWatched(episode); - } else { - continue; + } else if (video.getType().equals("movie")) { + //markEpisodeAsWatched(video.getTitle()); } - } else { - LOG.info("All episodes are processed successfully ..."); - System.exit(0); } } + LOG.info("All episodes are processed successfully ..."); + System.exit(0); } private void markEpisodeAsWatched(String episode) { diff --git a/src/main/java/org/nuvola/tvshowtime/business/plex/Video.java b/src/main/java/org/nuvola/tvshowtime/business/plex/Video.java index eca9e25..e49feef 100644 --- a/src/main/java/org/nuvola/tvshowtime/business/plex/Video.java +++ b/src/main/java/org/nuvola/tvshowtime/business/plex/Video.java @@ -19,10 +19,7 @@ package org.nuvola.tvshowtime.business.plex; import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; -import java.util.List; @XmlRootElement(name = "Video") public class Video { @@ -31,9 +28,10 @@ public class Video { private Integer index; private Long viewedAt; private String type; - private List user; + private String title; - @XmlAttribute(name="grandparentTitle") + + @XmlAttribute(name = "grandparentTitle") public String getGrandparentTitle() { return grandparentTitle; } @@ -42,7 +40,7 @@ public void setGrandparentTitle(String grandparentTitle) { this.grandparentTitle = grandparentTitle; } - @XmlAttribute(name="parentIndex") + @XmlAttribute(name = "parentIndex") public Integer getParentIndex() { return parentIndex; } @@ -51,7 +49,7 @@ public void setParentIndex(Integer parentIndex) { this.parentIndex = parentIndex; } - @XmlAttribute(name="index") + @XmlAttribute(name = "index") public Integer getIndex() { return index; } @@ -60,7 +58,7 @@ public void setIndex(Integer index) { this.index = index; } - @XmlAttribute(name="viewedAt") + @XmlAttribute(name = "viewedAt") public Long getViewedAt() { return viewedAt; } @@ -69,7 +67,7 @@ public void setViewedAt(Long viewedAt) { this.viewedAt = viewedAt; } - @XmlAttribute(name="type") + @XmlAttribute(name = "type") public String getType() { return type; } @@ -78,12 +76,23 @@ public void setType(String type) { this.type = type; } - @XmlElement(name = "User") - public List getUser() { - return user; + @XmlAttribute(name = "title") + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; } - public void setUser(List user) { - this.user = user; + @Override + public String toString() { + return "Video={" + + "grandparentTitle=" + grandparentTitle + + ", parentIndex=" + parentIndex + + ", index=" + index + + ", viewedAt=" + viewedAt + + ", type=" + type + + ", title=" + title + "}"; } } diff --git a/src/main/java/org/nuvola/tvshowtime/util/Constants.java b/src/main/java/org/nuvola/tvshowtime/util/Constants.java index fee9ffa..ebeef41 100644 --- a/src/main/java/org/nuvola/tvshowtime/util/Constants.java +++ b/src/main/java/org/nuvola/tvshowtime/util/Constants.java @@ -23,9 +23,9 @@ public class Constants { public static final String TVST_CLIENT_SECRET = "ZHYcO8n8h6WbYuMDWVgXr7T571ZF_s1r1Rzu1-3B"; public static final String TVST_USER_AGENT = "tvshowtime-plex"; public static final String TVST_RATE_REMAINING_HEADER = "X-RateLimit-Remaining"; - public static final String TVST_AUTHORIZE_URI = "https://api.tvshowtime.com/v1/oauth/device/code"; - public static final String TVST_ACCESS_TOKEN_URI = "https://api.tvshowtime.com/v1/oauth/access_token"; - public static final String TVST_CHECKIN_URI = "https://api.tvshowtime.com/v1/checkin"; + public static final String TVST_AUTHORIZE_URI = "https://api.tvtime.com/v1/oauth/device/code"; + public static final String TVST_ACCESS_TOKEN_URI = "https://api.tvtime.com/v1/oauth/access_token"; + public static final String TVST_CHECKIN_URI = "https://api.tvtime.com/v1/checkin"; public static final String PMS_WATCH_HISTORY = "/status/sessions/history/all"; diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt index ad993e7..72c9a06 100644 --- a/src/main/resources/banner.txt +++ b/src/main/resources/banner.txt @@ -5,5 +5,5 @@ | |\ V //\__/ / | | | (_) \ V V /| | | | | | | | | __/ | | | | __/> < \_/ \_/ \____/|_| |_|\___/ \_/\_/ \_/ |_|_| |_| |_|\___| \_| |_|\___/_/\_\ - ::: TVShowTime-Plex (V1.0.5) ::: + ::: TVShowTime-Plex (V1.0.6) ::: From 76acc7df337745c8df10da1e8ca5f0fc17babcc0 Mon Sep 17 00:00:00 2001 From: Alessandro Scuderoni Date: Fri, 4 Oct 2019 15:03:54 +0100 Subject: [PATCH 2/3] moved to Json response and added check for Plex username when available. --- pom.xml | 2 +- .../tvshowtime/ApplicationLauncher.java | 146 +++++++++++------- .../tvshowtime/business/plex/Account.java | 108 +++++++++++++ .../business/plex/MediaContainer.java | 39 +++-- .../plex/{Video.java => MetaData.java} | 113 +++++++++++++- .../plex/{User.java => PlexResponse.java} | 28 ++-- .../org/nuvola/tvshowtime/util/Constants.java | 1 + 7 files changed, 340 insertions(+), 97 deletions(-) create mode 100644 src/main/java/org/nuvola/tvshowtime/business/plex/Account.java rename src/main/java/org/nuvola/tvshowtime/business/plex/{Video.java => MetaData.java} (53%) rename src/main/java/org/nuvola/tvshowtime/business/plex/{User.java => PlexResponse.java} (58%) diff --git a/pom.xml b/pom.xml index 4ecb845..10f8e4a 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.1.RELEASE + 2.1.9.RELEASE diff --git a/src/main/java/org/nuvola/tvshowtime/ApplicationLauncher.java b/src/main/java/org/nuvola/tvshowtime/ApplicationLauncher.java index e5125ae..5164753 100644 --- a/src/main/java/org/nuvola/tvshowtime/ApplicationLauncher.java +++ b/src/main/java/org/nuvola/tvshowtime/ApplicationLauncher.java @@ -18,20 +18,9 @@ package org.nuvola.tvshowtime; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.time.LocalDateTime; -import java.util.Timer; -import java.util.TimerTask; - -import org.nuvola.tvshowtime.business.plex.MediaContainer; -import org.nuvola.tvshowtime.business.plex.Video; -import org.nuvola.tvshowtime.business.tvshowtime.AccessToken; -import org.nuvola.tvshowtime.business.tvshowtime.AuthorizationCode; -import org.nuvola.tvshowtime.business.tvshowtime.Message; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.nuvola.tvshowtime.business.plex.*; +import org.nuvola.tvshowtime.business.tvshowtime.*; import org.nuvola.tvshowtime.config.PMSConfig; import org.nuvola.tvshowtime.config.TVShowTimeConfig; import org.nuvola.tvshowtime.util.DateUtils; @@ -40,23 +29,16 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.client.RestTemplate; -import static org.nuvola.tvshowtime.util.Constants.MINUTE_IN_MILIS; -import static org.nuvola.tvshowtime.util.Constants.PMS_WATCH_HISTORY; -import static org.nuvola.tvshowtime.util.Constants.TVST_ACCESS_TOKEN_URI; -import static org.nuvola.tvshowtime.util.Constants.TVST_AUTHORIZE_URI; -import static org.nuvola.tvshowtime.util.Constants.TVST_CHECKIN_URI; -import static org.nuvola.tvshowtime.util.Constants.TVST_CLIENT_ID; -import static org.nuvola.tvshowtime.util.Constants.TVST_CLIENT_SECRET; -import static org.nuvola.tvshowtime.util.Constants.TVST_RATE_REMAINING_HEADER; -import static org.nuvola.tvshowtime.util.Constants.TVST_USER_AGENT; +import java.io.*; +import java.time.LocalDateTime; +import java.util.*; + +import static org.nuvola.tvshowtime.util.Constants.*; import static org.springframework.http.HttpMethod.POST; @SpringBootApplication @@ -69,17 +51,21 @@ public class ApplicationLauncher { @Autowired private PMSConfig pmsConfig; + private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private RestTemplate tvShowTimeTemplate; private RestTemplate pmsTemplate; private AccessToken accessToken; private Timer tokenTimer; + private String m_url; + private Map m_users = new HashMap<>(); - public static void main(String[] args) { - SpringApplication.run(ApplicationLauncher.class, args); - } + public static void main(String[] args) { + SpringApplication.run(ApplicationLauncher.class, args); + } @Scheduled(fixedDelay = Long.MAX_VALUE) - public void init() { + public void init() { tvShowTimeTemplate = new RestTemplate(); File storeToken = new File(tvShowTimeConfig.getTokenFile()); @@ -92,7 +78,8 @@ public void init() { fileInputStream.close(); LOG.info("AccessToken loaded from file with success : " + accessToken); - } catch (Exception e) { + } + catch (Exception e) { LOG.error("Error parsing the AccessToken stored in 'session_token'."); LOG.error("Please remove the 'session_token' file, and try again."); LOG.error(e.getMessage()); @@ -101,14 +88,17 @@ public void init() { } try { + getUsersInPlex(); processWatchedEpisodes(); - } catch (Exception e) { + } + catch (Exception e) { LOG.error("Error during marking episodes as watched."); - LOG.error(e.getMessage()); + LOG.error("Error: ", e); System.exit(1); } - } else { + } + else { requestAccessToken(); } } @@ -134,15 +124,22 @@ private void requestAccessToken() { tokenTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { - loadAccessToken(authorizationCode.getDevice_code()); + try { + loadAccessToken(authorizationCode.getDevice_code()); + } + catch (IOException e) { + e.printStackTrace(); + } } }, 1000 * authorizationCode.getInterval(), 1000 * authorizationCode.getInterval()); - } else { + } + else { LOG.error("OAuth authentication TVShowTime failed."); System.exit(1); } - } catch (Exception e) { + } + catch (Exception e) { LOG.error("OAuth authentication TVShowTime failed."); LOG.error(e.getMessage()); @@ -150,7 +147,7 @@ public void run() { } } - private void loadAccessToken(String deviceCode) { + private void loadAccessToken(String deviceCode) throws IOException { String query = new StringBuilder("client_id=") .append(TVST_CLIENT_ID) .append("&client_secret=") @@ -173,7 +170,8 @@ private void loadAccessToken(String deviceCode) { storeAccessToken(); processWatchedEpisodes(); - } else { + } + else { if (!accessToken.getMessage().equals("Authorization pending") && !accessToken.getMessage().equals("Slow down")) { LOG.error("Unexpected error did arrive, please reload the service :-("); @@ -194,7 +192,8 @@ private void storeAccessToken() { fileOutputStream.close(); LOG.info("AccessToken store successfully inside a file..."); - } catch (Exception e) { + } + catch (Exception e) { LOG.error("Unexpected error did arrive when trying to store the AccessToken in a file "); LOG.error(e.getMessage()); @@ -202,34 +201,51 @@ private void storeAccessToken() { } } - private void processWatchedEpisodes() { + private void getUsersInPlex() throws IOException { pmsTemplate = new RestTemplate(); - String watchHistoryUrl = pmsConfig.getPath() + PMS_WATCH_HISTORY; + m_url = pmsConfig.getPath() + PMS_GET_ACCOUNTS; + appendPlexToken(); + LOG.debug("Get users from plex with url: " + m_url); + String response = pmsTemplate.getForObject(m_url, String.class); + PlexResponse plexResponse = OBJECT_MAPPER.readValue(response, PlexResponse.class); + plexResponse.getMediaContainer().getAccount().stream().filter(user -> !user.getName().equals("")).forEach(user -> m_users.put(user.getName(), user.getId())); + } - if (pmsConfig.getToken() != null && !pmsConfig.getToken().isEmpty()) { - watchHistoryUrl += "?X-Plex-Token=" + pmsConfig.getToken(); - LOG.info("Calling Plex with a X-Plex-Token..."); - } + private void processWatchedEpisodes() throws IOException { + pmsTemplate = new RestTemplate(); + m_url = pmsConfig.getPath() + PMS_WATCH_HISTORY; - ResponseEntity response = pmsTemplate.getForEntity(watchHistoryUrl, MediaContainer.class); - MediaContainer mediaContainer = response.getBody(); + appendPlexToken(); + String response = pmsTemplate.getForObject(m_url, String.class); - for (Video video : mediaContainer.getVideo()) { - LocalDateTime date = DateUtils.getDateTimeFromTimestamp(video.getViewedAt()); + PlexResponse plexResponse = OBJECT_MAPPER.readValue(response, PlexResponse.class); + LOG.debug(plexResponse.getMediaContainer().toString()); - LOG.trace(video.toString()); + for (MetaData metaData : plexResponse.getMediaContainer().getMetaData()) { + LocalDateTime date = DateUtils.getDateTimeFromTimestamp(metaData.getViewedAt()); + + LOG.info(metaData.toString()); + + // If present, mark as watched only episodes for configured user + if (pmsConfig.getUsername() != null) { + if (!metaData.getAccountID().equals(m_users.get(pmsConfig.getUsername()))) { + continue; + } + } // Mark as watched only today and yesterday episodes - if (DateUtils.isTodayOrYesterday(date) || pmsConfig.getMarkall() == true) { - if (video.getType().equals("episode")) { - String episode = new StringBuilder(video.getGrandparentTitle()) + assert date != null; + if (DateUtils.isTodayOrYesterday(date) || pmsConfig.getMarkall()) { + if (metaData.getType().equals("episode")) { + String episode = new StringBuilder(metaData.getGrandparentTitle()) .append(" - S") - .append(video.getParentIndex()) - .append("E").append(video.getIndex()) + .append(metaData.getParentIndex()) + .append("E").append(metaData.getIndex()) .toString(); markEpisodeAsWatched(episode); - } else if (video.getType().equals("movie")) { + } + else if (metaData.getType().equals("movie")) { //markEpisodeAsWatched(video.getTitle()); } } @@ -254,7 +270,8 @@ private void markEpisodeAsWatched(String episode) { if (message.getResult().equals("OK")) { LOG.info("Mark " + episode + " as watched in TVShowTime"); - } else { + } + else { LOG.error("Error while marking [" + episode + "] as watched in TVShowTime "); } @@ -264,11 +281,20 @@ private void markEpisodeAsWatched(String episode) { try { LOG.info("Consumed all available TVShowTime API calls slots, waiting for new slots ..."); Thread.sleep(MINUTE_IN_MILIS); - } catch (Exception e) { + } + catch (Exception e) { LOG.error(e.getMessage()); System.exit(1); } } } + + private void appendPlexToken() { + if (pmsConfig.getToken() != null && !pmsConfig.getToken().isEmpty()) { + m_url += "?X-Plex-Token=" + pmsConfig.getToken(); + LOG.info("Calling Plex with a X-Plex-Token = " + pmsConfig.getToken()); + } + } + } diff --git a/src/main/java/org/nuvola/tvshowtime/business/plex/Account.java b/src/main/java/org/nuvola/tvshowtime/business/plex/Account.java new file mode 100644 index 0000000..7df2cfa --- /dev/null +++ b/src/main/java/org/nuvola/tvshowtime/business/plex/Account.java @@ -0,0 +1,108 @@ +/* + * tvshowtimeplex + * Copyright (C) 2017 Appulize - Maciej Swic + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.nuvola.tvshowtime.business.plex; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "Account") +public class Account { + private Long id; + private String key; + private String name; + private String defaultAudioLanguage; + private Boolean autoSelectAudio; + private String defaultSubtitleLanguage; + private Integer subtitleMode; + private String thumb; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getDefaultAudioLanguage() { + return defaultAudioLanguage; + } + + public void setDefaultAudioLanguage(String defaultAudioLanguage) { + this.defaultAudioLanguage = defaultAudioLanguage; + } + + public Boolean getAutoSelectAudio() { + return autoSelectAudio; + } + + public void setAutoSelectAudio(Boolean autoSelectAudio) { + this.autoSelectAudio = autoSelectAudio; + } + + public String getDefaultSubtitleLanguage() { + return defaultSubtitleLanguage; + } + + public void setDefaultSubtitleLanguage(String defaultSubtitleLanguage) { + this.defaultSubtitleLanguage = defaultSubtitleLanguage; + } + + public Integer getSubtitleMode() { + return subtitleMode; + } + + public void setSubtitleMode(Integer subtitleMode) { + this.subtitleMode = subtitleMode; + } + + public String getThumb() { + return thumb; + } + + public void setThumb(String thumb) { + this.thumb = thumb; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @JsonProperty("title") + public String getTitle() { + return name; + } + + public void setTitle(String title) { + this.name = title; + } +} diff --git a/src/main/java/org/nuvola/tvshowtime/business/plex/MediaContainer.java b/src/main/java/org/nuvola/tvshowtime/business/plex/MediaContainer.java index 50ad752..1d2ede5 100644 --- a/src/main/java/org/nuvola/tvshowtime/business/plex/MediaContainer.java +++ b/src/main/java/org/nuvola/tvshowtime/business/plex/MediaContainer.java @@ -18,17 +18,22 @@ package org.nuvola.tvshowtime.business.plex; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; +import com.fasterxml.jackson.annotation.JsonProperty; + import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; import java.util.List; @XmlRootElement(name = "MediaContainer") public class MediaContainer { + @JsonProperty("size") private Integer size; - private List