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.

VlcMediaSourcePlugin.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. * Copyright (c) 2006-2013 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.addons.mediasource_vlc;
  23. import com.dmdirc.addons.nowplaying.MediaSource;
  24. import com.dmdirc.addons.nowplaying.MediaSourceState;
  25. import com.dmdirc.config.IdentityManager;
  26. import com.dmdirc.config.prefs.PluginPreferencesCategory;
  27. import com.dmdirc.config.prefs.PreferencesCategory;
  28. import com.dmdirc.config.prefs.PreferencesDialogModel;
  29. import com.dmdirc.config.prefs.PreferencesSetting;
  30. import com.dmdirc.config.prefs.PreferencesType;
  31. import com.dmdirc.plugins.BasePlugin;
  32. import com.dmdirc.plugins.PluginInfo;
  33. import com.dmdirc.util.io.Downloader;
  34. import java.io.File;
  35. import java.io.IOException;
  36. import java.net.MalformedURLException;
  37. import java.util.HashMap;
  38. import java.util.List;
  39. import java.util.Map;
  40. /**
  41. * Retrieves information from VLC using its HTTP interface.
  42. *
  43. * @author chris
  44. */
  45. public class VlcMediaSourcePlugin extends BasePlugin implements MediaSource {
  46. /** The information obtained from VLC. */
  47. private final Map<String, String> information
  48. = new HashMap<String, String>();
  49. /** This plugin's plugin info. */
  50. private final PluginInfo pluginInfo;
  51. /**
  52. * Creates a new instance of this plugin.
  53. *
  54. * @param pluginInfo This plugin's plugin info
  55. */
  56. public VlcMediaSourcePlugin(final PluginInfo pluginInfo) {
  57. super();
  58. this.pluginInfo = pluginInfo;
  59. }
  60. /** {@inheritDoc} */
  61. @Override
  62. public MediaSourceState getState() {
  63. if (fetchInformation()) {
  64. final String output = information.get("state");
  65. if (output.equalsIgnoreCase("stop")) {
  66. return MediaSourceState.STOPPED;
  67. } else if (output.equalsIgnoreCase("playing")) {
  68. return MediaSourceState.PLAYING;
  69. } else if (output.equalsIgnoreCase("paused")) {
  70. return MediaSourceState.PAUSED;
  71. } else {
  72. return MediaSourceState.NOTKNOWN;
  73. }
  74. } else {
  75. return MediaSourceState.CLOSED;
  76. }
  77. }
  78. /** {@inheritDoc} */
  79. @Override
  80. public String getAppName() {
  81. return "VLC";
  82. }
  83. /** {@inheritDoc} */
  84. @Override
  85. public String getArtist() {
  86. return information.containsKey("artist") ? information.get("artist") :
  87. getFallbackArtist();
  88. }
  89. /**
  90. * Retrieves the fallback artist (parsed from the file name).
  91. *
  92. * @return The fallback artist
  93. */
  94. private String getFallbackArtist() {
  95. String result = "unknown";
  96. if (information.containsKey("playlist_current")) {
  97. try {
  98. final int item = Integer.parseInt(information.get(
  99. "playlist_current"));
  100. String[] bits = information.get("playlist_item_" + item).split(
  101. (File.separatorChar == '\\' ? "\\\\" : File.separator));
  102. result = bits[bits.length - 1];
  103. bits = result.split("-");
  104. if (bits.length > 1) {
  105. result = bits[0];
  106. } else {
  107. // Whole filename is the title, so no artist is known.
  108. result = "unknown";
  109. }
  110. } catch (NumberFormatException nfe) {
  111. // DO nothing
  112. }
  113. }
  114. return result;
  115. }
  116. /** {@inheritDoc} */
  117. @Override
  118. public String getTitle() {
  119. return information.containsKey("title") ? information.get("title")
  120. : getFallbackTitle();
  121. }
  122. /**
  123. * Retrieves the fallback title (parsed from the file name).
  124. *
  125. * @return The fallback title
  126. */
  127. private String getFallbackTitle() {
  128. String result = "unknown";
  129. // Title is unknown, lets guess using the filename
  130. if (information.containsKey("playlist_current")) {
  131. try {
  132. final int item = Integer.parseInt(information.get(
  133. "playlist_current"));
  134. result = information.get("playlist_item_" + item);
  135. final int sepIndex = result.lastIndexOf(File.separatorChar);
  136. final int extIndex = result.lastIndexOf('.');
  137. result = result.substring(sepIndex,
  138. extIndex > sepIndex ? extIndex : result.length());
  139. final int offset = result.indexOf('-');
  140. if (offset > -1) {
  141. result = result.substring(offset + 1).trim();
  142. }
  143. } catch (NumberFormatException nfe) {
  144. // Do nothing
  145. }
  146. }
  147. return result;
  148. }
  149. /** {@inheritDoc} */
  150. @Override
  151. public String getAlbum() {
  152. return information.containsKey("album/movie/show title")
  153. ? information.get("album/movie/show title") : "unknown";
  154. }
  155. /** {@inheritDoc} */
  156. @Override
  157. public String getLength() {
  158. // This is just seconds, could do with formatting.
  159. return information.containsKey("length") ? information.get("length")
  160. : "unknown";
  161. }
  162. /** {@inheritDoc} */
  163. @Override
  164. public String getTime() {
  165. // This is just seconds, could do with formatting.
  166. return information.containsKey("time") ? information.get("time")
  167. : "unknown";
  168. }
  169. /** {@inheritDoc} */
  170. @Override
  171. public String getFormat() {
  172. return information.containsKey("codec") ? information.get("codec")
  173. : "unknown";
  174. }
  175. /** {@inheritDoc} */
  176. @Override
  177. public String getBitrate() {
  178. return information.containsKey("bitrate") ? information.get("bitrate")
  179. : "unknown";
  180. }
  181. /** {@inheritDoc} */
  182. @Override
  183. public void showConfig(final PreferencesDialogModel manager) {
  184. final PreferencesCategory general = new PluginPreferencesCategory(
  185. pluginInfo, "VLC Media Source",
  186. "", "category-vlc");
  187. final PreferencesSetting setting = new PreferencesSetting(
  188. PreferencesType.LABEL, getDomain(), "", "Instructions",
  189. "Instructions", manager.getConfigManager(), manager.getIdentity());
  190. setting.setValue("<html><p>"
  191. + "The VLC media source requires that VLC's web interface is"
  192. + " enabled. To do this, follow the steps below:</p>"
  193. + "<ol style='margin-left: 20px; padding-left: 0px;'>"
  194. + "<li>Open VLC's preferences dialog (found in the 'Tools' "
  195. + "menu)"
  196. + "<li>Set the 'Show settings' option to 'All'"
  197. + "<li>Expand the 'Interface' category by clicking on the plus "
  198. + "sign next to it"
  199. + "<li>Select the 'Main interfaces' category"
  200. + "<li>Check the box next to 'HTTP remote control interface'"
  201. + "<li>Expand the 'Main interfaces' category"
  202. + "<li>Select the 'HTTP' category"
  203. + "<li>In the 'Host address' field, enter 'localhost:8082'"
  204. + "<li>In the 'Source directory' field enter the path to VLC's"
  205. + " http directory<ul style='margin-left: 5px; padding-left: "
  206. + "0px; list-style-type: none;'>"
  207. + "<li style='padding-bottom: 5px'>For Linux users this may be "
  208. + "/usr/share/vlc/http/"
  209. + "<li>For Windows users this will be under the main VLC "
  210. + "directory, e.g. C:\\Program Files\\VLC\\http</ul><li>Click "
  211. + "'Save'<li>Restart VLC</ol></html>");
  212. general.addSetting(setting);
  213. general.addSetting(new PreferencesSetting(PreferencesType.TEXT,
  214. getDomain(), "host", "Hostname and port",
  215. "The host and port that VLC listens on for web connections",
  216. manager.getConfigManager(), manager.getIdentity()));
  217. manager.getCategory("Plugins").addSubCategory(general);
  218. }
  219. /**
  220. * Attempts to fetch information from VLC's web interface.
  221. *
  222. * @return True on success, false otherwise
  223. */
  224. private boolean fetchInformation() {
  225. information.clear();
  226. List<String> res;
  227. List<String> res2;
  228. try {
  229. res = Downloader.getPage("http://" + IdentityManager
  230. .getIdentityManager().getGlobalConfiguration().getOption(getDomain(),
  231. "host") + "/old/info.html");
  232. res2 = Downloader.getPage("http://" + IdentityManager
  233. .getIdentityManager().getGlobalConfiguration().getOption(getDomain(),
  234. "host") + "/old/");
  235. parseInformation(res, res2);
  236. return true;
  237. } catch (MalformedURLException ex) {
  238. return false;
  239. } catch (IOException ex) {
  240. return false;
  241. }
  242. }
  243. /**
  244. * Parses the information from the two pages obtained from VLC's web
  245. * interface.
  246. *
  247. * @param res The first page of VLC info (/old/info.html)
  248. * @param res2 The second page of VLC info (/old/)
  249. */
  250. protected void parseInformation(final List<String> res,
  251. final List<String> res2) {
  252. for (String line : res) {
  253. final String tline = line.trim();
  254. if (tline.startsWith("<li>")) {
  255. final int colon = tline.indexOf(':');
  256. final String key = tline.substring(5, colon).trim()
  257. .toLowerCase();
  258. final String value = tline.substring(colon + 1, tline.length()
  259. - 5).trim();
  260. information.put(key, value);
  261. }
  262. }
  263. boolean isPlaylist = false;
  264. boolean isCurrent = false;
  265. boolean isItem = false;
  266. int playlistItem = 0;
  267. for (String line : res2) {
  268. final String tline = line.trim();
  269. if (isPlaylist) {
  270. if (tline.startsWith("</ul>")) {
  271. isPlaylist = false;
  272. information.put("playlist_items", Integer.toString(
  273. playlistItem));
  274. } else if (tline.equalsIgnoreCase("<strong>")) {
  275. isCurrent = true;
  276. } else if (tline.equalsIgnoreCase("</strong>")) {
  277. isCurrent = false;
  278. } else if (tline.startsWith("<a href=\"?control=play&amp")) {
  279. isItem = true;
  280. } else if (isItem) {
  281. String itemname = tline;
  282. if (itemname.endsWith("</a>")) {
  283. itemname = itemname.substring(0, itemname.length() - 4);
  284. }
  285. if (!itemname.isEmpty()) {
  286. if (isCurrent) {
  287. information.put("playlist_current", Integer
  288. .toString(playlistItem));
  289. }
  290. information.put("playlist_item_" + Integer.toString(
  291. playlistItem++), itemname);
  292. }
  293. isItem = false;
  294. }
  295. } else if (tline.equalsIgnoreCase("<!-- Playlist -->")) {
  296. isPlaylist = true;
  297. } else if (tline.startsWith("State:")) {
  298. information.put("state", tline.substring(6, tline.indexOf('<'))
  299. .trim());
  300. } else if (tline.startsWith("got_")) {
  301. final int equals = tline.indexOf('=');
  302. information.put(tline.substring(4, equals).trim(),
  303. tline.substring(equals + 1, tline.length() - 1).trim());
  304. }
  305. }
  306. }
  307. }