From 1f9ea0f3a1f888a705ba8489542ec392507d09a0 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Wed, 21 Sep 2011 13:29:21 +0000 Subject: [PATCH] * added Google Analytics tracking for usage statistics (application startups, number of downloaded subtitles / episode lists) --- lib/jgat-custom.jar | Bin 0 -> 14580 bytes source/net/sourceforge/filebot/Analytics.java | 209 ++++++++++++++++++ source/net/sourceforge/filebot/Main.java | 10 +- source/net/sourceforge/filebot/Settings.java | 14 +- .../sourceforge/filebot/Settings.properties | 4 + .../sourceforge/filebot/cli/ArgumentBean.java | 7 +- .../filebot/cli/ArgumentProcessor.java | 14 +- .../net/sourceforge/filebot/ui/MainFrame.java | 2 + .../ui/panel/rename/EpisodeListMatcher.java | 3 + .../ui/panel/rename/MovieHashMatcher.java | 8 + .../filebot/ui/panel/rename/RenameAction.java | 10 + .../subtitle/SubtitleDownloadComponent.java | 5 +- .../ui/panel/subtitle/SubtitlePackage.java | 12 +- .../ui/panel/subtitle/SubtitlePanel.java | 2 +- .../VideoHashSubtitleDownloadDialog.java | 11 +- 15 files changed, 293 insertions(+), 18 deletions(-) create mode 100644 lib/jgat-custom.jar create mode 100644 source/net/sourceforge/filebot/Analytics.java diff --git a/lib/jgat-custom.jar b/lib/jgat-custom.jar new file mode 100644 index 0000000000000000000000000000000000000000..77b13610ee91bf3d8174e016c08ef77c59082b93 GIT binary patch literal 14580 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 g|kReG>NfnEbgd?@*>ImU-z15yFu}E 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 Q-ajSaKtOE26>S0iKW&e persistentData = Settings.forPackage(Analytics.class).node("analytics").asMap(); + private static final String VISITOR_ID = "visitorId"; + private static final String TIMESTAMP_FIRST = "timestampFirst"; + private static final String TIMESTAMP_LAST = "timestampLast"; + private static final String VISITS = "visits"; + + private static final VisitorData visitorData = restoreVisitorData(); + private static final JGoogleAnalyticsTracker tracker = new JGoogleAnalyticsTracker(getConfig(getApplicationProperty("analytics.WebPropertyID"), visitorData), V_4_7_2); + + + public static void trackView(Class view, String title) { + trackView(view.getName().replace(',', '/'), title); + } + + + public static synchronized void trackView(String view, String title) { + if (!tracker.isEnabled()) + return; + + tracker.trackPageViewFromSearch(view, title, "filebot.sourceforge.net", getJavaVersionIdentifier(), getDeploymentMethod()); + } + + + public static void trackEvent(String category, String action, String label) { + trackEvent(category, action, label, null); + } + + + public static synchronized void trackEvent(String category, String action, String label, Integer value) { + if (!tracker.isEnabled()) + return; + + tracker.trackEvent(category, action, label, value); + } + + + public static void setEnabled(boolean enabled) { + tracker.setEnabled(enabled); + } + + + private static String getDeploymentMethod() { + return getApplicationDeployment() == null ? "fatjar" : getApplicationDeployment(); + } + + + private static String getJavaVersionIdentifier() { + return System.getProperty("java.runtime.name") + " " + System.getProperty("java.version"); + } + + + private static AnalyticsConfigData getConfig(String webPropertyID, VisitorData visitorData) { + AnalyticsConfigData config = new AnalyticsConfigData(webPropertyID, visitorData); + + config.setUserAgent(getUserAgent()); + config.setEncoding(System.getProperty("file.encoding")); + config.setUserLanguage(getUserLanguage()); + + try { + GraphicsDevice[] display = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); + config.setScreenResolution(getScreenResolution(display)); + config.setColorDepth(getColorDepth(display)); + } catch (HeadlessException e) { + Logger.getLogger(Analytics.class.getName()).warning(e.getMessage()); + config.setScreenResolution("80x25"); + config.setColorDepth("1"); + } + + return config; + } + + + private static String getUserAgent() { + String wm = null; + String os = null; + + if (Platform.isWindows()) { + wm = "Windows"; + os = "Windows NT " + System.getProperty("os.version"); + } else if (Platform.isX11()) { + wm = "X11"; + if (Platform.isLinux()) + os = "Linux " + System.getProperty("os.arch"); + else if (Platform.isSolaris()) + os = "SunOS " + System.getProperty("os.version"); + else if (Platform.isFreeBSD()) + os = "FreeBSD"; + else if (Platform.isOpenBSD()) + os = "OpenBSD"; + } else if (Platform.isMac()) { + wm = "Macintosh"; + os = System.getProperty("os.name"); + } + + return String.format("%s/%s (%s; U; %s; JRE %s)", getApplicationName(), getApplicationVersion(), wm, os, System.getProperty("java.version")); + } + + + private static String getUserLanguage() { + String region = System.getProperty("user.region"); + if (region == null) + region = System.getProperty("user.country"); + + return System.getProperty("user.language") + "-" + region; + } + + + private static String getScreenResolution(GraphicsDevice[] display) { + int screenHeight = 0; + int screenWidth = 0; + + // get size of each screen + for (int i = 0; i < display.length; i++) { + DisplayMode dm = display[i].getDisplayMode(); + screenWidth += dm.getWidth(); + screenHeight += dm.getHeight(); + } + + if (screenHeight <= 0 && screenWidth <= 0) + throw new HeadlessException("Illegal screen size"); + + return screenWidth + "x" + screenHeight; + } + + + private static String getColorDepth(GraphicsDevice[] display) { + if (display[0] == null) + return null; + + String colorDepth = display[0].getDisplayMode().getBitDepth() + ""; + for (int i = 1; i < display.length; i++) { + colorDepth += ", " + display[i].getDisplayMode().getBitDepth(); + } + + return colorDepth; + } + + + private static VisitorData restoreVisitorData() { + try { + // try to restore visitor + int visitorId = Integer.parseInt(persistentData.get(VISITOR_ID)); + long timestampFirst = Long.parseLong(persistentData.get(TIMESTAMP_FIRST)); + long timestampLast = Long.parseLong(persistentData.get(TIMESTAMP_LAST)); + int visits = Integer.parseInt(persistentData.get(VISITS)); + + return VisitorData.newSession(visitorId, timestampFirst, timestampLast, visits); + } catch (Exception e) { + // new visitor + return VisitorData.newVisitor(); + } + } + + + private static void storeVisitorData(VisitorData visitor) { + persistentData.put(VISITOR_ID, String.valueOf(visitor.getVisitorId())); + persistentData.put(TIMESTAMP_FIRST, String.valueOf(visitor.getTimestampFirst())); + persistentData.put(TIMESTAMP_LAST, String.valueOf(visitor.getTimestampPrevious())); + persistentData.put(VISITS, String.valueOf(visitor.getVisits())); + } + + + public static void completeTracking(long timeout) { + storeVisitorData(visitorData); + JGoogleAnalyticsTracker.completeBackgroundTasks(timeout); + } + + + static { + Runtime.getRuntime().addShutdownHook(new Thread("AnalyticsShutdownHook") { + + @Override + public void run() { + completeTracking(2000); + } + }); + } + + + /** + * Dummy constructor to prevent instantiation. + */ + private Analytics() { + throw new UnsupportedOperationException(); + } + +} diff --git a/source/net/sourceforge/filebot/Main.java b/source/net/sourceforge/filebot/Main.java index fcfbc6ab..16624d1c 100644 --- a/source/net/sourceforge/filebot/Main.java +++ b/source/net/sourceforge/filebot/Main.java @@ -71,12 +71,16 @@ public class Main { CacheManager.getInstance().clearAll(); } + // initialize analytics + Analytics.setEnabled(!argumentBean.disableAnalytics); + + // run command-line interface and then exit if (argumentBean.runCLI()) { - // run cmdline interface and then exit - System.exit(new ArgumentProcessor().process(argumentBean)); + int status = new ArgumentProcessor().process(argumentBean); + System.exit(status); } - // Start user interface + // start user interface SwingUtilities.invokeLater(new Runnable() { @Override diff --git a/source/net/sourceforge/filebot/Settings.java b/source/net/sourceforge/filebot/Settings.java index 58a7a38b..415fec97 100644 --- a/source/net/sourceforge/filebot/Settings.java +++ b/source/net/sourceforge/filebot/Settings.java @@ -32,9 +32,21 @@ public final class Settings { } + public static String getApplicationDeployment() { + String deployment = System.getProperty("application.deployment"); + if (deployment != null) + return deployment; + + if (System.getProperty("javawebstart.version") != null) + return "webstart"; + + return null; + } + + public static File getApplicationFolder() { // special handling for web start - if (System.getProperty("application.deployment") != null || System.getProperty("javawebstart.version") != null) { + if (getApplicationDeployment() != null) { // can't use working directory for web start applications File folder = new File(System.getProperty("user.home"), ".filebot"); diff --git a/source/net/sourceforge/filebot/Settings.properties b/source/net/sourceforge/filebot/Settings.properties index 83f3044a..24d89631 100644 --- a/source/net/sourceforge/filebot/Settings.properties +++ b/source/net/sourceforge/filebot/Settings.properties @@ -2,6 +2,10 @@ application.name: FileBot application.version: 2.0 +# google analytics +analytics.WebPropertyID: UA-25379256-2 + +# database api keys thetvdb.apikey: 58B4AA94C59AD656 themoviedb.apikey: 5a6edae568130bf10617b6d45be99f13 sublight.apikey: afa9ecb2-a3ee-42b1-9225-000b4038bc85 diff --git a/source/net/sourceforge/filebot/cli/ArgumentBean.java b/source/net/sourceforge/filebot/cli/ArgumentBean.java index 419f2e0f..a14a7ad8 100644 --- a/source/net/sourceforge/filebot/cli/ArgumentBean.java +++ b/source/net/sourceforge/filebot/cli/ArgumentBean.java @@ -57,11 +57,14 @@ public class ArgumentBean { @Option(name = "--log", usage = "Log level", metaVar = "[all, config, info, warning]") public String log = "all"; + @Option(name = "-open", usage = "Open file in GUI", metaVar = "file") + public boolean open = false; + @Option(name = "-clear", usage = "Clear cache and application settings") public boolean clear = false; - @Option(name = "-open", usage = "Open file in GUI", metaVar = "file") - public boolean open = false; + @Option(name = "-no-analytics", usage = "Disable analytics") + public boolean disableAnalytics = false; @Option(name = "-help", usage = "Print this help message") public boolean help = false; diff --git a/source/net/sourceforge/filebot/cli/ArgumentProcessor.java b/source/net/sourceforge/filebot/cli/ArgumentProcessor.java index 20078920..78940d53 100644 --- a/source/net/sourceforge/filebot/cli/ArgumentProcessor.java +++ b/source/net/sourceforge/filebot/cli/ArgumentProcessor.java @@ -28,10 +28,11 @@ import java.util.TreeSet; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.Map.Entry; +import net.sourceforge.filebot.Analytics; import net.sourceforge.filebot.MediaTypes; import net.sourceforge.filebot.WebServices; -import net.sourceforge.filebot.format.MediaBindingBean; import net.sourceforge.filebot.format.ExpressionFormat; +import net.sourceforge.filebot.format.MediaBindingBean; import net.sourceforge.filebot.hash.HashType; import net.sourceforge.filebot.hash.VerificationFileReader; import net.sourceforge.filebot.hash.VerificationFileWriter; @@ -60,8 +61,10 @@ import net.sourceforge.filebot.web.VideoHashSubtitleService; public class ArgumentProcessor { public int process(ArgumentBean args) throws Exception { + Analytics.trackView(ArgumentProcessor.class, "FileBot CLI"); + CLILogger.setLevel(args.getLogLevel()); + try { - CLILogger.setLevel(args.getLogLevel()); Set files = new LinkedHashSet(args.getFiles(true)); if (args.getSubtitles) { @@ -185,6 +188,7 @@ public class ArgumentProcessor { } // rename episodes + Analytics.trackEvent("CLI", "Rename", "Episode", renameMap.size()); return renameAll(renameMap); } @@ -243,7 +247,8 @@ public class ArgumentProcessor { } } - // rename episodes + // rename movies + Analytics.trackEvent("CLI", "Rename", "Movie", renameMap.size()); return renameAll(renameMap); } @@ -279,6 +284,7 @@ public class ArgumentProcessor { if (it.getValue() != null && it.getValue().size() > 0) { // auto-select first element if there are multiple hash matches for the same video files File subtitle = fetchSubtitle(it.getValue().get(0), it.getKey(), outputFormat, outputEncoding); + Analytics.trackEvent(service.getName(), "DownloadSubtitle", it.getValue().get(0).getLanguageName(), 1); // download complete, cross this video off the list remainingVideos.remove(it.getKey()); @@ -305,6 +311,7 @@ public class ArgumentProcessor { for (SubtitleDescriptor descriptor : subtitles) { if (isDerived(descriptor.getName(), video)) { File subtitle = fetchSubtitle(descriptor, video, outputFormat, outputEncoding); + Analytics.trackEvent(service.getName(), "DownloadSubtitle", descriptor.getLanguageName(), 1); // download complete, cross this video off the list remainingVideos.remove(video); @@ -324,6 +331,7 @@ public class ArgumentProcessor { CLILogger.warning("No matching subtitles found: " + video); } + Analytics.trackEvent("CLI", "Download", "Subtitle", downloadedSubtitles.size()); return downloadedSubtitles; } diff --git a/source/net/sourceforge/filebot/ui/MainFrame.java b/source/net/sourceforge/filebot/ui/MainFrame.java index 8c786c91..c8c81f54 100644 --- a/source/net/sourceforge/filebot/ui/MainFrame.java +++ b/source/net/sourceforge/filebot/ui/MainFrame.java @@ -27,6 +27,7 @@ import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import net.miginfocom.swing.MigLayout; +import net.sourceforge.filebot.Analytics; import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.Settings; import net.sourceforge.filebot.ui.panel.analyze.AnalyzePanelBuilder; @@ -131,6 +132,7 @@ public class MainFrame extends JFrame { contentPane.add(panel); } + Analytics.trackView(panel.getClass(), selectedBuilder.getName()); headerPanel.setTitle(selectedBuilder.getName()); panel.setVisible(true); } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/EpisodeListMatcher.java b/source/net/sourceforge/filebot/ui/panel/rename/EpisodeListMatcher.java index 8c424340..0121e448 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/EpisodeListMatcher.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/EpisodeListMatcher.java @@ -29,6 +29,7 @@ import java.util.concurrent.RunnableFuture; import javax.swing.Action; import javax.swing.SwingUtilities; +import net.sourceforge.filebot.Analytics; import net.sourceforge.filebot.similarity.Match; import net.sourceforge.filebot.similarity.Matcher; import net.sourceforge.filebot.similarity.NameSimilarityMetric; @@ -122,6 +123,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher { SearchResult selectedSearchResult = selectSearchResult(query, results); if (selectedSearchResult != null) { + Analytics.trackEvent(provider.getName(), "FetchEpisodeList", selectedSearchResult.getName()); return provider.getEpisodeList(selectedSearchResult, locale); } } @@ -194,6 +196,7 @@ class EpisodeListMatcher implements AutoCompleteMatcher { } }); + Analytics.trackEvent(provider.getName(), "Match", "Episode", matches.size()); return matches; } } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java b/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java index d9bfd9d5..44eff64a 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/MovieHashMatcher.java @@ -30,6 +30,7 @@ import java.util.concurrent.RunnableFuture; import javax.swing.Action; import javax.swing.SwingUtilities; +import net.sourceforge.filebot.Analytics; import net.sourceforge.filebot.similarity.Match; import net.sourceforge.filebot.ui.SelectDialog; import net.sourceforge.filebot.web.MovieDescriptor; @@ -65,6 +66,10 @@ class MovieHashMatcher implements AutoCompleteMatcher { // unknown hash, try via imdb id from nfo file if (movie == null || !autodetect) { movie = grabMovieName(movieFiles[i], locale, autodetect, movie); + + if (movie != null) { + Analytics.trackEvent(service.getName(), "SearchMovie", movie.getName()); + } } // check if we managed to lookup the movie descriptor @@ -81,6 +86,8 @@ class MovieHashMatcher implements AutoCompleteMatcher { } } + Analytics.trackEvent(service.getName(), "HashLookup", "Movie", filesByMovie.size()); // number of positive hash lookups + // collect all File/MoviePart matches List> matches = new ArrayList>(); @@ -125,6 +132,7 @@ class MovieHashMatcher implements AutoCompleteMatcher { } }); + Analytics.trackEvent(service.getName(), "Match", "Movie", matches.size()); return matches; } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java b/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java index 5440c9a2..846da50d 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/RenameAction.java @@ -12,6 +12,7 @@ import java.awt.event.ActionEvent; import java.io.File; import java.util.AbstractList; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Map; @@ -19,6 +20,7 @@ import java.util.Map.Entry; import javax.swing.AbstractAction; +import net.sourceforge.filebot.Analytics; import net.sourceforge.filebot.ResourceManager; @@ -74,10 +76,14 @@ class RenameAction extends AbstractAction { } } + // collect renamed types + List types = new ArrayList(); + // remove renamed matches for (Entry entry : renameLog) { // find index of source file int index = model.files().indexOf(entry.getKey()); + types.add(model.values().get(index).getClass()); // remove complete match model.matches().remove(index); @@ -86,6 +92,10 @@ class RenameAction extends AbstractAction { // update history if (renameLog.size() > 0) { HistorySpooler.getInstance().append(renameLog); + + for (Class it : new HashSet(types)) { + Analytics.trackEvent("GUI", "Rename", it.getSimpleName(), frequency(types, it)); + } } } diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleDownloadComponent.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleDownloadComponent.java index 16503a5c..56abd6e5 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleDownloadComponent.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleDownloadComponent.java @@ -52,6 +52,7 @@ import ca.odell.glazedlists.swing.EventSelectionModel; import ca.odell.glazedlists.swing.TextComponentMatcherEditor; import net.miginfocom.swing.MigLayout; +import net.sourceforge.filebot.Analytics; import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.subtitle.SubtitleFormat; import net.sourceforge.filebot.ui.panel.subtitle.SubtitlePackage.Download.Phase; @@ -239,7 +240,9 @@ class SubtitleDownloadComponent extends JComponent { public void propertyChange(PropertyChangeEvent evt) { if (evt.getNewValue() == Phase.DONE) { try { - files.addAll(subtitle.getDownload().get()); + List subtitles = subtitle.getDownload().get(); + Analytics.trackEvent(subtitle.getProvider().getName(), "DownloadSubtitle", subtitle.getLanguage().getName(), subtitles.size()); + files.addAll(subtitles); } catch (CancellationException e) { // ignore cancellation } catch (Exception e) { diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java index ba55fa0e..ecc440ba 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePackage.java @@ -24,19 +24,20 @@ import net.sourceforge.filebot.ui.Language; import net.sourceforge.filebot.vfs.ArchiveType; import net.sourceforge.filebot.vfs.MemoryFile; import net.sourceforge.filebot.web.SubtitleDescriptor; +import net.sourceforge.filebot.web.SubtitleProvider; import net.sourceforge.tuned.FileUtilities; public class SubtitlePackage { + private final SubtitleProvider provider; private final SubtitleDescriptor subtitle; - private final Language language; - private Download download; - public SubtitlePackage(SubtitleDescriptor subtitle) { + public SubtitlePackage(SubtitleProvider provider, SubtitleDescriptor subtitle) { + this.provider = provider; this.subtitle = subtitle; // resolve language name @@ -58,6 +59,11 @@ public class SubtitlePackage { } + public SubtitleProvider getProvider() { + return provider; + } + + public String getName() { return subtitle.getName(); } diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java index ef24be6c..c7b0130b 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java @@ -214,7 +214,7 @@ public class SubtitlePanel extends AbstractSearchPanel packages = new ArrayList(); for (SubtitleDescriptor subtitle : request.getProvider().getSubtitleList(getSearchResult(), request.getLanguageName())) { - packages.add(new SubtitlePackage(subtitle)); + packages.add(new SubtitlePackage(request.getProvider(), subtitle)); } return packages; diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java b/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java index d8692f9f..3c4ba404 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java @@ -52,6 +52,7 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import net.miginfocom.swing.MigLayout; +import net.sourceforge.filebot.Analytics; import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.web.SubtitleDescriptor; import net.sourceforge.filebot.web.VideoHashSubtitleService; @@ -161,7 +162,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog { // query services sequentially queryService = Executors.newFixedThreadPool(1); - for (VideoHashSubtitleServiceBean service : services) { + for (final VideoHashSubtitleServiceBean service : services) { QueryTask task = new QueryTask(service, mappingModel.getVideoFiles(), languageName) { @Override @@ -179,6 +180,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog { } // make subtitle column visible + Analytics.trackEvent(service.getName(), "HashLookup", "Movie", subtitles.size()); // number of positive hash lookups mappingModel.setOptionColumnVisible(true); } catch (Exception e) { Logger.getLogger(VideoHashSubtitleDownloadDialog.class.getName()).log(Level.WARNING, e.getMessage()); @@ -584,7 +586,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog { private static class SubtitleDescriptorBean extends AbstractBean { private final SubtitleDescriptor subtitle; - private final VideoHashSubtitleServiceBean source; + private final VideoHashSubtitleServiceBean service; private StateValue state; private Exception error; @@ -592,7 +594,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog { public SubtitleDescriptorBean(SubtitleDescriptor subtitle, VideoHashSubtitleServiceBean source) { this.subtitle = subtitle; - this.source = source; + this.service = source; } @@ -602,7 +604,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog { public Icon getIcon() { - return source.getIcon(); + return service.getIcon(); } @@ -615,6 +617,7 @@ class VideoHashSubtitleDownloadDialog extends JDialog { setState(StateValue.STARTED); try { + Analytics.trackEvent(service.getName(), "DownloadSubtitle", subtitle.getLanguageName(), 1); return subtitle.fetch(); } catch (Exception e) { // remember exception