Ignore chains and states default directories.
[rhynodge.git] / src / main / java / net / pterodactylus / reactor / triggers / NewEpisodeTrigger.java
1 /*
2  * Reactor - NewEpisodeTrigger.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.reactor.triggers;
19
20 import static com.google.common.base.Preconditions.checkState;
21
22 import java.util.Collection;
23
24 import net.pterodactylus.reactor.Reaction;
25 import net.pterodactylus.reactor.State;
26 import net.pterodactylus.reactor.Trigger;
27 import net.pterodactylus.reactor.output.DefaultOutput;
28 import net.pterodactylus.reactor.output.Output;
29 import net.pterodactylus.reactor.states.EpisodeState;
30 import net.pterodactylus.reactor.states.EpisodeState.Episode;
31 import net.pterodactylus.reactor.states.TorrentState.TorrentFile;
32
33 import org.apache.commons.lang3.StringEscapeUtils;
34
35 import com.google.common.base.Predicate;
36 import com.google.common.collect.Collections2;
37
38 /**
39  * {@link Trigger} implementation that compares two {@link EpisodeState}s for
40  * new and changed {@link Episode}s.
41  *
42  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
43  */
44 public class NewEpisodeTrigger implements Trigger {
45
46         /** All new episodes. */
47         private Collection<Episode> newEpisodes;
48
49         /** All changed episodes. */
50         private Collection<Episode> changedEpisodes;
51
52         //
53         // TRIGGER METHODS
54         //
55
56         /**
57          * {@inheritDoc}
58          */
59         @Override
60         public boolean triggers(State currentState, State previousState) {
61                 checkState(currentState instanceof EpisodeState, "currentState is not a EpisodeState but a %s", currentState.getClass().getName());
62                 checkState(previousState instanceof EpisodeState, "previousState is not a EpisodeState but a %s", currentState.getClass().getName());
63                 final EpisodeState currentEpisodeState = (EpisodeState) currentState;
64                 final EpisodeState previousEpisodeState = (EpisodeState) previousState;
65
66                 newEpisodes = Collections2.filter(currentEpisodeState.episodes(), new Predicate<Episode>() {
67
68                         @Override
69                         public boolean apply(Episode episode) {
70                                 return !previousEpisodeState.episodes().contains(episode);
71                         }
72                 });
73
74                 changedEpisodes = Collections2.filter(currentEpisodeState.episodes(), new Predicate<Episode>() {
75
76                         @Override
77                         public boolean apply(Episode episode) {
78                                 if (!previousEpisodeState.episodes().contains(episode)) {
79                                         return false;
80                                 }
81
82                                 /* find previous episode. */
83                                 final Episode previousEpisode = findPreviousEpisode(episode);
84
85                                 /* compare the list of torrent files. */
86                                 Collection<TorrentFile> newTorrentFiles = Collections2.filter(episode.torrentFiles(), new Predicate<TorrentFile>() {
87
88                                         @Override
89                                         public boolean apply(TorrentFile torrentFile) {
90                                                 return !previousEpisode.torrentFiles().contains(torrentFile);
91                                         }
92                                 });
93
94                                 return !newTorrentFiles.isEmpty();
95                         }
96
97                         private Episode findPreviousEpisode(Episode episode) {
98                                 for (Episode previousStateEpisode : previousEpisodeState) {
99                                         if (previousStateEpisode.equals(episode)) {
100                                                 return previousStateEpisode;
101                                         }
102                                 }
103                                 return null;
104                         }
105
106                 });
107
108                 return !newEpisodes.isEmpty() || !changedEpisodes.isEmpty();
109         }
110
111         /**
112          * {@inheritDoc}
113          */
114         @Override
115         public Output output(Reaction reaction) {
116                 String summary;
117                 if (!newEpisodes.isEmpty()) {
118                         if (!changedEpisodes.isEmpty()) {
119                                 summary = String.format("%d new and %d changed Torrent(s) for “%s!”", newEpisodes.size(), changedEpisodes.size(), reaction.name());
120                         } else {
121                                 summary = String.format("%d new Torrent(s) for “%s!”", newEpisodes.size(), reaction.name());
122                         }
123                 } else {
124                         summary = String.format("%d changed Torrent(s) for “%s!”", changedEpisodes.size(), reaction.name());
125                 }
126                 DefaultOutput output = new DefaultOutput(summary);
127                 output.addText("text/plain", generatePlainText(reaction, newEpisodes, changedEpisodes));
128                 output.addText("text/html", generateHtmlText(reaction, newEpisodes, changedEpisodes));
129                 return output;
130         }
131
132         //
133         // STATIC METHODS
134         //
135
136         /**
137          * Generates the plain text trigger output.
138          *
139          * @param reaction
140          *            The reaction that was triggered
141          * @param newEpisodes
142          *            The new episodes
143          * @param changedEpisodes
144          *            The changed episodes
145          * @return The plain text output
146          */
147         private static String generatePlainText(Reaction reaction, Collection<Episode> newEpisodes, Collection<Episode> changedEpisodes) {
148                 StringBuilder stringBuilder = new StringBuilder();
149                 if (!newEpisodes.isEmpty()) {
150                         stringBuilder.append(reaction.name()).append(" - New Episodes\n\n");
151                         for (Episode episode : newEpisodes) {
152                                 stringBuilder.append("- ").append(episode.identifier()).append("\n");
153                                 for (TorrentFile torrentFile : episode) {
154                                         stringBuilder.append("  - ").append(torrentFile.name()).append(", ").append(torrentFile.size()).append("\n");
155                                         stringBuilder.append("    Magnet: ").append(torrentFile.magnetUri()).append("\n");
156                                         stringBuilder.append("    Download: ").append(torrentFile.downloadUri()).append("\n");
157                                 }
158                         }
159                 }
160                 if (!changedEpisodes.isEmpty()) {
161                         stringBuilder.append(reaction.name()).append(" - Changed Episodes\n\n");
162                         for (Episode episode : changedEpisodes) {
163                                 stringBuilder.append("- ").append(episode.identifier()).append("\n");
164                                 for (TorrentFile torrentFile : episode) {
165                                         stringBuilder.append("  - ").append(torrentFile.name()).append(", ").append(torrentFile.size()).append("\n");
166                                         stringBuilder.append("    Magnet: ").append(torrentFile.magnetUri()).append("\n");
167                                         stringBuilder.append("    Download: ").append(torrentFile.downloadUri()).append("\n");
168                                 }
169                         }
170                 }
171                 return stringBuilder.toString();
172         }
173
174         /**
175          * Generates the HTML trigger output.
176          *
177          * @param reaction
178          *            The reaction that was triggered
179          * @param newEpisodes
180          *            The new episodes
181          * @param changedEpisodes
182          *            The changed episodes
183          * @return The HTML output
184          */
185         private static String generateHtmlText(Reaction reaction, Collection<Episode> newEpisodes, Collection<Episode> changedEpisodes) {
186                 StringBuilder htmlBuilder = new StringBuilder();
187                 htmlBuilder.append("<html><body>\n");
188                 htmlBuilder.append("<h1>").append(StringEscapeUtils.escapeHtml4(reaction.name())).append("</h1>\n");
189                 if (!newEpisodes.isEmpty()) {
190                         htmlBuilder.append("<h2>New Episodes</h2>\n");
191                         htmlBuilder.append("<ul>\n");
192                         for (Episode episode : newEpisodes) {
193                                 htmlBuilder.append("<li>Season ").append(episode.season()).append(", Episode ").append(episode.episode()).append("</li>\n");
194                                 htmlBuilder.append("<ul>\n");
195                                 for (TorrentFile torrentFile : episode) {
196                                         htmlBuilder.append("<li>").append(StringEscapeUtils.escapeHtml4(torrentFile.name())).append("</li>\n");
197                                         htmlBuilder.append("<div>");
198                                         htmlBuilder.append("<strong>").append(StringEscapeUtils.escapeHtml4(torrentFile.size())).append("</strong>, ");
199                                         htmlBuilder.append("<strong>").append(torrentFile.fileCount()).append("</strong> file(s), ");
200                                         htmlBuilder.append("<strong>").append(torrentFile.seedCount()).append("</strong> seed(s), ");
201                                         htmlBuilder.append("<strong>").append(torrentFile.leechCount()).append("</strong> leecher(s)</div>\n");
202                                         htmlBuilder.append("<div><a href=\"").append(StringEscapeUtils.escapeHtml4(torrentFile.magnetUri())).append("\">Magnet</a> ");
203                                         htmlBuilder.append("<a href=\"").append(StringEscapeUtils.escapeHtml4(torrentFile.downloadUri())).append("\">Download</a></div>\n");
204                                 }
205                                 htmlBuilder.append("</ul>\n");
206                         }
207                         htmlBuilder.append("</ul>\n");
208                 }
209                 if (!changedEpisodes.isEmpty()) {
210                         htmlBuilder.append("<h2>Changed Episodes</h2>\n");
211                         htmlBuilder.append("<ul>\n");
212                         for (Episode episode : changedEpisodes) {
213                                 htmlBuilder.append("<li>Season ").append(episode.season()).append(", Episode ").append(episode.episode()).append("</li>\n");
214                                 htmlBuilder.append("<ul>\n");
215                                 for (TorrentFile torrentFile : episode) {
216                                         htmlBuilder.append("<li>").append(StringEscapeUtils.escapeHtml4(torrentFile.name())).append("</li>\n");
217                                         htmlBuilder.append("<div>");
218                                         htmlBuilder.append("<strong>").append(StringEscapeUtils.escapeHtml4(torrentFile.size())).append("</strong>, ");
219                                         htmlBuilder.append("<strong>").append(torrentFile.fileCount()).append("</strong> file(s), ");
220                                         htmlBuilder.append("<strong>").append(torrentFile.seedCount()).append("</strong> seed(s), ");
221                                         htmlBuilder.append("<strong>").append(torrentFile.leechCount()).append("</strong> leecher(s)</div>\n");
222                                         htmlBuilder.append("<div><a href=\"").append(StringEscapeUtils.escapeHtml4(torrentFile.magnetUri())).append("\">Magnet</a> ");
223                                         htmlBuilder.append("<a href=\"").append(StringEscapeUtils.escapeHtml4(torrentFile.downloadUri())).append("\">Download</a></div>\n");
224                                 }
225                                 htmlBuilder.append("</ul>\n");
226                         }
227                         htmlBuilder.append("</ul>\n");
228                 }
229                 htmlBuilder.append("</body></html>\n");
230                 return htmlBuilder.toString();
231         }
232
233 }