You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VlcManager.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. package com.dmdirc.addons.mediasource_vlc;
  2. import com.dmdirc.addons.nowplaying.MediaSource;
  3. import com.dmdirc.addons.nowplaying.MediaSourceState;
  4. import com.dmdirc.config.prefs.PluginPreferencesCategory;
  5. import com.dmdirc.config.prefs.PreferencesCategory;
  6. import com.dmdirc.config.prefs.PreferencesDialogModel;
  7. import com.dmdirc.config.prefs.PreferencesSetting;
  8. import com.dmdirc.config.prefs.PreferencesType;
  9. import com.dmdirc.events.ClientPrefsOpenedEvent;
  10. import com.dmdirc.interfaces.config.IdentityController;
  11. import com.dmdirc.plugins.PluginDomain;
  12. import com.dmdirc.plugins.PluginInfo;
  13. import com.dmdirc.util.io.Downloader;
  14. import java.io.File;
  15. import java.io.IOException;
  16. import java.util.HashMap;
  17. import java.util.List;
  18. import java.util.Map;
  19. import javax.inject.Inject;
  20. import net.engio.mbassy.listener.Handler;
  21. /**
  22. * Retrieves information from VLC using its HTTP interface.
  23. */
  24. public class VlcManager implements MediaSource {
  25. /** The information obtained from VLC. */
  26. private final Map<String, String> information = new HashMap<>();
  27. /** This plugin's plugin info. */
  28. private final PluginInfo pluginInfo;
  29. /** The identity controller to read settings from. */
  30. private final IdentityController identityController;
  31. /** Downloader to download files. */
  32. private final Downloader downloader;
  33. @Inject
  34. public VlcManager(
  35. @PluginDomain(VlcMediaSourcePlugin.class) final PluginInfo pluginInfo,
  36. final IdentityController identityController) {
  37. this.pluginInfo = pluginInfo;
  38. this.identityController = identityController;
  39. this.downloader = new Downloader();
  40. }
  41. @Override
  42. public MediaSourceState getState() {
  43. if (fetchInformation()) {
  44. final String output = information.get("state");
  45. if (output.equalsIgnoreCase("stop")) {
  46. return MediaSourceState.STOPPED;
  47. } else if (output.equalsIgnoreCase("playing")) {
  48. return MediaSourceState.PLAYING;
  49. } else if (output.equalsIgnoreCase("paused")) {
  50. return MediaSourceState.PAUSED;
  51. } else {
  52. return MediaSourceState.NOTKNOWN;
  53. }
  54. } else {
  55. return MediaSourceState.CLOSED;
  56. }
  57. }
  58. @Override
  59. public String getAppName() {
  60. return "VLC";
  61. }
  62. @Override
  63. public String getArtist() {
  64. return information.containsKey("artist") ? information.get("artist") : getFallbackArtist();
  65. }
  66. /**
  67. * Retrieves the fallback artist (parsed from the file name).
  68. *
  69. * @return The fallback artist
  70. */
  71. private String getFallbackArtist() {
  72. String result = "unknown";
  73. if (information.containsKey("playlist_current")) {
  74. try {
  75. final int item = Integer.parseInt(information.get(
  76. "playlist_current"));
  77. String[] bits = information.get("playlist_item_" + item).split(
  78. (File.separatorChar == '\\' ? "\\\\" : File.separator));
  79. result = bits[bits.length - 1];
  80. bits = result.split("-");
  81. if (bits.length > 1) {
  82. result = bits[0];
  83. } else {
  84. // Whole filename is the title, so no artist is known.
  85. result = "unknown";
  86. }
  87. } catch (NumberFormatException nfe) {
  88. // DO nothing
  89. }
  90. }
  91. return result;
  92. }
  93. @Override
  94. public String getTitle() {
  95. return information.containsKey("title") ? information.get("title")
  96. : getFallbackTitle();
  97. }
  98. /**
  99. * Retrieves the fallback title (parsed from the file name).
  100. *
  101. * @return The fallback title
  102. */
  103. private String getFallbackTitle() {
  104. String result = "unknown";
  105. // Title is unknown, lets guess using the filename
  106. if (information.containsKey("playlist_current")) {
  107. try {
  108. final int item = Integer.parseInt(information.get(
  109. "playlist_current"));
  110. result = information.get("playlist_item_" + item);
  111. final int sepIndex = result.lastIndexOf(File.separatorChar);
  112. final int extIndex = result.lastIndexOf('.');
  113. result = result.substring(sepIndex,
  114. extIndex > sepIndex ? extIndex : result.length());
  115. final int offset = result.indexOf('-');
  116. if (offset > -1) {
  117. result = result.substring(offset + 1).trim();
  118. }
  119. } catch (NumberFormatException nfe) {
  120. // Do nothing
  121. }
  122. }
  123. return result;
  124. }
  125. @Override
  126. public String getAlbum() {
  127. return information.containsKey("album/movie/show title")
  128. ? information.get("album/movie/show title") : "unknown";
  129. }
  130. @Override
  131. public String getLength() {
  132. // This is just seconds, could do with formatting.
  133. return information.containsKey("length") ? information.get("length")
  134. : "unknown";
  135. }
  136. @Override
  137. public String getTime() {
  138. // This is just seconds, could do with formatting.
  139. return information.containsKey("time") ? information.get("time")
  140. : "unknown";
  141. }
  142. @Override
  143. public String getFormat() {
  144. return information.containsKey("codec") ? information.get("codec")
  145. : "unknown";
  146. }
  147. @Override
  148. public String getBitrate() {
  149. return information.containsKey("bitrate") ? information.get("bitrate")
  150. : "unknown";
  151. }
  152. @Handler
  153. public void showConfig(final ClientPrefsOpenedEvent event) {
  154. final PreferencesDialogModel manager = event.getModel();
  155. final PreferencesCategory general = new PluginPreferencesCategory(
  156. pluginInfo, "VLC Media Source",
  157. "", "category-vlc");
  158. final PreferencesSetting setting = new PreferencesSetting(
  159. PreferencesType.LABEL, pluginInfo.getDomain(), "", "Instructions",
  160. "Instructions", manager.getConfigManager(), manager.getIdentity());
  161. setting.setValue("<html><p>"
  162. + "The VLC media source requires that VLC's web interface is"
  163. + " enabled. To do this, follow the steps below:</p>"
  164. + "<ol style='margin-left: 20px; padding-left: 0px;'>"
  165. + "<li>Open VLC's preferences dialog (found in the 'Tools' "
  166. + "menu)"
  167. + "<li>Set the 'Show settings' option to 'All'"
  168. + "<li>Expand the 'Interface' category by clicking on the plus "
  169. + "sign next to it"
  170. + "<li>Select the 'Main interfaces' category"
  171. + "<li>Check the box next to 'HTTP remote control interface'"
  172. + "<li>Expand the 'Main interfaces' category"
  173. + "<li>Select the 'HTTP' category"
  174. + "<li>In the 'Host address' field, enter 'localhost:8082'"
  175. + "<li>In the 'Source directory' field enter the path to VLC's"
  176. + " http directory<ul style='margin-left: 5px; padding-left: "
  177. + "0px; list-style-type: none;'>"
  178. + "<li style='padding-bottom: 5px'>For Linux users this may be "
  179. + "/usr/share/vlc/http/"
  180. + "<li>For Windows users this will be under the main VLC "
  181. + "directory, e.g. C:\\Program Files\\VLC\\http</ul><li>Click "
  182. + "'Save'<li>Restart VLC</ol></html>");
  183. general.addSetting(setting);
  184. general.addSetting(new PreferencesSetting(PreferencesType.TEXT,
  185. pluginInfo.getDomain(), "host", "Hostname and port",
  186. "The host and port that VLC listens on for web connections",
  187. manager.getConfigManager(), manager.getIdentity()));
  188. manager.getCategory("Plugins").addSubCategory(general);
  189. }
  190. /**
  191. * Attempts to fetch information from VLC's web interface.
  192. *
  193. * @return True on success, false otherwise
  194. */
  195. private boolean fetchInformation() {
  196. information.clear();
  197. final List<String> res;
  198. final List<String> res2;
  199. try {
  200. final String host = identityController.getGlobalConfiguration()
  201. .getOption(pluginInfo.getDomain(), "host");
  202. res = downloader.getPage("http://" + host + "/old/info.html");
  203. res2 = downloader.getPage("http://" + host + "/old/");
  204. parseInformation(res, res2);
  205. return true;
  206. } catch (IOException ex) {
  207. return false;
  208. }
  209. }
  210. /**
  211. * Parses the information from the two pages obtained from VLC's web interface.
  212. *
  213. * @param res The first page of VLC info (/old/info.html)
  214. * @param res2 The second page of VLC info (/old/)
  215. */
  216. protected void parseInformation(final List<String> res,
  217. final List<String> res2) {
  218. for (String line : res) {
  219. final String tline = line.trim();
  220. if (tline.startsWith("<li>")) {
  221. final int colon = tline.indexOf(':');
  222. final String key = tline.substring(5, colon).trim()
  223. .toLowerCase();
  224. final String value = tline.substring(colon + 1, tline.length()
  225. - 5).trim();
  226. information.put(key, value);
  227. }
  228. }
  229. boolean isPlaylist = false;
  230. boolean isCurrent = false;
  231. boolean isItem = false;
  232. int playlistItem = 0;
  233. for (String line : res2) {
  234. final String tline = line.trim();
  235. if (isPlaylist) {
  236. if (tline.startsWith("</ul>")) {
  237. isPlaylist = false;
  238. information.put("playlist_items", Integer.toString(
  239. playlistItem));
  240. } else if (tline.equalsIgnoreCase("<strong>")) {
  241. isCurrent = true;
  242. } else if (tline.equalsIgnoreCase("</strong>")) {
  243. isCurrent = false;
  244. } else if (tline.startsWith("<a href=\"?control=play&amp")) {
  245. isItem = true;
  246. } else if (isItem) {
  247. String itemname = tline;
  248. if (itemname.endsWith("</a>")) {
  249. itemname = itemname.substring(0, itemname.length() - 4);
  250. }
  251. if (!itemname.isEmpty()) {
  252. if (isCurrent) {
  253. information.put("playlist_current", Integer
  254. .toString(playlistItem));
  255. }
  256. information.put("playlist_item_" + Integer.toString(
  257. playlistItem++), itemname);
  258. }
  259. isItem = false;
  260. }
  261. } else if (tline.equalsIgnoreCase("<!-- Playlist -->")) {
  262. isPlaylist = true;
  263. } else if (tline.startsWith("State:")) {
  264. information.put("state", tline.substring(6, tline.indexOf('<'))
  265. .trim());
  266. } else if (tline.startsWith("got_")) {
  267. final int equals = tline.indexOf('=');
  268. information.put(tline.substring(4, equals).trim(),
  269. tline.substring(equals + 1, tline.length() - 1).trim());
  270. }
  271. }
  272. }
  273. }